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