Fork of daniellemaywood.uk/gleam — Wasm codegen work
105 kB
3382 lines
1mod constant;
2mod typed;
3mod untyped;
4
5#[cfg(test)]
6mod tests;
7pub mod visit;
8
9pub use self::typed::TypedExpr;
10pub use self::untyped::{FunctionLiteralKind, UntypedExpr};
11
12pub use self::constant::{Constant, TypedConstant, UntypedConstant};
13
14use crate::analyse::Inferred;
15use crate::bit_array;
16use crate::build::{ExpressionPosition, Located, Target, module_erlang_name};
17use crate::exhaustiveness::CompiledCase;
18use crate::parse::SpannedString;
19use crate::type_::error::VariableOrigin;
20use crate::type_::expression::{Implementations, Purity};
21use crate::type_::printer::Names;
22use crate::type_::{
23 self, Deprecation, HasType, ModuleValueConstructor, PatternConstructor, Type, TypedCallArg,
24 ValueConstructor, nil,
25};
26use std::collections::HashSet;
27use std::sync::Arc;
28
29use ecow::EcoString;
30use num_bigint::{BigInt, Sign};
31use num_traits::{One, ToPrimitive};
32#[cfg(test)]
33use pretty_assertions::assert_eq;
34use vec1::Vec1;
35
36pub const PIPE_VARIABLE: &str = "_pipe";
37pub const USE_ASSIGNMENT_VARIABLE: &str = "_use";
38pub const RECORD_UPDATE_VARIABLE: &str = "_record";
39pub const ASSERT_FAIL_VARIABLE: &str = "_assert_fail";
40pub const ASSERT_SUBJECT_VARIABLE: &str = "_assert_subject";
41pub const CAPTURE_VARIABLE: &str = "_capture";
42pub const BLOCK_VARIABLE: &str = "_block";
43
44pub trait HasLocation {
45 fn location(&self) -> SrcSpan;
46}
47
48pub type TypedModule = Module<type_::ModuleInterface, TypedDefinition>;
49
50pub type UntypedModule = Module<(), TargetedDefinition>;
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct Module<Info, Definitions> {
54 pub name: EcoString,
55 pub documentation: Vec<EcoString>,
56 pub type_info: Info,
57 pub definitions: Vec<Definitions>,
58 pub names: Names,
59 /// The source byte locations of definition that are unused.
60 /// This is used in code generation to know when definitions can be safely omitted.
61 pub unused_definition_positions: HashSet<u32>,
62}
63
64impl<Info, Definitions> Module<Info, Definitions> {
65 pub fn erlang_name(&self) -> EcoString {
66 module_erlang_name(&self.name)
67 }
68}
69
70impl TypedModule {
71 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
72 self.definitions
73 .iter()
74 .find_map(|definition| definition.find_node(byte_index))
75 }
76
77 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
78 self.definitions
79 .iter()
80 .find_map(|definition| definition.find_statement(byte_index))
81 }
82}
83
84/// The `@target(erlang)` and `@target(javascript)` attributes can be used to
85/// mark a definition as only being for a specific target.
86///
87/// ```gleam
88/// const x: Int = 1
89///
90/// @target(erlang)
91/// pub fn main(a) { ...}
92/// ```
93///
94#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct TargetedDefinition {
96 pub definition: UntypedDefinition,
97 pub target: Option<Target>,
98}
99
100impl TargetedDefinition {
101 pub fn is_for(&self, target: Target) -> bool {
102 self.target.map(|t| t == target).unwrap_or(true)
103 }
104}
105
106impl UntypedModule {
107 pub fn dependencies(&self, target: Target) -> Vec<(EcoString, SrcSpan)> {
108 self.iter_definitions(target)
109 .flat_map(|definition| match definition {
110 Definition::Import(Import {
111 module, location, ..
112 }) => Some((module.clone(), *location)),
113 _ => None,
114 })
115 .collect()
116 }
117
118 pub fn iter_definitions(&self, target: Target) -> impl Iterator<Item = &UntypedDefinition> {
119 self.definitions
120 .iter()
121 .filter(move |definition| definition.is_for(target))
122 .map(|definition| &definition.definition)
123 }
124
125 pub fn into_iter_definitions(self, target: Target) -> impl Iterator<Item = UntypedDefinition> {
126 self.definitions
127 .into_iter()
128 .filter(move |definition| definition.is_for(target))
129 .map(|definition| definition.definition)
130 }
131}
132
133#[test]
134fn module_dependencies_test() {
135 let parsed = crate::parse::parse_module(
136 camino::Utf8PathBuf::from("test/path"),
137 "import one
138 @target(erlang)
139 import two
140
141 @target(javascript)
142 import three
143
144 import four",
145 &crate::warning::WarningEmitter::null(),
146 )
147 .expect("syntax error");
148 let module = parsed.module;
149
150 assert_eq!(
151 vec![
152 ("one".into(), SrcSpan::new(0, 10)),
153 ("two".into(), SrcSpan::new(45, 55)),
154 ("four".into(), SrcSpan::new(118, 129)),
155 ],
156 module.dependencies(Target::Erlang)
157 );
158}
159
160pub type TypedArg = Arg<Arc<Type>>;
161pub type UntypedArg = Arg<()>;
162
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct Arg<T> {
165 pub names: ArgNames,
166 pub location: SrcSpan,
167 pub annotation: Option<TypeAst>,
168 pub type_: T,
169}
170
171impl<A> Arg<A> {
172 pub fn set_type<B>(self, t: B) -> Arg<B> {
173 Arg {
174 type_: t,
175 names: self.names,
176 location: self.location,
177 annotation: self.annotation,
178 }
179 }
180
181 pub fn get_variable_name(&self) -> Option<&EcoString> {
182 self.names.get_variable_name()
183 }
184
185 pub fn is_capture_hole(&self) -> bool {
186 match &self.names {
187 ArgNames::Named { name, .. } if name == CAPTURE_VARIABLE => true,
188 _ => false,
189 }
190 }
191}
192
193impl TypedArg {
194 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
195 if self.location.contains(byte_index) {
196 if let Some(annotation) = &self.annotation {
197 return annotation
198 .find_node(byte_index, self.type_.clone())
199 .or(Some(Located::Arg(self)));
200 }
201 Some(Located::Arg(self))
202 } else {
203 None
204 }
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub enum ArgNames {
210 Discard {
211 name: EcoString,
212 location: SrcSpan,
213 },
214 LabelledDiscard {
215 label: EcoString,
216 label_location: SrcSpan,
217 name: EcoString,
218 name_location: SrcSpan,
219 },
220 Named {
221 name: EcoString,
222 location: SrcSpan,
223 },
224 NamedLabelled {
225 label: EcoString,
226 label_location: SrcSpan,
227 name: EcoString,
228 name_location: SrcSpan,
229 },
230}
231
232impl ArgNames {
233 pub fn get_label(&self) -> Option<&EcoString> {
234 match self {
235 ArgNames::Discard { .. } | ArgNames::Named { .. } => None,
236 ArgNames::LabelledDiscard { label, .. } | ArgNames::NamedLabelled { label, .. } => {
237 Some(label)
238 }
239 }
240 }
241 pub fn get_variable_name(&self) -> Option<&EcoString> {
242 match self {
243 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => None,
244 ArgNames::NamedLabelled { name, .. } | ArgNames::Named { name, .. } => Some(name),
245 }
246 }
247}
248
249pub type TypedRecordConstructor = RecordConstructor<Arc<Type>>;
250
251#[derive(Debug, Clone, PartialEq, Eq)]
252pub struct RecordConstructor<T> {
253 pub location: SrcSpan,
254 pub name_location: SrcSpan,
255 pub name: EcoString,
256 pub arguments: Vec<RecordConstructorArg<T>>,
257 pub documentation: Option<(u32, EcoString)>,
258 pub deprecation: Deprecation,
259}
260
261impl<A> RecordConstructor<A> {
262 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
263 self.documentation = Some(new_doc);
264 }
265}
266
267pub type TypedRecordConstructorArg = RecordConstructorArg<Arc<Type>>;
268
269#[derive(Debug, Clone, PartialEq, Eq)]
270pub struct RecordConstructorArg<T> {
271 pub label: Option<SpannedString>,
272 pub ast: TypeAst,
273 pub location: SrcSpan,
274 pub type_: T,
275 pub doc: Option<(u32, EcoString)>,
276}
277
278impl<T: PartialEq> RecordConstructorArg<T> {
279 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
280 self.doc = Some(new_doc);
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
285pub struct TypeAstConstructor {
286 pub location: SrcSpan,
287 pub name_location: SrcSpan,
288 pub module: Option<(EcoString, SrcSpan)>,
289 pub name: EcoString,
290 pub arguments: Vec<TypeAst>,
291}
292
293#[derive(Debug, Clone, PartialEq, Eq)]
294pub struct TypeAstFn {
295 pub location: SrcSpan,
296 pub arguments: Vec<TypeAst>,
297 pub return_: Box<TypeAst>,
298}
299
300#[derive(Debug, Clone, PartialEq, Eq)]
301pub struct TypeAstVar {
302 pub location: SrcSpan,
303 pub name: EcoString,
304}
305
306#[derive(Debug, Clone, PartialEq, Eq)]
307pub struct TypeAstTuple {
308 pub location: SrcSpan,
309 pub elements: Vec<TypeAst>,
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
313pub struct TypeAstHole {
314 pub location: SrcSpan,
315 pub name: EcoString,
316}
317
318#[derive(Debug, Clone, PartialEq, Eq)]
319pub enum TypeAst {
320 Constructor(TypeAstConstructor),
321 Fn(TypeAstFn),
322 Var(TypeAstVar),
323 Tuple(TypeAstTuple),
324 Hole(TypeAstHole),
325}
326
327impl TypeAst {
328 pub fn location(&self) -> SrcSpan {
329 match self {
330 TypeAst::Fn(TypeAstFn { location, .. })
331 | TypeAst::Var(TypeAstVar { location, .. })
332 | TypeAst::Hole(TypeAstHole { location, .. })
333 | TypeAst::Tuple(TypeAstTuple { location, .. })
334 | TypeAst::Constructor(TypeAstConstructor { location, .. }) => *location,
335 }
336 }
337
338 pub fn is_logically_equal(&self, other: &TypeAst) -> bool {
339 match self {
340 TypeAst::Constructor(TypeAstConstructor {
341 module,
342 name,
343 arguments,
344 location: _,
345 name_location: _,
346 }) => match other {
347 TypeAst::Constructor(TypeAstConstructor {
348 module: o_module,
349 name: o_name,
350 arguments: o_arguments,
351 location: _,
352 name_location: _,
353 }) => {
354 let module_name =
355 |m: &Option<(EcoString, _)>| m.as_ref().map(|(m, _)| m.clone());
356 module_name(module) == module_name(o_module)
357 && name == o_name
358 && arguments.len() == o_arguments.len()
359 && arguments
360 .iter()
361 .zip(o_arguments)
362 .all(|a| a.0.is_logically_equal(a.1))
363 }
364 _ => false,
365 },
366 TypeAst::Fn(TypeAstFn {
367 arguments,
368 return_,
369 location: _,
370 }) => match other {
371 TypeAst::Fn(TypeAstFn {
372 arguments: o_arguments,
373 return_: o_return_,
374 location: _,
375 }) => {
376 arguments.len() == o_arguments.len()
377 && arguments
378 .iter()
379 .zip(o_arguments)
380 .all(|a| a.0.is_logically_equal(a.1))
381 && return_.is_logically_equal(o_return_)
382 }
383 _ => false,
384 },
385 TypeAst::Var(TypeAstVar { name, location: _ }) => match other {
386 TypeAst::Var(TypeAstVar {
387 name: o_name,
388 location: _,
389 }) => name == o_name,
390 _ => false,
391 },
392 TypeAst::Tuple(TypeAstTuple {
393 elements,
394 location: _,
395 }) => match other {
396 TypeAst::Tuple(TypeAstTuple {
397 elements: other_elements,
398 location: _,
399 }) => {
400 elements.len() == other_elements.len()
401 && elements
402 .iter()
403 .zip(other_elements)
404 .all(|a| a.0.is_logically_equal(a.1))
405 }
406 _ => false,
407 },
408 TypeAst::Hole(TypeAstHole { name, location: _ }) => match other {
409 TypeAst::Hole(TypeAstHole {
410 name: o_name,
411 location: _,
412 }) => name == o_name,
413 _ => false,
414 },
415 }
416 }
417
418 pub fn find_node(&self, byte_index: u32, type_: Arc<Type>) -> Option<Located<'_>> {
419 if !self.location().contains(byte_index) {
420 return None;
421 }
422
423 match self {
424 TypeAst::Fn(TypeAstFn {
425 arguments, return_, ..
426 }) => type_
427 .fn_types()
428 .and_then(|(arg_types, ret_type)| {
429 if let Some(arg) = arguments
430 .iter()
431 .zip(arg_types)
432 .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone()))
433 {
434 return Some(arg);
435 }
436 if let Some(ret) = return_.find_node(byte_index, ret_type) {
437 return Some(ret);
438 }
439
440 None
441 })
442 .or(Some(Located::Annotation { ast: self, type_ })),
443 TypeAst::Constructor(TypeAstConstructor {
444 arguments, module, ..
445 }) => type_
446 .constructor_types()
447 .and_then(|arg_types| {
448 if let Some(arg) = arguments
449 .iter()
450 .zip(arg_types)
451 .find_map(|(arg, arg_type)| arg.find_node(byte_index, arg_type.clone()))
452 {
453 return Some(arg);
454 }
455
456 None
457 })
458 .or(module.as_ref().and_then(|(name, location)| {
459 if location.contains(byte_index) {
460 Some(Located::ModuleName {
461 location: *location,
462 name,
463 layer: Layer::Type,
464 })
465 } else {
466 None
467 }
468 }))
469 .or(Some(Located::Annotation { ast: self, type_ })),
470 TypeAst::Tuple(TypeAstTuple { elements, .. }) => type_
471 .tuple_types()
472 .and_then(|elem_types| {
473 if let Some(e) = elements
474 .iter()
475 .zip(elem_types)
476 .find_map(|(e, e_type)| e.find_node(byte_index, e_type.clone()))
477 {
478 return Some(e);
479 }
480
481 None
482 })
483 .or(Some(Located::Annotation { ast: self, type_ })),
484 TypeAst::Var(_) | TypeAst::Hole(_) => Some(Located::Annotation { ast: self, type_ }),
485 }
486 }
487
488 /// Generates an annotation corresponding to the type.
489 pub fn print(&self, buffer: &mut EcoString) {
490 match &self {
491 TypeAst::Var(var) => buffer.push_str(&var.name),
492 TypeAst::Hole(hole) => buffer.push_str(&hole.name),
493 TypeAst::Tuple(tuple) => {
494 buffer.push_str("#(");
495 for (i, element) in tuple.elements.iter().enumerate() {
496 element.print(buffer);
497 if i < tuple.elements.len() - 1 {
498 buffer.push_str(", ");
499 }
500 }
501 buffer.push(')')
502 }
503 TypeAst::Fn(func) => {
504 buffer.push_str("fn(");
505 for (i, argument) in func.arguments.iter().enumerate() {
506 argument.print(buffer);
507 if i < func.arguments.len() - 1 {
508 buffer.push_str(", ");
509 }
510 }
511 buffer.push(')');
512 buffer.push_str(" -> ");
513 func.return_.print(buffer);
514 }
515 TypeAst::Constructor(constructor) => {
516 if let Some((module, _)) = &constructor.module {
517 buffer.push_str(module);
518 buffer.push('.');
519 }
520 buffer.push_str(&constructor.name);
521 if !constructor.arguments.is_empty() {
522 buffer.push('(');
523 for (i, argument) in constructor.arguments.iter().enumerate() {
524 argument.print(buffer);
525 if i < constructor.arguments.len() - 1 {
526 buffer.push_str(", ");
527 }
528 }
529 buffer.push(')');
530 }
531 }
532 }
533 }
534}
535
536#[test]
537fn type_ast_print_fn() {
538 let mut buffer = EcoString::new();
539 let ast = TypeAst::Fn(TypeAstFn {
540 location: SrcSpan { start: 1, end: 1 },
541 arguments: vec![
542 TypeAst::Var(TypeAstVar {
543 location: SrcSpan { start: 1, end: 1 },
544 name: "String".into(),
545 }),
546 TypeAst::Var(TypeAstVar {
547 location: SrcSpan { start: 1, end: 1 },
548 name: "Bool".into(),
549 }),
550 ],
551 return_: Box::new(TypeAst::Var(TypeAstVar {
552 location: SrcSpan { start: 1, end: 1 },
553 name: "Int".into(),
554 })),
555 });
556 ast.print(&mut buffer);
557 assert_eq!(&buffer, "fn(String, Bool) -> Int")
558}
559
560#[test]
561fn type_ast_print_constructor() {
562 let mut buffer = EcoString::new();
563 let ast = TypeAst::Constructor(TypeAstConstructor {
564 name: "SomeType".into(),
565 module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })),
566 location: SrcSpan { start: 1, end: 1 },
567 name_location: SrcSpan { start: 1, end: 1 },
568 arguments: vec![
569 TypeAst::Var(TypeAstVar {
570 location: SrcSpan { start: 1, end: 1 },
571 name: "String".into(),
572 }),
573 TypeAst::Var(TypeAstVar {
574 location: SrcSpan { start: 1, end: 1 },
575 name: "Bool".into(),
576 }),
577 ],
578 });
579 ast.print(&mut buffer);
580 assert_eq!(&buffer, "some_module.SomeType(String, Bool)")
581}
582
583#[test]
584fn type_ast_print_tuple() {
585 let mut buffer = EcoString::new();
586 let ast = TypeAst::Tuple(TypeAstTuple {
587 location: SrcSpan { start: 1, end: 1 },
588 elements: vec![
589 TypeAst::Constructor(TypeAstConstructor {
590 name: "SomeType".into(),
591 module: Some(("some_module".into(), SrcSpan { start: 1, end: 1 })),
592 location: SrcSpan { start: 1, end: 1 },
593 name_location: SrcSpan { start: 1, end: 1 },
594 arguments: vec![
595 TypeAst::Var(TypeAstVar {
596 location: SrcSpan { start: 1, end: 1 },
597 name: "String".into(),
598 }),
599 TypeAst::Var(TypeAstVar {
600 location: SrcSpan { start: 1, end: 1 },
601 name: "Bool".into(),
602 }),
603 ],
604 }),
605 TypeAst::Fn(TypeAstFn {
606 location: SrcSpan { start: 1, end: 1 },
607 arguments: vec![
608 TypeAst::Var(TypeAstVar {
609 location: SrcSpan { start: 1, end: 1 },
610 name: "String".into(),
611 }),
612 TypeAst::Var(TypeAstVar {
613 location: SrcSpan { start: 1, end: 1 },
614 name: "Bool".into(),
615 }),
616 ],
617 return_: Box::new(TypeAst::Var(TypeAstVar {
618 location: SrcSpan { start: 1, end: 1 },
619 name: "Int".into(),
620 })),
621 }),
622 ],
623 });
624 ast.print(&mut buffer);
625 assert_eq!(
626 &buffer,
627 "#(some_module.SomeType(String, Bool), fn(String, Bool) -> Int)"
628 )
629}
630
631#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
632pub enum Publicity {
633 Public,
634 Private,
635 Internal { attribute_location: Option<SrcSpan> },
636}
637
638impl Publicity {
639 pub fn is_private(&self) -> bool {
640 match self {
641 Self::Private => true,
642 Self::Public | Self::Internal { .. } => false,
643 }
644 }
645
646 pub fn is_internal(&self) -> bool {
647 match self {
648 Self::Internal { .. } => true,
649 Self::Public | Self::Private => false,
650 }
651 }
652
653 pub fn is_public(&self) -> bool {
654 match self {
655 Self::Public => true,
656 Self::Internal { .. } | Self::Private => false,
657 }
658 }
659
660 pub fn is_importable(&self) -> bool {
661 match self {
662 Self::Internal { .. } | Self::Public => true,
663 Self::Private => false,
664 }
665 }
666}
667
668#[derive(Debug, Clone, PartialEq, Eq)]
669/// A function definition
670///
671/// Note that an anonymous function will have `None` as the name field, while a
672/// named function will have `Some`.
673///
674/// # Example(s)
675///
676/// ```gleam
677/// // Public function
678/// pub fn wobble() -> String { ... }
679/// // Private function
680/// fn wibble(x: Int) -> Int { ... }
681/// // Anonymous function
682/// fn(x: Int) { ... }
683/// ```
684pub struct Function<T, Expr> {
685 pub location: SrcSpan,
686 pub end_position: u32,
687 pub name: Option<SpannedString>,
688 pub arguments: Vec<Arg<T>>,
689 pub body: Vec1<Statement<T, Expr>>,
690 pub publicity: Publicity,
691 pub deprecation: Deprecation,
692 pub return_annotation: Option<TypeAst>,
693 pub return_type: T,
694 pub documentation: Option<(u32, EcoString)>,
695 pub external_erlang: Option<(EcoString, EcoString, SrcSpan)>,
696 pub external_javascript: Option<(EcoString, EcoString, SrcSpan)>,
697 pub implementations: Implementations,
698 pub purity: Purity,
699}
700
701pub type TypedFunction = Function<Arc<Type>, TypedExpr>;
702pub type UntypedFunction = Function<(), UntypedExpr>;
703
704impl<T, E> Function<T, E> {
705 pub fn full_location(&self) -> SrcSpan {
706 SrcSpan::new(self.location.start, self.end_position)
707 }
708}
709
710pub type UntypedImport = Import<()>;
711
712#[derive(Debug, Clone, PartialEq, Eq)]
713/// Import another Gleam module so the current module can use the types and
714/// values it defines.
715///
716/// # Example(s)
717///
718/// ```gleam
719/// import unix/cat
720/// // Import with alias
721/// import animal/cat as kitty
722/// ```
723pub struct Import<PackageName> {
724 pub documentation: Option<EcoString>,
725 pub location: SrcSpan,
726 pub module: EcoString,
727 pub as_name: Option<(AssignName, SrcSpan)>,
728 pub unqualified_values: Vec<UnqualifiedImport>,
729 pub unqualified_types: Vec<UnqualifiedImport>,
730 pub package: PackageName,
731}
732
733impl<T> Import<T> {
734 pub(crate) fn used_name(&self) -> Option<EcoString> {
735 match self.as_name.as_ref() {
736 Some((AssignName::Variable(name), _)) => Some(name.clone()),
737 Some((AssignName::Discard(_), _)) => None,
738 None => self.module.split('/').next_back().map(EcoString::from),
739 }
740 }
741
742 pub(crate) fn alias_location(&self) -> Option<SrcSpan> {
743 self.as_name.as_ref().map(|(_, location)| *location)
744 }
745}
746
747pub type UntypedModuleConstant = ModuleConstant<(), ()>;
748pub type TypedModuleConstant = ModuleConstant<Arc<Type>, EcoString>;
749
750#[derive(Debug, Clone, PartialEq, Eq)]
751/// A certain fixed value that can be used in multiple places
752///
753/// # Example(s)
754///
755/// ```gleam
756/// pub const start_year = 2101
757/// pub const end_year = 2111
758/// ```
759pub struct ModuleConstant<T, ConstantRecordTag> {
760 pub documentation: Option<(u32, EcoString)>,
761 /// The location of the constant, starting at the "(pub) const" keywords and
762 /// ending after the ": Type" annotation, or (without an annotation) after its name.
763 pub location: SrcSpan,
764 pub publicity: Publicity,
765 pub name: EcoString,
766 pub name_location: SrcSpan,
767 pub annotation: Option<TypeAst>,
768 pub value: Box<Constant<T, ConstantRecordTag>>,
769 pub type_: T,
770 pub deprecation: Deprecation,
771 pub implementations: Implementations,
772}
773
774pub type UntypedCustomType = CustomType<()>;
775pub type TypedCustomType = CustomType<Arc<Type>>;
776
777#[derive(Debug, Clone, PartialEq, Eq)]
778/// A newly defined type with one or more constructors.
779/// Each variant of the custom type can contain different types, so the type is
780/// the product of the types contained by each variant.
781///
782/// This might be called an algebraic data type (ADT) or tagged union in other
783/// languages and type systems.
784///
785///
786/// # Example(s)
787///
788/// ```gleam
789/// pub type Cat {
790/// Cat(name: String, cuteness: Int)
791/// }
792/// ```
793pub struct CustomType<T> {
794 pub location: SrcSpan,
795 pub end_position: u32,
796 pub name: EcoString,
797 pub name_location: SrcSpan,
798 pub publicity: Publicity,
799 pub constructors: Vec<RecordConstructor<T>>,
800 pub documentation: Option<(u32, EcoString)>,
801 pub deprecation: Deprecation,
802 pub opaque: bool,
803 /// The names of the type parameters.
804 pub parameters: Vec<SpannedString>,
805 /// Once type checked this field will contain the type information for the
806 /// type parameters.
807 pub typed_parameters: Vec<T>,
808}
809
810impl<T> CustomType<T> {
811 /// The `location` field of a `CustomType` is only the location of `pub type
812 /// TheName`. This method returns a `SrcSpan` that includes the entire type
813 /// definition.
814 pub fn full_location(&self) -> SrcSpan {
815 SrcSpan::new(self.location.start, self.end_position)
816 }
817}
818
819pub type UntypedTypeAlias = TypeAlias<()>;
820
821#[derive(Debug, Clone, PartialEq, Eq)]
822/// A new name for an existing type
823///
824/// # Example(s)
825///
826/// ```gleam
827/// pub type Headers =
828/// List(#(String, String))
829/// ```
830pub struct TypeAlias<T> {
831 pub location: SrcSpan,
832 pub alias: EcoString,
833 pub name_location: SrcSpan,
834 pub parameters: Vec<SpannedString>,
835 pub type_ast: TypeAst,
836 pub type_: T,
837 pub publicity: Publicity,
838 pub documentation: Option<(u32, EcoString)>,
839 pub deprecation: Deprecation,
840}
841
842pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, EcoString, EcoString>;
843pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
844
845#[derive(Debug, Clone, PartialEq, Eq)]
846pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
847 Function(Function<T, Expr>),
848
849 TypeAlias(TypeAlias<T>),
850
851 CustomType(CustomType<T>),
852
853 Import(Import<PackageName>),
854
855 ModuleConstant(ModuleConstant<T, ConstantRecordTag>),
856}
857
858impl TypedDefinition {
859 pub fn main_function(&self) -> Option<&TypedFunction> {
860 match self {
861 Definition::Function(f) if f.name.as_ref().is_some_and(|(_, name)| name == "main") => {
862 Some(f)
863 }
864 _ => None,
865 }
866 }
867
868 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
869 match self {
870 Definition::Function(function) => {
871 // Search for the corresponding node inside the function
872 // only if the index falls within the function's full location.
873 if !function.full_location().contains(byte_index) {
874 return None;
875 }
876
877 if let Some(found) = function.body.iter().find_map(|s| s.find_node(byte_index)) {
878 return Some(found);
879 }
880
881 if let Some(found_arg) = function
882 .arguments
883 .iter()
884 .find_map(|arg| arg.find_node(byte_index))
885 {
886 return Some(found_arg);
887 };
888
889 if let Some(found_statement) = function
890 .body
891 .iter()
892 .find(|statement| statement.location().contains(byte_index))
893 {
894 return Some(Located::Statement(found_statement));
895 };
896
897 // Check if location is within the return annotation.
898 if let Some(l) = function
899 .return_annotation
900 .iter()
901 .find_map(|a| a.find_node(byte_index, function.return_type.clone()))
902 {
903 return Some(l);
904 };
905
906 // Note that the fn `.location` covers the function head, not
907 // the entire statement.
908 if function.location.contains(byte_index) {
909 Some(Located::ModuleStatement(self))
910 } else if function.full_location().contains(byte_index) {
911 Some(Located::FunctionBody(function))
912 } else {
913 None
914 }
915 }
916
917 Definition::CustomType(custom) => {
918 // Check if location is within the type of one of the arguments of a constructor.
919 if let Some(constructor) = custom
920 .constructors
921 .iter()
922 .find(|constructor| constructor.location.contains(byte_index))
923 {
924 if let Some(annotation) = constructor
925 .arguments
926 .iter()
927 .find(|arg| arg.location.contains(byte_index))
928 .and_then(|arg| arg.ast.find_node(byte_index, arg.type_.clone()))
929 {
930 return Some(annotation);
931 }
932
933 return Some(Located::VariantConstructorDefinition(constructor));
934 }
935
936 // Note that the custom type `.location` covers the function
937 // head, not the entire statement.
938 if custom.full_location().contains(byte_index) {
939 Some(Located::ModuleStatement(self))
940 } else {
941 None
942 }
943 }
944
945 Definition::TypeAlias(alias) => {
946 // Check if location is within the type being aliased.
947 if let Some(l) = alias.type_ast.find_node(byte_index, alias.type_.clone()) {
948 return Some(l);
949 }
950
951 if alias.location.contains(byte_index) {
952 Some(Located::ModuleStatement(self))
953 } else {
954 None
955 }
956 }
957
958 Definition::ModuleConstant(constant) => {
959 // Check if location is within the annotation.
960 if let Some(annotation) = &constant.annotation {
961 if let Some(l) = annotation.find_node(byte_index, constant.type_.clone()) {
962 return Some(l);
963 }
964 }
965
966 if let Some(located) = constant.value.find_node(byte_index) {
967 return Some(located);
968 }
969
970 if constant.location.contains(byte_index) {
971 Some(Located::ModuleStatement(self))
972 } else {
973 None
974 }
975 }
976
977 Definition::Import(import) => {
978 if self.location().contains(byte_index) {
979 if let Some(unqualified) = import
980 .unqualified_values
981 .iter()
982 .find(|i| i.location.contains(byte_index))
983 {
984 return Some(Located::UnqualifiedImport(
985 crate::build::UnqualifiedImport {
986 name: &unqualified.name,
987 module: &import.module,
988 is_type: false,
989 location: &unqualified.location,
990 },
991 ));
992 }
993
994 if let Some(unqualified) = import
995 .unqualified_types
996 .iter()
997 .find(|i| i.location.contains(byte_index))
998 {
999 return Some(Located::UnqualifiedImport(
1000 crate::build::UnqualifiedImport {
1001 name: &unqualified.name,
1002 module: &import.module,
1003 is_type: true,
1004 location: &unqualified.location,
1005 },
1006 ));
1007 }
1008
1009 Some(Located::ModuleStatement(self))
1010 } else {
1011 None
1012 }
1013 }
1014 }
1015 }
1016
1017 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
1018 match self {
1019 Definition::Function(function) => {
1020 if !function.full_location().contains(byte_index) {
1021 return None;
1022 }
1023
1024 function
1025 .body
1026 .iter()
1027 .find_map(|statement| statement.find_statement(byte_index))
1028 }
1029
1030 _ => None,
1031 }
1032 }
1033}
1034
1035impl<A, B, C, E> Definition<A, B, C, E> {
1036 pub fn location(&self) -> SrcSpan {
1037 match self {
1038 Definition::Function(Function { location, .. })
1039 | Definition::Import(Import { location, .. })
1040 | Definition::TypeAlias(TypeAlias { location, .. })
1041 | Definition::CustomType(CustomType { location, .. })
1042 | Definition::ModuleConstant(ModuleConstant { location, .. }) => *location,
1043 }
1044 }
1045
1046 /// Returns `true` if the definition is [`Import`].
1047 ///
1048 /// [`Import`]: Definition::Import
1049 #[must_use]
1050 pub fn is_import(&self) -> bool {
1051 matches!(self, Self::Import(..))
1052 }
1053
1054 /// Returns `true` if the module statement is [`Function`].
1055 ///
1056 /// [`Function`]: ModuleStatement::Function
1057 #[must_use]
1058 pub fn is_function(&self) -> bool {
1059 matches!(self, Self::Function(..))
1060 }
1061
1062 pub fn put_doc(&mut self, new_doc: (u32, EcoString)) {
1063 match self {
1064 Definition::Import(Import { .. }) => (),
1065
1066 Definition::Function(Function { documentation, .. })
1067 | Definition::TypeAlias(TypeAlias { documentation, .. })
1068 | Definition::CustomType(CustomType { documentation, .. })
1069 | Definition::ModuleConstant(ModuleConstant { documentation, .. }) => {
1070 let _ = documentation.replace(new_doc);
1071 }
1072 }
1073 }
1074
1075 pub fn get_doc(&self) -> Option<EcoString> {
1076 match self {
1077 Definition::Import(Import { .. }) => None,
1078
1079 Definition::Function(Function {
1080 documentation: doc, ..
1081 })
1082 | Definition::TypeAlias(TypeAlias {
1083 documentation: doc, ..
1084 })
1085 | Definition::CustomType(CustomType {
1086 documentation: doc, ..
1087 })
1088 | Definition::ModuleConstant(ModuleConstant {
1089 documentation: doc, ..
1090 }) => doc.as_ref().map(|(_, doc)| doc.clone()),
1091 }
1092 }
1093
1094 pub fn is_internal(&self) -> bool {
1095 match self {
1096 Definition::Function(Function { publicity, .. })
1097 | Definition::CustomType(CustomType { publicity, .. })
1098 | Definition::ModuleConstant(ModuleConstant { publicity, .. })
1099 | Definition::TypeAlias(TypeAlias { publicity, .. }) => publicity.is_internal(),
1100
1101 Definition::Import(_) => false,
1102 }
1103 }
1104}
1105
1106#[derive(Debug, Clone, PartialEq, Eq)]
1107pub struct UnqualifiedImport {
1108 pub location: SrcSpan,
1109 /// The location excluding the potential `as ...` clause, or the `type` keyword
1110 pub imported_name_location: SrcSpan,
1111 pub name: EcoString,
1112 pub as_name: Option<EcoString>,
1113}
1114
1115impl UnqualifiedImport {
1116 pub fn used_name(&self) -> &EcoString {
1117 self.as_name.as_ref().unwrap_or(&self.name)
1118 }
1119}
1120
1121#[derive(Debug, Clone, PartialEq, Eq, Copy, Default, serde::Serialize, serde::Deserialize)]
1122pub enum Layer {
1123 #[default]
1124 Value,
1125 Type,
1126}
1127
1128impl Layer {
1129 /// Returns `true` if the layer is [`Value`].
1130 pub fn is_value(&self) -> bool {
1131 matches!(self, Self::Value)
1132 }
1133}
1134
1135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1136pub enum BinOp {
1137 // Boolean logic
1138 And,
1139 Or,
1140
1141 // Equality
1142 Eq,
1143 NotEq,
1144
1145 // Order comparison
1146 LtInt,
1147 LtEqInt,
1148 LtFloat,
1149 LtEqFloat,
1150 GtEqInt,
1151 GtInt,
1152 GtEqFloat,
1153 GtFloat,
1154
1155 // Maths
1156 AddInt,
1157 AddFloat,
1158 SubInt,
1159 SubFloat,
1160 MultInt,
1161 MultFloat,
1162 DivInt,
1163 DivFloat,
1164 RemainderInt,
1165
1166 // Strings
1167 Concatenate,
1168}
1169
1170#[derive(Clone, Copy, Debug, PartialEq)]
1171pub enum OperatorKind {
1172 BooleanLogic,
1173 Equality,
1174 IntComparison,
1175 FLoatComparison,
1176 IntMath,
1177 FloatMath,
1178 StringConcatenation,
1179}
1180
1181pub const PIPE_PRECEDENCE: u8 = 6;
1182
1183impl BinOp {
1184 pub fn precedence(&self) -> u8 {
1185 // Ensure that this matches the other precedence function for guards
1186 match self {
1187 Self::Or => 1,
1188
1189 Self::And => 2,
1190
1191 Self::Eq | Self::NotEq => 3,
1192
1193 Self::LtInt
1194 | Self::LtEqInt
1195 | Self::LtFloat
1196 | Self::LtEqFloat
1197 | Self::GtEqInt
1198 | Self::GtInt
1199 | Self::GtEqFloat
1200 | Self::GtFloat => 4,
1201
1202 Self::Concatenate => 5,
1203
1204 // Pipe is 6
1205 Self::AddInt | Self::AddFloat | Self::SubInt | Self::SubFloat => 7,
1206
1207 Self::MultInt
1208 | Self::MultFloat
1209 | Self::DivInt
1210 | Self::DivFloat
1211 | Self::RemainderInt => 8,
1212 }
1213 }
1214
1215 pub fn name(&self) -> &'static str {
1216 match self {
1217 Self::And => "&&",
1218 Self::Or => "||",
1219 Self::LtInt => "<",
1220 Self::LtEqInt => "<=",
1221 Self::LtFloat => "<.",
1222 Self::LtEqFloat => "<=.",
1223 Self::Eq => "==",
1224 Self::NotEq => "!=",
1225 Self::GtEqInt => ">=",
1226 Self::GtInt => ">",
1227 Self::GtEqFloat => ">=.",
1228 Self::GtFloat => ">.",
1229 Self::AddInt => "+",
1230 Self::AddFloat => "+.",
1231 Self::SubInt => "-",
1232 Self::SubFloat => "-.",
1233 Self::MultInt => "*",
1234 Self::MultFloat => "*.",
1235 Self::DivInt => "/",
1236 Self::DivFloat => "/.",
1237 Self::RemainderInt => "%",
1238 Self::Concatenate => "<>",
1239 }
1240 }
1241
1242 pub fn operator_kind(&self) -> OperatorKind {
1243 match self {
1244 Self::Concatenate => OperatorKind::StringConcatenation,
1245 Self::Eq | Self::NotEq => OperatorKind::Equality,
1246 Self::And | Self::Or => OperatorKind::BooleanLogic,
1247 Self::LtInt | Self::LtEqInt | Self::GtEqInt | Self::GtInt => {
1248 OperatorKind::IntComparison
1249 }
1250 Self::LtFloat | Self::LtEqFloat | Self::GtEqFloat | Self::GtFloat => {
1251 OperatorKind::FLoatComparison
1252 }
1253 Self::AddInt | Self::SubInt | Self::MultInt | Self::RemainderInt | Self::DivInt => {
1254 OperatorKind::IntMath
1255 }
1256 Self::AddFloat | Self::SubFloat | Self::MultFloat | Self::DivFloat => {
1257 OperatorKind::FloatMath
1258 }
1259 }
1260 }
1261
1262 pub fn can_be_grouped_with(&self, other: &BinOp) -> bool {
1263 self.operator_kind() == other.operator_kind()
1264 }
1265
1266 pub(crate) fn is_float_operator(&self) -> bool {
1267 match self {
1268 BinOp::LtFloat
1269 | BinOp::LtEqFloat
1270 | BinOp::GtEqFloat
1271 | BinOp::GtFloat
1272 | BinOp::AddFloat
1273 | BinOp::SubFloat
1274 | BinOp::MultFloat
1275 | BinOp::DivFloat => true,
1276
1277 BinOp::And
1278 | BinOp::Or
1279 | BinOp::Eq
1280 | BinOp::NotEq
1281 | BinOp::LtInt
1282 | BinOp::LtEqInt
1283 | BinOp::GtEqInt
1284 | BinOp::GtInt
1285 | BinOp::AddInt
1286 | BinOp::SubInt
1287 | BinOp::MultInt
1288 | BinOp::DivInt
1289 | BinOp::RemainderInt
1290 | BinOp::Concatenate => false,
1291 }
1292 }
1293
1294 fn is_bool_operator(&self) -> bool {
1295 match self {
1296 BinOp::And | BinOp::Or => true,
1297 _ => false,
1298 }
1299 }
1300
1301 pub(crate) fn is_int_operator(&self) -> bool {
1302 match self {
1303 BinOp::LtInt
1304 | BinOp::LtEqInt
1305 | BinOp::GtEqInt
1306 | BinOp::GtInt
1307 | BinOp::AddInt
1308 | BinOp::SubInt
1309 | BinOp::MultInt
1310 | BinOp::DivInt
1311 | BinOp::RemainderInt => true,
1312
1313 BinOp::And
1314 | BinOp::Or
1315 | BinOp::Eq
1316 | BinOp::NotEq
1317 | BinOp::LtFloat
1318 | BinOp::LtEqFloat
1319 | BinOp::GtEqFloat
1320 | BinOp::GtFloat
1321 | BinOp::AddFloat
1322 | BinOp::SubFloat
1323 | BinOp::MultFloat
1324 | BinOp::DivFloat
1325 | BinOp::Concatenate => false,
1326 }
1327 }
1328
1329 pub fn float_equivalent(&self) -> Option<BinOp> {
1330 match self {
1331 BinOp::LtInt => Some(BinOp::LtFloat),
1332 BinOp::LtEqInt => Some(BinOp::LtEqFloat),
1333 BinOp::GtEqInt => Some(BinOp::GtEqFloat),
1334 BinOp::GtInt => Some(BinOp::GtFloat),
1335 BinOp::AddInt => Some(BinOp::AddFloat),
1336 BinOp::SubInt => Some(BinOp::SubFloat),
1337 BinOp::MultInt => Some(BinOp::MultFloat),
1338 BinOp::DivInt => Some(BinOp::DivFloat),
1339 _ => None,
1340 }
1341 }
1342
1343 pub fn int_equivalent(&self) -> Option<BinOp> {
1344 match self {
1345 BinOp::LtFloat => Some(BinOp::LtInt),
1346 BinOp::LtEqFloat => Some(BinOp::LtEqInt),
1347 BinOp::GtEqFloat => Some(BinOp::GtEqInt),
1348 BinOp::GtFloat => Some(BinOp::GtInt),
1349 BinOp::AddFloat => Some(BinOp::AddInt),
1350 BinOp::SubFloat => Some(BinOp::SubInt),
1351 BinOp::MultFloat => Some(BinOp::MultInt),
1352 BinOp::DivFloat => Some(BinOp::DivInt),
1353 _ => None,
1354 }
1355 }
1356}
1357
1358#[derive(Debug, PartialEq, Eq, Clone)]
1359pub struct CallArg<A> {
1360 pub label: Option<EcoString>,
1361 pub location: SrcSpan,
1362 pub value: A,
1363 pub implicit: Option<ImplicitCallArgOrigin>,
1364}
1365
1366#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1367pub enum ImplicitCallArgOrigin {
1368 /// The implicit callback argument passed as the last argument to the
1369 /// function on the right hand side of `use`.
1370 ///
1371 Use,
1372 /// An argument added by the compiler when rewriting a pipe `left |> right`.
1373 ///
1374 Pipe,
1375 /// An argument added by the compiler to fill in all the missing fields of a
1376 /// record that are being ignored with the `..` syntax.
1377 ///
1378 PatternFieldSpread,
1379 /// An argument used to fill in the missing args when a function on the
1380 /// right hand side of `use` is being called with the wrong arity.
1381 ///
1382 IncorrectArityUse,
1383 /// An argument adde by the compiler to fill in the missing args when using
1384 /// the record update synax.
1385 ///
1386 RecordUpdate,
1387}
1388
1389impl<A> CallArg<A> {
1390 #[must_use]
1391 pub fn is_implicit(&self) -> bool {
1392 self.implicit.is_some()
1393 }
1394
1395 #[must_use]
1396 pub fn is_use_implicit_callback(&self) -> bool {
1397 match self.implicit {
1398 Some(ImplicitCallArgOrigin::Use | ImplicitCallArgOrigin::IncorrectArityUse) => true,
1399 Some(_) | None => false,
1400 }
1401 }
1402}
1403
1404impl CallArg<TypedExpr> {
1405 pub fn find_node<'a>(
1406 &'a self,
1407 byte_index: u32,
1408 called_function: &'a TypedExpr,
1409 function_arguments: &'a [TypedCallArg],
1410 ) -> Option<Located<'a>> {
1411 match (self.implicit, &self.value) {
1412 // If a call argument is the implicit use callback then we don't
1413 // want to look at its arguments and body but we don't want to
1414 // return the whole anonymous function if anything else doesn't
1415 // match.
1416 //
1417 // In addition, if the callback is invalid because it couldn't be
1418 // typed, we don't want to return it as it would make it hard for
1419 // the LSP to give any suggestions on the use function being typed.
1420 //
1421 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Invalid { .. }) => None,
1422 // So the code below is exactly the same as
1423 // `TypedExpr::Fn{}.find_node()` except we do not return self as a
1424 // fallback.
1425 //
1426 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Fn { args, body, .. }) => args
1427 .iter()
1428 .find_map(|arg| arg.find_node(byte_index))
1429 .or_else(|| body.iter().find_map(|s| s.find_node(byte_index))),
1430 // In all other cases we're happy with the default behaviour.
1431 //
1432 _ => match self.value.find_node(byte_index) {
1433 Some(Located::Expression { expression, .. })
1434 // This is only possibly a label if we are at the end of the expression
1435 // (so not in the middle like `[abc|]`) and if this argument doesn't
1436 // already have a label.
1437 if byte_index == self.value.location().end && self.label.is_none() =>
1438 {
1439 Some(Located::Expression {
1440 expression,
1441 position: ExpressionPosition::ArgumentOrLabel {
1442 called_function,
1443 function_arguments,
1444 },
1445 })
1446 }
1447 Some(located) => Some(located),
1448 None => {
1449 if self.location.contains(byte_index) && self.label.is_some() {
1450 Some(Located::Label(self.location, self.value.type_()))
1451 } else {
1452 None
1453 }
1454 }
1455 },
1456 }
1457 }
1458
1459 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
1460 match (self.implicit, &self.value) {
1461 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Invalid { .. }) => None,
1462 (Some(ImplicitCallArgOrigin::Use), TypedExpr::Fn { body, .. }) => {
1463 body.iter().find_map(|s| s.find_statement(byte_index))
1464 }
1465
1466 _ => self.value.find_statement(byte_index),
1467 }
1468 }
1469
1470 pub fn is_capture_hole(&self) -> bool {
1471 match &self.value {
1472 TypedExpr::Var { name, .. } => name == CAPTURE_VARIABLE,
1473 _ => false,
1474 }
1475 }
1476}
1477
1478impl CallArg<TypedPattern> {
1479 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1480 match self.value.find_node(byte_index) {
1481 Some(located) => Some(located),
1482 _ => {
1483 if self.location.contains(byte_index) && self.label.is_some() {
1484 Some(Located::Label(self.location, self.value.type_()))
1485 } else {
1486 None
1487 }
1488 }
1489 }
1490 }
1491}
1492
1493impl CallArg<TypedConstant> {
1494 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1495 match self.value.find_node(byte_index) {
1496 Some(located) => Some(located),
1497 _ => {
1498 if self.location.contains(byte_index) && self.label.is_some() {
1499 Some(Located::Label(self.location, self.value.type_()))
1500 } else {
1501 None
1502 }
1503 }
1504 }
1505 }
1506}
1507
1508impl CallArg<UntypedExpr> {
1509 pub fn is_capture_hole(&self) -> bool {
1510 match &self.value {
1511 UntypedExpr::Var { name, .. } => name == CAPTURE_VARIABLE,
1512 _ => false,
1513 }
1514 }
1515}
1516
1517impl<T> CallArg<T>
1518where
1519 T: HasLocation,
1520{
1521 #[must_use]
1522 pub fn uses_label_shorthand(&self) -> bool {
1523 self.label.is_some() && self.location == self.value.location()
1524 }
1525}
1526
1527impl<T> HasLocation for CallArg<T> {
1528 fn location(&self) -> SrcSpan {
1529 self.location
1530 }
1531}
1532
1533#[derive(Debug, Clone, PartialEq, Eq)]
1534pub struct RecordBeingUpdated {
1535 pub base: Box<UntypedExpr>,
1536 pub location: SrcSpan,
1537}
1538
1539#[derive(Debug, Clone, PartialEq, Eq)]
1540pub struct UntypedRecordUpdateArg {
1541 pub label: EcoString,
1542 pub location: SrcSpan,
1543 pub value: UntypedExpr,
1544}
1545
1546impl UntypedRecordUpdateArg {
1547 #[must_use]
1548 pub fn uses_label_shorthand(&self) -> bool {
1549 self.value.location() == self.location
1550 }
1551}
1552
1553impl HasLocation for UntypedRecordUpdateArg {
1554 fn location(&self) -> SrcSpan {
1555 self.location
1556 }
1557}
1558
1559pub type MultiPattern<Type> = Vec<Pattern<Type>>;
1560
1561pub type UntypedMultiPattern = MultiPattern<()>;
1562pub type TypedMultiPattern = MultiPattern<Arc<Type>>;
1563
1564pub type TypedClause = Clause<TypedExpr, Arc<Type>, EcoString>;
1565
1566pub type UntypedClause = Clause<UntypedExpr, (), ()>;
1567
1568#[derive(Debug, Clone, PartialEq, Eq)]
1569pub struct Clause<Expr, Type, RecordTag> {
1570 pub location: SrcSpan,
1571 pub pattern: MultiPattern<Type>,
1572 pub alternative_patterns: Vec<MultiPattern<Type>>,
1573 pub guard: Option<ClauseGuard<Type, RecordTag>>,
1574 pub then: Expr,
1575}
1576
1577impl<A, B, C> Clause<A, B, C> {
1578 pub fn pattern_count(&self) -> usize {
1579 1 + self.alternative_patterns.len()
1580 }
1581}
1582
1583impl TypedClause {
1584 pub fn location(&self) -> SrcSpan {
1585 SrcSpan {
1586 start: self
1587 .pattern
1588 .first()
1589 .map(|p| p.location().start)
1590 .unwrap_or_default(),
1591 end: self.then.location().end,
1592 }
1593 }
1594
1595 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
1596 self.pattern
1597 .iter()
1598 .find_map(|p| p.find_node(byte_index))
1599 .or_else(|| self.then.find_node(byte_index))
1600 }
1601
1602 pub fn pattern_location(&self) -> SrcSpan {
1603 let start = self.pattern.first().map(|pattern| pattern.location().start);
1604
1605 let end = if let Some(last_pattern) = self
1606 .alternative_patterns
1607 .last()
1608 .and_then(|patterns| patterns.last())
1609 {
1610 Some(last_pattern.location().end)
1611 } else {
1612 self.pattern.last().map(|pattern| pattern.location().end)
1613 };
1614
1615 SrcSpan::new(start.unwrap_or_default(), end.unwrap_or_default())
1616 }
1617}
1618
1619pub type UntypedClauseGuard = ClauseGuard<(), ()>;
1620pub type TypedClauseGuard = ClauseGuard<Arc<Type>, EcoString>;
1621
1622#[derive(Debug, Clone, PartialEq, Eq)]
1623pub enum ClauseGuard<Type, RecordTag> {
1624 Equals {
1625 location: SrcSpan,
1626 left: Box<Self>,
1627 right: Box<Self>,
1628 },
1629
1630 NotEquals {
1631 location: SrcSpan,
1632 left: Box<Self>,
1633 right: Box<Self>,
1634 },
1635
1636 GtInt {
1637 location: SrcSpan,
1638 left: Box<Self>,
1639 right: Box<Self>,
1640 },
1641
1642 GtEqInt {
1643 location: SrcSpan,
1644 left: Box<Self>,
1645 right: Box<Self>,
1646 },
1647
1648 LtInt {
1649 location: SrcSpan,
1650 left: Box<Self>,
1651 right: Box<Self>,
1652 },
1653
1654 LtEqInt {
1655 location: SrcSpan,
1656 left: Box<Self>,
1657 right: Box<Self>,
1658 },
1659
1660 GtFloat {
1661 location: SrcSpan,
1662 left: Box<Self>,
1663 right: Box<Self>,
1664 },
1665
1666 GtEqFloat {
1667 location: SrcSpan,
1668 left: Box<Self>,
1669 right: Box<Self>,
1670 },
1671
1672 LtFloat {
1673 location: SrcSpan,
1674 left: Box<Self>,
1675 right: Box<Self>,
1676 },
1677
1678 LtEqFloat {
1679 location: SrcSpan,
1680 left: Box<Self>,
1681 right: Box<Self>,
1682 },
1683
1684 AddInt {
1685 location: SrcSpan,
1686 left: Box<Self>,
1687 right: Box<Self>,
1688 },
1689
1690 AddFloat {
1691 location: SrcSpan,
1692 left: Box<Self>,
1693 right: Box<Self>,
1694 },
1695
1696 SubInt {
1697 location: SrcSpan,
1698 left: Box<Self>,
1699 right: Box<Self>,
1700 },
1701
1702 SubFloat {
1703 location: SrcSpan,
1704 left: Box<Self>,
1705 right: Box<Self>,
1706 },
1707
1708 MultInt {
1709 location: SrcSpan,
1710 left: Box<Self>,
1711 right: Box<Self>,
1712 },
1713
1714 MultFloat {
1715 location: SrcSpan,
1716 left: Box<Self>,
1717 right: Box<Self>,
1718 },
1719
1720 DivInt {
1721 location: SrcSpan,
1722 left: Box<Self>,
1723 right: Box<Self>,
1724 },
1725
1726 DivFloat {
1727 location: SrcSpan,
1728 left: Box<Self>,
1729 right: Box<Self>,
1730 },
1731
1732 RemainderInt {
1733 location: SrcSpan,
1734 left: Box<Self>,
1735 right: Box<Self>,
1736 },
1737
1738 Or {
1739 location: SrcSpan,
1740 left: Box<Self>,
1741 right: Box<Self>,
1742 },
1743
1744 And {
1745 location: SrcSpan,
1746 left: Box<Self>,
1747 right: Box<Self>,
1748 },
1749
1750 Not {
1751 location: SrcSpan,
1752 expression: Box<Self>,
1753 },
1754
1755 Var {
1756 location: SrcSpan,
1757 type_: Type,
1758 name: EcoString,
1759 definition_location: SrcSpan,
1760 },
1761
1762 TupleIndex {
1763 location: SrcSpan,
1764 index: u64,
1765 type_: Type,
1766 tuple: Box<Self>,
1767 },
1768
1769 FieldAccess {
1770 location: SrcSpan,
1771 index: Option<u64>,
1772 label: EcoString,
1773 type_: Type,
1774 container: Box<Self>,
1775 },
1776
1777 ModuleSelect {
1778 location: SrcSpan,
1779 type_: Type,
1780 label: EcoString,
1781 module_name: EcoString,
1782 module_alias: EcoString,
1783 literal: Constant<Type, RecordTag>,
1784 },
1785
1786 Constant(Constant<Type, RecordTag>),
1787}
1788
1789impl<A, B> ClauseGuard<A, B> {
1790 pub fn location(&self) -> SrcSpan {
1791 match self {
1792 ClauseGuard::Constant(constant) => constant.location(),
1793 ClauseGuard::Or { location, .. }
1794 | ClauseGuard::And { location, .. }
1795 | ClauseGuard::Not { location, .. }
1796 | ClauseGuard::Var { location, .. }
1797 | ClauseGuard::TupleIndex { location, .. }
1798 | ClauseGuard::Equals { location, .. }
1799 | ClauseGuard::NotEquals { location, .. }
1800 | ClauseGuard::GtInt { location, .. }
1801 | ClauseGuard::GtEqInt { location, .. }
1802 | ClauseGuard::LtInt { location, .. }
1803 | ClauseGuard::LtEqInt { location, .. }
1804 | ClauseGuard::GtFloat { location, .. }
1805 | ClauseGuard::GtEqFloat { location, .. }
1806 | ClauseGuard::LtFloat { location, .. }
1807 | ClauseGuard::AddInt { location, .. }
1808 | ClauseGuard::AddFloat { location, .. }
1809 | ClauseGuard::SubInt { location, .. }
1810 | ClauseGuard::SubFloat { location, .. }
1811 | ClauseGuard::MultInt { location, .. }
1812 | ClauseGuard::MultFloat { location, .. }
1813 | ClauseGuard::DivInt { location, .. }
1814 | ClauseGuard::DivFloat { location, .. }
1815 | ClauseGuard::RemainderInt { location, .. }
1816 | ClauseGuard::FieldAccess { location, .. }
1817 | ClauseGuard::LtEqFloat { location, .. }
1818 | ClauseGuard::ModuleSelect { location, .. } => *location,
1819 }
1820 }
1821
1822 pub fn precedence(&self) -> u8 {
1823 // Ensure that this matches the other precedence function for guards
1824 match self.bin_op_name() {
1825 Some(name) => name.precedence(),
1826 None => u8::MAX,
1827 }
1828 }
1829
1830 pub fn bin_op_name(&self) -> Option<BinOp> {
1831 match self {
1832 ClauseGuard::Or { .. } => Some(BinOp::Or),
1833 ClauseGuard::And { .. } => Some(BinOp::And),
1834 ClauseGuard::Equals { .. } => Some(BinOp::Eq),
1835 ClauseGuard::NotEquals { .. } => Some(BinOp::NotEq),
1836 ClauseGuard::GtInt { .. } => Some(BinOp::GtInt),
1837 ClauseGuard::GtEqInt { .. } => Some(BinOp::GtEqInt),
1838 ClauseGuard::LtInt { .. } => Some(BinOp::LtInt),
1839 ClauseGuard::LtEqInt { .. } => Some(BinOp::LtEqInt),
1840 ClauseGuard::GtFloat { .. } => Some(BinOp::GtFloat),
1841 ClauseGuard::GtEqFloat { .. } => Some(BinOp::GtEqFloat),
1842 ClauseGuard::LtFloat { .. } => Some(BinOp::LtFloat),
1843 ClauseGuard::LtEqFloat { .. } => Some(BinOp::LtEqFloat),
1844 ClauseGuard::AddInt { .. } => Some(BinOp::AddInt),
1845 ClauseGuard::AddFloat { .. } => Some(BinOp::AddFloat),
1846 ClauseGuard::SubInt { .. } => Some(BinOp::SubInt),
1847 ClauseGuard::SubFloat { .. } => Some(BinOp::SubFloat),
1848 ClauseGuard::MultInt { .. } => Some(BinOp::MultInt),
1849 ClauseGuard::MultFloat { .. } => Some(BinOp::MultFloat),
1850 ClauseGuard::DivInt { .. } => Some(BinOp::DivInt),
1851 ClauseGuard::DivFloat { .. } => Some(BinOp::DivFloat),
1852 ClauseGuard::RemainderInt { .. } => Some(BinOp::RemainderInt),
1853
1854 ClauseGuard::Constant(_)
1855 | ClauseGuard::Var { .. }
1856 | ClauseGuard::Not { .. }
1857 | ClauseGuard::TupleIndex { .. }
1858 | ClauseGuard::FieldAccess { .. }
1859 | ClauseGuard::ModuleSelect { .. } => None,
1860 }
1861 }
1862}
1863
1864impl TypedClauseGuard {
1865 pub fn type_(&self) -> Arc<Type> {
1866 match self {
1867 ClauseGuard::Var { type_, .. } => type_.clone(),
1868 ClauseGuard::TupleIndex { type_, .. } => type_.clone(),
1869 ClauseGuard::FieldAccess { type_, .. } => type_.clone(),
1870 ClauseGuard::ModuleSelect { type_, .. } => type_.clone(),
1871 ClauseGuard::Constant(constant) => constant.type_(),
1872
1873 ClauseGuard::AddInt { .. }
1874 | ClauseGuard::SubInt { .. }
1875 | ClauseGuard::MultInt { .. }
1876 | ClauseGuard::DivInt { .. }
1877 | ClauseGuard::RemainderInt { .. } => type_::int(),
1878
1879 ClauseGuard::AddFloat { .. }
1880 | ClauseGuard::SubFloat { .. }
1881 | ClauseGuard::MultFloat { .. }
1882 | ClauseGuard::DivFloat { .. } => type_::float(),
1883
1884 ClauseGuard::Or { .. }
1885 | ClauseGuard::Not { .. }
1886 | ClauseGuard::And { .. }
1887 | ClauseGuard::Equals { .. }
1888 | ClauseGuard::NotEquals { .. }
1889 | ClauseGuard::GtInt { .. }
1890 | ClauseGuard::GtEqInt { .. }
1891 | ClauseGuard::LtInt { .. }
1892 | ClauseGuard::LtEqInt { .. }
1893 | ClauseGuard::GtFloat { .. }
1894 | ClauseGuard::GtEqFloat { .. }
1895 | ClauseGuard::LtFloat { .. }
1896 | ClauseGuard::LtEqFloat { .. } => type_::bool(),
1897 }
1898 }
1899
1900 pub(crate) fn referenced_variables(&self) -> im::HashSet<&EcoString> {
1901 match self {
1902 ClauseGuard::Var { name, .. } => im::hashset![name],
1903
1904 ClauseGuard::Not { expression, .. } => expression.referenced_variables(),
1905 ClauseGuard::TupleIndex { tuple, .. } => tuple.referenced_variables(),
1906 ClauseGuard::FieldAccess { container, .. } => container.referenced_variables(),
1907 ClauseGuard::Constant(constant) => constant.referenced_variables(),
1908 ClauseGuard::ModuleSelect { .. } => im::HashSet::new(),
1909
1910 ClauseGuard::Equals { left, right, .. }
1911 | ClauseGuard::NotEquals { left, right, .. }
1912 | ClauseGuard::GtInt { left, right, .. }
1913 | ClauseGuard::GtEqInt { left, right, .. }
1914 | ClauseGuard::LtInt { left, right, .. }
1915 | ClauseGuard::LtEqInt { left, right, .. }
1916 | ClauseGuard::GtFloat { left, right, .. }
1917 | ClauseGuard::GtEqFloat { left, right, .. }
1918 | ClauseGuard::LtFloat { left, right, .. }
1919 | ClauseGuard::LtEqFloat { left, right, .. }
1920 | ClauseGuard::AddInt { left, right, .. }
1921 | ClauseGuard::AddFloat { left, right, .. }
1922 | ClauseGuard::SubInt { left, right, .. }
1923 | ClauseGuard::SubFloat { left, right, .. }
1924 | ClauseGuard::MultInt { left, right, .. }
1925 | ClauseGuard::MultFloat { left, right, .. }
1926 | ClauseGuard::DivInt { left, right, .. }
1927 | ClauseGuard::DivFloat { left, right, .. }
1928 | ClauseGuard::RemainderInt { left, right, .. }
1929 | ClauseGuard::And { left, right, .. }
1930 | ClauseGuard::Or { left, right, .. } => left
1931 .referenced_variables()
1932 .union(right.referenced_variables()),
1933 }
1934 }
1935}
1936
1937#[derive(
1938 Debug,
1939 PartialEq,
1940 Eq,
1941 PartialOrd,
1942 Ord,
1943 Default,
1944 Clone,
1945 Copy,
1946 serde::Serialize,
1947 serde::Deserialize,
1948)]
1949pub struct SrcSpan {
1950 pub start: u32,
1951 pub end: u32,
1952}
1953
1954impl SrcSpan {
1955 pub fn new(start: u32, end: u32) -> Self {
1956 Self { start, end }
1957 }
1958
1959 pub fn contains(&self, byte_index: u32) -> bool {
1960 byte_index >= self.start && byte_index <= self.end
1961 }
1962
1963 /// Merges two spans into a new one that starts at the start of the smaller
1964 /// one and ends at the end of the bigger one. For example:
1965 ///
1966 /// ```txt
1967 /// wibble wobble
1968 /// ─┬──── ─┬────
1969 /// │ ╰─ one span
1970 /// ╰─ the other span
1971 /// ─┬──────────────
1972 /// ╰─ the span you get by merging the two
1973 /// ```
1974 pub fn merge(&self, with: &SrcSpan) -> SrcSpan {
1975 Self {
1976 start: self.start.min(with.start),
1977 end: self.end.max(with.end),
1978 }
1979 }
1980}
1981
1982#[derive(Debug, PartialEq, Eq, Clone)]
1983pub struct DefinitionLocation {
1984 pub module: Option<EcoString>,
1985 pub span: SrcSpan,
1986}
1987
1988pub type UntypedPattern = Pattern<()>;
1989pub type TypedPattern = Pattern<Arc<Type>>;
1990
1991#[derive(Debug, Clone, PartialEq, Eq)]
1992pub enum Pattern<Type> {
1993 Int {
1994 location: SrcSpan,
1995 value: EcoString,
1996 int_value: BigInt,
1997 },
1998
1999 Float {
2000 location: SrcSpan,
2001 value: EcoString,
2002 },
2003
2004 String {
2005 location: SrcSpan,
2006 value: EcoString,
2007 },
2008
2009 /// The creation of a variable.
2010 /// e.g. `assert [this_is_a_var, .._] = x`
2011 Variable {
2012 location: SrcSpan,
2013 name: EcoString,
2014 type_: Type,
2015 origin: VariableOrigin,
2016 },
2017
2018 /// A reference to a variable in a bit array. This is always a variable
2019 /// being used rather than a new variable being assigned.
2020 /// e.g. `assert <<y:size(somevar)>> = x`
2021 VarUsage {
2022 location: SrcSpan,
2023 name: EcoString,
2024 constructor: Option<ValueConstructor>,
2025 type_: Type,
2026 },
2027
2028 /// A name given to a sub-pattern using the `as` keyword.
2029 /// e.g. `assert #(1, [_, _] as the_list) = x`
2030 Assign {
2031 name: EcoString,
2032 location: SrcSpan,
2033 pattern: Box<Self>,
2034 },
2035
2036 /// A pattern that binds to any value but does not assign a variable.
2037 /// Always starts with an underscore.
2038 Discard {
2039 name: EcoString,
2040 location: SrcSpan,
2041 type_: Type,
2042 },
2043
2044 List {
2045 location: SrcSpan,
2046 elements: Vec<Self>,
2047 tail: Option<Box<Self>>,
2048 type_: Type,
2049 },
2050
2051 /// The constructor for a custom type. Starts with an uppercase letter.
2052 Constructor {
2053 location: SrcSpan,
2054 name_location: SrcSpan,
2055 name: EcoString,
2056 arguments: Vec<CallArg<Self>>,
2057 module: Option<(EcoString, SrcSpan)>,
2058 constructor: Inferred<PatternConstructor>,
2059 spread: Option<SrcSpan>,
2060 type_: Type,
2061 },
2062
2063 Tuple {
2064 location: SrcSpan,
2065 elements: Vec<Self>,
2066 },
2067
2068 BitArray {
2069 location: SrcSpan,
2070 segments: Vec<BitArraySegment<Self, Type>>,
2071 },
2072
2073 // "prefix" <> variable
2074 StringPrefix {
2075 location: SrcSpan,
2076 left_location: SrcSpan,
2077 left_side_assignment: Option<(EcoString, SrcSpan)>,
2078 right_location: SrcSpan,
2079 left_side_string: EcoString,
2080 /// The variable on the right hand side of the `<>`.
2081 right_side_assignment: AssignName,
2082 },
2083
2084 /// A placeholder pattern used to allow module analysis to continue
2085 /// even when there are type errors. Should never end up in generated code.
2086 Invalid {
2087 location: SrcSpan,
2088 type_: Type,
2089 },
2090}
2091
2092impl Default for Inferred<()> {
2093 fn default() -> Self {
2094 Self::Unknown
2095 }
2096}
2097
2098#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2099pub enum AssignName {
2100 Variable(EcoString),
2101 Discard(EcoString),
2102}
2103
2104impl AssignName {
2105 pub fn name(&self) -> &EcoString {
2106 match self {
2107 AssignName::Variable(name) | AssignName::Discard(name) => name,
2108 }
2109 }
2110
2111 pub fn to_arg_names(self, location: SrcSpan) -> ArgNames {
2112 match self {
2113 AssignName::Variable(name) => ArgNames::Named { name, location },
2114 AssignName::Discard(name) => ArgNames::Discard { name, location },
2115 }
2116 }
2117
2118 pub fn assigned_name(&self) -> Option<&str> {
2119 match self {
2120 AssignName::Variable(name) => Some(name),
2121 AssignName::Discard(_) => None,
2122 }
2123 }
2124}
2125
2126impl<A> Pattern<A> {
2127 pub fn location(&self) -> SrcSpan {
2128 match self {
2129 Pattern::Assign {
2130 pattern, location, ..
2131 } => SrcSpan::new(pattern.location().start, location.end),
2132 Pattern::Int { location, .. }
2133 | Pattern::Variable { location, .. }
2134 | Pattern::VarUsage { location, .. }
2135 | Pattern::List { location, .. }
2136 | Pattern::Float { location, .. }
2137 | Pattern::Discard { location, .. }
2138 | Pattern::String { location, .. }
2139 | Pattern::Tuple { location, .. }
2140 | Pattern::Constructor { location, .. }
2141 | Pattern::StringPrefix { location, .. }
2142 | Pattern::BitArray { location, .. }
2143 | Pattern::Invalid { location, .. } => *location,
2144 }
2145 }
2146
2147 /// Returns `true` if the pattern is [`Discard`].
2148 ///
2149 /// [`Discard`]: Pattern::Discard
2150 #[must_use]
2151 pub fn is_discard(&self) -> bool {
2152 matches!(self, Self::Discard { .. })
2153 }
2154
2155 #[must_use]
2156 pub fn is_variable(&self) -> bool {
2157 match self {
2158 Pattern::Variable { .. } => true,
2159 _ => false,
2160 }
2161 }
2162
2163 #[must_use]
2164 pub fn is_string(&self) -> bool {
2165 matches!(self, Self::String { .. })
2166 }
2167}
2168
2169impl TypedPattern {
2170 pub fn definition_location(&self) -> Option<DefinitionLocation> {
2171 match self {
2172 Pattern::Int { .. }
2173 | Pattern::Float { .. }
2174 | Pattern::String { .. }
2175 | Pattern::Variable { .. }
2176 | Pattern::VarUsage { .. }
2177 | Pattern::Assign { .. }
2178 | Pattern::Discard { .. }
2179 | Pattern::List { .. }
2180 | Pattern::Tuple { .. }
2181 | Pattern::BitArray { .. }
2182 | Pattern::StringPrefix { .. }
2183 | Pattern::Invalid { .. } => None,
2184
2185 Pattern::Constructor { constructor, .. } => constructor.definition_location(),
2186 }
2187 }
2188
2189 pub fn get_documentation(&self) -> Option<&str> {
2190 match self {
2191 Pattern::Int { .. }
2192 | Pattern::Float { .. }
2193 | Pattern::String { .. }
2194 | Pattern::Variable { .. }
2195 | Pattern::VarUsage { .. }
2196 | Pattern::Assign { .. }
2197 | Pattern::Discard { .. }
2198 | Pattern::List { .. }
2199 | Pattern::Tuple { .. }
2200 | Pattern::BitArray { .. }
2201 | Pattern::StringPrefix { .. }
2202 | Pattern::Invalid { .. } => None,
2203
2204 Pattern::Constructor { constructor, .. } => constructor.get_documentation(),
2205 }
2206 }
2207
2208 pub fn type_(&self) -> Arc<Type> {
2209 match self {
2210 Pattern::Int { .. } => type_::int(),
2211 Pattern::Float { .. } => type_::float(),
2212 Pattern::String { .. } => type_::string(),
2213 Pattern::BitArray { .. } => type_::bit_array(),
2214 Pattern::StringPrefix { .. } => type_::string(),
2215
2216 Pattern::Variable { type_, .. }
2217 | Pattern::List { type_, .. }
2218 | Pattern::VarUsage { type_, .. }
2219 | Pattern::Constructor { type_, .. }
2220 | Pattern::Invalid { type_, .. } => type_.clone(),
2221
2222 Pattern::Assign { pattern, .. } => pattern.type_(),
2223
2224 Pattern::Discard { type_, .. } => type_.clone(),
2225
2226 Pattern::Tuple { elements, .. } => {
2227 type_::tuple(elements.iter().map(|p| p.type_()).collect())
2228 }
2229 }
2230 }
2231
2232 fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2233 if !self.location().contains(byte_index) {
2234 return None;
2235 }
2236
2237 if let Pattern::Variable { name, .. } = self {
2238 // For pipes the pattern can't be pointed to
2239 if name.as_str().eq(PIPE_VARIABLE) {
2240 return None;
2241 }
2242 }
2243
2244 match self {
2245 Pattern::Int { .. }
2246 | Pattern::Float { .. }
2247 | Pattern::String { .. }
2248 | Pattern::Variable { .. }
2249 | Pattern::VarUsage { .. }
2250 | Pattern::Assign { .. }
2251 | Pattern::Discard { .. }
2252 | Pattern::StringPrefix { .. }
2253 | Pattern::Invalid { .. } => Some(Located::Pattern(self)),
2254
2255 Pattern::Constructor {
2256 arguments, spread, ..
2257 } => match spread {
2258 Some(spread_location) if spread_location.contains(byte_index) => {
2259 Some(Located::PatternSpread {
2260 spread_location: *spread_location,
2261 pattern: self,
2262 })
2263 }
2264
2265 Some(_) | None => arguments.iter().find_map(|arg| arg.find_node(byte_index)),
2266 },
2267 Pattern::List { elements, tail, .. } => elements
2268 .iter()
2269 .find_map(|p| p.find_node(byte_index))
2270 .or_else(|| tail.as_ref().and_then(|p| p.find_node(byte_index))),
2271
2272 Pattern::Tuple { elements, .. } => {
2273 elements.iter().find_map(|p| p.find_node(byte_index))
2274 }
2275
2276 Pattern::BitArray { segments, .. } => segments
2277 .iter()
2278 .find_map(|segment| segment.find_node(byte_index))
2279 .or(Some(Located::Pattern(self))),
2280 }
2281 .or(Some(Located::Pattern(self)))
2282 }
2283
2284 /// If the pattern is a `Constructor` with a spread, it returns a tuple with
2285 /// all the ignored fields. Split in unlabelled and labelled ones.
2286 ///
2287 pub(crate) fn unused_arguments(&self) -> Option<PatternUnusedArguments> {
2288 let TypedPattern::Constructor {
2289 arguments,
2290 spread: Some(_),
2291 ..
2292 } = self
2293 else {
2294 return None;
2295 };
2296
2297 let mut positional = vec![];
2298 let mut labelled = vec![];
2299 for argument in arguments {
2300 // We only want to display the arguments that were ignored using `..`.
2301 // Any argument ignored that way is marked as implicit, so if it is
2302 // not implicit we just ignore it.
2303 if !argument.is_implicit() {
2304 continue;
2305 }
2306 let type_ = argument.value.type_();
2307 match &argument.label {
2308 Some(label) => labelled.push((label.clone(), type_)),
2309 None => positional.push(type_),
2310 }
2311 }
2312
2313 Some(PatternUnusedArguments {
2314 positional,
2315 labelled,
2316 })
2317 }
2318
2319 /// Whether the pattern always matches. For example, a tuple or simple
2320 /// variable assignment always match and can never fail.
2321 #[must_use]
2322 pub fn always_matches(&self) -> bool {
2323 match self {
2324 Pattern::Variable { .. } | Pattern::Discard { .. } => true,
2325 Pattern::Assign { pattern, .. } => pattern.always_matches(),
2326 Pattern::Tuple { elements, .. } => {
2327 elements.iter().all(|element| element.always_matches())
2328 }
2329 Pattern::Int { .. }
2330 | Pattern::Float { .. }
2331 | Pattern::String { .. }
2332 | Pattern::VarUsage { .. }
2333 | Pattern::List { .. }
2334 | Pattern::Constructor { .. }
2335 | Pattern::BitArray { .. }
2336 | Pattern::StringPrefix { .. }
2337 | Pattern::Invalid { .. } => false,
2338 }
2339 }
2340
2341 pub fn bound_variables(&self) -> Vec<EcoString> {
2342 let mut variables = Vec::new();
2343 self.collect_bound_variables(&mut variables);
2344 variables
2345 }
2346
2347 fn collect_bound_variables(&self, variables: &mut Vec<EcoString>) {
2348 match self {
2349 Pattern::Int { .. }
2350 | Pattern::Float { .. }
2351 | Pattern::String { .. }
2352 | Pattern::Discard { .. }
2353 | Pattern::Invalid { .. } => {}
2354
2355 Pattern::Variable { name, .. } => variables.push(name.clone()),
2356 Pattern::VarUsage { .. } => {}
2357 Pattern::Assign { name, pattern, .. } => {
2358 variables.push(name.clone());
2359 pattern.collect_bound_variables(variables);
2360 }
2361 Pattern::List { elements, tail, .. } => {
2362 for element in elements {
2363 element.collect_bound_variables(variables);
2364 }
2365 if let Some(tail) = tail {
2366 tail.collect_bound_variables(variables);
2367 }
2368 }
2369 Pattern::Constructor { arguments, .. } => {
2370 for argument in arguments {
2371 argument.value.collect_bound_variables(variables);
2372 }
2373 }
2374 Pattern::Tuple { elements, .. } => {
2375 for element in elements {
2376 element.collect_bound_variables(variables);
2377 }
2378 }
2379 Pattern::BitArray { segments, .. } => {
2380 for segment in segments {
2381 segment.value.collect_bound_variables(variables);
2382 }
2383 }
2384 Pattern::StringPrefix {
2385 left_side_assignment,
2386 right_side_assignment,
2387 ..
2388 } => {
2389 if let Some((left_variable, _)) = left_side_assignment {
2390 variables.push(left_variable.clone());
2391 }
2392 match right_side_assignment {
2393 AssignName::Variable(name) => variables.push(name.clone()),
2394 AssignName::Discard(_) => {}
2395 }
2396 }
2397 }
2398 }
2399}
2400
2401#[derive(Debug, Default)]
2402pub struct PatternUnusedArguments {
2403 pub positional: Vec<Arc<Type>>,
2404 pub labelled: Vec<(EcoString, Arc<Type>)>,
2405}
2406
2407impl<A> HasLocation for Pattern<A> {
2408 fn location(&self) -> SrcSpan {
2409 self.location()
2410 }
2411}
2412
2413#[derive(Debug, Clone, PartialEq, Eq)]
2414pub enum AssignmentKind<Expression> {
2415 /// let x = ...
2416 Let,
2417 /// This is a let assignment generated by the compiler for intermediate variables
2418 /// needed by record updates and `use`.
2419 /// Like a regular `Let` assignment this can never fail.
2420 ///
2421 Generated,
2422 /// let assert x = ...
2423 Assert {
2424 /// The src byte span of the `let assert`
2425 ///
2426 /// ```gleam
2427 /// let assert Wibble = todo
2428 /// ^^^^^^^^^^
2429 /// ```
2430 location: SrcSpan,
2431
2432 /// The byte index of the start of `assert`
2433 ///
2434 /// ```gleam
2435 /// let assert Wibble = todo
2436 /// ^
2437 /// ```
2438 assert_keyword_start: u32,
2439
2440 /// The message given to the assertion:
2441 ///
2442 /// ```gleam
2443 /// let asset Ok(a) = something() as "This will never fail"
2444 /// ^^^^^^^^^^^^^^^^^^^^^^
2445 /// ```
2446 message: Option<Expression>,
2447 },
2448}
2449
2450impl<Expression> AssignmentKind<Expression> {
2451 /// Returns `true` if the assignment kind is [`Assert`].
2452 ///
2453 /// [`Assert`]: AssignmentKind::Assert
2454 #[must_use]
2455 pub fn is_assert(&self) -> bool {
2456 match self {
2457 Self::Assert { .. } => true,
2458 Self::Let | Self::Generated => false,
2459 }
2460 }
2461}
2462
2463// BitArrays
2464
2465pub type UntypedExprBitArraySegment = BitArraySegment<UntypedExpr, ()>;
2466pub type TypedExprBitArraySegment = BitArraySegment<TypedExpr, Arc<Type>>;
2467
2468pub type UntypedConstantBitArraySegment = BitArraySegment<UntypedConstant, ()>;
2469pub type TypedConstantBitArraySegment = BitArraySegment<TypedConstant, Arc<Type>>;
2470
2471pub type UntypedPatternBitArraySegment = BitArraySegment<UntypedPattern, ()>;
2472pub type TypedPatternBitArraySegment = BitArraySegment<TypedPattern, Arc<Type>>;
2473
2474#[derive(Debug, Clone, PartialEq, Eq)]
2475pub struct BitArraySegment<Value, Type> {
2476 pub location: SrcSpan,
2477 pub value: Box<Value>,
2478 pub options: Vec<BitArrayOption<Value>>,
2479 pub type_: Type,
2480}
2481
2482#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
2483pub enum Endianness {
2484 Big,
2485 Little,
2486}
2487
2488impl Endianness {
2489 pub fn is_big(&self) -> bool {
2490 *self == Endianness::Big
2491 }
2492}
2493
2494impl<Type> BitArraySegment<Pattern<Type>, Type> {
2495 /// Returns the value of the pattern unwrapping any assign pattern.
2496 ///
2497 pub fn value_unwrapping_assign(&self) -> &Pattern<Type> {
2498 match self.value.as_ref() {
2499 Pattern::Assign { pattern, .. } => pattern,
2500 Pattern::Int { .. }
2501 | Pattern::Float { .. }
2502 | Pattern::String { .. }
2503 | Pattern::Variable { .. }
2504 | Pattern::VarUsage { .. }
2505 | Pattern::Discard { .. }
2506 | Pattern::List { .. }
2507 | Pattern::Constructor { .. }
2508 | Pattern::Tuple { .. }
2509 | Pattern::BitArray { .. }
2510 | Pattern::StringPrefix { .. }
2511 | Pattern::Invalid { .. } => self.value.as_ref(),
2512 }
2513 }
2514}
2515
2516impl<Value, Type> BitArraySegment<Value, Type> {
2517 #[must_use]
2518 pub fn has_native_option(&self) -> bool {
2519 self.options
2520 .iter()
2521 .any(|x| matches!(x, BitArrayOption::Native { .. }))
2522 }
2523
2524 #[must_use]
2525 pub fn has_utf16_codepoint_option(&self) -> bool {
2526 self.options
2527 .iter()
2528 .any(|x| matches!(x, BitArrayOption::Utf16Codepoint { .. }))
2529 }
2530
2531 #[must_use]
2532 pub fn has_utf32_codepoint_option(&self) -> bool {
2533 self.options
2534 .iter()
2535 .any(|x| matches!(x, BitArrayOption::Utf32Codepoint { .. }))
2536 }
2537
2538 #[must_use]
2539 pub fn has_utf16_option(&self) -> bool {
2540 self.options
2541 .iter()
2542 .any(|x| matches!(x, BitArrayOption::Utf16 { .. }))
2543 }
2544
2545 #[must_use]
2546 pub fn has_utf32_option(&self) -> bool {
2547 self.options
2548 .iter()
2549 .any(|x| matches!(x, BitArrayOption::Utf32 { .. }))
2550 }
2551
2552 pub fn endianness(&self) -> Endianness {
2553 if self
2554 .options
2555 .iter()
2556 .any(|x| matches!(x, BitArrayOption::Little { .. }))
2557 {
2558 Endianness::Little
2559 } else {
2560 Endianness::Big
2561 }
2562 }
2563
2564 pub(crate) fn signed(&self) -> bool {
2565 self.options
2566 .iter()
2567 .any(|x| matches!(x, BitArrayOption::Signed { .. }))
2568 }
2569
2570 pub fn size(&self) -> Option<&Value> {
2571 self.options.iter().find_map(|x| match x {
2572 BitArrayOption::Size { value, .. } => Some(value.as_ref()),
2573 _ => None,
2574 })
2575 }
2576
2577 pub fn unit(&self) -> u8 {
2578 self.options
2579 .iter()
2580 .find_map(|option| match option {
2581 BitArrayOption::Unit { value, .. } => Some(*value),
2582 BitArrayOption::Bytes { .. } => Some(8),
2583 _ => None,
2584 })
2585 .unwrap_or(1)
2586 }
2587
2588 pub(crate) fn has_bits_option(&self) -> bool {
2589 self.options.iter().any(|option| match option {
2590 BitArrayOption::Bits { .. } => true,
2591 _ => false,
2592 })
2593 }
2594
2595 pub(crate) fn has_bytes_option(&self) -> bool {
2596 self.options.iter().any(|option| match option {
2597 BitArrayOption::Bytes { .. } => true,
2598 _ => false,
2599 })
2600 }
2601}
2602
2603impl<Value, Type> BitArraySegment<Value, Type> {
2604 #[must_use]
2605 pub(crate) fn has_type_option(&self) -> bool {
2606 self.options.iter().any(|option| option.is_type_option())
2607 }
2608}
2609
2610impl TypedExprBitArraySegment {
2611 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2612 self.value.find_node(byte_index)
2613 }
2614}
2615
2616impl<TypedValue> BitArraySegment<TypedValue, Arc<Type>>
2617where
2618 TypedValue: HasType + HasLocation + Clone + bit_array::GetLiteralValue,
2619{
2620 pub(crate) fn check_for_truncated_value(&self) -> Option<BitArraySegmentTruncation> {
2621 // Both the size and the value must be two compile-time known constants.
2622 let segment_bits = self.bits_size()?.to_i64()?;
2623 let literal_value = self.value.as_int_literal()?;
2624 if segment_bits <= 0 {
2625 return None;
2626 }
2627
2628 let safe_range = match literal_value.sign() {
2629 Sign::NoSign => return None,
2630 Sign::Minus => {
2631 (-(BigInt::one() << (segment_bits - 1)))
2632 ..((BigInt::one() << (segment_bits - 1)) - 1)
2633 }
2634 Sign::Plus => BigInt::ZERO..(BigInt::one() << segment_bits),
2635 };
2636
2637 if !safe_range.contains(&literal_value) {
2638 Some(BitArraySegmentTruncation {
2639 truncated_value: literal_value.clone(),
2640 truncated_into: truncate(&literal_value, segment_bits),
2641 value_location: self.value.location(),
2642 segment_bits,
2643 })
2644 } else {
2645 None
2646 }
2647 }
2648
2649 /// If the segment size is a compile-time known constant this returns the
2650 /// segment size in bits, taking the segment's unit into consideration!
2651 ///
2652 fn bits_size(&self) -> Option<BigInt> {
2653 let size = match self.size() {
2654 None if self.type_.is_int() => 8.into(),
2655 None => 64.into(),
2656 Some(value) => value.as_int_literal()?,
2657 };
2658
2659 let unit = self.unit();
2660 Some(size * unit)
2661 }
2662}
2663
2664/// As Björn said, when a value is smaller than the segment's size it will be
2665/// truncated, only taking the first `n` bits:
2666///
2667/// > It will be silently truncated. In general, when storing value an integer
2668/// > `I` into a segment of size `N`, the actual value stored will be
2669/// > `I band ((1 bsl N) - 1)`.
2670///
2671/// <https://erlangforums.com/t/what-happens-when-a-bit-array-segment-size-is-smaller-than-its-value/4650/2?u=giacomocavalieri>
2672///
2673/// Thank you Björn!
2674///
2675fn truncate(literal_value: &BigInt, segment_bits: i64) -> BigInt {
2676 literal_value & ((BigInt::one() << segment_bits) - BigInt::one())
2677}
2678
2679#[derive(serde::Deserialize, serde::Serialize, Eq, PartialEq, Clone, Debug)]
2680pub struct BitArraySegmentTruncation {
2681 /// The value that would end up being truncated.
2682 pub truncated_value: BigInt,
2683 /// What the value would be truncated into.
2684 pub truncated_into: BigInt,
2685 /// The span of the segment's value being truncated.
2686 pub value_location: SrcSpan,
2687 /// The size of the segment.
2688 pub segment_bits: i64,
2689}
2690
2691impl TypedPatternBitArraySegment {
2692 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2693 self.value.find_node(byte_index).or_else(|| {
2694 self.options
2695 .iter()
2696 .find_map(|option| option.find_node(byte_index))
2697 })
2698 }
2699}
2700
2701impl TypedConstantBitArraySegment {
2702 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2703 self.value.find_node(byte_index).or_else(|| {
2704 self.options
2705 .iter()
2706 .find_map(|option| option.find_node(byte_index))
2707 })
2708 }
2709}
2710
2711pub type TypedConstantBitArraySegmentOption = BitArrayOption<TypedConstant>;
2712
2713#[derive(Debug, PartialEq, Eq, Clone)]
2714pub enum BitArrayOption<Value> {
2715 Bytes {
2716 location: SrcSpan,
2717 },
2718
2719 Int {
2720 location: SrcSpan,
2721 },
2722
2723 Float {
2724 location: SrcSpan,
2725 },
2726
2727 Bits {
2728 location: SrcSpan,
2729 },
2730
2731 Utf8 {
2732 location: SrcSpan,
2733 },
2734
2735 Utf16 {
2736 location: SrcSpan,
2737 },
2738
2739 Utf32 {
2740 location: SrcSpan,
2741 },
2742
2743 Utf8Codepoint {
2744 location: SrcSpan,
2745 },
2746
2747 Utf16Codepoint {
2748 location: SrcSpan,
2749 },
2750
2751 Utf32Codepoint {
2752 location: SrcSpan,
2753 },
2754
2755 Signed {
2756 location: SrcSpan,
2757 },
2758
2759 Unsigned {
2760 location: SrcSpan,
2761 },
2762
2763 Big {
2764 location: SrcSpan,
2765 },
2766
2767 Little {
2768 location: SrcSpan,
2769 },
2770
2771 Native {
2772 location: SrcSpan,
2773 },
2774
2775 Size {
2776 location: SrcSpan,
2777 value: Box<Value>,
2778 short_form: bool,
2779 },
2780
2781 Unit {
2782 location: SrcSpan,
2783 value: u8,
2784 },
2785}
2786
2787impl<A> BitArrayOption<A> {
2788 pub fn value(&self) -> Option<&A> {
2789 match self {
2790 BitArrayOption::Size { value, .. } => Some(value),
2791 _ => None,
2792 }
2793 }
2794
2795 pub fn location(&self) -> SrcSpan {
2796 match self {
2797 BitArrayOption::Bytes { location }
2798 | BitArrayOption::Int { location }
2799 | BitArrayOption::Float { location }
2800 | BitArrayOption::Bits { location }
2801 | BitArrayOption::Utf8 { location }
2802 | BitArrayOption::Utf16 { location }
2803 | BitArrayOption::Utf32 { location }
2804 | BitArrayOption::Utf8Codepoint { location }
2805 | BitArrayOption::Utf16Codepoint { location }
2806 | BitArrayOption::Utf32Codepoint { location }
2807 | BitArrayOption::Signed { location }
2808 | BitArrayOption::Unsigned { location }
2809 | BitArrayOption::Big { location }
2810 | BitArrayOption::Little { location }
2811 | BitArrayOption::Native { location }
2812 | BitArrayOption::Size { location, .. }
2813 | BitArrayOption::Unit { location, .. } => *location,
2814 }
2815 }
2816
2817 pub fn label(&self) -> EcoString {
2818 match self {
2819 BitArrayOption::Bytes { .. } => "bytes".into(),
2820 BitArrayOption::Int { .. } => "int".into(),
2821 BitArrayOption::Float { .. } => "float".into(),
2822 BitArrayOption::Bits { .. } => "bits".into(),
2823 BitArrayOption::Utf8 { .. } => "utf8".into(),
2824 BitArrayOption::Utf16 { .. } => "utf16".into(),
2825 BitArrayOption::Utf32 { .. } => "utf32".into(),
2826 BitArrayOption::Utf8Codepoint { .. } => "utf8_codepoint".into(),
2827 BitArrayOption::Utf16Codepoint { .. } => "utf16_codepoint".into(),
2828 BitArrayOption::Utf32Codepoint { .. } => "utf32_codepoint".into(),
2829 BitArrayOption::Signed { .. } => "signed".into(),
2830 BitArrayOption::Unsigned { .. } => "unsigned".into(),
2831 BitArrayOption::Big { .. } => "big".into(),
2832 BitArrayOption::Little { .. } => "little".into(),
2833 BitArrayOption::Native { .. } => "native".into(),
2834 BitArrayOption::Size { .. } => "size".into(),
2835 BitArrayOption::Unit { .. } => "unit".into(),
2836 }
2837 }
2838
2839 fn is_type_option(&self) -> bool {
2840 match self {
2841 BitArrayOption::Bytes { .. }
2842 | BitArrayOption::Int { .. }
2843 | BitArrayOption::Float { .. }
2844 | BitArrayOption::Bits { .. }
2845 | BitArrayOption::Utf8 { .. }
2846 | BitArrayOption::Utf16 { .. }
2847 | BitArrayOption::Utf32 { .. }
2848 | BitArrayOption::Utf8Codepoint { .. }
2849 | BitArrayOption::Utf16Codepoint { .. }
2850 | BitArrayOption::Utf32Codepoint { .. } => true,
2851
2852 BitArrayOption::Signed { .. }
2853 | BitArrayOption::Unsigned { .. }
2854 | BitArrayOption::Big { .. }
2855 | BitArrayOption::Little { .. }
2856 | BitArrayOption::Native { .. }
2857 | BitArrayOption::Size { .. }
2858 | BitArrayOption::Unit { .. } => false,
2859 }
2860 }
2861}
2862
2863impl BitArrayOption<TypedConstant> {
2864 fn referenced_variables(&self) -> im::HashSet<&EcoString> {
2865 match self {
2866 BitArrayOption::Bytes { .. }
2867 | BitArrayOption::Int { .. }
2868 | BitArrayOption::Float { .. }
2869 | BitArrayOption::Bits { .. }
2870 | BitArrayOption::Utf8 { .. }
2871 | BitArrayOption::Utf16 { .. }
2872 | BitArrayOption::Utf32 { .. }
2873 | BitArrayOption::Utf8Codepoint { .. }
2874 | BitArrayOption::Utf16Codepoint { .. }
2875 | BitArrayOption::Utf32Codepoint { .. }
2876 | BitArrayOption::Signed { .. }
2877 | BitArrayOption::Unsigned { .. }
2878 | BitArrayOption::Big { .. }
2879 | BitArrayOption::Little { .. }
2880 | BitArrayOption::Unit { .. }
2881 | BitArrayOption::Native { .. } => im::hashset![],
2882
2883 BitArrayOption::Size { value, .. } => value.referenced_variables(),
2884 }
2885 }
2886}
2887
2888impl BitArrayOption<TypedPattern> {
2889 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2890 match self {
2891 BitArrayOption::Bytes { .. }
2892 | BitArrayOption::Int { .. }
2893 | BitArrayOption::Float { .. }
2894 | BitArrayOption::Bits { .. }
2895 | BitArrayOption::Utf8 { .. }
2896 | BitArrayOption::Utf16 { .. }
2897 | BitArrayOption::Utf32 { .. }
2898 | BitArrayOption::Utf8Codepoint { .. }
2899 | BitArrayOption::Utf16Codepoint { .. }
2900 | BitArrayOption::Utf32Codepoint { .. }
2901 | BitArrayOption::Signed { .. }
2902 | BitArrayOption::Unsigned { .. }
2903 | BitArrayOption::Big { .. }
2904 | BitArrayOption::Little { .. }
2905 | BitArrayOption::Native { .. }
2906 | BitArrayOption::Unit { .. } => None,
2907 BitArrayOption::Size { value, .. } => value.find_node(byte_index),
2908 }
2909 }
2910}
2911
2912impl BitArrayOption<TypedConstant> {
2913 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
2914 match self {
2915 BitArrayOption::Bytes { .. }
2916 | BitArrayOption::Int { .. }
2917 | BitArrayOption::Float { .. }
2918 | BitArrayOption::Bits { .. }
2919 | BitArrayOption::Utf8 { .. }
2920 | BitArrayOption::Utf16 { .. }
2921 | BitArrayOption::Utf32 { .. }
2922 | BitArrayOption::Utf8Codepoint { .. }
2923 | BitArrayOption::Utf16Codepoint { .. }
2924 | BitArrayOption::Utf32Codepoint { .. }
2925 | BitArrayOption::Signed { .. }
2926 | BitArrayOption::Unsigned { .. }
2927 | BitArrayOption::Big { .. }
2928 | BitArrayOption::Little { .. }
2929 | BitArrayOption::Native { .. }
2930 | BitArrayOption::Unit { .. } => None,
2931 BitArrayOption::Size { value, .. } => value.find_node(byte_index),
2932 }
2933 }
2934}
2935
2936#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2937pub enum TodoKind {
2938 Keyword,
2939 EmptyFunction { function_location: SrcSpan },
2940 IncompleteUse,
2941 EmptyBlock,
2942}
2943
2944#[derive(Debug, Default)]
2945pub struct GroupedDefinitions {
2946 pub functions: Vec<UntypedFunction>,
2947 pub constants: Vec<UntypedModuleConstant>,
2948 pub custom_types: Vec<UntypedCustomType>,
2949 pub imports: Vec<UntypedImport>,
2950 pub type_aliases: Vec<UntypedTypeAlias>,
2951}
2952
2953impl GroupedDefinitions {
2954 pub fn new(definitions: impl IntoIterator<Item = UntypedDefinition>) -> Self {
2955 let mut this = Self::default();
2956
2957 for definition in definitions {
2958 this.add(definition)
2959 }
2960
2961 this
2962 }
2963
2964 pub fn is_empty(&self) -> bool {
2965 self.len() == 0
2966 }
2967
2968 pub fn len(&self) -> usize {
2969 let Self {
2970 custom_types,
2971 functions,
2972 constants,
2973 imports,
2974 type_aliases,
2975 } = self;
2976 functions.len() + constants.len() + imports.len() + custom_types.len() + type_aliases.len()
2977 }
2978
2979 fn add(&mut self, statement: UntypedDefinition) {
2980 match statement {
2981 Definition::Import(import) => self.imports.push(import),
2982 Definition::Function(function) => self.functions.push(function),
2983 Definition::TypeAlias(type_alias) => self.type_aliases.push(type_alias),
2984 Definition::CustomType(custom_type) => self.custom_types.push(custom_type),
2985 Definition::ModuleConstant(constant) => self.constants.push(constant),
2986 }
2987 }
2988}
2989
2990/// A statement with in a function body.
2991#[derive(Debug, Clone, PartialEq, Eq)]
2992pub enum Statement<TypeT, ExpressionT> {
2993 /// A bare expression that is not assigned to any variable.
2994 Expression(ExpressionT),
2995 /// Assigning an expression to variables using a pattern.
2996 Assignment(Box<Assignment<TypeT, ExpressionT>>),
2997 /// A `use` expression.
2998 Use(Use<TypeT, ExpressionT>),
2999 /// A bool assertion.
3000 Assert(Assert<ExpressionT>),
3001}
3002
3003pub type UntypedUse = Use<(), UntypedExpr>;
3004pub type TypedUse = Use<Arc<Type>, TypedExpr>;
3005
3006#[derive(Debug, Clone, PartialEq, Eq)]
3007pub struct Use<TypeT, ExpressionT> {
3008 /// In an untyped use this is the expression with the untyped code of the
3009 /// callback function.
3010 ///
3011 /// In a typed use this is the typed function call the use expression
3012 /// desugars to.
3013 ///
3014 pub call: Box<ExpressionT>,
3015
3016 /// This is the location of the whole use line, starting from the `use`
3017 /// keyword and ending with the function call on the right hand side of
3018 /// `<-`.
3019 ///
3020 /// ```gleam
3021 /// use a <- result.try(result)
3022 /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
3023 /// ```
3024 ///
3025 pub location: SrcSpan,
3026
3027 /// This is the location of the expression on the right hand side of the use
3028 /// arrow.
3029 ///
3030 /// ```gleam
3031 /// use a <- result.try(result)
3032 /// ^^^^^^^^^^^^^^^^^^
3033 /// ```
3034 ///
3035 pub right_hand_side_location: SrcSpan,
3036
3037 /// This is the SrcSpan of the patterns you find on the left hand side of
3038 /// `<-` in a use expression.
3039 ///
3040 /// ```gleam
3041 /// use pattern1, pattern2 <- todo
3042 /// ^^^^^^^^^^^^^^^^^^
3043 /// ```
3044 ///
3045 /// In case there's no patterns it will be corresponding to the SrcSpan of
3046 /// the `use` keyword itself.
3047 ///
3048 pub assignments_location: SrcSpan,
3049
3050 /// The patterns on the left hand side of `<-` in a use expression.
3051 ///
3052 pub assignments: Vec<UseAssignment<TypeT>>,
3053}
3054
3055pub type UntypedUseAssignment = UseAssignment<()>;
3056pub type TypedUseAssignment = UseAssignment<Arc<Type>>;
3057
3058#[derive(Debug, Clone, PartialEq, Eq)]
3059pub struct UseAssignment<TypeT> {
3060 pub location: SrcSpan,
3061 pub pattern: Pattern<TypeT>,
3062 pub annotation: Option<TypeAst>,
3063}
3064
3065impl TypedUse {
3066 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3067 for assignment in self.assignments.iter() {
3068 if let Some(found) = assignment.pattern.find_node(byte_index) {
3069 return Some(found);
3070 }
3071 if let Some(found) = assignment
3072 .annotation
3073 .as_ref()
3074 .and_then(|annotation| annotation.find_node(byte_index, assignment.pattern.type_()))
3075 {
3076 return Some(found);
3077 }
3078 }
3079 self.call.find_node(byte_index)
3080 }
3081
3082 pub fn callback_arguments(&self) -> Option<&Vec<TypedArg>> {
3083 let TypedExpr::Call { args, .. } = self.call.as_ref() else {
3084 return None;
3085 };
3086 let callback = args.iter().last()?;
3087 let TypedExpr::Fn { args, .. } = &callback.value else {
3088 // The expression might be invalid so we have to return a None here
3089 return None;
3090 };
3091 Some(args)
3092 }
3093}
3094
3095pub type TypedStatement = Statement<Arc<Type>, TypedExpr>;
3096pub type UntypedStatement = Statement<(), UntypedExpr>;
3097
3098impl<T, E> Statement<T, E> {
3099 /// Returns `true` if the statement is [`Expression`].
3100 ///
3101 /// [`Expression`]: Statement::Expression
3102 #[must_use]
3103 pub fn is_expression(&self) -> bool {
3104 matches!(self, Self::Expression(..))
3105 }
3106
3107 #[must_use]
3108 pub(crate) fn is_use(&self) -> bool {
3109 match self {
3110 Self::Use(_) => true,
3111 _ => false,
3112 }
3113 }
3114}
3115
3116impl UntypedStatement {
3117 pub fn location(&self) -> SrcSpan {
3118 match self {
3119 Statement::Expression(expression) => expression.location(),
3120 Statement::Assignment(assignment) => assignment.location,
3121 Statement::Use(use_) => use_.location,
3122 Statement::Assert(assert) => assert.location,
3123 }
3124 }
3125
3126 pub fn start_byte_index(&self) -> u32 {
3127 match self {
3128 Statement::Expression(expression) => expression.start_byte_index(),
3129 Statement::Assignment(assignment) => assignment.location.start,
3130 Statement::Use(use_) => use_.location.start,
3131 Statement::Assert(assert) => assert.location.start,
3132 }
3133 }
3134
3135 pub fn is_placeholder(&self) -> bool {
3136 match self {
3137 Statement::Expression(expression) => expression.is_placeholder(),
3138 Statement::Assignment(_) | Statement::Use(_) | Statement::Assert(_) => false,
3139 }
3140 }
3141}
3142
3143impl TypedStatement {
3144 pub fn is_println(&self) -> bool {
3145 match self {
3146 Statement::Expression(e) => e.is_println(),
3147 Statement::Assignment(_) => false,
3148 Statement::Use(_) => false,
3149 Statement::Assert(_) => false,
3150 }
3151 }
3152
3153 pub fn location(&self) -> SrcSpan {
3154 match self {
3155 Statement::Expression(expression) => expression.location(),
3156 Statement::Assignment(assignment) => assignment.location,
3157 Statement::Use(use_) => use_.location,
3158 Statement::Assert(assert) => assert.location,
3159 }
3160 }
3161
3162 /// Returns the location of the last element of a statement. This means that
3163 /// if the statement is a use you'll get the location of the last item at
3164 /// the end of its block.
3165 pub fn last_location(&self) -> SrcSpan {
3166 match self {
3167 Statement::Expression(expression) => expression.last_location(),
3168 Statement::Assignment(assignment) => assignment.value.last_location(),
3169 Statement::Use(use_) => use_.call.last_location(),
3170 Statement::Assert(assert) => assert.value.last_location(),
3171 }
3172 }
3173
3174 pub fn type_(&self) -> Arc<Type> {
3175 match self {
3176 Statement::Expression(expression) => expression.type_(),
3177 Statement::Assignment(assignment) => assignment.type_(),
3178 Statement::Use(use_) => use_.call.type_(),
3179 Statement::Assert(_) => nil(),
3180 }
3181 }
3182
3183 pub fn definition_location(&self) -> Option<DefinitionLocation> {
3184 match self {
3185 Statement::Expression(expression) => expression.definition_location(),
3186 Statement::Assignment(_) => None,
3187 Statement::Use(use_) => use_.call.definition_location(),
3188 Statement::Assert(_) => None,
3189 }
3190 }
3191
3192 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3193 match self {
3194 Statement::Use(use_) => use_.find_node(byte_index),
3195 Statement::Expression(expression) => expression.find_node(byte_index),
3196 Statement::Assignment(assignment) => assignment.find_node(byte_index).or_else(|| {
3197 if assignment.location.contains(byte_index) {
3198 Some(Located::Statement(self))
3199 } else {
3200 None
3201 }
3202 }),
3203 Statement::Assert(assert) => assert.find_node(byte_index).or_else(|| {
3204 if assert.location.contains(byte_index) {
3205 Some(Located::Statement(self))
3206 } else {
3207 None
3208 }
3209 }),
3210 }
3211 }
3212
3213 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
3214 match self {
3215 Statement::Use(use_) => use_.call.find_statement(byte_index),
3216 Statement::Expression(expression) => expression.find_statement(byte_index),
3217 Statement::Assignment(assignment) => {
3218 assignment.value.find_statement(byte_index).or_else(|| {
3219 if assignment.location.contains(byte_index) {
3220 Some(self)
3221 } else {
3222 None
3223 }
3224 })
3225 }
3226 Statement::Assert(assert) => assert.value.find_statement(byte_index).or_else(|| {
3227 if assert.location.contains(byte_index) {
3228 Some(self)
3229 } else {
3230 None
3231 }
3232 }),
3233 }
3234 }
3235
3236 pub fn type_defining_location(&self) -> SrcSpan {
3237 match self {
3238 Statement::Expression(expression) => expression.type_defining_location(),
3239 Statement::Assignment(assignment) => assignment.location,
3240 Statement::Use(use_) => use_.location,
3241 Statement::Assert(assert) => assert.location,
3242 }
3243 }
3244
3245 fn is_pure_value_constructor(&self) -> bool {
3246 match self {
3247 Statement::Expression(expression) => expression.is_pure_value_constructor(),
3248 Statement::Assignment(assignment) => {
3249 // A let assert is not considered a pure value constructor
3250 // as it could crash the program!
3251 !assignment.kind.is_assert() && assignment.value.is_pure_value_constructor()
3252 }
3253 Statement::Use(Use { call, .. }) => call.is_pure_value_constructor(),
3254 // Assert statements by definition are not pure
3255 Statement::Assert(_) => false,
3256 }
3257 }
3258}
3259
3260#[derive(Debug, Clone, PartialEq, Eq)]
3261pub struct Assignment<TypeT, ExpressionT> {
3262 pub location: SrcSpan,
3263 pub value: ExpressionT,
3264 pub pattern: Pattern<TypeT>,
3265 pub kind: AssignmentKind<ExpressionT>,
3266 pub compiled_case: CompiledCase,
3267 /// This will be true for assignments that are automatically generated by
3268 /// the compiler.
3269 pub annotation: Option<TypeAst>,
3270}
3271
3272pub type TypedAssignment = Assignment<Arc<Type>, TypedExpr>;
3273pub type UntypedAssignment = Assignment<(), UntypedExpr>;
3274
3275impl TypedAssignment {
3276 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3277 if let Some(annotation) = &self.annotation {
3278 if let Some(l) = annotation.find_node(byte_index, self.pattern.type_()) {
3279 return Some(l);
3280 }
3281 }
3282 self.pattern
3283 .find_node(byte_index)
3284 .or_else(|| self.value.find_node(byte_index))
3285 }
3286
3287 pub fn type_(&self) -> Arc<Type> {
3288 self.value.type_()
3289 }
3290}
3291
3292pub type TypedAssert = Assert<TypedExpr>;
3293pub type UntypedAssert = Assert<UntypedExpr>;
3294
3295#[derive(Debug, Clone, PartialEq, Eq)]
3296pub struct Assert<Expression> {
3297 pub location: SrcSpan,
3298 pub value: Expression,
3299 pub message: Option<Expression>,
3300}
3301
3302impl TypedAssert {
3303 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3304 if let Some(found) = self.value.find_node(byte_index) {
3305 return Some(found);
3306 }
3307 if let Some(message) = &self.message {
3308 if let Some(found) = message.find_node(byte_index) {
3309 return Some(found);
3310 }
3311 }
3312 None
3313 }
3314}
3315
3316/// A pipeline is desugared to a series of assignments:
3317///
3318/// ```gleam
3319/// wibble |> wobble |> woo
3320/// ```
3321///
3322/// Becomes:
3323///
3324/// ```erl
3325/// Pipe1 = wibble
3326/// Pipe2 = wobble(Pipe1)
3327/// woo(Pipe2)
3328/// ```
3329///
3330/// This represents one of such assignments once the pipeline has been desugared
3331/// and each step has been typed.
3332///
3333/// > We're not using a more general `TypedAssignment` node since that has much
3334/// > more informations to carry around. This one is limited since we know it
3335/// > will always be in the form `VarName = <Expr>`, with no patterns on the
3336/// > left hand side of the assignment.
3337/// > Being more constrained simplifies code generation for pipelines!
3338///
3339#[derive(Debug, Clone, PartialEq, Eq)]
3340pub struct TypedPipelineAssignment {
3341 pub location: SrcSpan,
3342 pub name: EcoString,
3343 pub value: Box<TypedExpr>,
3344}
3345
3346impl TypedPipelineAssignment {
3347 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
3348 self.value.find_node(byte_index)
3349 }
3350
3351 pub fn find_statement(&self, byte_index: u32) -> Option<&TypedStatement> {
3352 self.value.find_statement(byte_index)
3353 }
3354
3355 pub fn type_(&self) -> Arc<Type> {
3356 self.value.type_()
3357 }
3358}
3359
3360/// The kind of desugaring that might take place when rewriting a pipeline to
3361/// regular assignments.
3362///
3363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3364pub enum PipelineAssignmentKind {
3365 /// In case `a |> b(c)` is desugared to `b(a, c)`.
3366 FirstArgument {
3367 /// The location of the second argument of the call, in case there's any:
3368 /// - `a |> b(c, d)`: here it's `Some` wrapping the location of `c`.
3369 /// - `a |> b()`: here it's `None`.
3370 second_argument: Option<SrcSpan>,
3371 },
3372
3373 /// In case there's an explicit hole and `a |> b(_, c)` is desugared to
3374 /// `b(a, c)`.
3375 Hole { hole: SrcSpan },
3376
3377 /// In case `a |> b(c)` is desugared to `b(c)(a)`
3378 FunctionCall,
3379
3380 /// In case there's an echo in the middle of a pipeline `a |> echo`
3381 Echo,
3382}