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