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