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