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
50 kB 1804 lines
1mod constant; 2mod typed; 3mod untyped; 4 5#[cfg(test)] 6mod tests; 7 8pub use self::typed::TypedExpr; 9pub use self::untyped::{UntypedExpr, Use}; 10 11pub use self::constant::{Constant, TypedConstant, UntypedConstant}; 12 13use crate::analyse::Inferred; 14use crate::build::{Located, Target}; 15use crate::type_::expression::Implementations; 16use crate::type_::{ 17 self, Deprecation, ModuleValueConstructor, PatternConstructor, Type, ValueConstructor, 18}; 19use std::sync::Arc; 20 21use ecow::EcoString; 22#[cfg(test)] 23use pretty_assertions::assert_eq; 24use vec1::Vec1; 25 26pub const TRY_VARIABLE: &str = "_try"; 27pub const PIPE_VARIABLE: &str = "_pipe"; 28pub const USE_ASSIGNMENT_VARIABLE: &str = "_use"; 29pub const ASSERT_FAIL_VARIABLE: &str = "_assert_fail"; 30pub const ASSERT_SUBJECT_VARIABLE: &str = "_assert_subject"; 31pub const CAPTURE_VARIABLE: &str = "_capture"; 32 33pub trait HasLocation { 34 fn location(&self) -> SrcSpan; 35} 36 37pub type TypedModule = Module<type_::ModuleInterface, TypedDefinition>; 38 39pub type UntypedModule = Module<(), TargetedDefinition>; 40 41#[derive(Debug, Clone, PartialEq, Eq)] 42pub struct Module<Info, Statements> { 43 pub name: EcoString, 44 pub documentation: Vec<EcoString>, 45 pub type_info: Info, 46 pub definitions: Vec<Statements>, 47} 48 49impl TypedModule { 50 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 51 self.definitions 52 .iter() 53 .find_map(|statement| statement.find_node(byte_index)) 54 } 55} 56 57/// The `@target(erlang)` and `@target(javascript)` attributes can be used to 58/// mark a definition as only being for a specific target. 59/// 60/// ```gleam 61/// const x: Int = 1 62/// 63/// @target(erlang) 64/// pub fn main(a) { ...} 65/// ``` 66/// 67#[derive(Debug, Clone, PartialEq, Eq)] 68pub struct TargetedDefinition { 69 pub definition: UntypedDefinition, 70 pub target: Option<Target>, 71} 72 73impl TargetedDefinition { 74 pub fn is_for(&self, target: Target) -> bool { 75 self.target.map(|t| t == target).unwrap_or(true) 76 } 77} 78 79impl UntypedModule { 80 pub fn dependencies(&self, target: Target) -> Vec<(EcoString, SrcSpan)> { 81 self.iter_statements(target) 82 .flat_map(|s| match s { 83 Definition::Import(Import { 84 module, location, .. 85 }) => Some((module.clone(), *location)), 86 _ => None, 87 }) 88 .collect() 89 } 90 91 pub fn iter_statements(&self, target: Target) -> impl Iterator<Item = &UntypedDefinition> { 92 self.definitions 93 .iter() 94 .filter(move |def| def.is_for(target)) 95 .map(|def| &def.definition) 96 } 97 98 pub fn into_iter_statements(self, target: Target) -> impl Iterator<Item = UntypedDefinition> { 99 self.definitions 100 .into_iter() 101 .filter(move |def| def.is_for(target)) 102 .map(|def| def.definition) 103 } 104} 105 106#[test] 107fn module_dependencies_test() { 108 let parsed = crate::parse::parse_module( 109 "import one 110 @target(erlang) 111 import two 112 113 @target(javascript) 114 import three 115 116 import four", 117 ) 118 .expect("syntax error"); 119 let module = parsed.module; 120 121 assert_eq!( 122 vec![ 123 ("one".into(), SrcSpan::new(0, 10)), 124 ("two".into(), SrcSpan::new(45, 55)), 125 ("four".into(), SrcSpan::new(118, 129)), 126 ], 127 module.dependencies(Target::Erlang) 128 ); 129} 130 131pub type TypedArg = Arg<Arc<Type>>; 132pub type UntypedArg = Arg<()>; 133 134#[derive(Debug, Clone, PartialEq, Eq)] 135pub struct Arg<T> { 136 pub names: ArgNames, 137 pub location: SrcSpan, 138 pub annotation: Option<TypeAst>, 139 pub type_: T, 140} 141 142impl<A> Arg<A> { 143 pub fn set_type<B>(self, t: B) -> Arg<B> { 144 Arg { 145 type_: t, 146 names: self.names, 147 location: self.location, 148 annotation: self.annotation, 149 } 150 } 151 152 pub fn get_variable_name(&self) -> Option<&EcoString> { 153 self.names.get_variable_name() 154 } 155} 156 157impl TypedArg { 158 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 159 if self.location.contains(byte_index) { 160 Some(Located::Arg(self)) 161 } else { 162 None 163 } 164 } 165} 166 167#[derive(Debug, Clone, PartialEq, Eq)] 168pub enum ArgNames { 169 Discard { name: EcoString }, 170 LabelledDiscard { label: EcoString, name: EcoString }, 171 Named { name: EcoString }, 172 NamedLabelled { name: EcoString, label: EcoString }, 173} 174 175impl ArgNames { 176 pub fn get_label(&self) -> Option<&EcoString> { 177 match self { 178 ArgNames::Discard { .. } | ArgNames::Named { .. } => None, 179 ArgNames::LabelledDiscard { label, .. } | ArgNames::NamedLabelled { label, .. } => { 180 Some(label) 181 } 182 } 183 } 184 pub fn get_variable_name(&self) -> Option<&EcoString> { 185 match self { 186 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => None, 187 ArgNames::NamedLabelled { name, .. } | ArgNames::Named { name } => Some(name), 188 } 189 } 190} 191 192pub type TypedRecordConstructor = RecordConstructor<Arc<Type>>; 193 194#[derive(Debug, Clone, PartialEq, Eq)] 195pub struct RecordConstructor<T> { 196 pub location: SrcSpan, 197 pub name: EcoString, 198 pub arguments: Vec<RecordConstructorArg<T>>, 199 pub documentation: Option<EcoString>, 200} 201 202impl<A> RecordConstructor<A> { 203 pub fn put_doc(&mut self, new_doc: EcoString) { 204 self.documentation = Some(new_doc); 205 } 206} 207 208pub type TypedRecordConstructorArg = RecordConstructorArg<Arc<Type>>; 209 210#[derive(Debug, Clone, PartialEq, Eq)] 211pub struct RecordConstructorArg<T> { 212 pub label: Option<EcoString>, 213 pub ast: TypeAst, 214 pub location: SrcSpan, 215 pub type_: T, 216 pub doc: Option<EcoString>, 217} 218 219impl<T: PartialEq> RecordConstructorArg<T> { 220 pub fn put_doc(&mut self, new_doc: EcoString) { 221 self.doc = Some(new_doc); 222 } 223} 224 225#[derive(Debug, Clone, PartialEq, Eq)] 226pub struct TypeAstConstructor { 227 pub location: SrcSpan, 228 pub module: Option<EcoString>, 229 pub name: EcoString, 230 pub arguments: Vec<TypeAst>, 231} 232 233#[derive(Debug, Clone, PartialEq, Eq)] 234pub struct TypeAstFn { 235 pub location: SrcSpan, 236 pub arguments: Vec<TypeAst>, 237 pub return_: Box<TypeAst>, 238} 239 240#[derive(Debug, Clone, PartialEq, Eq)] 241pub struct TypeAstVar { 242 pub location: SrcSpan, 243 pub name: EcoString, 244} 245 246#[derive(Debug, Clone, PartialEq, Eq)] 247pub struct TypeAstTuple { 248 pub location: SrcSpan, 249 pub elems: Vec<TypeAst>, 250} 251 252#[derive(Debug, Clone, PartialEq, Eq)] 253pub struct TypeAstHole { 254 pub location: SrcSpan, 255 pub name: EcoString, 256} 257 258#[derive(Debug, Clone, PartialEq, Eq)] 259pub enum TypeAst { 260 Constructor(TypeAstConstructor), 261 Fn(TypeAstFn), 262 Var(TypeAstVar), 263 Tuple(TypeAstTuple), 264 Hole(TypeAstHole), 265} 266 267impl TypeAst { 268 pub fn location(&self) -> SrcSpan { 269 match self { 270 TypeAst::Fn(TypeAstFn { location, .. }) 271 | TypeAst::Var(TypeAstVar { location, .. }) 272 | TypeAst::Hole(TypeAstHole { location, .. }) 273 | TypeAst::Tuple(TypeAstTuple { location, .. }) 274 | TypeAst::Constructor(TypeAstConstructor { location, .. }) => *location, 275 } 276 } 277 278 pub fn is_logically_equal(&self, other: &TypeAst) -> bool { 279 match self { 280 TypeAst::Constructor(TypeAstConstructor { 281 module, 282 name, 283 arguments, 284 location: _, 285 }) => match other { 286 TypeAst::Constructor(TypeAstConstructor { 287 module: o_module, 288 name: o_name, 289 arguments: o_arguments, 290 location: _, 291 }) => { 292 module == o_module 293 && name == o_name 294 && arguments.len() == o_arguments.len() 295 && arguments 296 .iter() 297 .zip(o_arguments) 298 .all(|a| a.0.is_logically_equal(a.1)) 299 } 300 _ => false, 301 }, 302 TypeAst::Fn(TypeAstFn { 303 arguments, 304 return_, 305 location: _, 306 }) => match other { 307 TypeAst::Fn(TypeAstFn { 308 arguments: o_arguments, 309 return_: o_return_, 310 location: _, 311 }) => { 312 arguments.len() == o_arguments.len() 313 && arguments 314 .iter() 315 .zip(o_arguments) 316 .all(|a| a.0.is_logically_equal(a.1)) 317 && return_.is_logically_equal(o_return_) 318 } 319 _ => false, 320 }, 321 TypeAst::Var(TypeAstVar { name, location: _ }) => match other { 322 TypeAst::Var(TypeAstVar { 323 name: o_name, 324 location: _, 325 }) => name == o_name, 326 _ => false, 327 }, 328 TypeAst::Tuple(TypeAstTuple { elems, location: _ }) => match other { 329 TypeAst::Tuple(TypeAstTuple { 330 elems: o_elems, 331 location: _, 332 }) => { 333 elems.len() == o_elems.len() 334 && elems 335 .iter() 336 .zip(o_elems) 337 .all(|a| a.0.is_logically_equal(a.1)) 338 } 339 _ => false, 340 }, 341 TypeAst::Hole(TypeAstHole { name, location: _ }) => match other { 342 TypeAst::Hole(TypeAstHole { 343 name: o_name, 344 location: _, 345 }) => name == o_name, 346 _ => false, 347 }, 348 } 349 } 350} 351 352#[derive(Debug, Clone, Copy, PartialEq, Eq)] 353pub enum Publicity { 354 Public, 355 Private, 356 Internal, 357} 358 359impl Publicity { 360 pub fn is_private(&self) -> bool { 361 match self { 362 Self::Private => true, 363 Self::Public | Self::Internal => false, 364 } 365 } 366 367 pub fn is_internal(&self) -> bool { 368 match self { 369 Self::Internal => true, 370 Self::Public | Self::Private => false, 371 } 372 } 373 374 pub fn is_public(&self) -> bool { 375 match self { 376 Self::Public => true, 377 Self::Internal | Self::Private => false, 378 } 379 } 380 381 pub fn is_importable(&self) -> bool { 382 match self { 383 Self::Internal | Self::Public => true, 384 Self::Private => false, 385 } 386 } 387} 388 389#[derive(Debug, Clone, PartialEq, Eq)] 390/// A function definition 391/// 392/// # Example(s) 393/// 394/// ```gleam 395/// // Public function 396/// pub fn bar() -> String { ... } 397/// // Private function 398/// fn foo(x: Int) -> Int { ... } 399/// ``` 400pub struct Function<T, Expr> { 401 pub location: SrcSpan, 402 pub end_position: u32, 403 pub name: EcoString, 404 pub arguments: Vec<Arg<T>>, 405 pub body: Vec1<Statement<T, Expr>>, 406 pub publicity: Publicity, 407 pub deprecation: Deprecation, 408 pub return_annotation: Option<TypeAst>, 409 pub return_type: T, 410 pub documentation: Option<EcoString>, 411 pub external_erlang: Option<(EcoString, EcoString)>, 412 pub external_javascript: Option<(EcoString, EcoString)>, 413 pub implementations: Implementations, 414} 415 416pub type TypedFunction = Function<Arc<Type>, TypedExpr>; 417pub type UntypedFunction = Function<(), UntypedExpr>; 418 419impl<T, E> Function<T, E> { 420 fn full_location(&self) -> SrcSpan { 421 SrcSpan::new(self.location.start, self.end_position) 422 } 423} 424 425pub type UntypedImport = Import<()>; 426 427#[derive(Debug, Clone, PartialEq, Eq)] 428/// Import another Gleam module so the current module can use the types and 429/// values it defines. 430/// 431/// # Example(s) 432/// 433/// ```gleam 434/// import unix/cat 435/// // Import with alias 436/// import animal/cat as kitty 437/// ``` 438pub struct Import<PackageName> { 439 pub documentation: Option<EcoString>, 440 pub location: SrcSpan, 441 pub module: EcoString, 442 pub as_name: Option<(AssignName, SrcSpan)>, 443 pub unqualified_values: Vec<UnqualifiedImport>, 444 pub unqualified_types: Vec<UnqualifiedImport>, 445 pub package: PackageName, 446} 447 448impl<T> Import<T> { 449 pub(crate) fn used_name(&self) -> Option<EcoString> { 450 match self.as_name.as_ref() { 451 Some((AssignName::Variable(name), _)) => Some(name.clone()), 452 Some((AssignName::Discard(_), _)) => None, 453 None => self.module.split('/').last().map(EcoString::from), 454 } 455 } 456 457 pub(crate) fn alias_location(&self) -> Option<SrcSpan> { 458 self.as_name.as_ref().map(|(_, location)| *location) 459 } 460} 461 462pub type UntypedModuleConstant = ModuleConstant<(), ()>; 463 464#[derive(Debug, Clone, PartialEq, Eq)] 465/// A certain fixed value that can be used in multiple places 466/// 467/// # Example(s) 468/// 469/// ```gleam 470/// pub const start_year = 2101 471/// pub const end_year = 2111 472/// ``` 473pub struct ModuleConstant<T, ConstantRecordTag> { 474 pub documentation: Option<EcoString>, 475 pub location: SrcSpan, 476 pub publicity: Publicity, 477 pub name: EcoString, 478 pub annotation: Option<TypeAst>, 479 pub value: Box<Constant<T, ConstantRecordTag>>, 480 pub type_: T, 481 pub deprecation: Deprecation, 482 pub implementations: Implementations, 483} 484 485pub type UntypedCustomType = CustomType<()>; 486 487#[derive(Debug, Clone, PartialEq, Eq)] 488/// A newly defined type with one or more constructors. 489/// Each variant of the custom type can contain different types, so the type is 490/// the product of the types contained by each variant. 491/// 492/// This might be called an algebraic data type (ADT) or tagged union in other 493/// languages and type systems. 494/// 495/// 496/// # Example(s) 497/// 498/// ```gleam 499/// pub type Cat { 500/// Cat(name: String, cuteness: Int) 501/// } 502/// ``` 503pub struct CustomType<T> { 504 pub location: SrcSpan, 505 pub end_position: u32, 506 pub name: EcoString, 507 pub publicity: Publicity, 508 pub constructors: Vec<RecordConstructor<T>>, 509 pub documentation: Option<EcoString>, 510 pub deprecation: Deprecation, 511 pub opaque: bool, 512 /// The names of the type parameters. 513 pub parameters: Vec<EcoString>, 514 /// Once type checked this field will contain the type information for the 515 /// type parameters. 516 pub typed_parameters: Vec<T>, 517} 518 519impl<T> CustomType<T> { 520 /// The `location` field of a `CustomType` is only the location of `pub type 521 /// TheName`. This method returns a `SrcSpan` that includes the entire type 522 /// definition. 523 pub fn full_location(&self) -> SrcSpan { 524 SrcSpan::new(self.location.start, self.end_position) 525 } 526} 527 528pub type UntypedTypeAlias = TypeAlias<()>; 529 530#[derive(Debug, Clone, PartialEq, Eq)] 531/// A new name for an existing type 532/// 533/// # Example(s) 534/// 535/// ```gleam 536/// pub type Headers = 537/// List(#(String, String)) 538/// ``` 539pub struct TypeAlias<T> { 540 pub location: SrcSpan, 541 pub alias: EcoString, 542 pub parameters: Vec<EcoString>, 543 pub type_ast: TypeAst, 544 pub type_: T, 545 pub publicity: Publicity, 546 pub documentation: Option<EcoString>, 547 pub deprecation: Deprecation, 548} 549 550pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, EcoString, EcoString>; 551pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>; 552 553#[derive(Debug, Clone, PartialEq, Eq)] 554pub enum Definition<T, Expr, ConstantRecordTag, PackageName> { 555 Function(Function<T, Expr>), 556 557 TypeAlias(TypeAlias<T>), 558 559 CustomType(CustomType<T>), 560 561 Import(Import<PackageName>), 562 563 ModuleConstant(ModuleConstant<T, ConstantRecordTag>), 564} 565 566impl TypedDefinition { 567 pub fn main_function(&self) -> Option<&TypedFunction> { 568 match self { 569 Definition::Function(f) if f.name == "main" => Some(f), 570 _ => None, 571 } 572 } 573 574 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 575 match self { 576 Definition::Function(function) => { 577 if let Some(found) = function.body.iter().find_map(|s| s.find_node(byte_index)) { 578 return Some(found); 579 }; 580 581 if let Some(found_arg) = function 582 .arguments 583 .iter() 584 .find(|arg| arg.location.contains(byte_index)) 585 { 586 return Some(Located::Arg(found_arg)); 587 }; 588 589 if let Some(found_statement) = function 590 .body 591 .iter() 592 .find(|statement| statement.location().contains(byte_index)) 593 { 594 return Some(Located::Statement(found_statement)); 595 }; 596 597 // Note that the fn `.location` covers the function head, not 598 // the entire statement. 599 if function.location.contains(byte_index) { 600 Some(Located::ModuleStatement(self)) 601 } else if function.full_location().contains(byte_index) { 602 Some(Located::FunctionBody(function)) 603 } else { 604 None 605 } 606 } 607 608 Definition::CustomType(custom) => { 609 // Note that the custom type `.location` covers the function 610 // head, not the entire statement. 611 if custom.full_location().contains(byte_index) { 612 Some(Located::ModuleStatement(self)) 613 } else { 614 None 615 } 616 } 617 618 Definition::TypeAlias(_) | Definition::Import(_) | Definition::ModuleConstant(_) => { 619 if self.location().contains(byte_index) { 620 Some(Located::ModuleStatement(self)) 621 } else { 622 None 623 } 624 } 625 } 626 } 627} 628 629impl<A, B, C, E> Definition<A, B, C, E> { 630 pub fn location(&self) -> SrcSpan { 631 match self { 632 Definition::Function(Function { location, .. }) 633 | Definition::Import(Import { location, .. }) 634 | Definition::TypeAlias(TypeAlias { location, .. }) 635 | Definition::CustomType(CustomType { location, .. }) 636 | Definition::ModuleConstant(ModuleConstant { location, .. }) => *location, 637 } 638 } 639 640 /// Returns `true` if the definition is [`Import`]. 641 /// 642 /// [`Import`]: Definition::Import 643 #[must_use] 644 pub fn is_import(&self) -> bool { 645 matches!(self, Self::Import(..)) 646 } 647 648 /// Returns `true` if the module statement is [`Function`]. 649 /// 650 /// [`Function`]: ModuleStatement::Function 651 #[must_use] 652 pub fn is_function(&self) -> bool { 653 matches!(self, Self::Function(..)) 654 } 655 656 pub fn put_doc(&mut self, new_doc: EcoString) { 657 match self { 658 Definition::Import(Import { .. }) => (), 659 660 Definition::Function(Function { 661 documentation: doc, .. 662 }) 663 | Definition::TypeAlias(TypeAlias { 664 documentation: doc, .. 665 }) 666 | Definition::CustomType(CustomType { 667 documentation: doc, .. 668 }) 669 | Definition::ModuleConstant(ModuleConstant { 670 documentation: doc, .. 671 }) => { 672 let _ = std::mem::replace(doc, Some(new_doc)); 673 } 674 } 675 } 676 677 pub fn is_internal(&self) -> bool { 678 match self { 679 Definition::Function(Function { publicity, .. }) 680 | Definition::CustomType(CustomType { publicity, .. }) 681 | Definition::ModuleConstant(ModuleConstant { publicity, .. }) 682 | Definition::TypeAlias(TypeAlias { publicity, .. }) => publicity.is_internal(), 683 684 Definition::Import(_) => false, 685 } 686 } 687} 688 689#[derive(Debug, Clone, PartialEq, Eq)] 690pub struct UnqualifiedImport { 691 pub location: SrcSpan, 692 pub name: EcoString, 693 pub as_name: Option<EcoString>, 694} 695 696impl UnqualifiedImport { 697 pub fn used_name(&self) -> &EcoString { 698 self.as_name.as_ref().unwrap_or(&self.name) 699 } 700} 701 702#[derive(Debug, Clone, PartialEq, Eq, Copy, Default)] 703pub enum Layer { 704 #[default] 705 Value, 706 Type, 707} 708 709impl Layer { 710 /// Returns `true` if the layer is [`Value`]. 711 pub fn is_value(&self) -> bool { 712 matches!(self, Self::Value) 713 } 714} 715 716#[derive(Debug, Clone, Copy, PartialEq, Eq)] 717pub enum BinOp { 718 // Boolean logic 719 And, 720 Or, 721 722 // Equality 723 Eq, 724 NotEq, 725 726 // Order comparison 727 LtInt, 728 LtEqInt, 729 LtFloat, 730 LtEqFloat, 731 GtEqInt, 732 GtInt, 733 GtEqFloat, 734 GtFloat, 735 736 // Maths 737 AddInt, 738 AddFloat, 739 SubInt, 740 SubFloat, 741 MultInt, 742 MultFloat, 743 DivInt, 744 DivFloat, 745 RemainderInt, 746 747 // Strings 748 Concatenate, 749} 750 751#[derive(Clone, Copy, Debug, PartialEq)] 752pub enum OperatorKind { 753 BooleanLogic, 754 Equality, 755 IntComparison, 756 FLoatComparison, 757 IntMath, 758 FloatMath, 759 StringConcatenation, 760} 761 762impl BinOp { 763 pub fn precedence(&self) -> u8 { 764 // Ensure that this matches the other precedence function for guards 765 match self { 766 Self::Or => 1, 767 768 Self::And => 2, 769 770 Self::Eq | Self::NotEq => 3, 771 772 Self::LtInt 773 | Self::LtEqInt 774 | Self::LtFloat 775 | Self::LtEqFloat 776 | Self::GtEqInt 777 | Self::GtInt 778 | Self::GtEqFloat 779 | Self::GtFloat => 4, 780 781 Self::Concatenate => 5, 782 783 // Pipe is 6 784 Self::AddInt | Self::AddFloat | Self::SubInt | Self::SubFloat => 7, 785 786 Self::MultInt 787 | Self::MultFloat 788 | Self::DivInt 789 | Self::DivFloat 790 | Self::RemainderInt => 8, 791 } 792 } 793 794 pub fn name(&self) -> &'static str { 795 match self { 796 Self::And => "&&", 797 Self::Or => "||", 798 Self::LtInt => "<", 799 Self::LtEqInt => "<=", 800 Self::LtFloat => "<.", 801 Self::LtEqFloat => "<=.", 802 Self::Eq => "==", 803 Self::NotEq => "!=", 804 Self::GtEqInt => ">=", 805 Self::GtInt => ">", 806 Self::GtEqFloat => ">=.", 807 Self::GtFloat => ">.", 808 Self::AddInt => "+", 809 Self::AddFloat => "+.", 810 Self::SubInt => "-", 811 Self::SubFloat => "-.", 812 Self::MultInt => "*", 813 Self::MultFloat => "*.", 814 Self::DivInt => "/", 815 Self::DivFloat => "/.", 816 Self::RemainderInt => "%", 817 Self::Concatenate => "<>", 818 } 819 } 820 821 pub fn operator_kind(&self) -> OperatorKind { 822 match self { 823 Self::Concatenate => OperatorKind::StringConcatenation, 824 Self::Eq | Self::NotEq => OperatorKind::Equality, 825 Self::And | Self::Or => OperatorKind::BooleanLogic, 826 Self::LtInt | Self::LtEqInt | Self::GtEqInt | Self::GtInt => { 827 OperatorKind::IntComparison 828 } 829 Self::LtFloat | Self::LtEqFloat | Self::GtEqFloat | Self::GtFloat => { 830 OperatorKind::FLoatComparison 831 } 832 Self::AddInt | Self::SubInt | Self::MultInt | Self::RemainderInt | Self::DivInt => { 833 OperatorKind::IntMath 834 } 835 Self::AddFloat | Self::SubFloat | Self::MultFloat | Self::DivFloat => { 836 OperatorKind::FloatMath 837 } 838 } 839 } 840 841 pub fn can_be_grouped_with(&self, other: &BinOp) -> bool { 842 self.operator_kind() == other.operator_kind() 843 } 844} 845 846#[derive(Debug, PartialEq, Eq, Clone)] 847pub struct CallArg<A> { 848 pub label: Option<EcoString>, 849 pub location: SrcSpan, 850 pub value: A, 851 // This is true if this argument is given as the callback in a `use` 852 // expression. In future it may also be true for pipes too. It is used to 853 // determine if we should error if an argument without a label is given or 854 // not, which is not permitted if the argument is given explicitly by the 855 // programmer rather than implicitly by Gleam's syntactic sugar. 856 pub implicit: bool, 857} 858 859impl CallArg<TypedExpr> { 860 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 861 self.value.find_node(byte_index) 862 } 863} 864 865impl CallArg<TypedPattern> { 866 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 867 self.value.find_node(byte_index) 868 } 869} 870 871impl CallArg<UntypedExpr> { 872 pub fn is_capture_hole(&self) -> bool { 873 match &self.value { 874 UntypedExpr::Var { ref name, .. } => name == CAPTURE_VARIABLE, 875 _ => false, 876 } 877 } 878} 879 880impl<T> HasLocation for CallArg<T> { 881 fn location(&self) -> SrcSpan { 882 self.location 883 } 884} 885 886#[derive(Debug, Clone, PartialEq, Eq)] 887pub struct RecordUpdateSpread { 888 pub base: Box<UntypedExpr>, 889 pub location: SrcSpan, 890} 891 892#[derive(Debug, Clone, PartialEq, Eq)] 893pub struct UntypedRecordUpdateArg { 894 pub label: EcoString, 895 pub location: SrcSpan, 896 pub value: UntypedExpr, 897} 898 899#[derive(Debug, Clone, PartialEq, Eq)] 900pub struct TypedRecordUpdateArg { 901 pub label: EcoString, 902 pub location: SrcSpan, 903 pub value: TypedExpr, 904 pub index: u32, 905} 906 907impl TypedRecordUpdateArg { 908 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 909 self.value.find_node(byte_index) 910 } 911} 912 913pub type MultiPattern<Type> = Vec<Pattern<Type>>; 914 915pub type UntypedMultiPattern = MultiPattern<()>; 916pub type TypedMultiPattern = MultiPattern<Arc<Type>>; 917 918pub type TypedClause = Clause<TypedExpr, Arc<Type>, EcoString>; 919 920pub type UntypedClause = Clause<UntypedExpr, (), ()>; 921 922#[derive(Debug, Clone, PartialEq, Eq)] 923pub struct Clause<Expr, Type, RecordTag> { 924 pub location: SrcSpan, 925 pub pattern: MultiPattern<Type>, 926 pub alternative_patterns: Vec<MultiPattern<Type>>, 927 pub guard: Option<ClauseGuard<Type, RecordTag>>, 928 pub then: Expr, 929} 930 931impl<A, B, C> Clause<A, B, C> { 932 pub fn pattern_count(&self) -> usize { 933 1 + self.alternative_patterns.len() 934 } 935} 936 937impl TypedClause { 938 pub fn location(&self) -> SrcSpan { 939 SrcSpan { 940 start: self 941 .pattern 942 .first() 943 .map(|p| p.location().start) 944 .unwrap_or_default(), 945 end: self.then.location().end, 946 } 947 } 948 949 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 950 self.pattern 951 .iter() 952 .find_map(|p| p.find_node(byte_index)) 953 .or_else(|| self.then.find_node(byte_index)) 954 } 955} 956 957pub type UntypedClauseGuard = ClauseGuard<(), ()>; 958pub type TypedClauseGuard = ClauseGuard<Arc<Type>, EcoString>; 959 960#[derive(Debug, Clone, PartialEq, Eq)] 961pub enum ClauseGuard<Type, RecordTag> { 962 Equals { 963 location: SrcSpan, 964 left: Box<Self>, 965 right: Box<Self>, 966 }, 967 968 NotEquals { 969 location: SrcSpan, 970 left: Box<Self>, 971 right: Box<Self>, 972 }, 973 974 GtInt { 975 location: SrcSpan, 976 left: Box<Self>, 977 right: Box<Self>, 978 }, 979 980 GtEqInt { 981 location: SrcSpan, 982 left: Box<Self>, 983 right: Box<Self>, 984 }, 985 986 LtInt { 987 location: SrcSpan, 988 left: Box<Self>, 989 right: Box<Self>, 990 }, 991 992 LtEqInt { 993 location: SrcSpan, 994 left: Box<Self>, 995 right: Box<Self>, 996 }, 997 998 GtFloat { 999 location: SrcSpan, 1000 left: Box<Self>, 1001 right: Box<Self>, 1002 }, 1003 1004 GtEqFloat { 1005 location: SrcSpan, 1006 left: Box<Self>, 1007 right: Box<Self>, 1008 }, 1009 1010 LtFloat { 1011 location: SrcSpan, 1012 left: Box<Self>, 1013 right: Box<Self>, 1014 }, 1015 1016 LtEqFloat { 1017 location: SrcSpan, 1018 left: Box<Self>, 1019 right: Box<Self>, 1020 }, 1021 1022 Or { 1023 location: SrcSpan, 1024 left: Box<Self>, 1025 right: Box<Self>, 1026 }, 1027 1028 And { 1029 location: SrcSpan, 1030 left: Box<Self>, 1031 right: Box<Self>, 1032 }, 1033 1034 Not { 1035 location: SrcSpan, 1036 expression: Box<Self>, 1037 }, 1038 1039 Var { 1040 location: SrcSpan, 1041 type_: Type, 1042 name: EcoString, 1043 }, 1044 1045 TupleIndex { 1046 location: SrcSpan, 1047 index: u64, 1048 type_: Type, 1049 tuple: Box<Self>, 1050 }, 1051 1052 FieldAccess { 1053 location: SrcSpan, 1054 index: Option<u64>, 1055 label: EcoString, 1056 type_: Type, 1057 container: Box<Self>, 1058 }, 1059 1060 ModuleSelect { 1061 location: SrcSpan, 1062 type_: Type, 1063 label: EcoString, 1064 module_name: EcoString, 1065 module_alias: EcoString, 1066 literal: Constant<Type, RecordTag>, 1067 }, 1068 1069 Constant(Constant<Type, RecordTag>), 1070} 1071 1072impl<A, B> ClauseGuard<A, B> { 1073 pub fn location(&self) -> SrcSpan { 1074 match self { 1075 ClauseGuard::Constant(constant) => constant.location(), 1076 ClauseGuard::Or { location, .. } 1077 | ClauseGuard::And { location, .. } 1078 | ClauseGuard::Not { location, .. } 1079 | ClauseGuard::Var { location, .. } 1080 | ClauseGuard::TupleIndex { location, .. } 1081 | ClauseGuard::Equals { location, .. } 1082 | ClauseGuard::NotEquals { location, .. } 1083 | ClauseGuard::GtInt { location, .. } 1084 | ClauseGuard::GtEqInt { location, .. } 1085 | ClauseGuard::LtInt { location, .. } 1086 | ClauseGuard::LtEqInt { location, .. } 1087 | ClauseGuard::GtFloat { location, .. } 1088 | ClauseGuard::GtEqFloat { location, .. } 1089 | ClauseGuard::LtFloat { location, .. } 1090 | ClauseGuard::FieldAccess { location, .. } 1091 | ClauseGuard::LtEqFloat { location, .. } 1092 | ClauseGuard::ModuleSelect { location, .. } => *location, 1093 } 1094 } 1095 1096 pub fn precedence(&self) -> u8 { 1097 // Ensure that this matches the other precedence function for guards 1098 match self.bin_op_name() { 1099 Some(name) => name.precedence(), 1100 None => u8::MAX, 1101 } 1102 } 1103 1104 pub fn bin_op_name(&self) -> Option<BinOp> { 1105 match self { 1106 ClauseGuard::Or { .. } => Some(BinOp::Or), 1107 ClauseGuard::And { .. } => Some(BinOp::And), 1108 ClauseGuard::Equals { .. } => Some(BinOp::Eq), 1109 ClauseGuard::NotEquals { .. } => Some(BinOp::NotEq), 1110 ClauseGuard::GtInt { .. } => Some(BinOp::GtInt), 1111 ClauseGuard::GtEqInt { .. } => Some(BinOp::GtEqInt), 1112 ClauseGuard::LtInt { .. } => Some(BinOp::LtInt), 1113 ClauseGuard::LtEqInt { .. } => Some(BinOp::LtEqInt), 1114 ClauseGuard::GtFloat { .. } => Some(BinOp::GtFloat), 1115 ClauseGuard::GtEqFloat { .. } => Some(BinOp::GtEqFloat), 1116 ClauseGuard::LtFloat { .. } => Some(BinOp::LtFloat), 1117 ClauseGuard::LtEqFloat { .. } => Some(BinOp::LtEqFloat), 1118 1119 ClauseGuard::Constant(_) 1120 | ClauseGuard::Var { .. } 1121 | ClauseGuard::Not { .. } 1122 | ClauseGuard::TupleIndex { .. } 1123 | ClauseGuard::FieldAccess { .. } 1124 | ClauseGuard::ModuleSelect { .. } => None, 1125 } 1126 } 1127} 1128 1129impl TypedClauseGuard { 1130 pub fn type_(&self) -> Arc<Type> { 1131 match self { 1132 ClauseGuard::Var { type_, .. } => type_.clone(), 1133 ClauseGuard::TupleIndex { type_, .. } => type_.clone(), 1134 ClauseGuard::FieldAccess { type_, .. } => type_.clone(), 1135 ClauseGuard::ModuleSelect { type_, .. } => type_.clone(), 1136 ClauseGuard::Constant(constant) => constant.type_(), 1137 1138 ClauseGuard::Or { .. } 1139 | ClauseGuard::Not { .. } 1140 | ClauseGuard::And { .. } 1141 | ClauseGuard::Equals { .. } 1142 | ClauseGuard::NotEquals { .. } 1143 | ClauseGuard::GtInt { .. } 1144 | ClauseGuard::GtEqInt { .. } 1145 | ClauseGuard::LtInt { .. } 1146 | ClauseGuard::LtEqInt { .. } 1147 | ClauseGuard::GtFloat { .. } 1148 | ClauseGuard::GtEqFloat { .. } 1149 | ClauseGuard::LtFloat { .. } 1150 | ClauseGuard::LtEqFloat { .. } => type_::bool(), 1151 } 1152 } 1153} 1154 1155#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)] 1156pub struct SrcSpan { 1157 pub start: u32, 1158 pub end: u32, 1159} 1160 1161impl SrcSpan { 1162 pub fn new(start: u32, end: u32) -> Self { 1163 Self { start, end } 1164 } 1165 1166 pub fn contains(&self, byte_index: u32) -> bool { 1167 byte_index >= self.start && byte_index < self.end 1168 } 1169} 1170 1171#[derive(Debug, PartialEq, Eq, Clone)] 1172pub struct DefinitionLocation<'module> { 1173 pub module: Option<&'module str>, 1174 pub span: SrcSpan, 1175} 1176 1177pub type UntypedPattern = Pattern<()>; 1178pub type TypedPattern = Pattern<Arc<Type>>; 1179 1180#[derive(Debug, Clone, PartialEq, Eq)] 1181pub enum Pattern<Type> { 1182 Int { 1183 location: SrcSpan, 1184 value: EcoString, 1185 }, 1186 1187 Float { 1188 location: SrcSpan, 1189 value: EcoString, 1190 }, 1191 1192 String { 1193 location: SrcSpan, 1194 value: EcoString, 1195 }, 1196 1197 /// The creation of a variable. 1198 /// e.g. `assert [this_is_a_var, .._] = x` 1199 Variable { 1200 location: SrcSpan, 1201 name: EcoString, 1202 type_: Type, 1203 }, 1204 1205 /// A reference to a variable in a bit array. This is always a variable 1206 /// being used rather than a new variable being assigned. 1207 /// e.g. `assert <<y:size(somevar)>> = x` 1208 VarUsage { 1209 location: SrcSpan, 1210 name: EcoString, 1211 constructor: Option<ValueConstructor>, 1212 type_: Type, 1213 }, 1214 1215 /// A name given to a sub-pattern using the `as` keyword. 1216 /// e.g. `assert #(1, [_, _] as the_list) = x` 1217 Assign { 1218 name: EcoString, 1219 location: SrcSpan, 1220 pattern: Box<Self>, 1221 }, 1222 1223 /// A pattern that binds to any value but does not assign a variable. 1224 /// Always starts with an underscore. 1225 Discard { 1226 name: EcoString, 1227 location: SrcSpan, 1228 type_: Type, 1229 }, 1230 1231 List { 1232 location: SrcSpan, 1233 elements: Vec<Self>, 1234 tail: Option<Box<Self>>, 1235 type_: Type, 1236 }, 1237 1238 /// The constructor for a custom type. Starts with an uppercase letter. 1239 Constructor { 1240 location: SrcSpan, 1241 name: EcoString, 1242 arguments: Vec<CallArg<Self>>, 1243 module: Option<EcoString>, 1244 constructor: Inferred<PatternConstructor>, 1245 with_spread: bool, 1246 type_: Type, 1247 }, 1248 1249 Tuple { 1250 location: SrcSpan, 1251 elems: Vec<Self>, 1252 }, 1253 1254 BitArray { 1255 location: SrcSpan, 1256 segments: Vec<BitArraySegment<Self, Type>>, 1257 }, 1258 1259 // "prefix" <> variable 1260 StringPrefix { 1261 location: SrcSpan, 1262 left_location: SrcSpan, 1263 left_side_assignment: Option<(EcoString, SrcSpan)>, 1264 right_location: SrcSpan, 1265 left_side_string: EcoString, 1266 /// The variable on the right hand side of the `<>`. 1267 right_side_assignment: AssignName, 1268 }, 1269} 1270 1271impl Default for Inferred<()> { 1272 fn default() -> Self { 1273 Self::Unknown 1274 } 1275} 1276 1277#[derive(Debug, Clone, PartialEq, Eq)] 1278pub enum AssignName { 1279 Variable(EcoString), 1280 Discard(EcoString), 1281} 1282 1283impl AssignName { 1284 pub fn name(&self) -> &str { 1285 match self { 1286 AssignName::Variable(name) | AssignName::Discard(name) => name, 1287 } 1288 } 1289 1290 pub fn to_arg_names(self) -> ArgNames { 1291 match self { 1292 AssignName::Variable(name) => ArgNames::Named { name }, 1293 AssignName::Discard(name) => ArgNames::Discard { name }, 1294 } 1295 } 1296 1297 pub fn assigned_name(&self) -> Option<&str> { 1298 match self { 1299 AssignName::Variable(name) => Some(name), 1300 AssignName::Discard(_) => None, 1301 } 1302 } 1303} 1304 1305impl<A> Pattern<A> { 1306 pub fn location(&self) -> SrcSpan { 1307 match self { 1308 Pattern::Assign { pattern, .. } => pattern.location(), 1309 Pattern::Int { location, .. } 1310 | Pattern::Variable { location, .. } 1311 | Pattern::VarUsage { location, .. } 1312 | Pattern::List { location, .. } 1313 | Pattern::Float { location, .. } 1314 | Pattern::Discard { location, .. } 1315 | Pattern::String { location, .. } 1316 | Pattern::Tuple { location, .. } 1317 | Pattern::Constructor { location, .. } 1318 | Pattern::StringPrefix { location, .. } 1319 | Pattern::BitArray { location, .. } => *location, 1320 } 1321 } 1322 1323 /// Returns `true` if the pattern is [`Discard`]. 1324 /// 1325 /// [`Discard`]: Pattern::Discard 1326 pub fn is_discard(&self) -> bool { 1327 matches!(self, Self::Discard { .. }) 1328 } 1329} 1330 1331impl TypedPattern { 1332 pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> { 1333 match self { 1334 Pattern::Int { .. } 1335 | Pattern::Float { .. } 1336 | Pattern::String { .. } 1337 | Pattern::Variable { .. } 1338 | Pattern::VarUsage { .. } 1339 | Pattern::Assign { .. } 1340 | Pattern::Discard { .. } 1341 | Pattern::List { .. } 1342 | Pattern::Tuple { .. } 1343 | Pattern::BitArray { .. } 1344 | Pattern::StringPrefix { .. } => None, 1345 1346 Pattern::Constructor { constructor, .. } => constructor.definition_location(), 1347 } 1348 } 1349 1350 pub fn get_documentation(&self) -> Option<&str> { 1351 match self { 1352 Pattern::Int { .. } 1353 | Pattern::Float { .. } 1354 | Pattern::String { .. } 1355 | Pattern::Variable { .. } 1356 | Pattern::VarUsage { .. } 1357 | Pattern::Assign { .. } 1358 | Pattern::Discard { .. } 1359 | Pattern::List { .. } 1360 | Pattern::Tuple { .. } 1361 | Pattern::BitArray { .. } 1362 | Pattern::StringPrefix { .. } => None, 1363 1364 Pattern::Constructor { constructor, .. } => constructor.get_documentation(), 1365 } 1366 } 1367 1368 pub fn type_(&self) -> Arc<Type> { 1369 match self { 1370 Pattern::Int { .. } => type_::int(), 1371 Pattern::Float { .. } => type_::float(), 1372 Pattern::String { .. } => type_::string(), 1373 Pattern::BitArray { .. } => type_::bits(), 1374 Pattern::StringPrefix { .. } => type_::string(), 1375 1376 Pattern::Variable { type_, .. } 1377 | Pattern::List { type_, .. } 1378 | Pattern::VarUsage { type_, .. } 1379 | Pattern::Constructor { type_, .. } => type_.clone(), 1380 1381 Pattern::Assign { pattern, .. } => pattern.type_(), 1382 1383 Pattern::Discard { type_, .. } => type_.clone(), 1384 1385 Pattern::Tuple { elems, .. } => type_::tuple(elems.iter().map(|p| p.type_()).collect()), 1386 } 1387 } 1388 1389 fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 1390 if !self.location().contains(byte_index) { 1391 return None; 1392 } 1393 1394 if let Pattern::Variable { name, .. } = self { 1395 // For pipes the pattern can't be pointed to 1396 if name.as_str().eq(PIPE_VARIABLE) { 1397 return None; 1398 } 1399 } 1400 1401 match self { 1402 Pattern::Int { .. } 1403 | Pattern::Float { .. } 1404 | Pattern::String { .. } 1405 | Pattern::Variable { .. } 1406 | Pattern::VarUsage { .. } 1407 | Pattern::Assign { .. } 1408 | Pattern::Discard { .. } 1409 | Pattern::BitArray { .. } 1410 | Pattern::StringPrefix { .. } => Some(Located::Pattern(self)), 1411 1412 Pattern::Constructor { arguments, .. } => { 1413 arguments.iter().find_map(|arg| arg.find_node(byte_index)) 1414 } 1415 Pattern::List { elements, tail, .. } => elements 1416 .iter() 1417 .find_map(|p| p.find_node(byte_index)) 1418 .or_else(|| tail.as_ref().and_then(|p| p.find_node(byte_index))), 1419 1420 Pattern::Tuple { elems, .. } => elems.iter().find_map(|p| p.find_node(byte_index)), 1421 } 1422 .or(Some(Located::Pattern(self))) 1423 } 1424} 1425impl<A> HasLocation for Pattern<A> { 1426 fn location(&self) -> SrcSpan { 1427 self.location() 1428 } 1429} 1430 1431#[derive(Debug, Clone, Copy, PartialEq, Eq)] 1432pub enum AssignmentKind { 1433 // let x = ... 1434 Let, 1435 // let assert x = ... 1436 Assert, 1437} 1438 1439impl AssignmentKind { 1440 pub(crate) fn performs_exhaustiveness_check(&self) -> bool { 1441 match self { 1442 AssignmentKind::Let => true, 1443 AssignmentKind::Assert => false, 1444 } 1445 } 1446 1447 /// Returns `true` if the assignment kind is [`Assert`]. 1448 /// 1449 /// [`Assert`]: AssignmentKind::Assert 1450 #[must_use] 1451 pub fn is_assert(&self) -> bool { 1452 matches!(self, Self::Assert) 1453 } 1454} 1455 1456// BitArrays 1457 1458pub type UntypedExprBitArraySegment = BitArraySegment<UntypedExpr, ()>; 1459pub type TypedExprBitArraySegment = BitArraySegment<TypedExpr, Arc<Type>>; 1460 1461pub type UntypedConstantBitArraySegment = BitArraySegment<UntypedConstant, ()>; 1462pub type TypedConstantBitArraySegment = BitArraySegment<TypedConstant, Arc<Type>>; 1463 1464pub type UntypedPatternBitArraySegment = BitArraySegment<UntypedPattern, ()>; 1465pub type TypedPatternBitArraySegment = BitArraySegment<TypedPattern, Arc<Type>>; 1466 1467#[derive(Debug, Clone, PartialEq, Eq)] 1468pub struct BitArraySegment<Value, Type> { 1469 pub location: SrcSpan, 1470 pub value: Box<Value>, 1471 pub options: Vec<BitArrayOption<Value>>, 1472 pub type_: Type, 1473} 1474 1475impl TypedExprBitArraySegment { 1476 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 1477 self.value.find_node(byte_index) 1478 } 1479} 1480 1481pub type TypedConstantBitArraySegmentOption = BitArrayOption<TypedConstant>; 1482 1483#[derive(Debug, PartialEq, Eq, Clone)] 1484pub enum BitArrayOption<Value> { 1485 Bytes { 1486 location: SrcSpan, 1487 }, 1488 1489 Int { 1490 location: SrcSpan, 1491 }, 1492 1493 Float { 1494 location: SrcSpan, 1495 }, 1496 1497 Bits { 1498 location: SrcSpan, 1499 }, 1500 1501 Utf8 { 1502 location: SrcSpan, 1503 }, 1504 1505 Utf16 { 1506 location: SrcSpan, 1507 }, 1508 1509 Utf32 { 1510 location: SrcSpan, 1511 }, 1512 1513 Utf8Codepoint { 1514 location: SrcSpan, 1515 }, 1516 1517 Utf16Codepoint { 1518 location: SrcSpan, 1519 }, 1520 1521 Utf32Codepoint { 1522 location: SrcSpan, 1523 }, 1524 1525 Signed { 1526 location: SrcSpan, 1527 }, 1528 1529 Unsigned { 1530 location: SrcSpan, 1531 }, 1532 1533 Big { 1534 location: SrcSpan, 1535 }, 1536 1537 Little { 1538 location: SrcSpan, 1539 }, 1540 1541 Native { 1542 location: SrcSpan, 1543 }, 1544 1545 Size { 1546 location: SrcSpan, 1547 value: Box<Value>, 1548 short_form: bool, 1549 }, 1550 1551 Unit { 1552 location: SrcSpan, 1553 value: u8, 1554 }, 1555} 1556 1557impl<A> BitArrayOption<A> { 1558 pub fn value(&self) -> Option<&A> { 1559 match self { 1560 BitArrayOption::Size { value, .. } => Some(value), 1561 _ => None, 1562 } 1563 } 1564 1565 pub fn location(&self) -> SrcSpan { 1566 match self { 1567 BitArrayOption::Bytes { location } 1568 | BitArrayOption::Int { location } 1569 | BitArrayOption::Float { location } 1570 | BitArrayOption::Bits { location } 1571 | BitArrayOption::Utf8 { location } 1572 | BitArrayOption::Utf16 { location } 1573 | BitArrayOption::Utf32 { location } 1574 | BitArrayOption::Utf8Codepoint { location } 1575 | BitArrayOption::Utf16Codepoint { location } 1576 | BitArrayOption::Utf32Codepoint { location } 1577 | BitArrayOption::Signed { location } 1578 | BitArrayOption::Unsigned { location } 1579 | BitArrayOption::Big { location } 1580 | BitArrayOption::Little { location } 1581 | BitArrayOption::Native { location } 1582 | BitArrayOption::Size { location, .. } 1583 | BitArrayOption::Unit { location, .. } => *location, 1584 } 1585 } 1586 1587 pub fn label(&self) -> EcoString { 1588 match self { 1589 BitArrayOption::Bytes { .. } => "bytes".into(), 1590 BitArrayOption::Int { .. } => "int".into(), 1591 BitArrayOption::Float { .. } => "float".into(), 1592 BitArrayOption::Bits { .. } => "bits".into(), 1593 BitArrayOption::Utf8 { .. } => "utf8".into(), 1594 BitArrayOption::Utf16 { .. } => "utf16".into(), 1595 BitArrayOption::Utf32 { .. } => "utf32".into(), 1596 BitArrayOption::Utf8Codepoint { .. } => "utf8_codepoint".into(), 1597 BitArrayOption::Utf16Codepoint { .. } => "utf16_codepoint".into(), 1598 BitArrayOption::Utf32Codepoint { .. } => "utf32_codepoint".into(), 1599 BitArrayOption::Signed { .. } => "signed".into(), 1600 BitArrayOption::Unsigned { .. } => "unsigned".into(), 1601 BitArrayOption::Big { .. } => "big".into(), 1602 BitArrayOption::Little { .. } => "little".into(), 1603 BitArrayOption::Native { .. } => "native".into(), 1604 BitArrayOption::Size { .. } => "size".into(), 1605 BitArrayOption::Unit { .. } => "unit".into(), 1606 } 1607 } 1608} 1609 1610#[derive(Debug, Clone, Copy, PartialEq, Eq)] 1611pub enum TodoKind { 1612 Keyword, 1613 EmptyFunction, 1614 IncompleteUse, 1615} 1616 1617#[derive(Debug, Default)] 1618pub struct GroupedStatements { 1619 pub functions: Vec<Function<(), UntypedExpr>>, 1620 pub constants: Vec<UntypedModuleConstant>, 1621 pub custom_types: Vec<CustomType<()>>, 1622 pub imports: Vec<Import<()>>, 1623 pub type_aliases: Vec<TypeAlias<()>>, 1624} 1625 1626impl GroupedStatements { 1627 pub fn new(statements: impl IntoIterator<Item = UntypedDefinition>) -> Self { 1628 let mut this = Self::default(); 1629 1630 for statement in statements { 1631 this.add(statement) 1632 } 1633 1634 this 1635 } 1636 1637 pub fn is_empty(&self) -> bool { 1638 self.len() == 0 1639 } 1640 1641 pub fn len(&self) -> usize { 1642 let Self { 1643 custom_types, 1644 functions, 1645 constants, 1646 imports, 1647 type_aliases, 1648 } = self; 1649 functions.len() + constants.len() + imports.len() + custom_types.len() + type_aliases.len() 1650 } 1651 1652 fn add(&mut self, statement: UntypedDefinition) { 1653 match statement { 1654 Definition::Import(i) => self.imports.push(i), 1655 Definition::Function(f) => self.functions.push(f), 1656 Definition::TypeAlias(t) => self.type_aliases.push(t), 1657 Definition::CustomType(c) => self.custom_types.push(c), 1658 Definition::ModuleConstant(c) => self.constants.push(c), 1659 } 1660 } 1661} 1662 1663/// A statement with in a function body. 1664#[derive(Debug, Clone, PartialEq, Eq)] 1665pub enum Statement<TypeT, ExpressionT> { 1666 /// A bare expression that is not assigned to any variable. 1667 Expression(ExpressionT), 1668 /// Assigning an expression to variables using a pattern. 1669 Assignment(Assignment<TypeT, ExpressionT>), 1670 /// A `use` expression. 1671 Use(Use), 1672} 1673 1674pub type TypedStatement = Statement<Arc<Type>, TypedExpr>; 1675pub type UntypedStatement = Statement<(), UntypedExpr>; 1676 1677impl<T, E> Statement<T, E> { 1678 /// Returns `true` if the statement is [`Expression`]. 1679 /// 1680 /// [`Expression`]: Statement::Expression 1681 #[must_use] 1682 pub fn is_expression(&self) -> bool { 1683 matches!(self, Self::Expression(..)) 1684 } 1685} 1686 1687impl UntypedStatement { 1688 pub fn location(&self) -> SrcSpan { 1689 match self { 1690 Statement::Expression(expression) => expression.location(), 1691 Statement::Assignment(assignment) => assignment.location, 1692 Statement::Use(use_) => use_.location, 1693 } 1694 } 1695 1696 pub fn start_byte_index(&self) -> u32 { 1697 match self { 1698 Statement::Expression(expression) => expression.start_byte_index(), 1699 Statement::Assignment(assignment) => assignment.location.start, 1700 Statement::Use(use_) => use_.location.start, 1701 } 1702 } 1703 1704 pub fn is_placeholder(&self) -> bool { 1705 match self { 1706 Statement::Expression(expression) => expression.is_placeholder(), 1707 Statement::Assignment(_) | Statement::Use(_) => false, 1708 } 1709 } 1710} 1711 1712impl TypedStatement { 1713 pub fn is_println(&self) -> bool { 1714 match self { 1715 Statement::Expression(e) => e.is_println(), 1716 Statement::Assignment(_) => false, 1717 Statement::Use(_) => false, 1718 } 1719 } 1720 1721 pub fn is_non_pipe_expression(&self) -> bool { 1722 match self { 1723 Statement::Expression(expression) => !expression.is_pipeline(), 1724 _ => false, 1725 } 1726 } 1727 1728 pub fn location(&self) -> SrcSpan { 1729 match self { 1730 Statement::Expression(expression) => expression.location(), 1731 Statement::Assignment(assignment) => assignment.location, 1732 Statement::Use(use_) => use_.location, 1733 } 1734 } 1735 1736 pub fn type_(&self) -> Arc<Type> { 1737 match self { 1738 Statement::Expression(expression) => expression.type_(), 1739 Statement::Assignment(assignment) => assignment.type_(), 1740 Statement::Use(_use) => unreachable!("Use must not exist for typed code"), 1741 } 1742 } 1743 1744 pub fn definition_location(&self) -> Option<DefinitionLocation<'_>> { 1745 match self { 1746 Statement::Expression(expression) => expression.definition_location(), 1747 Statement::Assignment(_) => None, 1748 Statement::Use(_) => None, 1749 } 1750 } 1751 1752 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 1753 match self { 1754 Statement::Use(_) => None, 1755 Statement::Expression(expression) => expression.find_node(byte_index), 1756 Statement::Assignment(assignment) => assignment.find_node(byte_index).or_else(|| { 1757 if assignment.location.contains(byte_index) { 1758 Some(Located::Statement(self)) 1759 } else { 1760 None 1761 } 1762 }), 1763 } 1764 } 1765 1766 pub fn type_defining_location(&self) -> SrcSpan { 1767 match self { 1768 Statement::Expression(expression) => expression.type_defining_location(), 1769 Statement::Assignment(assignment) => assignment.location, 1770 Statement::Use(use_) => use_.location, 1771 } 1772 } 1773} 1774 1775#[derive(Debug, Clone, PartialEq, Eq)] 1776pub struct Assignment<TypeT, ExpressionT> { 1777 pub location: SrcSpan, 1778 pub value: Box<ExpressionT>, 1779 pub pattern: Pattern<TypeT>, 1780 pub kind: AssignmentKind, 1781 pub annotation: Option<TypeAst>, 1782} 1783 1784pub type TypedAssignment = Assignment<Arc<Type>, TypedExpr>; 1785pub type UntypedAssignment = Assignment<(), UntypedExpr>; 1786 1787impl TypedAssignment { 1788 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> { 1789 self.pattern 1790 .find_node(byte_index) 1791 .or_else(|| self.value.find_node(byte_index)) 1792 } 1793 1794 pub fn type_(&self) -> Arc<Type> { 1795 self.value.type_() 1796 } 1797} 1798 1799#[derive(Debug, Clone, PartialEq, Eq)] 1800pub struct UseAssignment { 1801 pub location: SrcSpan, 1802 pub pattern: UntypedPattern, 1803 pub annotation: Option<TypeAst>, 1804}