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