Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

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