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