Fork of daniellemaywood.uk/gleam — Wasm codegen work
106 kB
3433 lines
1mod constant;
2mod typed;
3mod untyped;
4
5#[cfg(test)]
6mod tests;
7pub mod visit;
8
9pub use self::typed::TypedExpr;
10pub use self::untyped::{FunctionLiteralKind, UntypedExpr};
11
12pub use self::constant::{Constant, TypedConstant, UntypedConstant};
13
14use crate::analyse::Inferred;
15use crate::bit_array;
16use crate::build::{ExpressionPosition, Located, Target, module_erlang_name};
17use crate::exhaustiveness::CompiledCase;
18use crate::parse::SpannedString;
19use crate::type_::error::VariableOrigin;
20use crate::type_::expression::{Implementations, Purity};
21use crate::type_::printer::Names;
22use crate::type_::{
23 self, Deprecation, HasType, ModuleValueConstructor, PatternConstructor, Type, TypedCallArg,
24 ValueConstructor, nil,
25};
26use std::collections::HashSet;
27use std::sync::Arc;
28
29use ecow::EcoString;
30use num_bigint::{BigInt, Sign};
31use num_traits::{One, ToPrimitive};
32#[cfg(test)]
33use pretty_assertions::assert_eq;
34use vec1::Vec1;
35
36pub const PIPE_VARIABLE: &str = "_pipe";
37pub const USE_ASSIGNMENT_VARIABLE: &str = "_use";
38pub const RECORD_UPDATE_VARIABLE: &str = "_record";
39pub const ASSERT_FAIL_VARIABLE: &str = "_assert_fail";
40pub const ASSERT_SUBJECT_VARIABLE: &str = "_assert_subject";
41pub const CAPTURE_VARIABLE: &str = "_capture";
42pub const BLOCK_VARIABLE: &str = "_block";
43
44pub trait HasLocation {
45 fn location(&self) -> SrcSpan;
46}
47
48pub type TypedModule = Module<type_::ModuleInterface, TypedDefinition>;
49
50pub type UntypedModule = Module<(), TargetedDefinition>;
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct Module<Info, Definitions> {
54 pub name: EcoString,
55 pub documentation: Vec<EcoString>,
56 pub type_info: Info,
57 pub definitions: Vec<Definitions>,
58 pub names: Names,
59 /// The source byte locations of definition that are unused.
60 /// This is used in code generation to know when definitions can be safely omitted.
61 pub unused_definition_positions: HashSet<u32>,
62}
63
64impl<Info, Definitions> Module<Info, Definitions> {
65 pub fn erlang_name(&self) -> EcoString {
66 module_erlang_name(&self.name)
67 }
68}
69
70impl TypedModule {
71 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
72 self.definitions
73 .iter()
74 .find_map(|definition| definition.find_node(byte_index))
75 }
76
77 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
78 self.definitions
79 .iter()
80 .find_map(|definition| definition.find_statement(byte_index))
81 }
82}
83
84/// The `@target(erlang)` and `@target(javascript)` attributes can be used to
85/// mark a definition as only being for a specific target.
86///
87/// ```gleam
88/// const x: Int = 1
89///
90/// @target(erlang)
91/// pub fn main(a) { ...}
92/// ```
93///
94#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct TargetedDefinition {
96 pub definition: UntypedDefinition,
97 pub target: Option<Target>,
98}
99
100impl TargetedDefinition {
101 pub fn is_for(&self, target: Target) -> bool {
102 self.target.map(|t| t == target).unwrap_or(true)
103 }
104}
105
106impl UntypedModule {
107 pub fn dependencies(&self, target: Target) -> Vec<(EcoString, SrcSpan)> {
108 self.iter_definitions(target)
109 .flat_map(|definition| match definition {
110 Definition::Import(Import {
111 module, location, ..
112 }) => Some((module.clone(), *location)),
113 _ => None,
114 })
115 .collect()
116 }
117
118 pub fn iter_definitions(&self, target: Target) -> impl Iterator<Item = &UntypedDefinition> {
119 self.definitions
120 .iter()
121 .filter(move |definition| definition.is_for(target))
122 .map(|definition| &definition.definition)
123 }
124
125 pub fn into_iter_definitions(self, target: Target) -> impl Iterator<Item = UntypedDefinition> {
126 self.definitions
127 .into_iter()
128 .filter(move |definition| definition.is_for(target))
129 .map(|definition| definition.definition)
130 }
131}
132
133#[test]
134fn module_dependencies_test() {
135 let parsed = crate::parse::parse_module(
136 camino::Utf8PathBuf::from("test/path"),
137 "import one
138 @target(erlang)
139 import two
140
141 @target(javascript)
142 import three
143
144 import four",
145 &crate::warning::WarningEmitter::null(),
146 )
147 .expect("syntax error");
148 let module = parsed.module;
149
150 assert_eq!(
151 vec![
152 ("one".into(), SrcSpan::new(0, 10)),
153 ("two".into(), SrcSpan::new(45, 55)),
154 ("four".into(), SrcSpan::new(118, 129)),
155 ],
156 module.dependencies(Target::Erlang)
157 );
158}
159
160pub type TypedArg = Arg<Arc<Type>>;
161pub type UntypedArg = Arg<()>;
162
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct Arg<T> {
165 pub names: ArgNames,
166 pub location: SrcSpan,
167 pub annotation: Option<TypeAst>,
168 pub type_: T,
169}
170
171impl<A> Arg<A> {
172 pub fn set_type<B>(self, t: B) -> Arg<B> {
173 Arg {
174 type_: t,
175 names: self.names,
176 location: self.location,
177 annotation: self.annotation,
178 }
179 }
180
181 pub fn get_variable_name(&self) -> Option<&EcoString> {
182 self.names.get_variable_name()
183 }
184
185 pub fn is_capture_hole(&self) -> bool {
186 match &self.names {
187 ArgNames::Named { name, .. } if name == CAPTURE_VARIABLE => true,
188 _ => false,
189 }
190 }
191}
192
193impl TypedArg {
194 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
195 if self.location.contains(byte_index) {
196 if let Some(annotation) = &self.annotation {
197 return annotation
198 .find_node(byte_index, self.type_.clone())
199 .or(Some(Located::Arg(self)));
200 }
201 Some(Located::Arg(self))
202 } else {
203 None
204 }
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub enum ArgNames {
210 Discard {
211 name: EcoString,
212 location: SrcSpan,
213 },
214 LabelledDiscard {
215 label: EcoString,
216 label_location: SrcSpan,
217 name: EcoString,
218 name_location: SrcSpan,
219 },
220 Named {
221 name: EcoString,
222 location: SrcSpan,
223 },
224 NamedLabelled {
225 label: EcoString,
226 label_location: SrcSpan,
227 name: EcoString,
228 name_location: SrcSpan,
229 },
230}
231
232impl ArgNames {
233 pub fn get_label(&self) -> Option<&EcoString> {
234 match self {
235 ArgNames::Discard { .. } | ArgNames::Named { .. } => None,
236 ArgNames::LabelledDiscard { label, .. } | ArgNames::NamedLabelled { label, .. } => {
237 Some(label)
238 }
239 }
240 }
241 pub fn get_variable_name(&self) -> Option<&EcoString> {
242 match self {
243 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => None,
244 ArgNames::NamedLabelled { name, .. } | ArgNames::Named { name, .. } => Some(name),
245 }
246 }
247}
248
249pub type TypedRecordConstructor = RecordConstructor<Arc<Type>>;
250
251#[derive(Debug, Clone, PartialEq, Eq)]
252pub struct RecordConstructor<T> {
253 pub location: SrcSpan,
254 pub name_location: SrcSpan,
255 pub name: EcoString,
256 pub arguments: Vec<RecordConstructorArg<T>>,
257 pub documentation: Option<(u32, EcoString)>,
258 pub deprecation: Deprecation,
259}
260
261impl<A> RecordConstructor<A> {
262 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
263 self.documentation = Some(new_doc);
264 }
265}
266
267pub type TypedRecordConstructorArg = RecordConstructorArg<Arc<Type>>;
268
269#[derive(Debug, Clone, PartialEq, Eq)]
270pub struct RecordConstructorArg<T> {
271 pub label: Option<SpannedString>,
272 pub ast: TypeAst,
273 pub location: SrcSpan,
274 pub type_: T,
275 pub doc: Option<(u32, EcoString)>,
276}
277
278impl<T: PartialEq> RecordConstructorArg<T> {
279 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
280 self.doc = Some(new_doc);
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
285pub struct TypeAstConstructor {
286 pub location: SrcSpan,
287 pub name_location: SrcSpan,
288 pub module: Option<(EcoString, SrcSpan)>,
289 pub name: EcoString,
290 pub arguments: Vec<TypeAst>,
291}
292
293#[derive(Debug, Clone, PartialEq, Eq)]
294pub struct TypeAstFn {
295 pub location: SrcSpan,
296 pub arguments: Vec<TypeAst>,
297 pub return_: Box<TypeAst>,
298}
299
300#[derive(Debug, Clone, PartialEq, Eq)]
301pub struct TypeAstVar {
302 pub location: SrcSpan,
303 pub name: EcoString,
304}
305
306#[derive(Debug, Clone, PartialEq, Eq)]
307pub struct TypeAstTuple {
308 pub location: SrcSpan,
309 pub elements: Vec<TypeAst>,
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
313pub struct TypeAstHole {
314 pub location: SrcSpan,
315 pub name: EcoString,
316}
317
318#[derive(Debug, Clone, PartialEq, Eq)]
319pub enum TypeAst {
320 Constructor(TypeAstConstructor),
321 Fn(TypeAstFn),
322 Var(TypeAstVar),
323 Tuple(TypeAstTuple),
324 Hole(TypeAstHole),
325}
326
327impl TypeAst {
328 pub fn location(&self) -> SrcSpan {
329 match self {
330 TypeAst::Fn(TypeAstFn { location, .. })
331 | TypeAst::Var(TypeAstVar { location, .. })
332 | TypeAst::Hole(TypeAstHole { location, .. })
333 | TypeAst::Tuple(TypeAstTuple { location, .. })
334 | TypeAst::Constructor(TypeAstConstructor { location, .. }) => *location,
335 }
336 }
337
338 pub fn is_logically_equal(&self, other: &TypeAst) -> bool {
339 match self {
340 TypeAst::Constructor(TypeAstConstructor {
341 module,
342 name,
343 arguments,
344 location: _,
345 name_location: _,
346 }) => match other {
347 TypeAst::Constructor(TypeAstConstructor {
348 module: o_module,
349 name: o_name,
350 arguments: o_arguments,
351 location: _,
352 name_location: _,
353 }) => {
354 let module_name =
355 |m: &Option<(EcoString, _)>| m.as_ref().map(|(m, _)| m.clone());
356 module_name(module) == module_name(o_module)
357 && name == o_name
358 && arguments.len() == o_arguments.len()
359 && arguments
360 .iter()
361 .zip(o_arguments)
362 .all(|a| a.0.is_logically_equal(a.1))
363 }
364 _ => false,
365 },
366 TypeAst::Fn(TypeAstFn {
367 arguments,
368 return_,
369 location: _,
370 }) => match other {
371 TypeAst::Fn(TypeAstFn {
372 arguments: o_arguments,
373 return_: o_return_,
374 location: _,
375 }) => {
376 arguments.len() == o_arguments.len()
377 && arguments
378 .iter()
379 .zip(o_arguments)
380 .all(|a| a.0.is_logically_equal(a.1))
381 && return_.is_logically_equal(o_return_)
382 }
383 _ => false,
384 },
385 TypeAst::Var(TypeAstVar { name, location: _ }) => match other {
386 TypeAst::Var(TypeAstVar {
387 name: o_name,
388 location: _,
389 }) => name == o_name,
390 _ => false,
391 },
392 TypeAst::Tuple(TypeAstTuple {
393 elements,
394 location: _,
395 }) => match other {
396 TypeAst::Tuple(TypeAstTuple {
397 elements: other_elements,
398 location: _,
399 }) => {
400 elements.len() == other_elements.len()
401 && elements
402 .iter()
403 .zip(other_elements)
404 .all(|a| a.0.is_logically_equal(a.1))
405 }
406 _ => false,
407 },
408 TypeAst::Hole(TypeAstHole { name, location: _ }) => match other {
409 TypeAst::Hole(TypeAstHole {
410 name: o_name,
411 location: _,
412 }) => name == o_name,
413 _ => false,
414 },
415 }
416 }
417
418 pub fn find_node(&self, byte_index: u32, type_: Arc<Type>) -> Option<Located<'_>> {
419 if !self.location().contains(byte_index) {
420 return None;
421 }
422
423 match self {
424 TypeAst::Fn(TypeAstFn {
425 arguments, return_, ..
426 }) => type_
427 .fn_types()
428 .and_then(|(arg_types, ret_type)| {
429 if let Some(arg) = arguments
430 .iter()
431 .zip(arg_types)
432 .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone()))
433 {
434 return Some(arg);
435 }
436 if let Some(ret) = return_.find_node(byte_index, ret_type) {
437 return Some(ret);
438 }
439
440 None
441 })
442 .or(Some(Located::Annotation { ast: self, type_ })),
443 TypeAst::Constructor(TypeAstConstructor {
444 arguments, module, ..
445 }) => type_
446 .constructor_types()
447 .and_then(|arg_types| {
448 if let Some(arg) = arguments
449 .iter()
450 .zip(arg_types)
451 .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone()))
452 {
453 return Some(arg);
454 }
455
456 None
457 })
458 .or(module.as_ref().and_then(|(name, location)| {
459 if location.contains(byte_index) {
460 Some(Located::ModuleName {
461 location: *location,
462 name,
463 layer: Layer::Type,
464 })
465 } else {
466 None
467 }
468 }))
469 .or(Some(Located::Annotation { ast: self, type_ })),
470 TypeAst::Tuple(TypeAstTuple { elements, .. }) => type_
471 .tuple_types()
472 .and_then(|elem_types| {
473 if let Some(e) = elements
474 .iter()
475 .zip(elem_types)
476 .find_map(|(e, e_type)| e.find_node(byte_index, e_type.clone()))
477 {
478 return Some(e);
479 }
480
481 None
482 })
483 .or(Some(Located::Annotation { ast: self, type_ })),
484 TypeAst::Var(_) | TypeAst::Hole(_) => Some(Located::Annotation { ast: self, type_ }),
485 }
486 }
487
488 /// Generates an annotation corresponding to the type.
489 pub fn print(&self, buffer: &mut EcoString) {
490 match &self {
491 TypeAst::Var(var) => buffer.push_str(&var.name),
492 TypeAst::Hole(hole) => buffer.push_str(&hole.name),
493 TypeAst::Tuple(tuple) => {
494 buffer.push_str("#(");
495 for (i, element) in tuple.elements.iter().enumerate() {
496 element.print(buffer);
497 if i < tuple.elements.len() - 1 {
498 buffer.push_str(", ");
499 }
500 }
501 buffer.push(')')
502 }
503 TypeAst::Fn(func) => {
504 buffer.push_str("fn(");
505 for (i, argument) in func.arguments.iter().enumerate() {
506 argument.print(buffer);
507 if i < func.arguments.len() - 1 {
508 buffer.push_str(", ");
509 }
510 }
511 buffer.push(')');
512 buffer.push_str(" -> ");
513 func.return_.print(buffer);
514 }
515 TypeAst::Constructor(constructor) => {
516 if let Some((module, _)) = &constructor.module {
517 buffer.push_str(module);
518 buffer.push('.');
519 }
520 buffer.push_str(&constructor.name);
521 if !constructor.arguments.is_empty() {
522 buffer.push('(');
523 for (i, argument) in constructor.arguments.iter().enumerate() {
524 argument.print(buffer);
525 if i < constructor.arguments.len() - 1 {
526 buffer.push_str(", ");
527 }
528 }
529 buffer.push(')');
530 }
531 }
532 }
533 }
534}
535
536#[test]
537fn type_ast_print_fn() {
538 let mut buffer = EcoString::new();
539 let ast = TypeAst::Fn(TypeAstFn {
540 location: SrcSpan { start: 1, end: 1 },
541 arguments: vec![
542 TypeAst::Var(TypeAstVar {
543 location: SrcSpan { start: 1, end: 1 },
544 name: "String".into(),
545 }),
546 TypeAst::Var(TypeAstVar {
547 location: SrcSpan { start: 1, end: 1 },
548 name: "Bool".into(),
549 }),
550 ],
551 return_: Box::new(TypeAst::Var(TypeAstVar {
552 location: SrcSpan { start: 1, end: 1 },
553 name: "Int".into(),
554 })),
555 });
556 ast.print(&mut buffer);
557 assert_eq!(&buffer, "fn(String, Bool) -> Int")
558}
559
560#[test]
561fn type_ast_print_constructor() {
562 let mut buffer = EcoString::new();
563 let ast = TypeAst::Constructor(TypeAstConstructor {
564 name: "SomeType".into(),
565 module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })),
566 location: SrcSpan { start: 1, end: 1 },
567 name_location: SrcSpan { start: 1, end: 1 },
568 arguments: vec![
569 TypeAst::Var(TypeAstVar {
570 location: SrcSpan { start: 1, end: 1 },
571 name: "String".into(),
572 }),
573 TypeAst::Var(TypeAstVar {
574 location: SrcSpan { start: 1, end: 1 },
575 name: "Bool".into(),
576 }),
577 ],
578 });
579 ast.print(&mut buffer);
580 assert_eq!(&buffer, "some_module.SomeType(String, Bool)")
581}
582
583#[test]
584fn type_ast_print_tuple() {
585 let mut buffer = EcoString::new();
586 let ast = TypeAst::Tuple(TypeAstTuple {
587 location: SrcSpan { start: 1, end: 1 },
588 elements: vec![
589 TypeAst::Constructor(TypeAstConstructor {
590 name: "SomeType".into(),
591 module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })),
592 location: SrcSpan { start: 1, end: 1 },
593 name_location: SrcSpan { start: 1, end: 1 },
594 arguments: vec![
595 TypeAst::Var(TypeAstVar {
596 location: SrcSpan { start: 1, end: 1 },
597 name: "String".into(),
598 }),
599 TypeAst::Var(TypeAstVar {
600 location: SrcSpan { start: 1, end: 1 },
601 name: "Bool".into(),
602 }),
603 ],
604 }),
605 TypeAst::Fn(TypeAstFn {
606 location: SrcSpan { start: 1, end: 1 },
607 arguments: vec![
608 TypeAst::Var(TypeAstVar {
609 location: SrcSpan { start: 1, end: 1 },
610 name: "String".into(),
611 }),
612 TypeAst::Var(TypeAstVar {
613 location: SrcSpan { start: 1, end: 1 },
614 name: "Bool".into(),
615 }),
616 ],
617 return_: Box::new(TypeAst::Var(TypeAstVar {
618 location: SrcSpan { start: 1, end: 1 },
619 name: "Int".into(),
620 })),
621 }),
622 ],
623 });
624 ast.print(&mut buffer);
625 assert_eq!(
626 &buffer,
627 "#(some_module.SomeType(String, Bool), fn(String, Bool) -> Int)"
628 )
629}
630
631#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
632pub enum Publicity {
633 Public,
634 Private,
635 Internal { attribute_location: Option<SrcSpan> },
636}
637
638impl Publicity {
639 pub fn is_private(&self) -> bool {
640 match self {
641 Self::Private => true,
642 Self::Public | Self::Internal { .. } => false,
643 }
644 }
645
646 pub fn is_internal(&self) -> bool {
647 match self {
648 Self::Internal { .. } => true,
649 Self::Public | Self::Private => false,
650 }
651 }
652
653 pub fn is_public(&self) -> bool {
654 match self {
655 Self::Public => true,
656 Self::Internal { .. } | Self::Private => false,
657 }
658 }
659
660 pub fn is_importable(&self) -> bool {
661 match self {
662 Self::Internal { .. } | Self::Public => true,
663 Self::Private => false,
664 }
665 }
666}
667
668#[derive(Debug, Clone, PartialEq, Eq)]
669/// A function definition
670///
671/// Note that an anonymous function will have `None` as the name field, while a
672/// named function will have `Some`.
673///
674/// # Example(s)
675///
676/// ```gleam
677/// // Public function
678/// pub fn wobble() -> String { ... }
679/// // Private function
680/// fn wibble(x: Int) -> Int { ... }
681/// // Anonymous function
682/// fn(x: Int) { ... }
683/// ```
684pub struct Function<T, Expr> {
685 pub location: SrcSpan,
686 pub end_position: u32,
687 pub name: Option<SpannedString>,
688 pub arguments: Vec<Arg<T>>,
689 pub body: Vec1<Statement<T, Expr>>,
690 pub publicity: Publicity,
691 pub deprecation: Deprecation,
692 pub return_annotation: Option<TypeAst>,
693 pub return_type: T,
694 pub documentation: Option<(u32, EcoString)>,
695 pub external_erlang: Option<(EcoString, EcoString, SrcSpan)>,
696 pub external_javascript: Option<(EcoString, EcoString, SrcSpan)>,
697 pub implementations: Implementations,
698 pub purity: Purity,
699}
700
701pub type TypedFunction = Function<Arc<Type>, TypedExpr>;
702pub type UntypedFunction = Function<(), UntypedExpr>;
703
704impl<T, E> Function<T, E> {
705 pub fn full_location(&self) -> SrcSpan {
706 SrcSpan::new(self.location.start, self.end_position)
707 }
708}
709
710pub type UntypedImport = Import<()>;
711
712#[derive(Debug, Clone, PartialEq, Eq)]
713/// Import another Gleam module so the current module can use the types and
714/// values it defines.
715///
716/// # Example(s)
717///
718/// ```gleam
719/// import unix/cat
720/// // Import with alias
721/// import animal/cat as kitty
722/// ```
723pub struct Import<PackageName> {
724 pub documentation: Option<EcoString>,
725 pub location: SrcSpan,
726 pub module: EcoString,
727 pub as_name: Option<(AssignName, SrcSpan)>,
728 pub unqualified_values: Vec<UnqualifiedImport>,
729 pub unqualified_types: Vec<UnqualifiedImport>,
730 pub package: PackageName,
731}
732
733impl<T> Import<T> {
734 pub(crate) fn used_name(&self) -> Option<EcoString> {
735 match self.as_name.as_ref() {
736 Some((AssignName::Variable(name), _)) => Some(name.clone()),
737 Some((AssignName::Discard(_), _)) => None,
738 None => self.module.split('/').next_back().map(EcoString::from),
739 }
740 }
741
742 pub(crate) fn alias_location(&self) -> Option<SrcSpan> {
743 self.as_name.as_ref().map(|(_, location)| *location)
744 }
745}
746
747pub type UntypedModuleConstant = ModuleConstant<(), ()>;
748pub type TypedModuleConstant = ModuleConstant<Arc<Type>, EcoString>;
749
750#[derive(Debug, Clone, PartialEq, Eq)]
751/// A certain fixed value that can be used in multiple places
752///
753/// # Example(s)
754///
755/// ```gleam
756/// pub const start_year = 2101
757/// pub const end_year = 2111
758/// ```
759pub struct ModuleConstant<T, ConstantRecordTag> {
760 pub documentation: Option<(u32, EcoString)>,
761 /// The location of the constant, starting at the "(pub) const" keywords and
762 /// ending after the ": Type" annotation, or (without an annotation) after its name.
763 pub location: SrcSpan,
764 pub publicity: Publicity,
765 pub name: EcoString,
766 pub name_location: SrcSpan,
767 pub annotation: Option<TypeAst>,
768 pub value: Box<Constant<T, ConstantRecordTag>>,
769 pub type_: T,
770 pub deprecation: Deprecation,
771 pub implementations: Implementations,
772}
773
774pub type UntypedCustomType = CustomType<()>;
775pub type TypedCustomType = CustomType<Arc<Type>>;
776
777#[derive(Debug, Clone, PartialEq, Eq)]
778/// A newly defined type with one or more constructors.
779/// Each variant of the custom type can contain different types, so the type is
780/// the product of the types contained by each variant.
781///
782/// This might be called an algebraic data type (ADT) or tagged union in other
783/// languages and type systems.
784///
785///
786/// # Example(s)
787///
788/// ```gleam
789/// pub type Cat {
790/// Cat(name: String, cuteness: Int)
791/// }
792/// ```
793pub struct CustomType<T> {
794 pub location: SrcSpan,
795 pub end_position: u32,
796 pub name: EcoString,
797 pub name_location: SrcSpan,
798 pub publicity: Publicity,
799 pub constructors: Vec<RecordConstructor<T>>,
800 pub documentation: Option<(u32, EcoString)>,
801 pub deprecation: Deprecation,
802 pub opaque: bool,
803 /// The names of the type parameters.
804 pub parameters: Vec<SpannedString>,
805 /// Once type checked this field will contain the type information for the
806 /// type parameters.
807 pub typed_parameters: Vec<T>,
808}
809
810impl<T> CustomType<T> {
811 /// The `location` field of a `CustomType` is only the location of `pub type
812 /// TheName`. This method returns a `SrcSpan` that includes the entire type
813 /// definition.
814 pub fn full_location(&self) -> SrcSpan {
815 SrcSpan::new(self.location.start, self.end_position)
816 }
817}
818
819pub type UntypedTypeAlias = TypeAlias<()>;
820
821#[derive(Debug, Clone, PartialEq, Eq)]
822/// A new name for an existing type
823///
824/// # Example(s)
825///
826/// ```gleam
827/// pub type Headers =
828/// List(#(String, String))
829/// ```
830pub struct TypeAlias<T> {
831 pub location: SrcSpan,
832 pub alias: EcoString,
833 pub name_location: SrcSpan,
834 pub parameters: Vec<SpannedString>,
835 pub type_ast: TypeAst,
836 pub type_: T,
837 pub publicity: Publicity,
838 pub documentation: Option<(u32, EcoString)>,
839 pub deprecation: Deprecation,
840}
841
842pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, EcoString, EcoString>;
843pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
844
845#[derive(Debug, Clone, PartialEq, Eq)]
846pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
847 Function(Function<T, Expr>),
848
849 TypeAlias(TypeAlias<T>),
850
851 CustomType(CustomType<T>),
852
853 Import(Import<PackageName>),
854
855 ModuleConstant(ModuleConstant<T, ConstantRecordTag>),
856}
857
858impl TypedDefinition {
859 pub fn main_function(&self) -> Option<&TypedFunction> {
860 match self {
861 Definition::Function(f) if f.name.as_ref().is_some_and(|(_, name)| name == "main") => {
862 Some(f)
863 }
864 _ => None,
865 }
866 }
867
868 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
869 match self {
870 Definition::Function(function) => {
871 // Search for the corresponding node inside the function
872 // only if the index falls within the function's full location.
873 if !function.full_location().contains(byte_index) {
874 return None;
875 }
876
877 if let Some(found) = function.body.iter().find_map(|s| s.find_node(byte_index)) {
878 return Some(found);
879 }
880
881 if let Some(found_arg) = function
882 .arguments
883 .iter()
884 .find_map(|arg| arg.find_node(byte_index))
885 {
886 return Some(found_arg);
887 };
888
889 if let Some(found_statement) = function
890 .body
891 .iter()
892 .find(|statement| statement.location().contains(byte_index))
893 {
894 return Some(Located::Statement(found_statement));
895 };
896
897 // Check if location is within the return annotation.
898 if let Some(l) = function
899 .return_annotation
900 .iter()
901 .find_map(|a| a.find_node(byte_index, function.return_type.clone()))
902 {
903 return Some(l);
904 };
905
906 // Note that the fn `.location` covers the function head, not
907 // the entire statement.
908 if function.location.contains(byte_index) {
909 Some(Located::ModuleStatement(self))
910 } else if function.full_location().contains(byte_index) {
911 Some(Located::FunctionBody(function))
912 } else {
913 None
914 }
915 }
916
917 Definition::CustomType(custom) => {
918 // Check if location is within the type of one of the arguments of a constructor.
919 if let Some(constructor) = custom
920 .constructors
921 .iter()
922 .find(|constructor| constructor.location.contains(byte_index))
923 {
924 if let Some(annotation) = constructor
925 .arguments
926 .iter()
927 .find(|arg| arg.location.contains(byte_index))
928 .and_then(|arg| arg.ast.find_node(byte_index, arg.type_.clone()))
929 {
930 return Some(annotation);
931 }
932
933 return Some(Located::VariantConstructorDefinition(constructor));
934 }
935
936 // Note that the custom type `.location` covers the function
937 // head, not the entire statement.
938 if custom.full_location().contains(byte_index) {
939 Some(Located::ModuleStatement(self))
940 } else {
941 None
942 }
943 }
944
945 Definition::TypeAlias(alias) => {
946 // Check if location is within the type being aliased.
947 if let Some(l) = alias.type_ast.find_node(byte_index, alias.type_.clone()) {
948 return Some(l);
949 }
950
951 if alias.location.contains(byte_index) {
952 Some(Located::ModuleStatement(self))
953 } else {
954 None
955 }
956 }
957
958 Definition::ModuleConstant(constant) => {
959 // Check if location is within the annotation.
960 if let Some(annotation) = &constant.annotation {
961 if let Some(l) = annotation.find_node(byte_index, constant.type_.clone()) {
962 return Some(l);
963 }
964 }
965
966 if let Some(located) = constant.value.find_node(byte_index) {
967 return Some(located);
968 }
969
970 if constant.location.contains(byte_index) {
971 Some(Located::ModuleStatement(self))
972 } else {
973 None
974 }
975 }
976
977 Definition::Import(import) => {
978 if self.location().contains(byte_index) {
979 if let Some(unqualified) = import
980 .unqualified_values
981 .iter()
982 .find(|i| i.location.contains(byte_index))
983 {
984 return Some(Located::UnqualifiedImport(
985 crate::build::UnqualifiedImport {
986 name: &unqualified.name,
987 module: &import.module,
988 is_type: false,
989 location: &unqualified.location,
990 },
991 ));
992 }
993
994 if let Some(unqualified) = import
995 .unqualified_types
996 .iter()
997 .find(|i| i.location.contains(byte_index))
998 {
999 return Some(Located::UnqualifiedImport(
1000 crate::build::UnqualifiedImport {
1001 name: &unqualified.name,
1002 module: &import.module,
1003 is_type: true,
1004 location: &unqualified.location,
1005 },
1006 ));
1007 }
1008
1009 Some(Located::ModuleStatement(self))
1010 } else {
1011 None
1012 }
1013 }
1014 }
1015 }
1016
1017 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
1018 match self {
1019 Definition::Function(function) => {
1020 if !function.full_location().contains(byte_index) {
1021 return None;
1022 }
1023
1024 function
1025 .body
1026 .iter()
1027 .find_map(|statement| statement.find_statement(byte_index))
1028 }
1029
1030 _ => None,
1031 }
1032 }
1033}
1034
1035impl<A, B, C, E> Definition<A, B, C, E> {
1036 pub fn location(&self) -> SrcSpan {
1037 match self {
1038 Definition::Function(Function { location, .. })
1039 | Definition::Import(Import { location, .. })
1040 | Definition::TypeAlias(TypeAlias { location, .. })
1041 | Definition::CustomType(CustomType { location, .. })
1042 | Definition::ModuleConstant(ModuleConstant { location, .. }) => *location,
1043 }
1044 }
1045
1046 /// Returns `true` if the definition is [`Import`].
1047 ///
1048 /// [`Import`]: Definition::Import
1049 #[must_use]
1050 pub fn is_import(&self) -> bool {
1051 matches!(self, Self::Import(..))
1052 }
1053
1054 /// Returns `true` if the module statement is [`Function`].
1055 ///
1056 /// [`Function`]: ModuleStatement::Function
1057 #[must_use]
1058 pub fn is_function(&self) -> bool {
1059 matches!(self, Self::Function(..))
1060 }
1061
1062 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
1063 match self {
1064 Definition::Import(Import { .. }) => (),
1065
1066 Definition::Function(Function { documentation, .. })
1067 | Definition::TypeAlias(TypeAlias { documentation, .. })
1068 | Definition::CustomType(CustomType { documentation, .. })
1069 | Definition::ModuleConstant(ModuleConstant { documentation, .. }) => {
1070 let _ = documentation.replace(new_doc);
1071 }
1072 }
1073 }
1074
1075 pub fn get_doc(&self) -> Option<EcoString> {
1076 match self {
1077 Definition::Import(Import { .. }) => None,
1078
1079 Definition::Function(Function {
1080 documentation: doc, ..
1081 })
1082 | Definition::TypeAlias(TypeAlias {
1083 documentation: doc, ..
1084 })
1085 | Definition::CustomType(CustomType {
1086 documentation: doc, ..
1087 })
1088 | Definition::ModuleConstant(ModuleConstant {
1089 documentation: doc, ..
1090 }) => doc.as_ref().map(|(_, doc)| doc.clone()),
1091 }
1092 }
1093
1094 pub fn is_internal(&self) -> bool {
1095 match self {
1096 Definition::Function(Function { publicity, .. })
1097 | Definition::CustomType(CustomType { publicity, .. })
1098 | Definition::ModuleConstant(ModuleConstant { publicity, .. })
1099 | Definition::TypeAlias(TypeAlias { publicity, .. }) => publicity.is_internal(),
1100
1101 Definition::Import(_) => false,
1102 }
1103 }
1104}
1105
1106#[derive(Debug, Clone, PartialEq, Eq)]
1107pub struct UnqualifiedImport {
1108 pub location: SrcSpan,
1109 /// The location excluding the potential `as ...` clause, or the `type` keyword
1110 pub imported_name_location: SrcSpan,
1111 pub name: EcoString,
1112 pub as_name: Option<EcoString>,
1113}
1114
1115impl UnqualifiedImport {
1116 pub fn used_name(&self) -> &EcoString {
1117 self.as_name.as_ref().unwrap_or(&self.name)
1118 }
1119}
1120
1121#[derive(Debug, Clone, PartialEq, Eq, Copy, Default, serde::Serialize, serde::Deserialize)]
1122pub enum Layer {
1123 #[default]
1124 Value,
1125 Type,
1126}
1127
1128impl Layer {
1129 /// Returns `true` if the layer is [`Value`].
1130 pub fn is_value(&self) -> bool {
1131 matches!(self, Self::Value)
1132 }
1133}
1134
1135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1136pub enum BinOp {
1137 // Boolean logic
1138 And,
1139 Or,
1140
1141 // Equality
1142 Eq,
1143 NotEq,
1144
1145 // Order comparison
1146 LtInt,
1147 LtEqInt,
1148 LtFloat,
1149 LtEqFloat,
1150 GtEqInt,
1151 GtInt,
1152 GtEqFloat,
1153 GtFloat,
1154
1155 // Maths
1156 AddInt,
1157 AddFloat,
1158 SubInt,
1159 SubFloat,
1160 MultInt,
1161 MultFloat,
1162 DivInt,
1163 DivFloat,
1164 RemainderInt,
1165
1166 // Strings
1167 Concatenate,
1168}
1169
1170#[derive(Clone, Copy, Debug, PartialEq)]
1171pub enum OperatorKind {
1172 BooleanLogic,
1173 Equality,
1174 IntComparison,
1175 FLoatComparison,
1176 IntMath,
1177 FloatMath,
1178 StringConcatenation,
1179}
1180
1181pub const PIPE_PRECEDENCE: u8 = 6;
1182
1183impl BinOp {
1184 pub fn precedence(&self) -> u8 {
1185 // Ensure that this matches the other precedence function for guards
1186 match self {
1187 Self::Or => 1,
1188
1189 Self::And => 2,
1190
1191 Self::Eq | Self::NotEq => 3,
1192
1193 Self::LtInt
1194 | Self::LtEqInt
1195 | Self::LtFloat
1196 | Self::LtEqFloat
1197 | Self::GtEqInt
1198 | Self::GtInt
1199 | Self::GtEqFloat
1200 | Self::GtFloat => 4,
1201
1202 Self::Concatenate => 5,
1203
1204 // Pipe is 6
1205 Self::AddInt | Self::AddFloat | Self::SubInt | Self::SubFloat => 7,
1206
1207 Self::MultInt
1208 | Self::MultFloat
1209 | Self::DivInt
1210 | Self::DivFloat
1211 | Self::RemainderInt => 8,
1212 }
1213 }
1214
1215 pub fn name(&self) -> &'static str {
1216 match self {
1217 Self::And => "&&",
1218 Self::Or => "||",
1219 Self::LtInt => "<",
1220 Self::LtEqInt => "<=",
1221 Self::LtFloat => "<.",
1222 Self::LtEqFloat => "<=.",
1223 Self::Eq => "==",
1224 Self::NotEq => "!=",
1225 Self::GtEqInt => ">=",
1226 Self::GtInt => ">",
1227 Self::GtEqFloat => ">=.",
1228 Self::GtFloat => ">.",
1229 Self::AddInt => "+",
1230 Self::AddFloat => "+.",
1231 Self::SubInt => "-",
1232 Self::SubFloat => "-.",
1233 Self::MultInt => "*",
1234 Self::MultFloat => "*.",
1235 Self::DivInt => "/",
1236 Self::DivFloat => "/.",
1237 Self::RemainderInt => "%",
1238 Self::Concatenate => "<>",
1239 }
1240 }
1241
1242 pub fn operator_kind(&self) -> OperatorKind {
1243 match self {
1244 Self::Concatenate => OperatorKind::StringConcatenation,
1245 Self::Eq | Self::NotEq => OperatorKind::Equality,
1246 Self::And | Self::Or => OperatorKind::BooleanLogic,
1247 Self::LtInt | Self::LtEqInt | Self::GtEqInt | Self::GtInt => {
1248 OperatorKind::IntComparison
1249 }
1250 Self::LtFloat | Self::LtEqFloat | Self::GtEqFloat | Self::GtFloat => {
1251 OperatorKind::FLoatComparison
1252 }
1253 Self::AddInt | Self::SubInt | Self::MultInt | Self::RemainderInt | Self::DivInt => {
1254 OperatorKind::IntMath
1255 }
1256 Self::AddFloat | Self::SubFloat | Self::MultFloat | Self::DivFloat => {
1257 OperatorKind::FloatMath
1258 }
1259 }
1260 }
1261
1262 pub fn can_be_grouped_with(&self, other: &BinOp) -> bool {
1263 self.operator_kind() == other.operator_kind()
1264 }
1265
1266 pub(crate) fn is_float_operator(&self) -> bool {
1267 match self {
1268 BinOp::LtFloat
1269 | BinOp::LtEqFloat
1270 | BinOp::GtEqFloat
1271 | BinOp::GtFloat
1272 | BinOp::AddFloat
1273 | BinOp::SubFloat
1274 | BinOp::MultFloat
1275 | BinOp::DivFloat => true,
1276
1277 BinOp::And
1278 | BinOp::Or
1279 | BinOp::Eq
1280 | BinOp::NotEq
1281 | BinOp::LtInt
1282 | BinOp::LtEqInt
1283 | BinOp::GtEqInt
1284 | BinOp::GtInt
1285 | BinOp::AddInt
1286 | BinOp::SubInt
1287 | BinOp::MultInt
1288 | BinOp::DivInt
1289 | BinOp::RemainderInt
1290 | BinOp::Concatenate => false,
1291 }
1292 }
1293
1294 fn is_bool_operator(&self) -> bool {
1295 match self {
1296 BinOp::And | BinOp::Or => true,
1297 _ => false,
1298 }
1299 }
1300
1301 pub(crate) fn is_int_operator(&self) -> bool {
1302 match self {
1303 BinOp::LtInt
1304 | BinOp::LtEqInt
1305 | BinOp::GtEqInt
1306 | BinOp::GtInt
1307 | BinOp::AddInt
1308 | BinOp::SubInt
1309 | BinOp::MultInt
1310 | BinOp::DivInt
1311 | BinOp::RemainderInt => true,
1312
1313 BinOp::And
1314 | BinOp::Or
1315 | BinOp::Eq
1316 | BinOp::NotEq
1317 | BinOp::LtFloat
1318 | BinOp::LtEqFloat
1319 | BinOp::GtEqFloat
1320 | BinOp::GtFloat
1321 | BinOp::AddFloat
1322 | BinOp::SubFloat
1323 | BinOp::MultFloat
1324 | BinOp::DivFloat
1325 | BinOp::Concatenate => false,
1326 }
1327 }
1328
1329 pub fn float_equivalent(&self) -> Option<BinOp> {
1330 match self {
1331 BinOp::LtInt => Some(BinOp::LtFloat),
1332 BinOp::LtEqInt => Some(BinOp::LtEqFloat),
1333 BinOp::GtEqInt => Some(BinOp::GtEqFloat),
1334 BinOp::GtInt => Some(BinOp::GtFloat),
1335 BinOp::AddInt => Some(BinOp::AddFloat),
1336 BinOp::SubInt => Some(BinOp::SubFloat),
1337 BinOp::MultInt => Some(BinOp::MultFloat),
1338 BinOp::DivInt => Some(BinOp::DivFloat),
1339 _ => None,
1340 }
1341 }
1342
1343 pub fn int_equivalent(&self) -> Option<BinOp> {
1344 match self {
1345 BinOp::LtFloat => Some(BinOp::LtInt),
1346 BinOp::LtEqFloat => Some(BinOp::LtEqInt),
1347 BinOp::GtEqFloat => Some(BinOp::GtEqInt),
1348 BinOp::GtFloat => Some(BinOp::GtInt),
1349 BinOp::AddFloat => Some(BinOp::AddInt),
1350 BinOp::SubFloat => Some(BinOp::SubInt),
1351 BinOp::MultFloat => Some(BinOp::MultInt),
1352 BinOp::DivFloat => Some(BinOp::DivInt),
1353 _ => None,
1354 }
1355 }
1356}
1357
1358#[derive(Debug, PartialEq, Eq, Clone)]
1359pub struct CallArg<A> {
1360 pub label: Option<EcoString>,
1361 pub location: SrcSpan,
1362 pub value: A,
1363 pub implicit: Option<ImplicitCallArgOrigin>,
1364}
1365
1366#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1367pub enum ImplicitCallArgOrigin {
1368 /// The implicit callback argument passed as the last argument to the
1369 /// function on the right hand side of `use`.
1370 ///
1371 Use,
1372 /// An argument added by the compiler when rewriting a pipe `left |> right`.
1373 ///
1374 Pipe,
1375 /// An argument added by the compiler to fill in all the missing fields of a
1376 /// record that are being ignored with the `..` syntax.
1377 ///
1378 PatternFieldSpread,
1379 /// An argument used to fill in the missing args when a function on the
1380 /// right hand side of `use` is being called with the wrong arity.
1381 ///
1382 IncorrectArityUse,
1383 /// An argument adde by the compiler to fill in the missing args when using
1384 /// the record update synax.
1385 ///
1386 RecordUpdate,
1387}
1388
1389impl<A> CallArg<A> {
1390 #[must_use]
1391 pub fn is_implicit(&self) -> bool {
1392 self.implicit.is_some()
1393 }
1394
1395 #[must_use]
1396 pub fn is_use_implicit_callback(&self) -> bool {
1397 match self.implicit {
1398 Some(ImplicitCallArgOrigin::Use | ImplicitCallArgOrigin::IncorrectArityUse) => true,
1399 Some(_) | None => false,
1400 }
1401 }
1402}
1403
1404impl CallArg<TypedExpr> {
1405 pub fn find_node<'a>(
1406 &'a self,
1407 byte_index: u32,
1408 called_function: &'a TypedExpr,
1409 function_arguments: &'a [TypedCallArg],
1410 ) -> Option<Located<'a>> {
1411 match (self.implicit, &self.value) {
1412 // If a call argument is the implicit use callback then we don't
1413 // want to look at its arguments and body but we don't want to
1414 // return the whole anonymous function if anything else doesn't
1415 // match.
1416 //
1417 // In addition, if the callback is invalid because it couldn't be
1418 // typed, we don't want to return it as it would make it hard for
1419 // the LSP to give any suggestions on the use function being typed.
1420 //
1421 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Invalid { .. }) => None,
1422 // So the code below is exactly the same as
1423 // `TypedExpr::Fn{}.find_node()` except we do not return self as a
1424 // fallback.
1425 //
1426 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Fn { args, body, .. }) => args
1427 .iter()
1428 .find_map(|arg| arg.find_node(byte_index))
1429 .or_else(|| body.iter().find_map(|s| s.find_node(byte_index))),
1430 // In all other cases we're happy with the default behaviour.
1431 //
1432 _ => match self.value.find_node(byte_index) {
1433 Some(Located::Expression { expression, .. })
1434 // This is only possibly a label if we are at the end of the expression
1435 // (so not in the middle like `[abc|]`) and if this argument doesn't
1436 // already have a label.
1437 if byte_index == self.value.location().end && self.label.is_none() =>
1438 {
1439 Some(Located::Expression {
1440 expression,
1441 position: ExpressionPosition::ArgumentOrLabel {
1442 called_function,
1443 function_arguments,
1444 },
1445 })
1446 }
1447 Some(located) => Some(located),
1448 None => {
1449 if self.location.contains(byte_index) && self.label.is_some() {
1450 Some(Located::Label(self.location, self.value.type_()))
1451 } else {
1452 None
1453 }
1454 }
1455 },
1456 }
1457 }
1458
1459 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
1460 match (self.implicit, &self.value) {
1461 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Invalid { .. }) => None,
1462 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Fn { body, .. }) => {
1463 body.iter().find_map(|s| s.find_statement(byte_index))
1464 }
1465
1466 _ => self.value.find_statement(byte_index),
1467 }
1468 }
1469
1470 pub fn is_capture_hole(&self) -> bool {
1471 match &self.value {
1472 TypedExpr::Var { name, .. } => name == CAPTURE_VARIABLE,
1473 _ => false,
1474 }
1475 }
1476}
1477
1478impl CallArg<TypedPattern> {
1479 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1480 match self.value.find_node(byte_index) {
1481 Some(located) => Some(located),
1482 _ => {
1483 if self.location.contains(byte_index) && self.label.is_some() {
1484 Some(Located::Label(self.location, self.value.type_()))
1485 } else {
1486 None
1487 }
1488 }
1489 }
1490 }
1491}
1492
1493impl CallArg<TypedConstant> {
1494 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1495 match self.value.find_node(byte_index) {
1496 Some(located) => Some(located),
1497 _ => {
1498 if self.location.contains(byte_index) && self.label.is_some() {
1499 Some(Located::Label(self.location, self.value.type_()))
1500 } else {
1501 None
1502 }
1503 }
1504 }
1505 }
1506}
1507
1508impl CallArg<UntypedExpr> {
1509 pub fn is_capture_hole(&self) -> bool {
1510 match &self.value {
1511 UntypedExpr::Var { name, .. } => name == CAPTURE_VARIABLE,
1512 _ => false,
1513 }
1514 }
1515}
1516
1517impl<T> CallArg<T>
1518where
1519 T: HasLocation,
1520{
1521 #[must_use]
1522 pub fn uses_label_shorthand(&self) -> bool {
1523 self.label.is_some() && self.location == self.value.location()
1524 }
1525}
1526
1527impl<T> HasLocation for CallArg<T> {
1528 fn location(&self) -> SrcSpan {
1529 self.location
1530 }
1531}
1532
1533#[derive(Debug, Clone, PartialEq, Eq)]
1534pub struct RecordBeingUpdated {
1535 pub base: Box<UntypedExpr>,
1536 pub location: SrcSpan,
1537}
1538
1539#[derive(Debug, Clone, PartialEq, Eq)]
1540pub struct UntypedRecordUpdateArg {
1541 pub label: EcoString,
1542 pub location: SrcSpan,
1543 pub value: UntypedExpr,
1544}
1545
1546impl UntypedRecordUpdateArg {
1547 #[must_use]
1548 pub fn uses_label_shorthand(&self) -> bool {
1549 self.value.location() == self.location
1550 }
1551}
1552
1553impl HasLocation for UntypedRecordUpdateArg {
1554 fn location(&self) -> SrcSpan {
1555 self.location
1556 }
1557}
1558
1559pub type MultiPattern<Type> = Vec<Pattern<Type>>;
1560
1561pub type UntypedMultiPattern = MultiPattern<()>;
1562pub type TypedMultiPattern = MultiPattern<Arc<Type>>;
1563
1564pub type TypedClause = Clause<TypedExpr, Arc<Type>, EcoString>;
1565
1566pub type UntypedClause = Clause<UntypedExpr, (), ()>;
1567
1568#[derive(Debug, Clone, PartialEq, Eq)]
1569pub struct Clause<Expr, Type, RecordTag> {
1570 pub location: SrcSpan,
1571 pub pattern: MultiPattern<Type>,
1572 pub alternative_patterns: Vec<MultiPattern<Type>>,
1573 pub guard: Option<ClauseGuard<Type, RecordTag>>,
1574 pub then: Expr,
1575}
1576
1577impl<A, B, C> Clause<A, B, C> {
1578 pub fn pattern_count(&self) -> usize {
1579 1 + self.alternative_patterns.len()
1580 }
1581}
1582
1583impl TypedClause {
1584 pub fn location(&self) -> SrcSpan {
1585 SrcSpan {
1586 start: self
1587 .pattern
1588 .first()
1589 .map(|p| p.location().start)
1590 .unwrap_or_default(),
1591 end: self.then.location().end,
1592 }
1593 }
1594
1595 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1596 self.pattern
1597 .iter()
1598 .find_map(|p| p.find_node(byte_index))
1599 .or_else(|| self.then.find_node(byte_index))
1600 }
1601
1602 pub fn pattern_location(&self) -> SrcSpan {
1603 let start = self.pattern.first().map(|pattern| pattern.location().start);
1604
1605 let end = if let Some(last_pattern) = self
1606 .alternative_patterns
1607 .last()
1608 .and_then(|patterns| patterns.last())
1609 {
1610 Some(last_pattern.location().end)
1611 } else {
1612 self.pattern.last().map(|pattern| pattern.location().end)
1613 };
1614
1615 SrcSpan::new(start.unwrap_or_default(), end.unwrap_or_default())
1616 }
1617}
1618
1619pub type UntypedClauseGuard = ClauseGuard<(), ()>;
1620pub type TypedClauseGuard = ClauseGuard<Arc<Type>, EcoString>;
1621
1622#[derive(Debug, Clone, PartialEq, Eq)]
1623pub enum ClauseGuard<Type, RecordTag> {
1624 Equals {
1625 location: SrcSpan,
1626 left: Box<Self>,
1627 right: Box<Self>,
1628 },
1629
1630 NotEquals {
1631 location: SrcSpan,
1632 left: Box<Self>,
1633 right: Box<Self>,
1634 },
1635
1636 GtInt {
1637 location: SrcSpan,
1638 left: Box<Self>,
1639 right: Box<Self>,
1640 },
1641
1642 GtEqInt {
1643 location: SrcSpan,
1644 left: Box<Self>,
1645 right: Box<Self>,
1646 },
1647
1648 LtInt {
1649 location: SrcSpan,
1650 left: Box<Self>,
1651 right: Box<Self>,
1652 },
1653
1654 LtEqInt {
1655 location: SrcSpan,
1656 left: Box<Self>,
1657 right: Box<Self>,
1658 },
1659
1660 GtFloat {
1661 location: SrcSpan,
1662 left: Box<Self>,
1663 right: Box<Self>,
1664 },
1665
1666 GtEqFloat {
1667 location: SrcSpan,
1668 left: Box<Self>,
1669 right: Box<Self>,
1670 },
1671
1672 LtFloat {
1673 location: SrcSpan,
1674 left: Box<Self>,
1675 right: Box<Self>,
1676 },
1677
1678 LtEqFloat {
1679 location: SrcSpan,
1680 left: Box<Self>,
1681 right: Box<Self>,
1682 },
1683
1684 AddInt {
1685 location: SrcSpan,
1686 left: Box<Self>,
1687 right: Box<Self>,
1688 },
1689
1690 AddFloat {
1691 location: SrcSpan,
1692 left: Box<Self>,
1693 right: Box<Self>,
1694 },
1695
1696 SubInt {
1697 location: SrcSpan,
1698 left: Box<Self>,
1699 right: Box<Self>,
1700 },
1701
1702 SubFloat {
1703 location: SrcSpan,
1704 left: Box<Self>,
1705 right: Box<Self>,
1706 },
1707
1708 MultInt {
1709 location: SrcSpan,
1710 left: Box<Self>,
1711 right: Box<Self>,
1712 },
1713
1714 MultFloat {
1715 location: SrcSpan,
1716 left: Box<Self>,
1717 right: Box<Self>,
1718 },
1719
1720 DivInt {
1721 location: SrcSpan,
1722 left: Box<Self>,
1723 right: Box<Self>,
1724 },
1725
1726 DivFloat {
1727 location: SrcSpan,
1728 left: Box<Self>,
1729 right: Box<Self>,
1730 },
1731
1732 RemainderInt {
1733 location: SrcSpan,
1734 left: Box<Self>,
1735 right: Box<Self>,
1736 },
1737
1738 Or {
1739 location: SrcSpan,
1740 left: Box<Self>,
1741 right: Box<Self>,
1742 },
1743
1744 And {
1745 location: SrcSpan,
1746 left: Box<Self>,
1747 right: Box<Self>,
1748 },
1749
1750 Not {
1751 location: SrcSpan,
1752 expression: Box<Self>,
1753 },
1754
1755 Var {
1756 location: SrcSpan,
1757 type_: Type,
1758 name: EcoString,
1759 definition_location: SrcSpan,
1760 },
1761
1762 TupleIndex {
1763 location: SrcSpan,
1764 index: u64,
1765 type_: Type,
1766 tuple: Box<Self>,
1767 },
1768
1769 FieldAccess {
1770 location: SrcSpan,
1771 index: Option<u64>,
1772 label: EcoString,
1773 type_: Type,
1774 container: Box<Self>,
1775 },
1776
1777 ModuleSelect {
1778 location: SrcSpan,
1779 type_: Type,
1780 label: EcoString,
1781 module_name: EcoString,
1782 module_alias: EcoString,
1783 literal: Constant<Type, RecordTag>,
1784 },
1785
1786 Constant(Constant<Type, RecordTag>),
1787}
1788
1789impl<A, B> ClauseGuard<A, B> {
1790 pub fn location(&self) -> SrcSpan {
1791 match self {
1792 ClauseGuard::Constant(constant) => constant.location(),
1793 ClauseGuard::Or { location, .. }
1794 | ClauseGuard::And { location, .. }
1795 | ClauseGuard::Not { location, .. }
1796 | ClauseGuard::Var { location, .. }
1797 | ClauseGuard::TupleIndex { location, .. }
1798 | ClauseGuard::Equals { location, .. }
1799 | ClauseGuard::NotEquals { location, .. }
1800 | ClauseGuard::GtInt { location, .. }
1801 | ClauseGuard::GtEqInt { location, .. }
1802 | ClauseGuard::LtInt { location, .. }
1803 | ClauseGuard::LtEqInt { location, .. }
1804 | ClauseGuard::GtFloat { location, .. }
1805 | ClauseGuard::GtEqFloat { location, .. }
1806 | ClauseGuard::LtFloat { location, .. }
1807 | ClauseGuard::AddInt { location, .. }
1808 | ClauseGuard::AddFloat { location, .. }
1809 | ClauseGuard::SubInt { location, .. }
1810 | ClauseGuard::SubFloat { location, .. }
1811 | ClauseGuard::MultInt { location, .. }
1812 | ClauseGuard::MultFloat { location, .. }
1813 | ClauseGuard::DivInt { location, .. }
1814 | ClauseGuard::DivFloat { location, .. }
1815 | ClauseGuard::RemainderInt { location, .. }
1816 | ClauseGuard::FieldAccess { location, .. }
1817 | ClauseGuard::LtEqFloat { location, .. }
1818 | ClauseGuard::ModuleSelect { location, .. } => *location,
1819 }
1820 }
1821
1822 pub fn precedence(&self) -> u8 {
1823 // Ensure that this matches the other precedence function for guards
1824 match self.bin_op_name() {
1825 Some(name) => name.precedence(),
1826 None => u8::MAX,
1827 }
1828 }
1829
1830 pub fn bin_op_name(&self) -> Option<BinOp> {
1831 match self {
1832 ClauseGuard::Or { .. } => Some(BinOp::Or),
1833 ClauseGuard::And { .. } => Some(BinOp::And),
1834 ClauseGuard::Equals { .. } => Some(BinOp::Eq),
1835 ClauseGuard::NotEquals { .. } => Some(BinOp::NotEq),
1836 ClauseGuard::GtInt { .. } => Some(BinOp::GtInt),
1837 ClauseGuard::GtEqInt { .. } => Some(BinOp::GtEqInt),
1838 ClauseGuard::LtInt { .. } => Some(BinOp::LtInt),
1839 ClauseGuard::LtEqInt { .. } => Some(BinOp::LtEqInt),
1840 ClauseGuard::GtFloat { .. } => Some(BinOp::GtFloat),
1841 ClauseGuard::GtEqFloat { .. } => Some(BinOp::GtEqFloat),
1842 ClauseGuard::LtFloat { .. } => Some(BinOp::LtFloat),
1843 ClauseGuard::LtEqFloat { .. } => Some(BinOp::LtEqFloat),
1844 ClauseGuard::AddInt { .. } => Some(BinOp::AddInt),
1845 ClauseGuard::AddFloat { .. } => Some(BinOp::AddFloat),
1846 ClauseGuard::SubInt { .. } => Some(BinOp::SubInt),
1847 ClauseGuard::SubFloat { .. } => Some(BinOp::SubFloat),
1848 ClauseGuard::MultInt { .. } => Some(BinOp::MultInt),
1849 ClauseGuard::MultFloat { .. } => Some(BinOp::MultFloat),
1850 ClauseGuard::DivInt { .. } => Some(BinOp::DivInt),
1851 ClauseGuard::DivFloat { .. } => Some(BinOp::DivFloat),
1852 ClauseGuard::RemainderInt { .. } => Some(BinOp::RemainderInt),
1853
1854 ClauseGuard::Constant(_)
1855 | ClauseGuard::Var { .. }
1856 | ClauseGuard::Not { .. }
1857 | ClauseGuard::TupleIndex { .. }
1858 | ClauseGuard::FieldAccess { .. }
1859 | ClauseGuard::ModuleSelect { .. } => None,
1860 }
1861 }
1862}
1863
1864impl TypedClauseGuard {
1865 pub fn type_(&self) -> Arc<Type> {
1866 match self {
1867 ClauseGuard::Var { type_, .. } => type_.clone(),
1868 ClauseGuard::TupleIndex { type_, .. } => type_.clone(),
1869 ClauseGuard::FieldAccess { type_, .. } => type_.clone(),
1870 ClauseGuard::ModuleSelect { type_, .. } => type_.clone(),
1871 ClauseGuard::Constant(constant) => constant.type_(),
1872
1873 ClauseGuard::AddInt { .. }
1874 | ClauseGuard::SubInt { .. }
1875 | ClauseGuard::MultInt { .. }
1876 | ClauseGuard::DivInt { .. }
1877 | ClauseGuard::RemainderInt { .. } => type_::int(),
1878
1879 ClauseGuard::AddFloat { .. }
1880 | ClauseGuard::SubFloat { .. }
1881 | ClauseGuard::MultFloat { .. }
1882 | ClauseGuard::DivFloat { .. } => type_::float(),
1883
1884 ClauseGuard::Or { .. }
1885 | ClauseGuard::Not { .. }
1886 | ClauseGuard::And { .. }
1887 | ClauseGuard::Equals { .. }
1888 | ClauseGuard::NotEquals { .. }
1889 | ClauseGuard::GtInt { .. }
1890 | ClauseGuard::GtEqInt { .. }
1891 | ClauseGuard::LtInt { .. }
1892 | ClauseGuard::LtEqInt { .. }
1893 | ClauseGuard::GtFloat { .. }
1894 | ClauseGuard::GtEqFloat { .. }
1895 | ClauseGuard::LtFloat { .. }
1896 | ClauseGuard::LtEqFloat { .. } => type_::bool(),
1897 }
1898 }
1899
1900 pub(crate) fn referenced_variables(&self) -> im::HashSet<&EcoString> {
1901 match self {
1902 ClauseGuard::Var { name, .. } => im::hashset![name],
1903
1904 ClauseGuard::Not { expression, .. } => expression.referenced_variables(),
1905 ClauseGuard::TupleIndex { tuple, .. } => tuple.referenced_variables(),
1906 ClauseGuard::FieldAccess { container, .. } => container.referenced_variables(),
1907 ClauseGuard::Constant(constant) => constant.referenced_variables(),
1908 ClauseGuard::ModuleSelect { .. } => im::HashSet::new(),
1909
1910 ClauseGuard::Equals { left, right, .. }
1911 | ClauseGuard::NotEquals { left, right, .. }
1912 | ClauseGuard::GtInt { left, right, .. }
1913 | ClauseGuard::GtEqInt { left, right, .. }
1914 | ClauseGuard::LtInt { left, right, .. }
1915 | ClauseGuard::LtEqInt { left, right, .. }
1916 | ClauseGuard::GtFloat { left, right, .. }
1917 | ClauseGuard::GtEqFloat { left, right, .. }
1918 | ClauseGuard::LtFloat { left, right, .. }
1919 | ClauseGuard::LtEqFloat { left, right, .. }
1920 | ClauseGuard::AddInt { left, right, .. }
1921 | ClauseGuard::AddFloat { left, right, .. }
1922 | ClauseGuard::SubInt { left, right, .. }
1923 | ClauseGuard::SubFloat { left, right, .. }
1924 | ClauseGuard::MultInt { left, right, .. }
1925 | ClauseGuard::MultFloat { left, right, .. }
1926 | ClauseGuard::DivInt { left, right, .. }
1927 | ClauseGuard::DivFloat { left, right, .. }
1928 | ClauseGuard::RemainderInt { left, right, .. }
1929 | ClauseGuard::And { left, right, .. }
1930 | ClauseGuard::Or { left, right, .. } => left
1931 .referenced_variables()
1932 .union(right.referenced_variables()),
1933 }
1934 }
1935}
1936
1937#[derive(
1938 Debug,
1939 PartialEq,
1940 Eq,
1941 PartialOrd,
1942 Ord,
1943 Default,
1944 Clone,
1945 Copy,
1946 serde::Serialize,
1947 serde::Deserialize,
1948)]
1949pub struct SrcSpan {
1950 pub start: u32,
1951 pub end: u32,
1952}
1953
1954impl SrcSpan {
1955 pub fn new(start: u32, end: u32) -> Self {
1956 Self { start, end }
1957 }
1958
1959 pub fn contains(&self, byte_index: u32) -> bool {
1960 byte_index >= self.start && byte_index <= self.end
1961 }
1962
1963 /// Merges two spans into a new one that starts at the start of the smaller
1964 /// one and ends at the end of the bigger one. For example:
1965 ///
1966 /// ```txt
1967 /// wibble wobble
1968 /// ─┬──── ─┬────
1969 /// │ ╰─ one span
1970 /// ╰─ the other span
1971 /// ─┬──────────────
1972 /// ╰─ the span you get by merging the two
1973 /// ```
1974 pub fn merge(&self, with: &SrcSpan) -> SrcSpan {
1975 Self {
1976 start: self.start.min(with.start),
1977 end: self.end.max(with.end),
1978 }
1979 }
1980}
1981
1982#[derive(Debug, PartialEq, Eq, Clone)]
1983pub struct DefinitionLocation {
1984 pub module: Option<EcoString>,
1985 pub span: SrcSpan,
1986}
1987
1988pub type UntypedPattern = Pattern<()>;
1989pub type TypedPattern = Pattern<Arc<Type>>;
1990
1991#[derive(Debug, Clone, PartialEq, Eq)]
1992pub enum Pattern<Type> {
1993 Int {
1994 location: SrcSpan,
1995 value: EcoString,
1996 int_value: BigInt,
1997 },
1998
1999 Float {
2000 location: SrcSpan,
2001 value: EcoString,
2002 },
2003
2004 String {
2005 location: SrcSpan,
2006 value: EcoString,
2007 },
2008
2009 /// The creation of a variable.
2010 /// e.g. `assert [this_is_a_var, .._] = x`
2011 Variable {
2012 location: SrcSpan,
2013 name: EcoString,
2014 type_: Type,
2015 origin: VariableOrigin,
2016 },
2017
2018 /// The specified size of a bit array. This can either be a literal integer,
2019 /// a reference to a variable, or a maths expression.
2020 /// e.g. `let assert <<y:size(somevar)>> = x`
2021 BitArraySize(BitArraySize<Type>),
2022
2023 /// A name given to a sub-pattern using the `as` keyword.
2024 /// e.g. `assert #(1, [_, _] as the_list) = x`
2025 Assign {
2026 name: EcoString,
2027 location: SrcSpan,
2028 pattern: Box<Self>,
2029 },
2030
2031 /// A pattern that binds to any value but does not assign a variable.
2032 /// Always starts with an underscore.
2033 Discard {
2034 name: EcoString,
2035 location: SrcSpan,
2036 type_: Type,
2037 },
2038
2039 List {
2040 location: SrcSpan,
2041 elements: Vec<Self>,
2042 tail: Option<Box<Self>>,
2043 type_: Type,
2044 },
2045
2046 /// The constructor for a custom type. Starts with an uppercase letter.
2047 Constructor {
2048 location: SrcSpan,
2049 name_location: SrcSpan,
2050 name: EcoString,
2051 arguments: Vec<CallArg<Self>>,
2052 module: Option<(EcoString, SrcSpan)>,
2053 constructor: Inferred<PatternConstructor>,
2054 spread: Option<SrcSpan>,
2055 type_: Type,
2056 },
2057
2058 Tuple {
2059 location: SrcSpan,
2060 elements: Vec<Self>,
2061 },
2062
2063 BitArray {
2064 location: SrcSpan,
2065 segments: Vec<BitArraySegment<Self, Type>>,
2066 },
2067
2068 // "prefix" <> variable
2069 StringPrefix {
2070 location: SrcSpan,
2071 left_location: SrcSpan,
2072 left_side_assignment: Option<(EcoString, SrcSpan)>,
2073 right_location: SrcSpan,
2074 left_side_string: EcoString,
2075 /// The variable on the right hand side of the `<>`.
2076 right_side_assignment: AssignName,
2077 },
2078
2079 /// A placeholder pattern used to allow module analysis to continue
2080 /// even when there are type errors. Should never end up in generated code.
2081 Invalid {
2082 location: SrcSpan,
2083 type_: Type,
2084 },
2085}
2086
2087pub type TypedBitArraySize = BitArraySize<Arc<Type>>;
2088
2089#[derive(Debug, Clone, PartialEq, Eq)]
2090pub enum BitArraySize<Type> {
2091 Int {
2092 location: SrcSpan,
2093 value: EcoString,
2094 int_value: BigInt,
2095 },
2096
2097 Variable {
2098 location: SrcSpan,
2099 name: EcoString,
2100 constructor: Option<ValueConstructor>,
2101 type_: Type,
2102 },
2103
2104 BinaryOperator {
2105 location: SrcSpan,
2106 operator: IntegerOperator,
2107 left: Box<Self>,
2108 right: Box<Self>,
2109 },
2110}
2111
2112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2113pub enum IntegerOperator {
2114 Add,
2115 Subtract,
2116 Multiply,
2117 Divide,
2118 Remainder,
2119}
2120
2121impl IntegerOperator {
2122 pub fn precedence(&self) -> u8 {
2123 match self {
2124 Self::Add | Self::Subtract => 7,
2125
2126 Self::Multiply | Self::Divide | Self::Remainder => 8,
2127 }
2128 }
2129}
2130
2131impl<T> BitArraySize<T> {
2132 pub fn location(&self) -> SrcSpan {
2133 match self {
2134 BitArraySize::Int { location, .. }
2135 | BitArraySize::Variable { location, .. }
2136 | BitArraySize::BinaryOperator { location, .. } => *location,
2137 }
2138 }
2139}
2140
2141impl Default for Inferred<()> {
2142 fn default() -> Self {
2143 Self::Unknown
2144 }
2145}
2146
2147#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2148pub enum AssignName {
2149 Variable(EcoString),
2150 Discard(EcoString),
2151}
2152
2153impl AssignName {
2154 pub fn name(&self) -> &EcoString {
2155 match self {
2156 AssignName::Variable(name) | AssignName::Discard(name) => name,
2157 }
2158 }
2159
2160 pub fn to_arg_names(self, location: SrcSpan) -> ArgNames {
2161 match self {
2162 AssignName::Variable(name) => ArgNames::Named { name, location },
2163 AssignName::Discard(name) => ArgNames::Discard { name, location },
2164 }
2165 }
2166
2167 pub fn assigned_name(&self) -> Option<&str> {
2168 match self {
2169 AssignName::Variable(name) => Some(name),
2170 AssignName::Discard(_) => None,
2171 }
2172 }
2173}
2174
2175impl<A> Pattern<A> {
2176 pub fn location(&self) -> SrcSpan {
2177 match self {
2178 Pattern::Assign {
2179 pattern, location, ..
2180 } => SrcSpan::new(pattern.location().start, location.end),
2181 Pattern::Int { location, .. }
2182 | Pattern::Variable { location, .. }
2183 | Pattern::List { location, .. }
2184 | Pattern::Float { location, .. }
2185 | Pattern::Discard { location, .. }
2186 | Pattern::String { location, .. }
2187 | Pattern::Tuple { location, .. }
2188 | Pattern::Constructor { location, .. }
2189 | Pattern::StringPrefix { location, .. }
2190 | Pattern::BitArray { location, .. }
2191 | Pattern::Invalid { location, .. } => *location,
2192 Pattern::BitArraySize(size) => size.location(),
2193 }
2194 }
2195
2196 /// Returns `true` if the pattern is [`Discard`].
2197 ///
2198 /// [`Discard`]: Pattern::Discard
2199 #[must_use]
2200 pub fn is_discard(&self) -> bool {
2201 matches!(self, Self::Discard { .. })
2202 }
2203
2204 #[must_use]
2205 pub fn is_variable(&self) -> bool {
2206 match self {
2207 Pattern::Variable { .. } => true,
2208 _ => false,
2209 }
2210 }
2211
2212 #[must_use]
2213 pub fn is_string(&self) -> bool {
2214 matches!(self, Self::String { .. })
2215 }
2216}
2217
2218impl TypedPattern {
2219 pub fn definition_location(&self) -> Option<DefinitionLocation> {
2220 match self {
2221 Pattern::Int { .. }
2222 | Pattern::Float { .. }
2223 | Pattern::String { .. }
2224 | Pattern::Variable { .. }
2225 | Pattern::BitArraySize { .. }
2226 | Pattern::Assign { .. }
2227 | Pattern::Discard { .. }
2228 | Pattern::List { .. }
2229 | Pattern::Tuple { .. }
2230 | Pattern::BitArray { .. }
2231 | Pattern::StringPrefix { .. }
2232 | Pattern::Invalid { .. } => None,
2233
2234 Pattern::Constructor { constructor, .. } => constructor.definition_location(),
2235 }
2236 }
2237
2238 pub fn get_documentation(&self) -> Option<&str> {
2239 match self {
2240 Pattern::Int { .. }
2241 | Pattern::Float { .. }
2242 | Pattern::String { .. }
2243 | Pattern::Variable { .. }
2244 | Pattern::BitArraySize { .. }
2245 | Pattern::Assign { .. }
2246 | Pattern::Discard { .. }
2247 | Pattern::List { .. }
2248 | Pattern::Tuple { .. }
2249 | Pattern::BitArray { .. }
2250 | Pattern::StringPrefix { .. }
2251 | Pattern::Invalid { .. } => None,
2252
2253 Pattern::Constructor { constructor, .. } => constructor.get_documentation(),
2254 }
2255 }
2256
2257 pub fn type_(&self) -> Arc<Type> {
2258 match self {
2259 Pattern::Int { .. } => type_::int(),
2260 Pattern::Float { .. } => type_::float(),
2261 Pattern::String { .. } => type_::string(),
2262 Pattern::BitArray { .. } => type_::bit_array(),
2263 Pattern::StringPrefix { .. } => type_::string(),
2264
2265 Pattern::Variable { type_, .. }
2266 | Pattern::List { type_, .. }
2267 | Pattern::Constructor { type_, .. }
2268 | Pattern::Invalid { type_, .. } => type_.clone(),
2269
2270 Pattern::Assign { pattern, .. } => pattern.type_(),
2271
2272 // Bit array sizes should always be integers
2273 Pattern::BitArraySize(_) => type_::int(),
2274
2275 Pattern::Discard { type_, .. } => type_.clone(),
2276
2277 Pattern::Tuple { elements, .. } => {
2278 type_::tuple(elements.iter().map(|p| p.type_()).collect())
2279 }
2280 }
2281 }
2282
2283 fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2284 if !self.location().contains(byte_index) {
2285 return None;
2286 }
2287
2288 if let Pattern::Variable { name, .. } = self {
2289 // For pipes the pattern can't be pointed to
2290 if name.as_str().eq(PIPE_VARIABLE) {
2291 return None;
2292 }
2293 }
2294
2295 match self {
2296 Pattern::Int { .. }
2297 | Pattern::Float { .. }
2298 | Pattern::String { .. }
2299 | Pattern::Variable { .. }
2300 | Pattern::BitArraySize { .. }
2301 | Pattern::Assign { .. }
2302 | Pattern::Discard { .. }
2303 | Pattern::StringPrefix { .. }
2304 | Pattern::Invalid { .. } => Some(Located::Pattern(self)),
2305
2306 Pattern::Constructor {
2307 arguments, spread, ..
2308 } => match spread {
2309 Some(spread_location) if spread_location.contains(byte_index) => {
2310 Some(Located::PatternSpread {
2311 spread_location: *spread_location,
2312 pattern: self,
2313 })
2314 }
2315
2316 Some(_) | None => arguments.iter().find_map(|arg| arg.find_node(byte_index)),
2317 },
2318 Pattern::List { elements, tail, .. } => elements
2319 .iter()
2320 .find_map(|p| p.find_node(byte_index))
2321 .or_else(|| tail.as_ref().and_then(|p| p.find_node(byte_index))),
2322
2323 Pattern::Tuple { elements, .. } => {
2324 elements.iter().find_map(|p| p.find_node(byte_index))
2325 }
2326
2327 Pattern::BitArray { segments, .. } => segments
2328 .iter()
2329 .find_map(|segment| segment.find_node(byte_index))
2330 .or(Some(Located::Pattern(self))),
2331 }
2332 .or(Some(Located::Pattern(self)))
2333 }
2334
2335 /// If the pattern is a `Constructor` with a spread, it returns a tuple with
2336 /// all the ignored fields. Split in unlabelled and labelled ones.
2337 ///
2338 pub(crate) fn unused_arguments(&self) -> Option<PatternUnusedArguments> {
2339 let TypedPattern::Constructor {
2340 arguments,
2341 spread: Some(_),
2342 ..
2343 } = self
2344 else {
2345 return None;
2346 };
2347
2348 let mut positional = vec![];
2349 let mut labelled = vec![];
2350 for argument in arguments {
2351 // We only want to display the arguments that were ignored using `..`.
2352 // Any argument ignored that way is marked as implicit, so if it is
2353 // not implicit we just ignore it.
2354 if !argument.is_implicit() {
2355 continue;
2356 }
2357 let type_ = argument.value.type_();
2358 match &argument.label {
2359 Some(label) => labelled.push((label.clone(), type_)),
2360 None => positional.push(type_),
2361 }
2362 }
2363
2364 Some(PatternUnusedArguments {
2365 positional,
2366 labelled,
2367 })
2368 }
2369
2370 /// Whether the pattern always matches. For example, a tuple or simple
2371 /// variable assignment always match and can never fail.
2372 #[must_use]
2373 pub fn always_matches(&self) -> bool {
2374 match self {
2375 Pattern::Variable { .. } | Pattern::Discard { .. } => true,
2376 Pattern::Assign { pattern, .. } => pattern.always_matches(),
2377 Pattern::Tuple { elements, .. } => {
2378 elements.iter().all(|element| element.always_matches())
2379 }
2380 Pattern::Int { .. }
2381 | Pattern::Float { .. }
2382 | Pattern::String { .. }
2383 | Pattern::BitArraySize { .. }
2384 | Pattern::List { .. }
2385 | Pattern::Constructor { .. }
2386 | Pattern::BitArray { .. }
2387 | Pattern::StringPrefix { .. }
2388 | Pattern::Invalid { .. } => false,
2389 }
2390 }
2391
2392 pub fn bound_variables(&self) -> Vec<EcoString> {
2393 let mut variables = Vec::new();
2394 self.collect_bound_variables(&mut variables);
2395 variables
2396 }
2397
2398 fn collect_bound_variables(&self, variables: &mut Vec<EcoString>) {
2399 match self {
2400 Pattern::Int { .. }
2401 | Pattern::Float { .. }
2402 | Pattern::String { .. }
2403 | Pattern::Discard { .. }
2404 | Pattern::Invalid { .. } => {}
2405
2406 Pattern::Variable { name, .. } => variables.push(name.clone()),
2407 Pattern::VarUsage { .. } => {}
2408 Pattern::Assign { name, pattern, .. } => {
2409 variables.push(name.clone());
2410 pattern.collect_bound_variables(variables);
2411 }
2412 Pattern::List { elements, tail, .. } => {
2413 for element in elements {
2414 element.collect_bound_variables(variables);
2415 }
2416 if let Some(tail) = tail {
2417 tail.collect_bound_variables(variables);
2418 }
2419 }
2420 Pattern::Constructor { arguments, .. } => {
2421 for argument in arguments {
2422 argument.value.collect_bound_variables(variables);
2423 }
2424 }
2425 Pattern::Tuple { elements, .. } => {
2426 for element in elements {
2427 element.collect_bound_variables(variables);
2428 }
2429 }
2430 Pattern::BitArray { segments, .. } => {
2431 for segment in segments {
2432 segment.value.collect_bound_variables(variables);
2433 }
2434 }
2435 Pattern::StringPrefix {
2436 left_side_assignment,
2437 right_side_assignment,
2438 ..
2439 } => {
2440 if let Some((left_variable, _)) = left_side_assignment {
2441 variables.push(left_variable.clone());
2442 }
2443 match right_side_assignment {
2444 AssignName::Variable(name) => variables.push(name.clone()),
2445 AssignName::Discard(_) => {}
2446 }
2447 }
2448 }
2449 }
2450}
2451
2452#[derive(Debug, Default)]
2453pub struct PatternUnusedArguments {
2454 pub positional: Vec<Arc<Type>>,
2455 pub labelled: Vec<(EcoString, Arc<Type>)>,
2456}
2457
2458impl<A> HasLocation for Pattern<A> {
2459 fn location(&self) -> SrcSpan {
2460 self.location()
2461 }
2462}
2463
2464#[derive(Debug, Clone, PartialEq, Eq)]
2465pub enum AssignmentKind<Expression> {
2466 /// let x = ...
2467 Let,
2468 /// This is a let assignment generated by the compiler for intermediate variables
2469 /// needed by record updates and `use`.
2470 /// Like a regular `Let` assignment this can never fail.
2471 ///
2472 Generated,
2473 /// let assert x = ...
2474 Assert {
2475 /// The src byte span of the `let assert`
2476 ///
2477 /// ```gleam
2478 /// let assert Wibble = todo
2479 /// ^^^^^^^^^^
2480 /// ```
2481 location: SrcSpan,
2482
2483 /// The byte index of the start of `assert`
2484 ///
2485 /// ```gleam
2486 /// let assert Wibble = todo
2487 /// ^
2488 /// ```
2489 assert_keyword_start: u32,
2490
2491 /// The message given to the assertion:
2492 ///
2493 /// ```gleam
2494 /// let asset Ok(a) = something() as "This will never fail"
2495 /// ^^^^^^^^^^^^^^^^^^^^^^
2496 /// ```
2497 message: Option<Expression>,
2498 },
2499}
2500
2501impl<Expression> AssignmentKind<Expression> {
2502 /// Returns `true` if the assignment kind is [`Assert`].
2503 ///
2504 /// [`Assert`]: AssignmentKind::Assert
2505 #[must_use]
2506 pub fn is_assert(&self) -> bool {
2507 match self {
2508 Self::Assert { .. } => true,
2509 Self::Let | Self::Generated => false,
2510 }
2511 }
2512}
2513
2514// BitArrays
2515
2516pub type UntypedExprBitArraySegment = BitArraySegment<UntypedExpr, ()>;
2517pub type TypedExprBitArraySegment = BitArraySegment<TypedExpr, Arc<Type>>;
2518
2519pub type UntypedConstantBitArraySegment = BitArraySegment<UntypedConstant, ()>;
2520pub type TypedConstantBitArraySegment = BitArraySegment<TypedConstant, Arc<Type>>;
2521
2522pub type UntypedPatternBitArraySegment = BitArraySegment<UntypedPattern, ()>;
2523pub type TypedPatternBitArraySegment = BitArraySegment<TypedPattern, Arc<Type>>;
2524
2525#[derive(Debug, Clone, PartialEq, Eq)]
2526pub struct BitArraySegment<Value, Type> {
2527 pub location: SrcSpan,
2528 pub value: Box<Value>,
2529 pub options: Vec<BitArrayOption<Value>>,
2530 pub type_: Type,
2531}
2532
2533#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
2534pub enum Endianness {
2535 Big,
2536 Little,
2537}
2538
2539impl Endianness {
2540 pub fn is_big(&self) -> bool {
2541 *self == Endianness::Big
2542 }
2543}
2544
2545impl<Type> BitArraySegment<Pattern<Type>, Type> {
2546 /// Returns the value of the pattern unwrapping any assign pattern.
2547 ///
2548 pub fn value_unwrapping_assign(&self) -> &Pattern<Type> {
2549 match self.value.as_ref() {
2550 Pattern::Assign { pattern, .. } => pattern,
2551 Pattern::Int { .. }
2552 | Pattern::Float { .. }
2553 | Pattern::String { .. }
2554 | Pattern::Variable { .. }
2555 | Pattern::BitArraySize { .. }
2556 | Pattern::Discard { .. }
2557 | Pattern::List { .. }
2558 | Pattern::Constructor { .. }
2559 | Pattern::Tuple { .. }
2560 | Pattern::BitArray { .. }
2561 | Pattern::StringPrefix { .. }
2562 | Pattern::Invalid { .. } => self.value.as_ref(),
2563 }
2564 }
2565}
2566
2567impl<Value, Type> BitArraySegment<Value, Type> {
2568 #[must_use]
2569 pub fn has_native_option(&self) -> bool {
2570 self.options
2571 .iter()
2572 .any(|x| matches!(x, BitArrayOption::Native { .. }))
2573 }
2574
2575 #[must_use]
2576 pub fn has_utf16_codepoint_option(&self) -> bool {
2577 self.options
2578 .iter()
2579 .any(|x| matches!(x, BitArrayOption::Utf16Codepoint { .. }))
2580 }
2581
2582 #[must_use]
2583 pub fn has_utf32_codepoint_option(&self) -> bool {
2584 self.options
2585 .iter()
2586 .any(|x| matches!(x, BitArrayOption::Utf32Codepoint { .. }))
2587 }
2588
2589 #[must_use]
2590 pub fn has_utf16_option(&self) -> bool {
2591 self.options
2592 .iter()
2593 .any(|x| matches!(x, BitArrayOption::Utf16 { .. }))
2594 }
2595
2596 #[must_use]
2597 pub fn has_utf32_option(&self) -> bool {
2598 self.options
2599 .iter()
2600 .any(|x| matches!(x, BitArrayOption::Utf32 { .. }))
2601 }
2602
2603 pub fn endianness(&self) -> Endianness {
2604 if self
2605 .options
2606 .iter()
2607 .any(|x| matches!(x, BitArrayOption::Little { .. }))
2608 {
2609 Endianness::Little
2610 } else {
2611 Endianness::Big
2612 }
2613 }
2614
2615 pub(crate) fn signed(&self) -> bool {
2616 self.options
2617 .iter()
2618 .any(|x| matches!(x, BitArrayOption::Signed { .. }))
2619 }
2620
2621 pub fn size(&self) -> Option<&Value> {
2622 self.options.iter().find_map(|x| match x {
2623 BitArrayOption::Size { value, .. } => Some(value.as_ref()),
2624 _ => None,
2625 })
2626 }
2627
2628 pub fn unit(&self) -> u8 {
2629 self.options
2630 .iter()
2631 .find_map(|option| match option {
2632 BitArrayOption::Unit { value, .. } => Some(*value),
2633 BitArrayOption::Bytes { .. } => Some(8),
2634 _ => None,
2635 })
2636 .unwrap_or(1)
2637 }
2638
2639 pub(crate) fn has_bits_option(&self) -> bool {
2640 self.options.iter().any(|option| match option {
2641 BitArrayOption::Bits { .. } => true,
2642 _ => false,
2643 })
2644 }
2645
2646 pub(crate) fn has_bytes_option(&self) -> bool {
2647 self.options.iter().any(|option| match option {
2648 BitArrayOption::Bytes { .. } => true,
2649 _ => false,
2650 })
2651 }
2652}
2653
2654impl<Value, Type> BitArraySegment<Value, Type> {
2655 #[must_use]
2656 pub(crate) fn has_type_option(&self) -> bool {
2657 self.options.iter().any(|option| option.is_type_option())
2658 }
2659}
2660
2661impl TypedExprBitArraySegment {
2662 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2663 self.value.find_node(byte_index)
2664 }
2665}
2666
2667impl<TypedValue> BitArraySegment<TypedValue, Arc<Type>>
2668where
2669 TypedValue: HasType + HasLocation + Clone + bit_array::GetLiteralValue,
2670{
2671 pub(crate) fn check_for_truncated_value(&self) -> Option<BitArraySegmentTruncation> {
2672 // Both the size and the value must be two compile-time known constants.
2673 let segment_bits = self.bits_size()?.to_i64()?;
2674 let literal_value = self.value.as_int_literal()?;
2675 if segment_bits <= 0 {
2676 return None;
2677 }
2678
2679 let safe_range = match literal_value.sign() {
2680 Sign::NoSign => return None,
2681 Sign::Minus => {
2682 (-(BigInt::one() << (segment_bits - 1)))
2683 ..((BigInt::one() << (segment_bits - 1)) - 1)
2684 }
2685 Sign::Plus => BigInt::ZERO..(BigInt::one() << segment_bits),
2686 };
2687
2688 if !safe_range.contains(&literal_value) {
2689 Some(BitArraySegmentTruncation {
2690 truncated_value: literal_value.clone(),
2691 truncated_into: truncate(&literal_value, segment_bits),
2692 value_location: self.value.location(),
2693 segment_bits,
2694 })
2695 } else {
2696 None
2697 }
2698 }
2699
2700 /// If the segment size is a compile-time known constant this returns the
2701 /// segment size in bits, taking the segment's unit into consideration!
2702 ///
2703 fn bits_size(&self) -> Option<BigInt> {
2704 let size = match self.size() {
2705 None if self.type_.is_int() => 8.into(),
2706 None => 64.into(),
2707 Some(value) => value.as_int_literal()?,
2708 };
2709
2710 let unit = self.unit();
2711 Some(size * unit)
2712 }
2713}
2714
2715/// As Björn said, when a value is smaller than the segment's size it will be
2716/// truncated, only taking the first `n` bits:
2717///
2718/// > It will be silently truncated. In general, when storing value an integer
2719/// > `I` into a segment of size `N`, the actual value stored will be
2720/// > `I band ((1 bsl N) - 1)`.
2721///
2722/// <https://erlangforums.com/t/what-happens-when-a-bit-array-segment-size-is-smaller-than-its-value/4650/2?u=giacomocavalieri>
2723///
2724/// Thank you Björn!
2725///
2726fn truncate(literal_value: &BigInt, segment_bits: i64) -> BigInt {
2727 literal_value & ((BigInt::one() << segment_bits) - BigInt::one())
2728}
2729
2730#[derive(serde::Deserialize, serde::Serialize, Eq, PartialEq, Clone, Debug)]
2731pub struct BitArraySegmentTruncation {
2732 /// The value that would end up being truncated.
2733 pub truncated_value: BigInt,
2734 /// What the value would be truncated into.
2735 pub truncated_into: BigInt,
2736 /// The span of the segment's value being truncated.
2737 pub value_location: SrcSpan,
2738 /// The size of the segment.
2739 pub segment_bits: i64,
2740}
2741
2742impl TypedPatternBitArraySegment {
2743 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2744 self.value.find_node(byte_index).or_else(|| {
2745 self.options
2746 .iter()
2747 .find_map(|option| option.find_node(byte_index))
2748 })
2749 }
2750}
2751
2752impl TypedConstantBitArraySegment {
2753 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2754 self.value.find_node(byte_index).or_else(|| {
2755 self.options
2756 .iter()
2757 .find_map(|option| option.find_node(byte_index))
2758 })
2759 }
2760}
2761
2762pub type TypedConstantBitArraySegmentOption = BitArrayOption<TypedConstant>;
2763
2764#[derive(Debug, PartialEq, Eq, Clone)]
2765pub enum BitArrayOption<Value> {
2766 Bytes {
2767 location: SrcSpan,
2768 },
2769
2770 Int {
2771 location: SrcSpan,
2772 },
2773
2774 Float {
2775 location: SrcSpan,
2776 },
2777
2778 Bits {
2779 location: SrcSpan,
2780 },
2781
2782 Utf8 {
2783 location: SrcSpan,
2784 },
2785
2786 Utf16 {
2787 location: SrcSpan,
2788 },
2789
2790 Utf32 {
2791 location: SrcSpan,
2792 },
2793
2794 Utf8Codepoint {
2795 location: SrcSpan,
2796 },
2797
2798 Utf16Codepoint {
2799 location: SrcSpan,
2800 },
2801
2802 Utf32Codepoint {
2803 location: SrcSpan,
2804 },
2805
2806 Signed {
2807 location: SrcSpan,
2808 },
2809
2810 Unsigned {
2811 location: SrcSpan,
2812 },
2813
2814 Big {
2815 location: SrcSpan,
2816 },
2817
2818 Little {
2819 location: SrcSpan,
2820 },
2821
2822 Native {
2823 location: SrcSpan,
2824 },
2825
2826 Size {
2827 location: SrcSpan,
2828 value: Box<Value>,
2829 short_form: bool,
2830 },
2831
2832 Unit {
2833 location: SrcSpan,
2834 value: u8,
2835 },
2836}
2837
2838impl<A> BitArrayOption<A> {
2839 pub fn value(&self) -> Option<&A> {
2840 match self {
2841 BitArrayOption::Size { value, .. } => Some(value),
2842 _ => None,
2843 }
2844 }
2845
2846 pub fn location(&self) -> SrcSpan {
2847 match self {
2848 BitArrayOption::Bytes { location }
2849 | BitArrayOption::Int { location }
2850 | BitArrayOption::Float { location }
2851 | BitArrayOption::Bits { location }
2852 | BitArrayOption::Utf8 { location }
2853 | BitArrayOption::Utf16 { location }
2854 | BitArrayOption::Utf32 { location }
2855 | BitArrayOption::Utf8Codepoint { location }
2856 | BitArrayOption::Utf16Codepoint { location }
2857 | BitArrayOption::Utf32Codepoint { location }
2858 | BitArrayOption::Signed { location }
2859 | BitArrayOption::Unsigned { location }
2860 | BitArrayOption::Big { location }
2861 | BitArrayOption::Little { location }
2862 | BitArrayOption::Native { location }
2863 | BitArrayOption::Size { location, .. }
2864 | BitArrayOption::Unit { location, .. } => *location,
2865 }
2866 }
2867
2868 pub fn label(&self) -> EcoString {
2869 match self {
2870 BitArrayOption::Bytes { .. } => "bytes".into(),
2871 BitArrayOption::Int { .. } => "int".into(),
2872 BitArrayOption::Float { .. } => "float".into(),
2873 BitArrayOption::Bits { .. } => "bits".into(),
2874 BitArrayOption::Utf8 { .. } => "utf8".into(),
2875 BitArrayOption::Utf16 { .. } => "utf16".into(),
2876 BitArrayOption::Utf32 { .. } => "utf32".into(),
2877 BitArrayOption::Utf8Codepoint { .. } => "utf8_codepoint".into(),
2878 BitArrayOption::Utf16Codepoint { .. } => "utf16_codepoint".into(),
2879 BitArrayOption::Utf32Codepoint { .. } => "utf32_codepoint".into(),
2880 BitArrayOption::Signed { .. } => "signed".into(),
2881 BitArrayOption::Unsigned { .. } => "unsigned".into(),
2882 BitArrayOption::Big { .. } => "big".into(),
2883 BitArrayOption::Little { .. } => "little".into(),
2884 BitArrayOption::Native { .. } => "native".into(),
2885 BitArrayOption::Size { .. } => "size".into(),
2886 BitArrayOption::Unit { .. } => "unit".into(),
2887 }
2888 }
2889
2890 fn is_type_option(&self) -> bool {
2891 match self {
2892 BitArrayOption::Bytes { .. }
2893 | BitArrayOption::Int { .. }
2894 | BitArrayOption::Float { .. }
2895 | BitArrayOption::Bits { .. }
2896 | BitArrayOption::Utf8 { .. }
2897 | BitArrayOption::Utf16 { .. }
2898 | BitArrayOption::Utf32 { .. }
2899 | BitArrayOption::Utf8Codepoint { .. }
2900 | BitArrayOption::Utf16Codepoint { .. }
2901 | BitArrayOption::Utf32Codepoint { .. } => true,
2902
2903 BitArrayOption::Signed { .. }
2904 | BitArrayOption::Unsigned { .. }
2905 | BitArrayOption::Big { .. }
2906 | BitArrayOption::Little { .. }
2907 | BitArrayOption::Native { .. }
2908 | BitArrayOption::Size { .. }
2909 | BitArrayOption::Unit { .. } => false,
2910 }
2911 }
2912}
2913
2914impl BitArrayOption<TypedConstant> {
2915 fn referenced_variables(&self) -> im::HashSet<&EcoString> {
2916 match self {
2917 BitArrayOption::Bytes { .. }
2918 | BitArrayOption::Int { .. }
2919 | BitArrayOption::Float { .. }
2920 | BitArrayOption::Bits { .. }
2921 | BitArrayOption::Utf8 { .. }
2922 | BitArrayOption::Utf16 { .. }
2923 | BitArrayOption::Utf32 { .. }
2924 | BitArrayOption::Utf8Codepoint { .. }
2925 | BitArrayOption::Utf16Codepoint { .. }
2926 | BitArrayOption::Utf32Codepoint { .. }
2927 | BitArrayOption::Signed { .. }
2928 | BitArrayOption::Unsigned { .. }
2929 | BitArrayOption::Big { .. }
2930 | BitArrayOption::Little { .. }
2931 | BitArrayOption::Unit { .. }
2932 | BitArrayOption::Native { .. } => im::hashset![],
2933
2934 BitArrayOption::Size { value, .. } => value.referenced_variables(),
2935 }
2936 }
2937}
2938
2939impl BitArrayOption<TypedPattern> {
2940 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2941 match self {
2942 BitArrayOption::Bytes { .. }
2943 | BitArrayOption::Int { .. }
2944 | BitArrayOption::Float { .. }
2945 | BitArrayOption::Bits { .. }
2946 | BitArrayOption::Utf8 { .. }
2947 | BitArrayOption::Utf16 { .. }
2948 | BitArrayOption::Utf32 { .. }
2949 | BitArrayOption::Utf8Codepoint { .. }
2950 | BitArrayOption::Utf16Codepoint { .. }
2951 | BitArrayOption::Utf32Codepoint { .. }
2952 | BitArrayOption::Signed { .. }
2953 | BitArrayOption::Unsigned { .. }
2954 | BitArrayOption::Big { .. }
2955 | BitArrayOption::Little { .. }
2956 | BitArrayOption::Native { .. }
2957 | BitArrayOption::Unit { .. } => None,
2958 BitArrayOption::Size { value, .. } => value.find_node(byte_index),
2959 }
2960 }
2961}
2962
2963impl BitArrayOption<TypedConstant> {
2964 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2965 match self {
2966 BitArrayOption::Bytes { .. }
2967 | BitArrayOption::Int { .. }
2968 | BitArrayOption::Float { .. }
2969 | BitArrayOption::Bits { .. }
2970 | BitArrayOption::Utf8 { .. }
2971 | BitArrayOption::Utf16 { .. }
2972 | BitArrayOption::Utf32 { .. }
2973 | BitArrayOption::Utf8Codepoint { .. }
2974 | BitArrayOption::Utf16Codepoint { .. }
2975 | BitArrayOption::Utf32Codepoint { .. }
2976 | BitArrayOption::Signed { .. }
2977 | BitArrayOption::Unsigned { .. }
2978 | BitArrayOption::Big { .. }
2979 | BitArrayOption::Little { .. }
2980 | BitArrayOption::Native { .. }
2981 | BitArrayOption::Unit { .. } => None,
2982 BitArrayOption::Size { value, .. } => value.find_node(byte_index),
2983 }
2984 }
2985}
2986
2987#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2988pub enum TodoKind {
2989 Keyword,
2990 EmptyFunction { function_location: SrcSpan },
2991 IncompleteUse,
2992 EmptyBlock,
2993}
2994
2995#[derive(Debug, Default)]
2996pub struct GroupedDefinitions {
2997 pub functions: Vec<UntypedFunction>,
2998 pub constants: Vec<UntypedModuleConstant>,
2999 pub custom_types: Vec<UntypedCustomType>,
3000 pub imports: Vec<UntypedImport>,
3001 pub type_aliases: Vec<UntypedTypeAlias>,
3002}
3003
3004impl GroupedDefinitions {
3005 pub fn new(definitions: impl IntoIterator<Item = UntypedDefinition>) -> Self {
3006 let mut this = Self::default();
3007
3008 for definition in definitions {
3009 this.add(definition)
3010 }
3011
3012 this
3013 }
3014
3015 pub fn is_empty(&self) -> bool {
3016 self.len() == 0
3017 }
3018
3019 pub fn len(&self) -> usize {
3020 let Self {
3021 custom_types,
3022 functions,
3023 constants,
3024 imports,
3025 type_aliases,
3026 } = self;
3027 functions.len() + constants.len() + imports.len() + custom_types.len() + type_aliases.len()
3028 }
3029
3030 fn add(&mut self, statement: UntypedDefinition) {
3031 match statement {
3032 Definition::Import(import) => self.imports.push(import),
3033 Definition::Function(function) => self.functions.push(function),
3034 Definition::TypeAlias(type_alias) => self.type_aliases.push(type_alias),
3035 Definition::CustomType(custom_type) => self.custom_types.push(custom_type),
3036 Definition::ModuleConstant(constant) => self.constants.push(constant),
3037 }
3038 }
3039}
3040
3041/// A statement with in a function body.
3042#[derive(Debug, Clone, PartialEq, Eq)]
3043pub enum Statement<TypeT, ExpressionT> {
3044 /// A bare expression that is not assigned to any variable.
3045 Expression(ExpressionT),
3046 /// Assigning an expression to variables using a pattern.
3047 Assignment(Box<Assignment<TypeT, ExpressionT>>),
3048 /// A `use` expression.
3049 Use(Use<TypeT, ExpressionT>),
3050 /// A bool assertion.
3051 Assert(Assert<ExpressionT>),
3052}
3053
3054pub type UntypedUse = Use<(), UntypedExpr>;
3055pub type TypedUse = Use<Arc<Type>, TypedExpr>;
3056
3057#[derive(Debug, Clone, PartialEq, Eq)]
3058pub struct Use<TypeT, ExpressionT> {
3059 /// In an untyped use this is the expression with the untyped code of the
3060 /// callback function.
3061 ///
3062 /// In a typed use this is the typed function call the use expression
3063 /// desugars to.
3064 ///
3065 pub call: Box<ExpressionT>,
3066
3067 /// This is the location of the whole use line, starting from the `use`
3068 /// keyword and ending with the function call on the right hand side of
3069 /// `<-`.
3070 ///
3071 /// ```gleam
3072 /// use a <- result.try(result)
3073 /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
3074 /// ```
3075 ///
3076 pub location: SrcSpan,
3077
3078 /// This is the location of the expression on the right hand side of the use
3079 /// arrow.
3080 ///
3081 /// ```gleam
3082 /// use a <- result.try(result)
3083 /// ^^^^^^^^^^^^^^^^^^
3084 /// ```
3085 ///
3086 pub right_hand_side_location: SrcSpan,
3087
3088 /// This is the SrcSpan of the patterns you find on the left hand side of
3089 /// `<-` in a use expression.
3090 ///
3091 /// ```gleam
3092 /// use pattern1, pattern2 <- todo
3093 /// ^^^^^^^^^^^^^^^^^^
3094 /// ```
3095 ///
3096 /// In case there's no patterns it will be corresponding to the SrcSpan of
3097 /// the `use` keyword itself.
3098 ///
3099 pub assignments_location: SrcSpan,
3100
3101 /// The patterns on the left hand side of `<-` in a use expression.
3102 ///
3103 pub assignments: Vec<UseAssignment<TypeT>>,
3104}
3105
3106pub type UntypedUseAssignment = UseAssignment<()>;
3107pub type TypedUseAssignment = UseAssignment<Arc<Type>>;
3108
3109#[derive(Debug, Clone, PartialEq, Eq)]
3110pub struct UseAssignment<TypeT> {
3111 pub location: SrcSpan,
3112 pub pattern: Pattern<TypeT>,
3113 pub annotation: Option<TypeAst>,
3114}
3115
3116impl TypedUse {
3117 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3118 for assignment in self.assignments.iter() {
3119 if let Some(found) = assignment.pattern.find_node(byte_index) {
3120 return Some(found);
3121 }
3122 if let Some(found) = assignment
3123 .annotation
3124 .as_ref()
3125 .and_then(|annotation| annotation.find_node(byte_index, assignment.pattern.type_()))
3126 {
3127 return Some(found);
3128 }
3129 }
3130 self.call.find_node(byte_index)
3131 }
3132
3133 pub fn callback_arguments(&self) -> Option<&Vec<TypedArg>> {
3134 let TypedExpr::Call { args, .. } = self.call.as_ref() else {
3135 return None;
3136 };
3137 let callback = args.iter().last()?;
3138 let TypedExpr::Fn { args, .. } = &callback.value else {
3139 // The expression might be invalid so we have to return a None here
3140 return None;
3141 };
3142 Some(args)
3143 }
3144}
3145
3146pub type TypedStatement = Statement<Arc<Type>, TypedExpr>;
3147pub type UntypedStatement = Statement<(), UntypedExpr>;
3148
3149impl<T, E> Statement<T, E> {
3150 /// Returns `true` if the statement is [`Expression`].
3151 ///
3152 /// [`Expression`]: Statement::Expression
3153 #[must_use]
3154 pub fn is_expression(&self) -> bool {
3155 matches!(self, Self::Expression(..))
3156 }
3157
3158 #[must_use]
3159 pub(crate) fn is_use(&self) -> bool {
3160 match self {
3161 Self::Use(_) => true,
3162 _ => false,
3163 }
3164 }
3165}
3166
3167impl UntypedStatement {
3168 pub fn location(&self) -> SrcSpan {
3169 match self {
3170 Statement::Expression(expression) => expression.location(),
3171 Statement::Assignment(assignment) => assignment.location,
3172 Statement::Use(use_) => use_.location,
3173 Statement::Assert(assert) => assert.location,
3174 }
3175 }
3176
3177 pub fn start_byte_index(&self) -> u32 {
3178 match self {
3179 Statement::Expression(expression) => expression.start_byte_index(),
3180 Statement::Assignment(assignment) => assignment.location.start,
3181 Statement::Use(use_) => use_.location.start,
3182 Statement::Assert(assert) => assert.location.start,
3183 }
3184 }
3185
3186 pub fn is_placeholder(&self) -> bool {
3187 match self {
3188 Statement::Expression(expression) => expression.is_placeholder(),
3189 Statement::Assignment(_) | Statement::Use(_) | Statement::Assert(_) => false,
3190 }
3191 }
3192}
3193
3194impl TypedStatement {
3195 pub fn is_println(&self) -> bool {
3196 match self {
3197 Statement::Expression(e) => e.is_println(),
3198 Statement::Assignment(_) => false,
3199 Statement::Use(_) => false,
3200 Statement::Assert(_) => false,
3201 }
3202 }
3203
3204 pub fn location(&self) -> SrcSpan {
3205 match self {
3206 Statement::Expression(expression) => expression.location(),
3207 Statement::Assignment(assignment) => assignment.location,
3208 Statement::Use(use_) => use_.location,
3209 Statement::Assert(assert) => assert.location,
3210 }
3211 }
3212
3213 /// Returns the location of the last element of a statement. This means that
3214 /// if the statement is a use you'll get the location of the last item at
3215 /// the end of its block.
3216 pub fn last_location(&self) -> SrcSpan {
3217 match self {
3218 Statement::Expression(expression) => expression.last_location(),
3219 Statement::Assignment(assignment) => assignment.value.last_location(),
3220 Statement::Use(use_) => use_.call.last_location(),
3221 Statement::Assert(assert) => assert.value.last_location(),
3222 }
3223 }
3224
3225 pub fn type_(&self) -> Arc<Type> {
3226 match self {
3227 Statement::Expression(expression) => expression.type_(),
3228 Statement::Assignment(assignment) => assignment.type_(),
3229 Statement::Use(use_) => use_.call.type_(),
3230 Statement::Assert(_) => nil(),
3231 }
3232 }
3233
3234 pub fn definition_location(&self) -> Option<DefinitionLocation> {
3235 match self {
3236 Statement::Expression(expression) => expression.definition_location(),
3237 Statement::Assignment(_) => None,
3238 Statement::Use(use_) => use_.call.definition_location(),
3239 Statement::Assert(_) => None,
3240 }
3241 }
3242
3243 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3244 match self {
3245 Statement::Use(use_) => use_.find_node(byte_index),
3246 Statement::Expression(expression) => expression.find_node(byte_index),
3247 Statement::Assignment(assignment) => assignment.find_node(byte_index).or_else(|| {
3248 if assignment.location.contains(byte_index) {
3249 Some(Located::Statement(self))
3250 } else {
3251 None
3252 }
3253 }),
3254 Statement::Assert(assert) => assert.find_node(byte_index).or_else(|| {
3255 if assert.location.contains(byte_index) {
3256 Some(Located::Statement(self))
3257 } else {
3258 None
3259 }
3260 }),
3261 }
3262 }
3263
3264 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
3265 match self {
3266 Statement::Use(use_) => use_.call.find_statement(byte_index),
3267 Statement::Expression(expression) => expression.find_statement(byte_index),
3268 Statement::Assignment(assignment) => {
3269 assignment.value.find_statement(byte_index).or_else(|| {
3270 if assignment.location.contains(byte_index) {
3271 Some(self)
3272 } else {
3273 None
3274 }
3275 })
3276 }
3277 Statement::Assert(assert) => assert.value.find_statement(byte_index).or_else(|| {
3278 if assert.location.contains(byte_index) {
3279 Some(self)
3280 } else {
3281 None
3282 }
3283 }),
3284 }
3285 }
3286
3287 pub fn type_defining_location(&self) -> SrcSpan {
3288 match self {
3289 Statement::Expression(expression) => expression.type_defining_location(),
3290 Statement::Assignment(assignment) => assignment.location,
3291 Statement::Use(use_) => use_.location,
3292 Statement::Assert(assert) => assert.location,
3293 }
3294 }
3295
3296 fn is_pure_value_constructor(&self) -> bool {
3297 match self {
3298 Statement::Expression(expression) => expression.is_pure_value_constructor(),
3299 Statement::Assignment(assignment) => {
3300 // A let assert is not considered a pure value constructor
3301 // as it could crash the program!
3302 !assignment.kind.is_assert() && assignment.value.is_pure_value_constructor()
3303 }
3304 Statement::Use(Use { call, .. }) => call.is_pure_value_constructor(),
3305 // Assert statements by definition are not pure
3306 Statement::Assert(_) => false,
3307 }
3308 }
3309}
3310
3311#[derive(Debug, Clone, PartialEq, Eq)]
3312pub struct Assignment<TypeT, ExpressionT> {
3313 pub location: SrcSpan,
3314 pub value: ExpressionT,
3315 pub pattern: Pattern<TypeT>,
3316 pub kind: AssignmentKind<ExpressionT>,
3317 pub compiled_case: CompiledCase,
3318 /// This will be true for assignments that are automatically generated by
3319 /// the compiler.
3320 pub annotation: Option<TypeAst>,
3321}
3322
3323pub type TypedAssignment = Assignment<Arc<Type>, TypedExpr>;
3324pub type UntypedAssignment = Assignment<(), UntypedExpr>;
3325
3326impl TypedAssignment {
3327 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3328 if let Some(annotation) = &self.annotation {
3329 if let Some(l) = annotation.find_node(byte_index, self.pattern.type_()) {
3330 return Some(l);
3331 }
3332 }
3333 self.pattern
3334 .find_node(byte_index)
3335 .or_else(|| self.value.find_node(byte_index))
3336 }
3337
3338 pub fn type_(&self) -> Arc<Type> {
3339 self.value.type_()
3340 }
3341}
3342
3343pub type TypedAssert = Assert<TypedExpr>;
3344pub type UntypedAssert = Assert<UntypedExpr>;
3345
3346#[derive(Debug, Clone, PartialEq, Eq)]
3347pub struct Assert<Expression> {
3348 pub location: SrcSpan,
3349 pub value: Expression,
3350 pub message: Option<Expression>,
3351}
3352
3353impl TypedAssert {
3354 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3355 if let Some(found) = self.value.find_node(byte_index) {
3356 return Some(found);
3357 }
3358 if let Some(message) = &self.message {
3359 if let Some(found) = message.find_node(byte_index) {
3360 return Some(found);
3361 }
3362 }
3363 None
3364 }
3365}
3366
3367/// A pipeline is desugared to a series of assignments:
3368///
3369/// ```gleam
3370/// wibble |> wobble |> woo
3371/// ```
3372///
3373/// Becomes:
3374///
3375/// ```erl
3376/// Pipe1 = wibble
3377/// Pipe2 = wobble(Pipe1)
3378/// woo(Pipe2)
3379/// ```
3380///
3381/// This represents one of such assignments once the pipeline has been desugared
3382/// and each step has been typed.
3383///
3384/// > We're not using a more general `TypedAssignment` node since that has much
3385/// > more informations to carry around. This one is limited since we know it
3386/// > will always be in the form `VarName = <Expr>`, with no patterns on the
3387/// > left hand side of the assignment.
3388/// > Being more constrained simplifies code generation for pipelines!
3389///
3390#[derive(Debug, Clone, PartialEq, Eq)]
3391pub struct TypedPipelineAssignment {
3392 pub location: SrcSpan,
3393 pub name: EcoString,
3394 pub value: Box<TypedExpr>,
3395}
3396
3397impl TypedPipelineAssignment {
3398 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3399 self.value.find_node(byte_index)
3400 }
3401
3402 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
3403 self.value.find_statement(byte_index)
3404 }
3405
3406 pub fn type_(&self) -> Arc<Type> {
3407 self.value.type_()
3408 }
3409}
3410
3411/// The kind of desugaring that might take place when rewriting a pipeline to
3412/// regular assignments.
3413///
3414#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3415pub enum PipelineAssignmentKind {
3416 /// In case `a |> b(c)` is desugared to `b(a, c)`.
3417 FirstArgument {
3418 /// The location of the second argument of the call, in case there's any:
3419 /// - `a |> b(c, d)`: here it's `Some` wrapping the location of `c`.
3420 /// - `a |> b()`: here it's `None`.
3421 second_argument: Option<SrcSpan>,
3422 },
3423
3424 /// In case there's an explicit hole and `a |> b(_, c)` is desugared to
3425 /// `b(a, c)`.
3426 Hole { hole: SrcSpan },
3427
3428 /// In case `a |> b(c)` is desugared to `b(c)(a)`
3429 FunctionCall,
3430
3431 /// In case there's an echo in the middle of a pipeline `a |> echo`
3432 Echo,
3433}