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