Fork of daniellemaywood.uk/gleam — Wasm codegen work
50 kB
1804 lines
1mod constant;
2mod typed;
3mod untyped;
4
5#[cfg(test)]
6mod tests;
7
8pub use self::typed::TypedExpr;
9pub use self::untyped::{UntypedExpr, Use};
10
11pub use self::constant::{Constant, TypedConstant, UntypedConstant};
12
13use crate::analyse::Inferred;
14use crate::build::{Located, Target};
15use crate::type_::expression::Implementations;
16use crate::type_::{
17 self, Deprecation, ModuleValueConstructor, PatternConstructor, Type, ValueConstructor,
18};
19use std::sync::Arc;
20
21use ecow::EcoString;
22#[cfg(test)]
23use pretty_assertions::assert_eq;
24use vec1::Vec1;
25
26pub const TRY_VARIABLE: &str = "_try";
27pub const PIPE_VARIABLE: &str = "_pipe";
28pub const USE_ASSIGNMENT_VARIABLE: &str = "_use";
29pub const ASSERT_FAIL_VARIABLE: &str = "_assert_fail";
30pub const ASSERT_SUBJECT_VARIABLE: &str = "_assert_subject";
31pub const CAPTURE_VARIABLE: &str = "_capture";
32
33pub trait HasLocation {
34 fn location(&self) -> SrcSpan;
35}
36
37pub type TypedModule = Module<type_::ModuleInterface, TypedDefinition>;
38
39pub type UntypedModule = Module<(), TargetedDefinition>;
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Module<Info, Statements> {
43 pub name: EcoString,
44 pub documentation: Vec<EcoString>,
45 pub type_info: Info,
46 pub definitions: Vec<Statements>,
47}
48
49impl TypedModule {
50 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
51 self.definitions
52 .iter()
53 .find_map(|statement| statement.find_node(byte_index))
54 }
55}
56
57/// The `@target(erlang)` and `@target(javascript)` attributes can be used to
58/// mark a definition as only being for a specific target.
59///
60/// ```gleam
61/// const x: Int = 1
62///
63/// @target(erlang)
64/// pub fn main(a) { ...}
65/// ```
66///
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct TargetedDefinition {
69 pub definition: UntypedDefinition,
70 pub target: Option<Target>,
71}
72
73impl TargetedDefinition {
74 pub fn is_for(&self, target: Target) -> bool {
75 self.target.map(|t| t == target).unwrap_or(true)
76 }
77}
78
79impl UntypedModule {
80 pub fn dependencies(&self, target: Target) -> Vec<(EcoString, SrcSpan)> {
81 self.iter_statements(target)
82 .flat_map(|s| match s {
83 Definition::Import(Import {
84 module, location, ..
85 }) => Some((module.clone(), *location)),
86 _ => None,
87 })
88 .collect()
89 }
90
91 pub fn iter_statements(&self, target: Target) -> impl Iterator<Item = &UntypedDefinition> {
92 self.definitions
93 .iter()
94 .filter(move |def| def.is_for(target))
95 .map(|def| &def.definition)
96 }
97
98 pub fn into_iter_statements(self, target: Target) -> impl Iterator<Item = UntypedDefinition> {
99 self.definitions
100 .into_iter()
101 .filter(move |def| def.is_for(target))
102 .map(|def| def.definition)
103 }
104}
105
106#[test]
107fn module_dependencies_test() {
108 let parsed = crate::parse::parse_module(
109 "import one
110 @target(erlang)
111 import two
112
113 @target(javascript)
114 import three
115
116 import four",
117 )
118 .expect("syntax error");
119 let module = parsed.module;
120
121 assert_eq!(
122 vec![
123 ("one".into(), SrcSpan::new(0, 10)),
124 ("two".into(), SrcSpan::new(45, 55)),
125 ("four".into(), SrcSpan::new(118, 129)),
126 ],
127 module.dependencies(Target::Erlang)
128 );
129}
130
131pub type TypedArg = Arg<Arc<Type>>;
132pub type UntypedArg = Arg<()>;
133
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct Arg<T> {
136 pub names: ArgNames,
137 pub location: SrcSpan,
138 pub annotation: Option<TypeAst>,
139 pub type_: T,
140}
141
142impl<A> Arg<A> {
143 pub fn set_type<B>(self, t: B) -> Arg<B> {
144 Arg {
145 type_: t,
146 names: self.names,
147 location: self.location,
148 annotation: self.annotation,
149 }
150 }
151
152 pub fn get_variable_name(&self) -> Option<&EcoString> {
153 self.names.get_variable_name()
154 }
155}
156
157impl TypedArg {
158 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
159 if self.location.contains(byte_index) {
160 Some(Located::Arg(self))
161 } else {
162 None
163 }
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
168pub enum ArgNames {
169 Discard { name: EcoString },
170 LabelledDiscard { label: EcoString, name: EcoString },
171 Named { name: EcoString },
172 NamedLabelled { name: EcoString, label: EcoString },
173}
174
175impl ArgNames {
176 pub fn get_label(&self) -> Option<&EcoString> {
177 match self {
178 ArgNames::Discard { .. } | ArgNames::Named { .. } => None,
179 ArgNames::LabelledDiscard { label, .. } | ArgNames::NamedLabelled { label, .. } => {
180 Some(label)
181 }
182 }
183 }
184 pub fn get_variable_name(&self) -> Option<&EcoString> {
185 match self {
186 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => None,
187 ArgNames::NamedLabelled { name, .. } | ArgNames::Named { name } => Some(name),
188 }
189 }
190}
191
192pub type TypedRecordConstructor = RecordConstructor<Arc<Type>>;
193
194#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct RecordConstructor<T> {
196 pub location: SrcSpan,
197 pub name: EcoString,
198 pub arguments: Vec<RecordConstructorArg<T>>,
199 pub documentation: Option<EcoString>,
200}
201
202impl<A> RecordConstructor<A> {
203 pub fn put_doc(&mut self, new_doc: EcoString) {
204 self.documentation = Some(new_doc);
205 }
206}
207
208pub type TypedRecordConstructorArg = RecordConstructorArg<Arc<Type>>;
209
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct RecordConstructorArg<T> {
212 pub label: Option<EcoString>,
213 pub ast: TypeAst,
214 pub location: SrcSpan,
215 pub type_: T,
216 pub doc: Option<EcoString>,
217}
218
219impl<T: PartialEq> RecordConstructorArg<T> {
220 pub fn put_doc(&mut self, new_doc: EcoString) {
221 self.doc = Some(new_doc);
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct TypeAstConstructor {
227 pub location: SrcSpan,
228 pub module: Option<EcoString>,
229 pub name: EcoString,
230 pub arguments: Vec<TypeAst>,
231}
232
233#[derive(Debug, Clone, PartialEq, Eq)]
234pub struct TypeAstFn {
235 pub location: SrcSpan,
236 pub arguments: Vec<TypeAst>,
237 pub return_: Box<TypeAst>,
238}
239
240#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct TypeAstVar {
242 pub location: SrcSpan,
243 pub name: EcoString,
244}
245
246#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct TypeAstTuple {
248 pub location: SrcSpan,
249 pub elems: Vec<TypeAst>,
250}
251
252#[derive(Debug, Clone, PartialEq, Eq)]
253pub struct TypeAstHole {
254 pub location: SrcSpan,
255 pub name: EcoString,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq)]
259pub enum TypeAst {
260 Constructor(TypeAstConstructor),
261 Fn(TypeAstFn),
262 Var(TypeAstVar),
263 Tuple(TypeAstTuple),
264 Hole(TypeAstHole),
265}
266
267impl TypeAst {
268 pub fn location(&self) -> SrcSpan {
269 match self {
270 TypeAst::Fn(TypeAstFn { location, .. })
271 | TypeAst::Var(TypeAstVar { location, .. })
272 | TypeAst::Hole(TypeAstHole { location, .. })
273 | TypeAst::Tuple(TypeAstTuple { location, .. })
274 | TypeAst::Constructor(TypeAstConstructor { location, .. }) => *location,
275 }
276 }
277
278 pub fn is_logically_equal(&self, other: &TypeAst) -> bool {
279 match self {
280 TypeAst::Constructor(TypeAstConstructor {
281 module,
282 name,
283 arguments,
284 location: _,
285 }) => match other {
286 TypeAst::Constructor(TypeAstConstructor {
287 module: o_module,
288 name: o_name,
289 arguments: o_arguments,
290 location: _,
291 }) => {
292 module == o_module
293 && name == o_name
294 && arguments.len() == o_arguments.len()
295 && arguments
296 .iter()
297 .zip(o_arguments)
298 .all(|a| a.0.is_logically_equal(a.1))
299 }
300 _ => false,
301 },
302 TypeAst::Fn(TypeAstFn {
303 arguments,
304 return_,
305 location: _,
306 }) => match other {
307 TypeAst::Fn(TypeAstFn {
308 arguments: o_arguments,
309 return_: o_return_,
310 location: _,
311 }) => {
312 arguments.len() == o_arguments.len()
313 && arguments
314 .iter()
315 .zip(o_arguments)
316 .all(|a| a.0.is_logically_equal(a.1))
317 && return_.is_logically_equal(o_return_)
318 }
319 _ => false,
320 },
321 TypeAst::Var(TypeAstVar { name, location: _ }) => match other {
322 TypeAst::Var(TypeAstVar {
323 name: o_name,
324 location: _,
325 }) => name == o_name,
326 _ => false,
327 },
328 TypeAst::Tuple(TypeAstTuple { elems, location: _ }) => match other {
329 TypeAst::Tuple(TypeAstTuple {
330 elems: o_elems,
331 location: _,
332 }) => {
333 elems.len() == o_elems.len()
334 && elems
335 .iter()
336 .zip(o_elems)
337 .all(|a| a.0.is_logically_equal(a.1))
338 }
339 _ => false,
340 },
341 TypeAst::Hole(TypeAstHole { name, location: _ }) => match other {
342 TypeAst::Hole(TypeAstHole {
343 name: o_name,
344 location: _,
345 }) => name == o_name,
346 _ => false,
347 },
348 }
349 }
350}
351
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
353pub enum Publicity {
354 Public,
355 Private,
356 Internal,
357}
358
359impl Publicity {
360 pub fn is_private(&self) -> bool {
361 match self {
362 Self::Private => true,
363 Self::Public | Self::Internal => false,
364 }
365 }
366
367 pub fn is_internal(&self) -> bool {
368 match self {
369 Self::Internal => true,
370 Self::Public | Self::Private => false,
371 }
372 }
373
374 pub fn is_public(&self) -> bool {
375 match self {
376 Self::Public => true,
377 Self::Internal | Self::Private => false,
378 }
379 }
380
381 pub fn is_importable(&self) -> bool {
382 match self {
383 Self::Internal | Self::Public => true,
384 Self::Private => false,
385 }
386 }
387}
388
389#[derive(Debug, Clone, PartialEq, Eq)]
390/// A function definition
391///
392/// # Example(s)
393///
394/// ```gleam
395/// // Public function
396/// pub fn bar() -> String { ... }
397/// // Private function
398/// fn foo(x: Int) -> Int { ... }
399/// ```
400pub struct Function<T, Expr> {
401 pub location: SrcSpan,
402 pub end_position: u32,
403 pub name: EcoString,
404 pub arguments: Vec<Arg<T>>,
405 pub body: Vec1<Statement<T, Expr>>,
406 pub publicity: Publicity,
407 pub deprecation: Deprecation,
408 pub return_annotation: Option<TypeAst>,
409 pub return_type: T,
410 pub documentation: Option<EcoString>,
411 pub external_erlang: Option<(EcoString, EcoString)>,
412 pub external_javascript: Option<(EcoString, EcoString)>,
413 pub implementations: Implementations,
414}
415
416pub type TypedFunction = Function<Arc<Type>, TypedExpr>;
417pub type UntypedFunction = Function<(), UntypedExpr>;
418
419impl<T, E> Function<T, E> {
420 fn full_location(&self) -> SrcSpan {
421 SrcSpan::new(self.location.start, self.end_position)
422 }
423}
424
425pub type UntypedImport = Import<()>;
426
427#[derive(Debug, Clone, PartialEq, Eq)]
428/// Import another Gleam module so the current module can use the types and
429/// values it defines.
430///
431/// # Example(s)
432///
433/// ```gleam
434/// import unix/cat
435/// // Import with alias
436/// import animal/cat as kitty
437/// ```
438pub struct Import<PackageName> {
439 pub documentation: Option<EcoString>,
440 pub location: SrcSpan,
441 pub module: EcoString,
442 pub as_name: Option<(AssignName, SrcSpan)>,
443 pub unqualified_values: Vec<UnqualifiedImport>,
444 pub unqualified_types: Vec<UnqualifiedImport>,
445 pub package: PackageName,
446}
447
448impl<T> Import<T> {
449 pub(crate) fn used_name(&self) -> Option<EcoString> {
450 match self.as_name.as_ref() {
451 Some((AssignName::Variable(name), _)) => Some(name.clone()),
452 Some((AssignName::Discard(_), _)) => None,
453 None => self.module.split('/').last().map(EcoString::from),
454 }
455 }
456
457 pub(crate) fn alias_location(&self) -> Option<SrcSpan> {
458 self.as_name.as_ref().map(|(_, location)| *location)
459 }
460}
461
462pub type UntypedModuleConstant = ModuleConstant<(), ()>;
463
464#[derive(Debug, Clone, PartialEq, Eq)]
465/// A certain fixed value that can be used in multiple places
466///
467/// # Example(s)
468///
469/// ```gleam
470/// pub const start_year = 2101
471/// pub const end_year = 2111
472/// ```
473pub struct ModuleConstant<T, ConstantRecordTag> {
474 pub documentation: Option<EcoString>,
475 pub location: SrcSpan,
476 pub publicity: Publicity,
477 pub name: EcoString,
478 pub annotation: Option<TypeAst>,
479 pub value: Box<Constant<T, ConstantRecordTag>>,
480 pub type_: T,
481 pub deprecation: Deprecation,
482 pub implementations: Implementations,
483}
484
485pub type UntypedCustomType = CustomType<()>;
486
487#[derive(Debug, Clone, PartialEq, Eq)]
488/// A newly defined type with one or more constructors.
489/// Each variant of the custom type can contain different types, so the type is
490/// the product of the types contained by each variant.
491///
492/// This might be called an algebraic data type (ADT) or tagged union in other
493/// languages and type systems.
494///
495///
496/// # Example(s)
497///
498/// ```gleam
499/// pub type Cat {
500/// Cat(name: String, cuteness: Int)
501/// }
502/// ```
503pub struct CustomType<T> {
504 pub location: SrcSpan,
505 pub end_position: u32,
506 pub name: EcoString,
507 pub publicity: Publicity,
508 pub constructors: Vec<RecordConstructor<T>>,
509 pub documentation: Option<EcoString>,
510 pub deprecation: Deprecation,
511 pub opaque: bool,
512 /// The names of the type parameters.
513 pub parameters: Vec<EcoString>,
514 /// Once type checked this field will contain the type information for the
515 /// type parameters.
516 pub typed_parameters: Vec<T>,
517}
518
519impl<T> CustomType<T> {
520 /// The `location` field of a `CustomType` is only the location of `pub type
521 /// TheName`. This method returns a `SrcSpan` that includes the entire type
522 /// definition.
523 pub fn full_location(&self) -> SrcSpan {
524 SrcSpan::new(self.location.start, self.end_position)
525 }
526}
527
528pub type UntypedTypeAlias = TypeAlias<()>;
529
530#[derive(Debug, Clone, PartialEq, Eq)]
531/// A new name for an existing type
532///
533/// # Example(s)
534///
535/// ```gleam
536/// pub type Headers =
537/// List(#(String, String))
538/// ```
539pub struct TypeAlias<T> {
540 pub location: SrcSpan,
541 pub alias: EcoString,
542 pub parameters: Vec<EcoString>,
543 pub type_ast: TypeAst,
544 pub type_: T,
545 pub publicity: Publicity,
546 pub documentation: Option<EcoString>,
547 pub deprecation: Deprecation,
548}
549
550pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, EcoString, EcoString>;
551pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
552
553#[derive(Debug, Clone, PartialEq, Eq)]
554pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
555 Function(Function<T, Expr>),
556
557 TypeAlias(TypeAlias<T>),
558
559 CustomType(CustomType<T>),
560
561 Import(Import<PackageName>),
562
563 ModuleConstant(ModuleConstant<T, ConstantRecordTag>),
564}
565
566impl TypedDefinition {
567 pub fn main_function(&self) -> Option<&TypedFunction> {
568 match self {
569 Definition::Function(f) if f.name == "main" => Some(f),
570 _ => None,
571 }
572 }
573
574 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
575 match self {
576 Definition::Function(function) => {
577 if let Some(found) = function.body.iter().find_map(|s| s.find_node(byte_index)) {
578 return Some(found);
579 };
580
581 if let Some(found_arg) = function
582 .arguments
583 .iter()
584 .find(|arg| arg.location.contains(byte_index))
585 {
586 return Some(Located::Arg(found_arg));
587 };
588
589 if let Some(found_statement) = function
590 .body
591 .iter()
592 .find(|statement| statement.location().contains(byte_index))
593 {
594 return Some(Located::Statement(found_statement));
595 };
596
597 // Note that the fn `.location` covers the function head, not
598 // the entire statement.
599 if function.location.contains(byte_index) {
600 Some(Located::ModuleStatement(self))
601 } else if function.full_location().contains(byte_index) {
602 Some(Located::FunctionBody(function))
603 } else {
604 None
605 }
606 }
607
608 Definition::CustomType(custom) => {
609 // Note that the custom type `.location` covers the function
610 // head, not the entire statement.
611 if custom.full_location().contains(byte_index) {
612 Some(Located::ModuleStatement(self))
613 } else {
614 None
615 }
616 }
617
618 Definition::TypeAlias(_) | Definition::Import(_) | Definition::ModuleConstant(_) => {
619 if self.location().contains(byte_index) {
620 Some(Located::ModuleStatement(self))
621 } else {
622 None
623 }
624 }
625 }
626 }
627}
628
629impl<A, B, C, E> Definition<A, B, C, E> {
630 pub fn location(&self) -> SrcSpan {
631 match self {
632 Definition::Function(Function { location, .. })
633 | Definition::Import(Import { location, .. })
634 | Definition::TypeAlias(TypeAlias { location, .. })
635 | Definition::CustomType(CustomType { location, .. })
636 | Definition::ModuleConstant(ModuleConstant { location, .. }) => *location,
637 }
638 }
639
640 /// Returns `true` if the definition is [`Import`].
641 ///
642 /// [`Import`]: Definition::Import
643 #[must_use]
644 pub fn is_import(&self) -> bool {
645 matches!(self, Self::Import(..))
646 }
647
648 /// Returns `true` if the module statement is [`Function`].
649 ///
650 /// [`Function`]: ModuleStatement::Function
651 #[must_use]
652 pub fn is_function(&self) -> bool {
653 matches!(self, Self::Function(..))
654 }
655
656 pub fn put_doc(&mut self, new_doc: EcoString) {
657 match self {
658 Definition::Import(Import { .. }) => (),
659
660 Definition::Function(Function {
661 documentation: doc, ..
662 })
663 | Definition::TypeAlias(TypeAlias {
664 documentation: doc, ..
665 })
666 | Definition::CustomType(CustomType {
667 documentation: doc, ..
668 })
669 | Definition::ModuleConstant(ModuleConstant {
670 documentation: doc, ..
671 }) => {
672 let _ = std::mem::replace(doc, Some(new_doc));
673 }
674 }
675 }
676
677 pub fn is_internal(&self) -> bool {
678 match self {
679 Definition::Function(Function { publicity, .. })
680 | Definition::CustomType(CustomType { publicity, .. })
681 | Definition::ModuleConstant(ModuleConstant { publicity, .. })
682 | Definition::TypeAlias(TypeAlias { publicity, .. }) => publicity.is_internal(),
683
684 Definition::Import(_) => false,
685 }
686 }
687}
688
689#[derive(Debug, Clone, PartialEq, Eq)]
690pub struct UnqualifiedImport {
691 pub location: SrcSpan,
692 pub name: EcoString,
693 pub as_name: Option<EcoString>,
694}
695
696impl UnqualifiedImport {
697 pub fn used_name(&self) -> &EcoString {
698 self.as_name.as_ref().unwrap_or(&self.name)
699 }
700}
701
702#[derive(Debug, Clone, PartialEq, Eq, Copy, Default)]
703pub enum Layer {
704 #[default]
705 Value,
706 Type,
707}
708
709impl Layer {
710 /// Returns `true` if the layer is [`Value`].
711 pub fn is_value(&self) -> bool {
712 matches!(self, Self::Value)
713 }
714}
715
716#[derive(Debug, Clone, Copy, PartialEq, Eq)]
717pub enum BinOp {
718 // Boolean logic
719 And,
720 Or,
721
722 // Equality
723 Eq,
724 NotEq,
725
726 // Order comparison
727 LtInt,
728 LtEqInt,
729 LtFloat,
730 LtEqFloat,
731 GtEqInt,
732 GtInt,
733 GtEqFloat,
734 GtFloat,
735
736 // Maths
737 AddInt,
738 AddFloat,
739 SubInt,
740 SubFloat,
741 MultInt,
742 MultFloat,
743 DivInt,
744 DivFloat,
745 RemainderInt,
746
747 // Strings
748 Concatenate,
749}
750
751#[derive(Clone, Copy, Debug, PartialEq)]
752pub enum OperatorKind {
753 BooleanLogic,
754 Equality,
755 IntComparison,
756 FLoatComparison,
757 IntMath,
758 FloatMath,
759 StringConcatenation,
760}
761
762impl BinOp {
763 pub fn precedence(&self) -> u8 {
764 // Ensure that this matches the other precedence function for guards
765 match self {
766 Self::Or => 1,
767
768 Self::And => 2,
769
770 Self::Eq | Self::NotEq => 3,
771
772 Self::LtInt
773 | Self::LtEqInt
774 | Self::LtFloat
775 | Self::LtEqFloat
776 | Self::GtEqInt
777 | Self::GtInt
778 | Self::GtEqFloat
779 | Self::GtFloat => 4,
780
781 Self::Concatenate => 5,
782
783 // Pipe is 6
784 Self::AddInt | Self::AddFloat | Self::SubInt | Self::SubFloat => 7,
785
786 Self::MultInt
787 | Self::MultFloat
788 | Self::DivInt
789 | Self::DivFloat
790 | Self::RemainderInt => 8,
791 }
792 }
793
794 pub fn name(&self) -> &'static str {
795 match self {
796 Self::And => "&&",
797 Self::Or => "||",
798 Self::LtInt => "<",
799 Self::LtEqInt => "<=",
800 Self::LtFloat => "<.",
801 Self::LtEqFloat => "<=.",
802 Self::Eq => "==",
803 Self::NotEq => "!=",
804 Self::GtEqInt => ">=",
805 Self::GtInt => ">",
806 Self::GtEqFloat => ">=.",
807 Self::GtFloat => ">.",
808 Self::AddInt => "+",
809 Self::AddFloat => "+.",
810 Self::SubInt => "-",
811 Self::SubFloat => "-.",
812 Self::MultInt => "*",
813 Self::MultFloat => "*.",
814 Self::DivInt => "/",
815 Self::DivFloat => "/.",
816 Self::RemainderInt => "%",
817 Self::Concatenate => "<>",
818 }
819 }
820
821 pub fn operator_kind(&self) -> OperatorKind {
822 match self {
823 Self::Concatenate => OperatorKind::StringConcatenation,
824 Self::Eq | Self::NotEq => OperatorKind::Equality,
825 Self::And | Self::Or => OperatorKind::BooleanLogic,
826 Self::LtInt | Self::LtEqInt | Self::GtEqInt | Self::GtInt => {
827 OperatorKind::IntComparison
828 }
829 Self::LtFloat | Self::LtEqFloat | Self::GtEqFloat | Self::GtFloat => {
830 OperatorKind::FLoatComparison
831 }
832 Self::AddInt | Self::SubInt | Self::MultInt | Self::RemainderInt | Self::DivInt => {
833 OperatorKind::IntMath
834 }
835 Self::AddFloat | Self::SubFloat | Self::MultFloat | Self::DivFloat => {
836 OperatorKind::FloatMath
837 }
838 }
839 }
840
841 pub fn can_be_grouped_with(&self, other: &BinOp) -> bool {
842 self.operator_kind() == other.operator_kind()
843 }
844}
845
846#[derive(Debug, PartialEq, Eq, Clone)]
847pub struct CallArg<A> {
848 pub label: Option<EcoString>,
849 pub location: SrcSpan,
850 pub value: A,
851 // This is true if this argument is given as the callback in a `use`
852 // expression. In future it may also be true for pipes too. It is used to
853 // determine if we should error if an argument without a label is given or
854 // not, which is not permitted if the argument is given explicitly by the
855 // programmer rather than implicitly by Gleam's syntactic sugar.
856 pub implicit: bool,
857}
858
859impl CallArg<TypedExpr> {
860 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
861 self.value.find_node(byte_index)
862 }
863}
864
865impl CallArg<TypedPattern> {
866 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
867 self.value.find_node(byte_index)
868 }
869}
870
871impl CallArg<UntypedExpr> {
872 pub fn is_capture_hole(&self) -> bool {
873 match &self.value {
874 UntypedExpr::Var { ref name, .. } => name == CAPTURE_VARIABLE,
875 _ => false,
876 }
877 }
878}
879
880impl<T> HasLocation for CallArg<T> {
881 fn location(&self) -> SrcSpan {
882 self.location
883 }
884}
885
886#[derive(Debug, Clone, PartialEq, Eq)]
887pub struct RecordUpdateSpread {
888 pub base: Box<UntypedExpr>,
889 pub location: SrcSpan,
890}
891
892#[derive(Debug, Clone, PartialEq, Eq)]
893pub struct UntypedRecordUpdateArg {
894 pub label: EcoString,
895 pub location: SrcSpan,
896 pub value: UntypedExpr,
897}
898
899#[derive(Debug, Clone, PartialEq, Eq)]
900pub struct TypedRecordUpdateArg {
901 pub label: EcoString,
902 pub location: SrcSpan,
903 pub value: TypedExpr,
904 pub index: u32,
905}
906
907impl TypedRecordUpdateArg {
908 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
909 self.value.find_node(byte_index)
910 }
911}
912
913pub type MultiPattern<Type> = Vec<Pattern<Type>>;
914
915pub type UntypedMultiPattern = MultiPattern<()>;
916pub type TypedMultiPattern = MultiPattern<Arc<Type>>;
917
918pub type TypedClause = Clause<TypedExpr, Arc<Type>, EcoString>;
919
920pub type UntypedClause = Clause<UntypedExpr, (), ()>;
921
922#[derive(Debug, Clone, PartialEq, Eq)]
923pub struct Clause<Expr, Type, RecordTag> {
924 pub location: SrcSpan,
925 pub pattern: MultiPattern<Type>,
926 pub alternative_patterns: Vec<MultiPattern<Type>>,
927 pub guard: Option<ClauseGuard<Type, RecordTag>>,
928 pub then: Expr,
929}
930
931impl<A, B, C> Clause<A, B, C> {
932 pub fn pattern_count(&self) -> usize {
933 1 + self.alternative_patterns.len()
934 }
935}
936
937impl TypedClause {
938 pub fn location(&self) -> SrcSpan {
939 SrcSpan {
940 start: self
941 .pattern
942 .first()
943 .map(|p| p.location().start)
944 .unwrap_or_default(),
945 end: self.then.location().end,
946 }
947 }
948
949 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
950 self.pattern
951 .iter()
952 .find_map(|p| p.find_node(byte_index))
953 .or_else(|| self.then.find_node(byte_index))
954 }
955}
956
957pub type UntypedClauseGuard = ClauseGuard<(), ()>;
958pub type TypedClauseGuard = ClauseGuard<Arc<Type>, EcoString>;
959
960#[derive(Debug, Clone, PartialEq, Eq)]
961pub enum ClauseGuard<Type, RecordTag> {
962 Equals {
963 location: SrcSpan,
964 left: Box<Self>,
965 right: Box<Self>,
966 },
967
968 NotEquals {
969 location: SrcSpan,
970 left: Box<Self>,
971 right: Box<Self>,
972 },
973
974 GtInt {
975 location: SrcSpan,
976 left: Box<Self>,
977 right: Box<Self>,
978 },
979
980 GtEqInt {
981 location: SrcSpan,
982 left: Box<Self>,
983 right: Box<Self>,
984 },
985
986 LtInt {
987 location: SrcSpan,
988 left: Box<Self>,
989 right: Box<Self>,
990 },
991
992 LtEqInt {
993 location: SrcSpan,
994 left: Box<Self>,
995 right: Box<Self>,
996 },
997
998 GtFloat {
999 location: SrcSpan,
1000 left: Box<Self>,
1001 right: Box<Self>,
1002 },
1003
1004 GtEqFloat {
1005 location: SrcSpan,
1006 left: Box<Self>,
1007 right: Box<Self>,
1008 },
1009
1010 LtFloat {
1011 location: SrcSpan,
1012 left: Box<Self>,
1013 right: Box<Self>,
1014 },
1015
1016 LtEqFloat {
1017 location: SrcSpan,
1018 left: Box<Self>,
1019 right: Box<Self>,
1020 },
1021
1022 Or {
1023 location: SrcSpan,
1024 left: Box<Self>,
1025 right: Box<Self>,
1026 },
1027
1028 And {
1029 location: SrcSpan,
1030 left: Box<Self>,
1031 right: Box<Self>,
1032 },
1033
1034 Not {
1035 location: SrcSpan,
1036 expression: Box<Self>,
1037 },
1038
1039 Var {
1040 location: SrcSpan,
1041 type_: Type,
1042 name: EcoString,
1043 },
1044
1045 TupleIndex {
1046 location: SrcSpan,
1047 index: u64,
1048 type_: Type,
1049 tuple: Box<Self>,
1050 },
1051
1052 FieldAccess {
1053 location: SrcSpan,
1054 index: Option<u64>,
1055 label: EcoString,
1056 type_: Type,
1057 container: Box<Self>,
1058 },
1059
1060 ModuleSelect {
1061 location: SrcSpan,
1062 type_: Type,
1063 label: EcoString,
1064 module_name: EcoString,
1065 module_alias: EcoString,
1066 literal: Constant<Type, RecordTag>,
1067 },
1068
1069 Constant(Constant<Type, RecordTag>),
1070}
1071
1072impl<A, B> ClauseGuard<A, B> {
1073 pub fn location(&self) -> SrcSpan {
1074 match self {
1075 ClauseGuard::Constant(constant) => constant.location(),
1076 ClauseGuard::Or { location, .. }
1077 | ClauseGuard::And { location, .. }
1078 | ClauseGuard::Not { location, .. }
1079 | ClauseGuard::Var { location, .. }
1080 | ClauseGuard::TupleIndex { location, .. }
1081 | ClauseGuard::Equals { location, .. }
1082 | ClauseGuard::NotEquals { location, .. }
1083 | ClauseGuard::GtInt { location, .. }
1084 | ClauseGuard::GtEqInt { location, .. }
1085 | ClauseGuard::LtInt { location, .. }
1086 | ClauseGuard::LtEqInt { location, .. }
1087 | ClauseGuard::GtFloat { location, .. }
1088 | ClauseGuard::GtEqFloat { location, .. }
1089 | ClauseGuard::LtFloat { location, .. }
1090 | ClauseGuard::FieldAccess { location, .. }
1091 | ClauseGuard::LtEqFloat { location, .. }
1092 | ClauseGuard::ModuleSelect { location, .. } => *location,
1093 }
1094 }
1095
1096 pub fn precedence(&self) -> u8 {
1097 // Ensure that this matches the other precedence function for guards
1098 match self.bin_op_name() {
1099 Some(name) => name.precedence(),
1100 None => u8::MAX,
1101 }
1102 }
1103
1104 pub fn bin_op_name(&self) -> Option<BinOp> {
1105 match self {
1106 ClauseGuard::Or { .. } => Some(BinOp::Or),
1107 ClauseGuard::And { .. } => Some(BinOp::And),
1108 ClauseGuard::Equals { .. } => Some(BinOp::Eq),
1109 ClauseGuard::NotEquals { .. } => Some(BinOp::NotEq),
1110 ClauseGuard::GtInt { .. } => Some(BinOp::GtInt),
1111 ClauseGuard::GtEqInt { .. } => Some(BinOp::GtEqInt),
1112 ClauseGuard::LtInt { .. } => Some(BinOp::LtInt),
1113 ClauseGuard::LtEqInt { .. } => Some(BinOp::LtEqInt),
1114 ClauseGuard::GtFloat { .. } => Some(BinOp::GtFloat),
1115 ClauseGuard::GtEqFloat { .. } => Some(BinOp::GtEqFloat),
1116 ClauseGuard::LtFloat { .. } => Some(BinOp::LtFloat),
1117 ClauseGuard::LtEqFloat { .. } => Some(BinOp::LtEqFloat),
1118
1119 ClauseGuard::Constant(_)
1120 | ClauseGuard::Var { .. }
1121 | ClauseGuard::Not { .. }
1122 | ClauseGuard::TupleIndex { .. }
1123 | ClauseGuard::FieldAccess { .. }
1124 | ClauseGuard::ModuleSelect { .. } => None,
1125 }
1126 }
1127}
1128
1129impl TypedClauseGuard {
1130 pub fn type_(&self) -> Arc<Type> {
1131 match self {
1132 ClauseGuard::Var { type_, .. } => type_.clone(),
1133 ClauseGuard::TupleIndex { type_, .. } => type_.clone(),
1134 ClauseGuard::FieldAccess { type_, .. } => type_.clone(),
1135 ClauseGuard::ModuleSelect { type_, .. } => type_.clone(),
1136 ClauseGuard::Constant(constant) => constant.type_(),
1137
1138 ClauseGuard::Or { .. }
1139 | ClauseGuard::Not { .. }
1140 | ClauseGuard::And { .. }
1141 | ClauseGuard::Equals { .. }
1142 | ClauseGuard::NotEquals { .. }
1143 | ClauseGuard::GtInt { .. }
1144 | ClauseGuard::GtEqInt { .. }
1145 | ClauseGuard::LtInt { .. }
1146 | ClauseGuard::LtEqInt { .. }
1147 | ClauseGuard::GtFloat { .. }
1148 | ClauseGuard::GtEqFloat { .. }
1149 | ClauseGuard::LtFloat { .. }
1150 | ClauseGuard::LtEqFloat { .. } => type_::bool(),
1151 }
1152 }
1153}
1154
1155#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
1156pub struct SrcSpan {
1157 pub start: u32,
1158 pub end: u32,
1159}
1160
1161impl SrcSpan {
1162 pub fn new(start: u32, end: u32) -> Self {
1163 Self { start, end }
1164 }
1165
1166 pub fn contains(&self, byte_index: u32) -> bool {
1167 byte_index >= self.start && byte_index < self.end
1168 }
1169}
1170
1171#[derive(Debug, PartialEq, Eq, Clone)]
1172pub struct DefinitionLocation<'module> {
1173 pub module: Option<&'module str>,
1174 pub span: SrcSpan,
1175}
1176
1177pub type UntypedPattern = Pattern<()>;
1178pub type TypedPattern = Pattern<Arc<Type>>;
1179
1180#[derive(Debug, Clone, PartialEq, Eq)]
1181pub enum Pattern<Type> {
1182 Int {
1183 location: SrcSpan,
1184 value: EcoString,
1185 },
1186
1187 Float {
1188 location: SrcSpan,
1189 value: EcoString,
1190 },
1191
1192 String {
1193 location: SrcSpan,
1194 value: EcoString,
1195 },
1196
1197 /// The creation of a variable.
1198 /// e.g. `assert [this_is_a_var, .._] = x`
1199 Variable {
1200 location: SrcSpan,
1201 name: EcoString,
1202 type_: Type,
1203 },
1204
1205 /// A reference to a variable in a bit array. This is always a variable
1206 /// being used rather than a new variable being assigned.
1207 /// e.g. `assert <<y:size(somevar)>> = x`
1208 VarUsage {
1209 location: SrcSpan,
1210 name: EcoString,
1211 constructor: Option<ValueConstructor>,
1212 type_: Type,
1213 },
1214
1215 /// A name given to a sub-pattern using the `as` keyword.
1216 /// e.g. `assert #(1, [_, _] as the_list) = x`
1217 Assign {
1218 name: EcoString,
1219 location: SrcSpan,
1220 pattern: Box<Self>,
1221 },
1222
1223 /// A pattern that binds to any value but does not assign a variable.
1224 /// Always starts with an underscore.
1225 Discard {
1226 name: EcoString,
1227 location: SrcSpan,
1228 type_: Type,
1229 },
1230
1231 List {
1232 location: SrcSpan,
1233 elements: Vec<Self>,
1234 tail: Option<Box<Self>>,
1235 type_: Type,
1236 },
1237
1238 /// The constructor for a custom type. Starts with an uppercase letter.
1239 Constructor {
1240 location: SrcSpan,
1241 name: EcoString,
1242 arguments: Vec<CallArg<Self>>,
1243 module: Option<EcoString>,
1244 constructor: Inferred<PatternConstructor>,
1245 with_spread: bool,
1246 type_: Type,
1247 },
1248
1249 Tuple {
1250 location: SrcSpan,
1251 elems: Vec<Self>,
1252 },
1253
1254 BitArray {
1255 location: SrcSpan,
1256 segments: Vec<BitArraySegment<Self, Type>>,
1257 },
1258
1259 // "prefix" <> variable
1260 StringPrefix {
1261 location: SrcSpan,
1262 left_location: SrcSpan,
1263 left_side_assignment: Option<(EcoString, SrcSpan)>,
1264 right_location: SrcSpan,
1265 left_side_string: EcoString,
1266 /// The variable on the right hand side of the `<>`.
1267 right_side_assignment: AssignName,
1268 },
1269}
1270
1271impl Default for Inferred<()> {
1272 fn default() -> Self {
1273 Self::Unknown
1274 }
1275}
1276
1277#[derive(Debug, Clone, PartialEq, Eq)]
1278pub enum AssignName {
1279 Variable(EcoString),
1280 Discard(EcoString),
1281}
1282
1283impl AssignName {
1284 pub fn name(&self) -> &str {
1285 match self {
1286 AssignName::Variable(name) | AssignName::Discard(name) => name,
1287 }
1288 }
1289
1290 pub fn to_arg_names(self) -> ArgNames {
1291 match self {
1292 AssignName::Variable(name) => ArgNames::Named { name },
1293 AssignName::Discard(name) => ArgNames::Discard { name },
1294 }
1295 }
1296
1297 pub fn assigned_name(&self) -> Option<&str> {
1298 match self {
1299 AssignName::Variable(name) => Some(name),
1300 AssignName::Discard(_) => None,
1301 }
1302 }
1303}
1304
1305impl<A> Pattern<A> {
1306 pub fn location(&self) -> SrcSpan {
1307 match self {
1308 Pattern::Assign { pattern, .. } => pattern.location(),
1309 Pattern::Int { location, .. }
1310 | Pattern::Variable { location, .. }
1311 | Pattern::VarUsage { location, .. }
1312 | Pattern::List { location, .. }
1313 | Pattern::Float { location, .. }
1314 | Pattern::Discard { location, .. }
1315 | Pattern::String { location, .. }
1316 | Pattern::Tuple { location, .. }
1317 | Pattern::Constructor { location, .. }
1318 | Pattern::StringPrefix { location, .. }
1319 | Pattern::BitArray { location, .. } => *location,
1320 }
1321 }
1322
1323 /// Returns `true` if the pattern is [`Discard`].
1324 ///
1325 /// [`Discard`]: Pattern::Discard
1326 pub fn is_discard(&self) -> bool {
1327 matches!(self, Self::Discard { .. })
1328 }
1329}
1330
1331impl TypedPattern {
1332 pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
1333 match self {
1334 Pattern::Int { .. }
1335 | Pattern::Float { .. }
1336 | Pattern::String { .. }
1337 | Pattern::Variable { .. }
1338 | Pattern::VarUsage { .. }
1339 | Pattern::Assign { .. }
1340 | Pattern::Discard { .. }
1341 | Pattern::List { .. }
1342 | Pattern::Tuple { .. }
1343 | Pattern::BitArray { .. }
1344 | Pattern::StringPrefix { .. } => None,
1345
1346 Pattern::Constructor { constructor, .. } => constructor.definition_location(),
1347 }
1348 }
1349
1350 pub fn get_documentation(&self) -> Option<&str> {
1351 match self {
1352 Pattern::Int { .. }
1353 | Pattern::Float { .. }
1354 | Pattern::String { .. }
1355 | Pattern::Variable { .. }
1356 | Pattern::VarUsage { .. }
1357 | Pattern::Assign { .. }
1358 | Pattern::Discard { .. }
1359 | Pattern::List { .. }
1360 | Pattern::Tuple { .. }
1361 | Pattern::BitArray { .. }
1362 | Pattern::StringPrefix { .. } => None,
1363
1364 Pattern::Constructor { constructor, .. } => constructor.get_documentation(),
1365 }
1366 }
1367
1368 pub fn type_(&self) -> Arc<Type> {
1369 match self {
1370 Pattern::Int { .. } => type_::int(),
1371 Pattern::Float { .. } => type_::float(),
1372 Pattern::String { .. } => type_::string(),
1373 Pattern::BitArray { .. } => type_::bits(),
1374 Pattern::StringPrefix { .. } => type_::string(),
1375
1376 Pattern::Variable { type_, .. }
1377 | Pattern::List { type_, .. }
1378 | Pattern::VarUsage { type_, .. }
1379 | Pattern::Constructor { type_, .. } => type_.clone(),
1380
1381 Pattern::Assign { pattern, .. } => pattern.type_(),
1382
1383 Pattern::Discard { type_, .. } => type_.clone(),
1384
1385 Pattern::Tuple { elems, .. } => type_::tuple(elems.iter().map(|p| p.type_()).collect()),
1386 }
1387 }
1388
1389 fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1390 if !self.location().contains(byte_index) {
1391 return None;
1392 }
1393
1394 if let Pattern::Variable { name, .. } = self {
1395 // For pipes the pattern can't be pointed to
1396 if name.as_str().eq(PIPE_VARIABLE) {
1397 return None;
1398 }
1399 }
1400
1401 match self {
1402 Pattern::Int { .. }
1403 | Pattern::Float { .. }
1404 | Pattern::String { .. }
1405 | Pattern::Variable { .. }
1406 | Pattern::VarUsage { .. }
1407 | Pattern::Assign { .. }
1408 | Pattern::Discard { .. }
1409 | Pattern::BitArray { .. }
1410 | Pattern::StringPrefix { .. } => Some(Located::Pattern(self)),
1411
1412 Pattern::Constructor { arguments, .. } => {
1413 arguments.iter().find_map(|arg| arg.find_node(byte_index))
1414 }
1415 Pattern::List { elements, tail, .. } => elements
1416 .iter()
1417 .find_map(|p| p.find_node(byte_index))
1418 .or_else(|| tail.as_ref().and_then(|p| p.find_node(byte_index))),
1419
1420 Pattern::Tuple { elems, .. } => elems.iter().find_map(|p| p.find_node(byte_index)),
1421 }
1422 .or(Some(Located::Pattern(self)))
1423 }
1424}
1425impl<A> HasLocation for Pattern<A> {
1426 fn location(&self) -> SrcSpan {
1427 self.location()
1428 }
1429}
1430
1431#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1432pub enum AssignmentKind {
1433 // let x = ...
1434 Let,
1435 // let assert x = ...
1436 Assert,
1437}
1438
1439impl AssignmentKind {
1440 pub(crate) fn performs_exhaustiveness_check(&self) -> bool {
1441 match self {
1442 AssignmentKind::Let => true,
1443 AssignmentKind::Assert => false,
1444 }
1445 }
1446
1447 /// Returns `true` if the assignment kind is [`Assert`].
1448 ///
1449 /// [`Assert`]: AssignmentKind::Assert
1450 #[must_use]
1451 pub fn is_assert(&self) -> bool {
1452 matches!(self, Self::Assert)
1453 }
1454}
1455
1456// BitArrays
1457
1458pub type UntypedExprBitArraySegment = BitArraySegment<UntypedExpr, ()>;
1459pub type TypedExprBitArraySegment = BitArraySegment<TypedExpr, Arc<Type>>;
1460
1461pub type UntypedConstantBitArraySegment = BitArraySegment<UntypedConstant, ()>;
1462pub type TypedConstantBitArraySegment = BitArraySegment<TypedConstant, Arc<Type>>;
1463
1464pub type UntypedPatternBitArraySegment = BitArraySegment<UntypedPattern, ()>;
1465pub type TypedPatternBitArraySegment = BitArraySegment<TypedPattern, Arc<Type>>;
1466
1467#[derive(Debug, Clone, PartialEq, Eq)]
1468pub struct BitArraySegment<Value, Type> {
1469 pub location: SrcSpan,
1470 pub value: Box<Value>,
1471 pub options: Vec<BitArrayOption<Value>>,
1472 pub type_: Type,
1473}
1474
1475impl TypedExprBitArraySegment {
1476 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1477 self.value.find_node(byte_index)
1478 }
1479}
1480
1481pub type TypedConstantBitArraySegmentOption = BitArrayOption<TypedConstant>;
1482
1483#[derive(Debug, PartialEq, Eq, Clone)]
1484pub enum BitArrayOption<Value> {
1485 Bytes {
1486 location: SrcSpan,
1487 },
1488
1489 Int {
1490 location: SrcSpan,
1491 },
1492
1493 Float {
1494 location: SrcSpan,
1495 },
1496
1497 Bits {
1498 location: SrcSpan,
1499 },
1500
1501 Utf8 {
1502 location: SrcSpan,
1503 },
1504
1505 Utf16 {
1506 location: SrcSpan,
1507 },
1508
1509 Utf32 {
1510 location: SrcSpan,
1511 },
1512
1513 Utf8Codepoint {
1514 location: SrcSpan,
1515 },
1516
1517 Utf16Codepoint {
1518 location: SrcSpan,
1519 },
1520
1521 Utf32Codepoint {
1522 location: SrcSpan,
1523 },
1524
1525 Signed {
1526 location: SrcSpan,
1527 },
1528
1529 Unsigned {
1530 location: SrcSpan,
1531 },
1532
1533 Big {
1534 location: SrcSpan,
1535 },
1536
1537 Little {
1538 location: SrcSpan,
1539 },
1540
1541 Native {
1542 location: SrcSpan,
1543 },
1544
1545 Size {
1546 location: SrcSpan,
1547 value: Box<Value>,
1548 short_form: bool,
1549 },
1550
1551 Unit {
1552 location: SrcSpan,
1553 value: u8,
1554 },
1555}
1556
1557impl<A> BitArrayOption<A> {
1558 pub fn value(&self) -> Option<&A> {
1559 match self {
1560 BitArrayOption::Size { value, .. } => Some(value),
1561 _ => None,
1562 }
1563 }
1564
1565 pub fn location(&self) -> SrcSpan {
1566 match self {
1567 BitArrayOption::Bytes { location }
1568 | BitArrayOption::Int { location }
1569 | BitArrayOption::Float { location }
1570 | BitArrayOption::Bits { location }
1571 | BitArrayOption::Utf8 { location }
1572 | BitArrayOption::Utf16 { location }
1573 | BitArrayOption::Utf32 { location }
1574 | BitArrayOption::Utf8Codepoint { location }
1575 | BitArrayOption::Utf16Codepoint { location }
1576 | BitArrayOption::Utf32Codepoint { location }
1577 | BitArrayOption::Signed { location }
1578 | BitArrayOption::Unsigned { location }
1579 | BitArrayOption::Big { location }
1580 | BitArrayOption::Little { location }
1581 | BitArrayOption::Native { location }
1582 | BitArrayOption::Size { location, .. }
1583 | BitArrayOption::Unit { location, .. } => *location,
1584 }
1585 }
1586
1587 pub fn label(&self) -> EcoString {
1588 match self {
1589 BitArrayOption::Bytes { .. } => "bytes".into(),
1590 BitArrayOption::Int { .. } => "int".into(),
1591 BitArrayOption::Float { .. } => "float".into(),
1592 BitArrayOption::Bits { .. } => "bits".into(),
1593 BitArrayOption::Utf8 { .. } => "utf8".into(),
1594 BitArrayOption::Utf16 { .. } => "utf16".into(),
1595 BitArrayOption::Utf32 { .. } => "utf32".into(),
1596 BitArrayOption::Utf8Codepoint { .. } => "utf8_codepoint".into(),
1597 BitArrayOption::Utf16Codepoint { .. } => "utf16_codepoint".into(),
1598 BitArrayOption::Utf32Codepoint { .. } => "utf32_codepoint".into(),
1599 BitArrayOption::Signed { .. } => "signed".into(),
1600 BitArrayOption::Unsigned { .. } => "unsigned".into(),
1601 BitArrayOption::Big { .. } => "big".into(),
1602 BitArrayOption::Little { .. } => "little".into(),
1603 BitArrayOption::Native { .. } => "native".into(),
1604 BitArrayOption::Size { .. } => "size".into(),
1605 BitArrayOption::Unit { .. } => "unit".into(),
1606 }
1607 }
1608}
1609
1610#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1611pub enum TodoKind {
1612 Keyword,
1613 EmptyFunction,
1614 IncompleteUse,
1615}
1616
1617#[derive(Debug, Default)]
1618pub struct GroupedStatements {
1619 pub functions: Vec<Function<(), UntypedExpr>>,
1620 pub constants: Vec<UntypedModuleConstant>,
1621 pub custom_types: Vec<CustomType<()>>,
1622 pub imports: Vec<Import<()>>,
1623 pub type_aliases: Vec<TypeAlias<()>>,
1624}
1625
1626impl GroupedStatements {
1627 pub fn new(statements: impl IntoIterator<Item = UntypedDefinition>) -> Self {
1628 let mut this = Self::default();
1629
1630 for statement in statements {
1631 this.add(statement)
1632 }
1633
1634 this
1635 }
1636
1637 pub fn is_empty(&self) -> bool {
1638 self.len() == 0
1639 }
1640
1641 pub fn len(&self) -> usize {
1642 let Self {
1643 custom_types,
1644 functions,
1645 constants,
1646 imports,
1647 type_aliases,
1648 } = self;
1649 functions.len() + constants.len() + imports.len() + custom_types.len() + type_aliases.len()
1650 }
1651
1652 fn add(&mut self, statement: UntypedDefinition) {
1653 match statement {
1654 Definition::Import(i) => self.imports.push(i),
1655 Definition::Function(f) => self.functions.push(f),
1656 Definition::TypeAlias(t) => self.type_aliases.push(t),
1657 Definition::CustomType(c) => self.custom_types.push(c),
1658 Definition::ModuleConstant(c) => self.constants.push(c),
1659 }
1660 }
1661}
1662
1663/// A statement with in a function body.
1664#[derive(Debug, Clone, PartialEq, Eq)]
1665pub enum Statement<TypeT, ExpressionT> {
1666 /// A bare expression that is not assigned to any variable.
1667 Expression(ExpressionT),
1668 /// Assigning an expression to variables using a pattern.
1669 Assignment(Assignment<TypeT, ExpressionT>),
1670 /// A `use` expression.
1671 Use(Use),
1672}
1673
1674pub type TypedStatement = Statement<Arc<Type>, TypedExpr>;
1675pub type UntypedStatement = Statement<(), UntypedExpr>;
1676
1677impl<T, E> Statement<T, E> {
1678 /// Returns `true` if the statement is [`Expression`].
1679 ///
1680 /// [`Expression`]: Statement::Expression
1681 #[must_use]
1682 pub fn is_expression(&self) -> bool {
1683 matches!(self, Self::Expression(..))
1684 }
1685}
1686
1687impl UntypedStatement {
1688 pub fn location(&self) -> SrcSpan {
1689 match self {
1690 Statement::Expression(expression) => expression.location(),
1691 Statement::Assignment(assignment) => assignment.location,
1692 Statement::Use(use_) => use_.location,
1693 }
1694 }
1695
1696 pub fn start_byte_index(&self) -> u32 {
1697 match self {
1698 Statement::Expression(expression) => expression.start_byte_index(),
1699 Statement::Assignment(assignment) => assignment.location.start,
1700 Statement::Use(use_) => use_.location.start,
1701 }
1702 }
1703
1704 pub fn is_placeholder(&self) -> bool {
1705 match self {
1706 Statement::Expression(expression) => expression.is_placeholder(),
1707 Statement::Assignment(_) | Statement::Use(_) => false,
1708 }
1709 }
1710}
1711
1712impl TypedStatement {
1713 pub fn is_println(&self) -> bool {
1714 match self {
1715 Statement::Expression(e) => e.is_println(),
1716 Statement::Assignment(_) => false,
1717 Statement::Use(_) => false,
1718 }
1719 }
1720
1721 pub fn is_non_pipe_expression(&self) -> bool {
1722 match self {
1723 Statement::Expression(expression) => !expression.is_pipeline(),
1724 _ => false,
1725 }
1726 }
1727
1728 pub fn location(&self) -> SrcSpan {
1729 match self {
1730 Statement::Expression(expression) => expression.location(),
1731 Statement::Assignment(assignment) => assignment.location,
1732 Statement::Use(use_) => use_.location,
1733 }
1734 }
1735
1736 pub fn type_(&self) -> Arc<Type> {
1737 match self {
1738 Statement::Expression(expression) => expression.type_(),
1739 Statement::Assignment(assignment) => assignment.type_(),
1740 Statement::Use(_use) => unreachable!("Use must not exist for typed code"),
1741 }
1742 }
1743
1744 pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> {
1745 match self {
1746 Statement::Expression(expression) => expression.definition_location(),
1747 Statement::Assignment(_) => None,
1748 Statement::Use(_) => None,
1749 }
1750 }
1751
1752 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1753 match self {
1754 Statement::Use(_) => None,
1755 Statement::Expression(expression) => expression.find_node(byte_index),
1756 Statement::Assignment(assignment) => assignment.find_node(byte_index).or_else(|| {
1757 if assignment.location.contains(byte_index) {
1758 Some(Located::Statement(self))
1759 } else {
1760 None
1761 }
1762 }),
1763 }
1764 }
1765
1766 pub fn type_defining_location(&self) -> SrcSpan {
1767 match self {
1768 Statement::Expression(expression) => expression.type_defining_location(),
1769 Statement::Assignment(assignment) => assignment.location,
1770 Statement::Use(use_) => use_.location,
1771 }
1772 }
1773}
1774
1775#[derive(Debug, Clone, PartialEq, Eq)]
1776pub struct Assignment<TypeT, ExpressionT> {
1777 pub location: SrcSpan,
1778 pub value: Box<ExpressionT>,
1779 pub pattern: Pattern<TypeT>,
1780 pub kind: AssignmentKind,
1781 pub annotation: Option<TypeAst>,
1782}
1783
1784pub type TypedAssignment = Assignment<Arc<Type>, TypedExpr>;
1785pub type UntypedAssignment = Assignment<(), UntypedExpr>;
1786
1787impl TypedAssignment {
1788 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1789 self.pattern
1790 .find_node(byte_index)
1791 .or_else(|| self.value.find_node(byte_index))
1792 }
1793
1794 pub fn type_(&self) -> Arc<Type> {
1795 self.value.type_()
1796 }
1797}
1798
1799#[derive(Debug, Clone, PartialEq, Eq)]
1800pub struct UseAssignment {
1801 pub location: SrcSpan,
1802 pub pattern: UntypedPattern,
1803 pub annotation: Option<TypeAst>,
1804}