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 / erlang.rs
156 kB 4096 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2018 The Gleam contributors 3 4mod pattern; 5#[cfg(test)] 6mod tests; 7 8use crate::build::Target; 9use crate::erlang::pattern::{AliasedLiteral, PatternGenerator}; 10use crate::strings::to_snake_case; 11use crate::type_::{self, is_prelude_module}; 12use crate::{ 13 ast::*, 14 line_numbers::LineNumbers, 15 type_::{ 16 ModuleValueConstructor, PatternConstructor, Type, TypeVar, TypedCallArg, ValueConstructor, 17 ValueConstructorVariant, 18 }, 19}; 20use camino::Utf8Path; 21use ecow::{EcoString, eco_format}; 22use erlang_generation::{ 23 BitArraySegmentSpecifier, DocContent, ErlangBuilder, ErlangModuleName, ErlangSourceBuilder, 24}; 25use itertools::Itertools; 26use num_bigint::BigInt; 27use num_traits::Signed; 28use regex::Regex; 29use std::collections::VecDeque; 30use std::sync::OnceLock; 31use std::{collections::HashMap, ops::Deref, sync::Arc}; 32 33/// This is an open runtime error to which more fields can still be added. 34#[must_use] 35struct RuntimeError<Map, Call> { 36 /// This is the map that is going to be thrown by the `erlang:error` call. 37 error_map: Map, 38 /// This is the call to `erlang:error` that will throw the error, with the 39 /// map as an argument. 40 erlang_error_call: Call, 41} 42 43/// Represents all the different kind of runtime errors that Gleam can raise. 44enum RuntimeErrorKind { 45 Todo, 46 Panic, 47 Assert, 48 LetAssert, 49} 50 51impl RuntimeErrorKind { 52 fn default_error_message(&self) -> &'static str { 53 match self { 54 RuntimeErrorKind::Panic => "`panic` expression evaluated.", 55 RuntimeErrorKind::Assert => "Assertion failed.", 56 RuntimeErrorKind::LetAssert => "Pattern match failed, no pattern matched the value.", 57 RuntimeErrorKind::Todo => { 58 "`todo` expression evaluated. This code has not yet been implemented." 59 } 60 } 61 } 62} 63 64enum EchoPrintedValue<'a> { 65 /// We're printing the result of a pipeline step. 66 PipeStep { 67 /// This is the name that was given to the variable holding the value 68 /// we have to print. 69 name: EcoString, 70 }, 71 /// We're printing any arbitrary expression. 72 Expression { value: &'a TypedExpr }, 73} 74 75/// This describes how an expression that is used in a Gleam's function call 76/// should be called in the Erlang generated code. 77enum FunctionCall<'a> { 78 /// We're calling a function from the given module. 79 /// It might be the same module we're generating code for, so the 80 /// qualification might not be needed at all; remember to check that! 81 /// 82 /// ```erl 83 /// io:println("wibble") 84 /// ``` 85 /// 86 Call { module: &'a str, name: &'a str }, 87 88 /// The expression is not a module level function and can be called directly 89 /// like thie: 90 /// 91 /// ```erl 92 /// SomeVariable("wibble"), 93 /// fun() -> nil end(). 94 /// ``` 95 DirectCall, 96 97 /// This is actually not a call but rather needs to build a tuple with the 98 /// given tag. 99 /// This is needed for records: those are function calls in Gleam, but 100 /// simple tuples on the Erlang side. 101 BuildRecord { name: &'a str }, 102} 103 104/// This is a structure used to generate code for an Erlang module. 105#[derive(Debug)] 106pub struct Generator<'a> { 107 /// The module for which we're currently generating Erlang code. 108 module: &'a TypedModule, 109 line_numbers: &'a LineNumbers, 110 111 /// The relative source path to the module that's gonna be used in error 112 /// messages in the generated Erlang code. 113 module_source_path: EcoString, 114 115 /// Wether `echo` has been used in this module, we're gonna need to know 116 /// this in order to add the code needed by the pretty printing. 117 echo_used: bool, 118} 119 120/// This is a generator that takes care of generating the code for a single 121/// function, taking care of things like the scope, variable renaming, and 122/// generating the function's attributes and statements. 123struct FunctionGenerator<'a, 'generator> { 124 /// The name of the function we're generating code for. 125 function_name: &'a str, 126 127 /// A reference to the module generator, this is needed to take care of some 128 /// global state shared by all the functions. 129 module_generator: &'generator mut Generator<'a>, 130 131 /// This maps from variable origin in the Gleam code to the name it was 132 /// assigned to it in the generated Erlang code. 133 /// 134 /// Erlang doesn't allow shadowing existing variables, so it's not always 135 /// the case that a variable named `wibble` in Gleam is going to correspond 136 /// to the Erlang `Wibble` variable. For example: 137 /// 138 /// ```gleam 139 /// let a = 1 140 /// let a = a + 1 141 /// ``` 142 /// 143 /// In Erlang this would become: 144 /// 145 /// ```erl 146 /// A = 1, 147 /// A@1 = A + 1, 148 /// ``` 149 /// 150 /// So variables might need renaming. 151 /// Whenever we find a variable usage in Gleam we have to check "what is 152 /// the name that was given to the variable that comes from this location?" 153 /// Only then we'll know what's the correct name to use for it. 154 /// 155 variable_names: im::HashMap<SrcSpan, EcoString>, 156 157 /// This keeps track of the number of generated variables that have already 158 /// been generated in the current function. 159 /// For example if this is `2` it means we've already generated: 160 /// 161 /// ```erl 162 /// _value 163 /// _value@1 164 /// _value@2 165 /// ``` 166 /// 167 /// We need this to make sure that every time we generate a new generated 168 /// variable it has a unique name not shadowing anything else. 169 /// 170 generated_variables: usize, 171 172 /// This keeps track of all the names that are taken for the current 173 /// function and can't be used when defining new variables. 174 /// For example if this is `hash_map![("wibble", 2), ("wobble", 1)]` 175 /// this means that all of these variables have already been defined 176 /// somewhere in the current function: 177 /// 178 /// ```erl 179 /// Wibble = ..., 180 /// Wibble@1 = ..., 181 /// Wibble@2 = ..., 182 /// 183 /// Wobble = ..., 184 /// Wobble@1 = ..., 185 /// ``` 186 /// 187 /// This is handy whenever we run into a new variable assignment and have to 188 /// generate a new name for it in Erlang. 189 /// 190 taken_names: im::HashMap<String, usize>, 191} 192 193impl<'a> Generator<'a> { 194 pub fn new( 195 module: &'a TypedModule, 196 line_numbers: &'a LineNumbers, 197 module_root: &'a Utf8Path, 198 ) -> Self { 199 let module_source_path = module 200 .type_info 201 .src_path 202 .strip_prefix(module_root) 203 .unwrap_or(&module.type_info.src_path) 204 .as_str() 205 .replace("\\", "\\\\") 206 .into(); 207 208 Self { 209 module, 210 module_source_path, 211 line_numbers, 212 echo_used: false, 213 } 214 } 215 216 fn module_document<Output>(&mut self, builder: &mut impl ErlangBuilder<Output>) { 217 // We need to know which private functions are referenced in importable 218 // constants so that we can export them anyway in the generated Erlang. 219 // This is because otherwise when the constant is used in another module it 220 // would result in an error as it tries to reference this private function. 221 let overridden_publicity = 222 find_private_functions_referenced_in_importable_constants(self.module); 223 224 // We add a `-compile` attribute at the top of each module to instruct 225 // the Erlang compiler. 226 builder.compile_attribute([ 227 "no_auto_import", 228 "nowarn_ignored", 229 "nowarn_unused_vars", 230 "nowarn_unused_function", 231 "nowarn_nomatch", 232 "inline", 233 ]); 234 235 // We then need to add an `-export` attribute for all the module's 236 // public functions. 237 builder.export_attribute( 238 (self.module.definitions.functions.iter()) 239 .filter_map(|function| function_export(function, &overridden_publicity)), 240 ); 241 // We do the same but with types. 242 builder.export_type_attribute(self.module.definitions.custom_types.iter().map(type_export)); 243 244 // We also add a `-module_doc` comment at the beginning of the module 245 // with its documentation. 246 self.module_documentation(builder); 247 248 // Then we generate `-type` definitions for the module's types. 249 for custom_type in &self.module.definitions.custom_types { 250 self.type_definition(builder, custom_type); 251 } 252 253 // And finally generate all the functions that the module defined. 254 for function in &self.module.definitions.functions { 255 FunctionGenerator::new(function, self).module_function(builder, function); 256 } 257 } 258 259 fn module_documentation<Output>(&mut self, builder: &mut impl ErlangBuilder<Output>) { 260 if self.module.type_info.is_internal { 261 // The module is internal so we need to add a `-moduledoc(false).` 262 // attribute to make sure its documentation is hidden. 263 builder.moduledoc_attribute(DocContent::False); 264 } else if self.module.documentation.is_empty() { 265 // The module is not internal, but it has no docs. 266 // We don't have to do anything. 267 } else { 268 // The module has some documentation that we're going to include 269 // with a `-moduledoc` attribute. 270 builder.moduledoc_attribute(DocContent::String( 271 &self.module.documentation.iter().join("\n"), 272 )); 273 } 274 } 275 276 fn type_definition<Output>( 277 &self, 278 builder: &mut impl ErlangBuilder<Output>, 279 custom_type: &TypedCustomType, 280 ) { 281 let TypedCustomType { 282 name, 283 constructors, 284 opaque, 285 typed_parameters, 286 external_erlang, 287 .. 288 } = custom_type; 289 290 let name = erl_safe_type_name(to_snake_case(name)); 291 292 // We start the type spec. 293 let type_spec = builder.start_type_spec( 294 *opaque, 295 &name, 296 typed_parameters 297 .iter() 298 .map(|type_| type_parameter_name(type_)), 299 ); 300 301 // Now we need to generate the type definition. 302 // Erlang doesn't allow to have phantom type variables, so if there's 303 // any type variable that is not used we will need to add one variant to 304 // the resulting type that is using all those phantom variables to avoid 305 // errors! 306 let phantom_type_variables = phantom_type_variables(custom_type); 307 let has_phantom_type_variables = !phantom_type_variables.is_empty(); 308 match (constructors.as_slice(), has_phantom_type_variables) { 309 // This is an external type with an annotation telling us what type 310 // it corresponds to in Erlang. 311 // In that case all type variables are phantom type variables! 312 ([], _) if let Some((module, type_name, _)) = external_erlang => { 313 let type_ = 314 builder.start_remote_named_type(ErlangModuleName::new(module), type_name); 315 for type_variable in phantom_type_variables { 316 builder.type_variable(&type_variable); 317 } 318 builder.end_remote_named_type(type_); 319 } 320 // This is an external type with no external annotation and no 321 // phantom type variables. It is just `any()`. 322 ([], false) => { 323 let any = builder.start_named_type("any"); 324 builder.end_named_type(any); 325 } 326 // This is an external type with no external annotation and some 327 // phantom type variables, we need to add an alternative to use 328 // them: `any() | {gleam_phantom, A, B, ...}` 329 ([], true) => { 330 let union = builder.start_union_type(); 331 let any = builder.start_named_type("any"); 332 builder.end_named_type(any); 333 self.phantom_type(builder, phantom_type_variables); 334 builder.end_union_type(union); 335 } 336 // This is an external type with a single constructor, no need to 337 // make it a union. 338 ([constructor], false) => self.constructor_type(builder, constructor), 339 // This is an external type with multiple constructors, we have to 340 // turn it into a union! 341 (constructors, has_phantom_type_variables) => { 342 let union = builder.start_union_type(); 343 for constructor in constructors { 344 self.constructor_type(builder, constructor); 345 } 346 if has_phantom_type_variables { 347 self.phantom_type(builder, phantom_type_variables); 348 } 349 builder.end_union_type(union); 350 } 351 } 352 353 builder.end_type_spec(type_spec); 354 } 355 356 /// Given a constructor this generates its type. For example: 357 /// 358 /// ```gleam 359 /// Wibble(Int, String) 360 /// ``` 361 /// 362 /// Would be turned into: 363 /// 364 /// ```erl 365 /// {wibble, integer(), binary()}. 366 /// ``` 367 /// 368 fn constructor_type<Output>( 369 &self, 370 builder: &mut impl ErlangBuilder<Output>, 371 constructor: &RecordConstructor<Arc<Type>>, 372 ) { 373 let constructor_atom = to_snake_case(&constructor.name); 374 if constructor.arguments.is_empty() { 375 // A constructor with no fields becomes a regular atom on the Erlang 376 // target. 377 builder.literal_atom_type(&constructor_atom); 378 } else { 379 // Othwerwise, it is a tuple tagged with the atom with the 380 // constructor name. 381 let generator = TypeGenerator::new(&self.module.name); 382 let tuple = builder.start_tuple_type(); 383 builder.literal_atom_type(&constructor_atom); 384 for argument in &constructor.arguments { 385 generator.type_(builder, &argument.type_); 386 } 387 builder.end_tuple_type(tuple); 388 } 389 } 390 391 /// Given a list of phantom type variabes, this generates a type using all 392 /// of those. 393 /// 394 /// Erlang doesn't allow having phantom type variables in type annotations, 395 /// so whenever there's any we need to manually add a type that uses them to 396 /// make sure we get no errors. For example: 397 /// 398 /// ```gleam 399 /// pub type Wibble(a, phantom) { 400 /// Wibble(a) 401 /// } 402 /// ``` 403 /// 404 /// Will have to be turned into: 405 /// 406 /// ```erl 407 /// -type wibble(A) :: {wibble, A} | {gleam_phantom, Phantom}. 408 /// ``` 409 /// 410 /// So the phantom type is nothing more than a tuple tagged with 411 /// `gleam_phantom`. 412 /// 413 fn phantom_type<Output>( 414 &self, 415 builder: &mut impl ErlangBuilder<Output>, 416 phantom_type_variables: Vec<EcoString>, 417 ) { 418 let phantom_tuple = builder.start_tuple_type(); 419 builder.literal_atom_type("gleam_phantom"); 420 for phantom_type_variable in phantom_type_variables { 421 builder.type_variable(&phantom_type_variable); 422 } 423 builder.end_tuple_type(phantom_tuple); 424 } 425} 426 427/// Given a custom type, this will return a vector with the names of all the 428/// phantom type varaibles that it has. The names returned are the names we can 429/// use in Erlang! 430fn phantom_type_variables(custom_type: &CustomType<Arc<Type>>) -> Vec<EcoString> { 431 // We first find all the variables that appear in the type definition 432 // itself: any of those that isn't used by any of the constructors is going 433 // to be a phantom type variable. 434 let mut definition_type_variables = 435 collect_type_var_usages(HashMap::new(), custom_type.typed_parameters.iter()); 436 437 // So we need to gather all the type variables referenced by all the 438 // constructors. 439 let mut constructors_type_variables = HashMap::new(); 440 for constructor in &custom_type.constructors { 441 constructors_type_variables = collect_type_var_usages( 442 constructors_type_variables, 443 constructor.arguments.iter().map(|argument| &argument.type_), 444 ); 445 } 446 447 // The phantom ones are the ones in the definition that are not referenced 448 // by any constructor: 449 for used_type_variable in constructors_type_variables.keys() { 450 let _ = definition_type_variables.remove(used_type_variable); 451 } 452 453 definition_type_variables 454 .into_keys() 455 .map(id_to_type_var_str) 456 .sorted() 457 .collect_vec() 458} 459 460/// Given a custom type's type parameter (that is expected to be generic or 461/// unbound), this will return the name the corresponding type variable should 462/// have in the generated erlang code. 463/// 464/// If the type passed is not generic this will panic! 465fn type_parameter_name(type_: &Type) -> EcoString { 466 let Type::Var { type_ } = type_ else { 467 panic!("non generic type as type parameter") 468 }; 469 match &*type_.borrow() { 470 TypeVar::Unbound { id } | TypeVar::Generic { id } => id_to_type_var_str(*id), 471 TypeVar::Link { type_ } => type_parameter_name(type_), 472 } 473} 474 475impl<'a, 'generator> FunctionGenerator<'a, 'generator> { 476 pub fn new( 477 function: &'a TypedFunction, 478 module_generator: &'generator mut Generator<'a>, 479 ) -> Self { 480 let function_name = match function.name.as_ref() { 481 Some((_, function_name)) => function_name, 482 None => panic!("Module functions should have a name"), 483 }; 484 485 Self { 486 function_name, 487 module_generator, 488 taken_names: im::HashMap::new(), 489 variable_names: im::HashMap::new(), 490 generated_variables: 0, 491 } 492 } 493 494 /// Given a variable name this returns a document with the name used to 495 /// reference such variable (names can change if a variable were to shadow 496 /// something with the same name!). 497 /// 498 /// ## Panics 499 /// This will panic if the variable is not in scope as that is most likely 500 /// the result of a bug in the compiler. 501 pub fn local_var_name(&self, variable_origin: &SrcSpan) -> EcoString { 502 self.variable_names 503 .get(variable_origin) 504 .expect("variable not in scope") 505 .clone() 506 } 507 508 /// Assigns a name to this new variable making sure it's not shadowing any 509 /// existing one. 510 /// 511 /// - `name` is the name of the variable as defined in the Gleam source code 512 /// - `location` is where that variable comes from, and it is used to then 513 /// get this newly generated name back. 514 /// 515 /// For example: 516 /// 517 /// ```gleam 518 /// let wibble = 1 519 /// ``` 520 /// 521 /// When we run into this Gleam assignment we will need to decide how to 522 /// call it on the Erlang side. So we would call: 523 /// 524 /// ```ignore 525 /// let location = todo!("the location of this variable") 526 /// new_erlang_variable("wibble", location) 527 /// // and later we can tell what name was picked by calling 528 /// // `local_variable_name` 529 /// local_variable_name(location) // "Wibble" 530 /// ``` 531 /// 532 /// 533 pub fn new_erlang_variable(&mut self, name: &str, location: SrcSpan) -> EcoString { 534 let next = self.taken_names.get(name).map_or(0, |i| i + 1); 535 let _ = self.taken_names.insert(name.to_string(), next); 536 let erlang_name = match next { 537 0 => variable_name(name), 538 _ => eco_format!("{}@{}", variable_name(name), next), 539 }; 540 let _ = self.variable_names.insert(location, erlang_name.clone()); 541 erlang_name 542 } 543 544 /// Sometimes during code generation we might need to create new variables 545 /// that were not accounted for during analysis. 546 /// Those variables don't really have an origin in the source code and are 547 /// usually generated and immediately used. 548 /// 549 /// For example: 550 /// 551 /// ```erl 552 /// _denominator = ..., 553 /// 1 / _denominator. 554 /// ``` 555 /// 556 /// Any time you need one such variable you can create it with this method 557 /// instead of `new_erlang_variable` which is meant to be used for variables 558 /// generated from Gleam code (and so wants the source location of the 559 /// variable). 560 /// 561 /// The generated name is guaranteed to always be unique for the given 562 /// function. 563 /// 564 fn new_generated_variable(&mut self) -> EcoString { 565 let name = if self.generated_variables == 0 { 566 EcoString::from("_value") 567 } else { 568 eco_format!("_value@{}", self.generated_variables) 569 }; 570 self.generated_variables += 1; 571 name 572 } 573 574 /// Generates code for an Erlang module function. This might return None 575 /// if there's no code to be generated at all! 576 /// For example if the function is unused, or if the function is a private 577 /// Erlang external (in which case, it would be inlined instead). 578 fn module_function<Output>( 579 &mut self, 580 builder: &mut impl ErlangBuilder<Output>, 581 function: &'a TypedFunction, 582 ) { 583 // We don't generate any code for unused functions. 584 if self 585 .module_generator 586 .module 587 .unused_definition_positions 588 .contains(&function.location.start) 589 { 590 return; 591 } 592 593 // Private external functions don't need to render anything, the 594 // underlying Erlang implementation is used directly at the call site. 595 if function.external_erlang.is_some() && function.publicity.is_private() { 596 return; 597 } 598 599 // If the function has no suitable Erlang implementation then there is 600 // nothing to generate for it. 601 if !function.implementations.supports(Target::Erlang) { 602 return; 603 } 604 605 let function_name = EcoString::from(escape_erlang_existing_name(self.function_name)); 606 607 // Then we add the function's documentation and type annotation. 608 builder.file_attribute( 609 &self.module_generator.module_source_path, 610 self.module_generator 611 .line_numbers 612 .line_number(function.location.start), 613 ); 614 self.function_spec_attribute(builder, &function_name, function); 615 self.function_doc_attribute(builder, function); 616 617 // Finally we start generating code for the function itself, how we do 618 // it depends if the function is external or not. 619 let arity = function.arguments.len(); 620 match function.external_erlang.as_ref() { 621 // If the function is not external we generate the code for all of 622 // its statements. 623 None => { 624 let arguments = self.function_arguments_names(&function.arguments, false); 625 let open_function = builder.start_function(&function_name, arity, arguments); 626 self.statement_sequence(builder, &function.body); 627 builder.end_function(open_function); 628 } 629 630 // An external function consists of just a remote call being 631 // passed all of the function's arguments. 632 Some((module, external_function_name, _location)) => { 633 let arguments = self 634 .function_arguments_names(&function.arguments, true) 635 .collect_vec(); 636 let open_function = 637 builder.start_function(&function_name, arity, arguments.clone()); 638 let call = builder 639 .start_remote_call(ErlangModuleName::new(module), external_function_name); 640 for argument in arguments { 641 builder.variable(&argument); 642 } 643 builder.end_call(call); 644 builder.end_function(open_function); 645 } 646 } 647 } 648 649 /// This generates the `-spec` attribute for a function with the given name. 650 /// 651 fn function_spec_attribute<Output>( 652 &mut self, 653 builder: &mut impl ErlangBuilder<Output>, 654 function_name: &EcoString, 655 function: &'a Function<Arc<Type>, TypedExpr>, 656 ) { 657 // We start by getting all the type variable usages from this function, 658 // both in the argument types and return type. 659 let module_name = &self.module_generator.module.name; 660 let var_usages = &collect_type_var_usages( 661 HashMap::new(), 662 function 663 .arguments 664 .iter() 665 .map(|argument| &argument.type_) 666 .chain(std::iter::once(&function.return_type)), 667 ); 668 let generator = TypeGenerator::new(module_name).with_var_usages(var_usages); 669 670 // We can then start generating the function spec. 671 let spec = builder.start_function_spec(function_name, function.arguments.len()); 672 let function_type = builder.start_function_type(); 673 for argument in &function.arguments { 674 generator.type_(builder, &argument.type_); 675 } 676 let function_type = builder.end_function_type_arguments(function_type); 677 generator.type_(builder, &function.return_type); 678 builder.end_function_type(function_type); 679 builder.end_function_spec(spec); 680 } 681 682 fn function_doc_attribute<Output>( 683 &self, 684 builder: &mut impl ErlangBuilder<Output>, 685 function: &TypedFunction, 686 ) { 687 // If a function is marked as internal or comes from an internal module 688 // we want to hide its documentation in the Erlang shell! 689 // So the doc directive will look like this: `-doc(false).` 690 let is_internal = 691 self.module_generator.module.type_info.is_internal || function.publicity.is_internal(); 692 693 if is_internal { 694 builder.doc_attribute(DocContent::False); 695 } else if let Some((_, documentation)) = &function.documentation 696 && !documentation.is_empty() 697 { 698 builder.doc_attribute(DocContent::String(documentation)); 699 } 700 } 701 702 /// Given a function, this will return the names of the arguments to be used 703 /// in this function's definition. This will also update the current scope 704 /// to add those names to the available local variables. 705 fn function_arguments_names( 706 &mut self, 707 arguments: &[TypedArg], 708 is_external: bool, 709 ) -> impl Iterator<Item = EcoString> { 710 arguments.iter().map(move |argument| match &argument.names { 711 // When the function is external we need to be careful with discarded 712 // arguments. _All_ of the function arguments are always used in an 713 // external function, regardless of them being discarded in Gleam: 714 // 715 // ```gleam 716 // @external(erlang, "io", "format") 717 // fn format(_string: String, _args: List(String)) -> Nil 718 // ``` 719 // 720 // Becomes: 721 // 722 // ```erl 723 // format(_string, _args) -> 724 // io:format(_string, _args). 725 // ``` 726 // 727 // If an argument is made of just underscores, then that would result 728 // in a syntax error in the generated Erlang, where the external 729 // function is called with a discard `io:format(_, _)`! 730 // So in this case we use a generated name to make sure the external 731 // function can be called correctly. 732 ArgNames::Discard { name, location } 733 | ArgNames::LabelledDiscard { 734 name, 735 name_location: location, 736 .. 737 } if is_external => self.new_generated_variable(), 738 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => EcoString::from("_"), 739 ArgNames::Named { name, location } 740 | ArgNames::NamedLabelled { 741 name, 742 name_location: location, 743 .. 744 } => self.new_erlang_variable(name, *location), 745 }) 746 } 747 748 fn statement_sequence<Output>( 749 &mut self, 750 builder: &mut impl ErlangBuilder<Output>, 751 statements: &'a [TypedStatement], 752 ) { 753 // We go over each statement one by one and produce the code they need. 754 for i in 0..statements.len() { 755 match statements.get(i).expect("statement in range") { 756 Statement::Expression(expression) => self.expression(builder, expression), 757 Statement::Use(use_) => self.expression(builder, &use_.call), 758 Statement::Assert(assert) => self.assert(builder, assert), 759 Statement::Assignment(assignment) => match &assignment.kind { 760 AssignmentKind::Let | AssignmentKind::Generated => { 761 self.let_(builder, &assignment.value, &assignment.pattern); 762 } 763 // Let asserts are slightly different from everything else: 764 // A let assert is compiled to a case expression where we 765 // have two branches: 766 // 767 // ```gleam 768 // let assert [a, b] = some_list 769 // // ... the remaining statements 770 // ``` 771 // 772 // It will turn into something that looks like this: 773 // 774 // ```erl 775 // case SomeList of 776 // [a, b] -> 777 // % ... the remaining statements; 778 // _ -> 779 // erlang:error(...) 780 // end. 781 // ``` 782 // 783 // So in case we find a let assert we need to break out of 784 // this cycle and pass it all the remaining statements so 785 // that it can put those under the correct branch of the 786 // case expression it's going to produce. 787 AssignmentKind::Assert { 788 message, location, .. 789 } => { 790 return self.let_assert( 791 builder, 792 &assignment.value, 793 &assignment.pattern, 794 message.as_ref(), 795 *location, 796 statements.get(i + 1..).unwrap_or_default(), 797 ); 798 } 799 }, 800 } 801 } 802 } 803 804 fn expression<Output>( 805 &mut self, 806 builder: &mut impl ErlangBuilder<Output>, 807 expression: &'a TypedExpr, 808 ) { 809 match expression { 810 // 811 // Simple scalar values, and blocks. 812 // 813 TypedExpr::Int { int_value, .. } => builder.int(int_value.clone()), 814 TypedExpr::Float { float_value, .. } => builder.float(float_value.value()), 815 TypedExpr::String { value, .. } => builder.string(value), 816 TypedExpr::Var { 817 name, constructor, .. 818 } => self.var(builder, name, constructor), 819 TypedExpr::Block { statements, .. } => { 820 // If the block has a single expression we don't bother wrapping 821 // it in an additional `begin ... end` block. 822 // It's going to be added only if strictly needed. 823 if statements.len() == 1 824 && let Statement::Expression(expression) = statements.first() 825 { 826 self.maybe_block_expr(builder, expression); 827 } else { 828 let block = builder.start_block(); 829 self.statement_sequence(builder, statements); 830 builder.end_block(block); 831 } 832 } 833 834 // 835 // Operators. 836 // 837 TypedExpr::NegateBool { value, .. } => { 838 builder.unary_operator("not"); 839 self.maybe_block_expr(builder, value); 840 } 841 TypedExpr::NegateInt { value, .. } => { 842 builder.unary_operator("-"); 843 self.maybe_block_expr(builder, value); 844 } 845 TypedExpr::BinOp { 846 operator, 847 left, 848 right, 849 .. 850 } => self.binary_operator(builder, operator, left, right), 851 852 // 853 // BitArrays, Lists, and Tuples. 854 // 855 TypedExpr::BitArray { segments, .. } => { 856 let bit_array = builder.start_bit_array(); 857 for segment in segments { 858 self.bit_array_expression_segment(builder, segment); 859 } 860 builder.end_bit_array(bit_array); 861 } 862 TypedExpr::List { elements, tail, .. } => { 863 // We generate all the items of the list as cons cells. 864 for element in elements { 865 builder.cons_list(); 866 self.maybe_block_expr(builder, element); 867 } 868 // Finally we close the list with the tail, or an empty list 869 // (so that we're sure we're building proper Erlang lists). 870 if let Some(tail) = tail { 871 self.maybe_block_expr(builder, tail); 872 } else { 873 builder.empty_list(); 874 } 875 } 876 TypedExpr::Tuple { elements, .. } => { 877 let tuple = builder.start_tuple(); 878 for element in elements { 879 self.maybe_block_expr(builder, element); 880 } 881 builder.end_tuple(tuple); 882 } 883 884 // 885 // Accessing data inside tuples, and records. 886 // They're all tuple accesses at the end of the day! 887 // 888 TypedExpr::TupleIndex { tuple, index, .. } => self.tuple_index(builder, tuple, *index), 889 TypedExpr::RecordAccess { record, index, .. } 890 | TypedExpr::PositionalAccess { record, index, .. } => { 891 self.tuple_index(builder, record, index + 1); 892 } 893 894 // 895 // Records and record updates. 896 // 897 TypedExpr::ModuleSelect { 898 constructor: ModuleValueConstructor::Record { name, arity: 0, .. }, 899 .. 900 } => builder.atom(&to_snake_case(name)), 901 TypedExpr::RecordUpdate { 902 updated_record_assigned_name, 903 updated_record, 904 constructor, 905 arguments, 906 .. 907 } => { 908 // If the record value itself needs to be bound to a variable 909 // before the update, we define it. 910 if let Some(name) = updated_record_assigned_name.as_ref() { 911 builder.match_operator(); 912 builder.variable_pattern( 913 &self.new_erlang_variable(name, updated_record.location()), 914 ); 915 self.maybe_block_expr(builder, updated_record); 916 } 917 // Then a record update is simply a call! 918 self.call(builder, constructor, arguments); 919 } 920 921 // 922 // All kinds of anonymous functions. 923 // 924 TypedExpr::Fn { 925 arguments, body, .. 926 } => { 927 let outer_scope = self.taken_names.clone(); 928 let argument_names = self.function_arguments_names(arguments, false); 929 let function = builder.start_anonymous_function(argument_names); 930 self.statement_sequence(builder, body); 931 builder.end_function(function); 932 self.taken_names = outer_scope; 933 } 934 TypedExpr::ModuleSelect { 935 constructor: ModuleValueConstructor::Record { name, arity, .. }, 936 .. 937 } => self.record_builder_anonymous_function(builder, name, *arity as usize), 938 TypedExpr::ModuleSelect { 939 type_, 940 constructor: 941 ModuleValueConstructor::Fn { 942 external_erlang: Some((module, name)), 943 .. 944 } 945 | ModuleValueConstructor::Fn { module, name, .. }, 946 .. 947 } => match type_::collapse_links(type_.clone()).as_ref() { 948 Type::Fn { arguments, .. } => builder.function_reference( 949 Some(ErlangModuleName::new(module)), 950 escape_erlang_existing_name(name), 951 arguments.len(), 952 ), 953 954 Type::Named { .. } | Type::Var { .. } | Type::Tuple { .. } => { 955 let name = escape_erlang_existing_name(name); 956 let call = builder.start_remote_call(ErlangModuleName::new(module), name); 957 builder.end_call(call); 958 } 959 }, 960 961 // 962 // Calling functions. 963 // 964 TypedExpr::Call { fun, arguments, .. } => self.call(builder, fun, arguments), 965 TypedExpr::Pipeline { 966 first_value, 967 assignments, 968 finally, 969 .. 970 } => self.pipeline(builder, first_value, assignments, finally), 971 972 // 973 // Todo, panic, and echo. 974 // 975 TypedExpr::Todo { 976 message, location, .. 977 } => { 978 let error = self.start_runtime_error( 979 builder, 980 RuntimeErrorKind::Todo, 981 *location, 982 message.as_deref(), 983 ); 984 self.end_runtime_error(builder, error); 985 } 986 TypedExpr::Panic { 987 location, message, .. 988 } => { 989 let error = self.start_runtime_error( 990 builder, 991 RuntimeErrorKind::Panic, 992 *location, 993 message.as_deref(), 994 ); 995 self.end_runtime_error(builder, error); 996 } 997 TypedExpr::Echo { 998 expression, 999 location, 1000 message, 1001 .. 1002 } => self.echo( 1003 builder, 1004 *location, 1005 message.as_deref(), 1006 EchoPrintedValue::Expression { 1007 value: expression 1008 .as_ref() 1009 .expect("echo with no expression outside of pipe"), 1010 }, 1011 ), 1012 1013 // 1014 // Module constants. 1015 // 1016 TypedExpr::ModuleSelect { 1017 constructor: ModuleValueConstructor::Constant { literal, .. }, 1018 .. 1019 } => self.inlined_constant(builder, literal), 1020 1021 // 1022 // Control flow. 1023 // 1024 TypedExpr::Case { 1025 subjects, clauses, .. 1026 } => self.case(builder, subjects, clauses), 1027 1028 // 1029 // Something went wrong! 1030 // 1031 TypedExpr::Invalid { .. } => { 1032 panic!("invalid expressions should not reach code generation") 1033 } 1034 } 1035 } 1036 1037 fn echo<Output>( 1038 &mut self, 1039 builder: &mut impl ErlangBuilder<Output>, 1040 echo_location: SrcSpan, 1041 message: Option<&'a TypedExpr>, 1042 printed_value: EchoPrintedValue<'a>, 1043 ) { 1044 self.module_generator.echo_used = true; 1045 1046 let call = builder.start_call(); 1047 builder.atom("echo"); 1048 let call = builder.end_called_expression(call); 1049 1050 // Echo has 4 arguments: the expression to print... 1051 match printed_value { 1052 EchoPrintedValue::PipeStep { name } => builder.variable(&name), 1053 EchoPrintedValue::Expression { value } => self.maybe_block_expr(builder, value), 1054 } 1055 // ...the message to print (or nil if there's no message)... 1056 if let Some(message) = message { 1057 self.maybe_block_expr(builder, message); 1058 } else { 1059 builder.atom("nil"); 1060 } 1061 1062 // ...the filepath of this module... 1063 builder.string(&self.module_generator.module_source_path); 1064 1065 // ...and the line number of the expression. 1066 builder.int( 1067 self.module_generator 1068 .line_numbers 1069 .line_number(echo_location.start) 1070 .into(), 1071 ); 1072 1073 builder.end_call(call); 1074 } 1075 1076 /// This starts a call to `erlang:error` with a map representing a Gleam 1077 /// runtime error of the given kind. 1078 /// Some fields are mandatory and always added, but if you need to add more 1079 /// fields you can still do so by calling `builder.map_field()`. 1080 /// 1081 /// After you're done generating those additional fields remember you _must_ 1082 /// call `end_runtime_error` before generating any other piece of code! 1083 /// 1084 fn start_runtime_error<Output, Builder: ErlangBuilder<Output>>( 1085 &mut self, 1086 builder: &mut Builder, 1087 error_kind: RuntimeErrorKind, 1088 location: SrcSpan, 1089 message: Option<&'a TypedExpr>, 1090 ) -> RuntimeError<Builder::Map, Builder::Call> { 1091 let call = builder.start_remote_call(ErlangModuleName::erlang(), "error"); 1092 let map = builder.start_map(); 1093 1094 builder.map_field(); 1095 builder.atom("gleam_error"); 1096 builder.atom(match error_kind { 1097 RuntimeErrorKind::Todo => "todo", 1098 RuntimeErrorKind::Panic => "panic", 1099 RuntimeErrorKind::Assert => "assert", 1100 RuntimeErrorKind::LetAssert => "let_assert", 1101 }); 1102 1103 builder.map_field(); 1104 builder.atom("message"); 1105 if let Some(message) = message { 1106 self.maybe_block_expr(builder, message); 1107 } else { 1108 builder.string(error_kind.default_error_message()); 1109 } 1110 1111 builder.map_field(); 1112 builder.atom("file"); 1113 builder.string(&self.module_generator.module_source_path); 1114 1115 builder.map_field(); 1116 builder.atom("module"); 1117 builder.string(&self.module_generator.module.name); 1118 1119 builder.map_field(); 1120 builder.atom("function"); 1121 builder.string(self.function_name); 1122 1123 builder.map_field(); 1124 builder.atom("line"); 1125 builder.int( 1126 self.module_generator 1127 .line_numbers 1128 .line_number(location.start) 1129 .into(), 1130 ); 1131 1132 RuntimeError { 1133 error_map: map, 1134 erlang_error_call: call, 1135 } 1136 } 1137 1138 /// This closes an open runtime error. 1139 fn end_runtime_error<Output, Builder: ErlangBuilder<Output>>( 1140 &self, 1141 builder: &mut Builder, 1142 runtime_error: RuntimeError<Builder::Map, Builder::Call>, 1143 ) { 1144 builder.end_map(runtime_error.error_map); 1145 builder.end_call(runtime_error.erlang_error_call); 1146 } 1147 1148 fn maybe_block_expr<Output>( 1149 &mut self, 1150 builder: &mut impl ErlangBuilder<Output>, 1151 expression: &'a TypedExpr, 1152 ) { 1153 if needs_begin_end_wrapping(expression) { 1154 let block = builder.start_block(); 1155 self.expression(builder, expression); 1156 builder.end_block(block); 1157 } else { 1158 self.expression(builder, expression); 1159 } 1160 } 1161 1162 fn let_<Output>( 1163 &mut self, 1164 builder: &mut impl ErlangBuilder<Output>, 1165 value: &'a TypedExpr, 1166 pattern: &'a TypedPattern, 1167 ) { 1168 builder.match_operator(); 1169 PatternGenerator::new(self).pattern(builder, pattern); 1170 self.maybe_block_expr(builder, value); 1171 } 1172 1173 fn let_assert<Output>( 1174 &mut self, 1175 builder: &mut impl ErlangBuilder<Output>, 1176 value: &'a TypedExpr, 1177 pattern: &'a TypedPattern, 1178 message: Option<&'a TypedExpr>, 1179 location: SrcSpan, 1180 following_statements: &'a [TypedStatement], 1181 ) { 1182 // If the pattern will never fail, like a tuple or a simple variable, we 1183 // simply treat it as if it were a `let` assignment. 1184 if pattern.always_matches() { 1185 self.let_(builder, value, pattern); 1186 self.statement_sequence(builder, following_statements); 1187 return; 1188 } 1189 1190 // Otherwise we turn the let assert into a case expression with two 1191 // branches: one for the asserted pattern, and one catch all to throw an 1192 // exception in case the pattern doesn't match. 1193 let case = builder.start_case(); 1194 self.maybe_block_expr(builder, value); 1195 let case = builder.end_case_subject(case); 1196 1197 // This is the first branch for when the asserted pattern matches: it's 1198 // going to run all the remaining statements in its body. 1199 if !following_statements.is_empty() { 1200 // If there's statements after this let assert we want to generate 1201 // them. 1202 let clause = builder.start_case_clause(); 1203 let mut generator = PatternGenerator::new(self); 1204 generator.pattern(builder, pattern); 1205 let clause = builder.end_clause_pattern(clause); 1206 let clause = builder.end_clause_guards(clause); 1207 let variables_to_add_later = generator.variables_to_add_later; 1208 self.pattern_assignments(builder, variables_to_add_later); 1209 self.statement_sequence(builder, following_statements); 1210 builder.end_clause_body(clause); 1211 } else { 1212 // If there's no statements following the let assert, that means 1213 // that it's the last statement in the block and we need to return 1214 // the value being matched on. 1215 // It will look something like this: 1216 // 1217 // ```erl 1218 // case MatchedValue of 1219 // [_, A | _] = _value -> _value; 1220 // % ^^^^^^ We bind the pattern to a variable 1221 // % and return it. 1222 // _ -> erlang:error(...) 1223 // end 1224 // ``` 1225 let clause = builder.start_case_clause(); 1226 let matched_value_name = self.new_generated_variable(); 1227 builder.match_pattern(); 1228 let mut generator = PatternGenerator::new(self); 1229 generator.pattern(builder, pattern); 1230 builder.variable_pattern(&matched_value_name); 1231 1232 let clause = builder.end_clause_pattern(clause); 1233 let clause = builder.end_clause_guards(clause); 1234 builder.variable(&matched_value_name); 1235 builder.end_clause_body(clause); 1236 } 1237 1238 // This is the catch all branch to throw an error otherwise. 1239 let clause = builder.start_case_clause(); 1240 let value_name = self.new_generated_variable(); 1241 builder.variable_pattern(&value_name); 1242 let clause = builder.end_clause_pattern(clause); 1243 let clause = builder.end_clause_guards(clause); 1244 let error = 1245 self.start_runtime_error(builder, RuntimeErrorKind::LetAssert, location, message); 1246 1247 // We want to add some additional fields to the error map: 1248 builder.map_field(); 1249 builder.atom("value"); 1250 builder.variable(&value_name); 1251 1252 builder.map_field(); 1253 builder.atom("start"); 1254 builder.int(location.start.into()); 1255 1256 builder.map_field(); 1257 builder.atom("end"); 1258 builder.int(value.location().end.into()); 1259 1260 builder.map_field(); 1261 builder.atom("pattern_start"); 1262 builder.int(pattern.location().start.into()); 1263 1264 builder.map_field(); 1265 builder.atom("pattern_end"); 1266 builder.int(pattern.location().end.into()); 1267 1268 self.end_runtime_error(builder, error); 1269 builder.end_clause_body(clause); 1270 1271 builder.end_case(case); 1272 } 1273 1274 fn pipeline<Output>( 1275 &mut self, 1276 builder: &mut impl ErlangBuilder<Output>, 1277 first_value: &'a TypedPipelineAssignment, 1278 assignments: &'a [(TypedPipelineAssignment, PipelineAssignmentKind)], 1279 finally: &'a TypedExpr, 1280 ) { 1281 // A pipeline is desugared as a sequence of assignments: 1282 // 1283 // ```erl 1284 // Step1 = fun() 1285 // Step2 = fun(Step1) 1286 // Step3 = fun(Step2) 1287 // % ... 1288 // ``` 1289 // 1290 // So we need to keep around the name the prevopis pipeline step had 1291 // to pass it as an argument to the following call. This is what this 1292 // variable is for. 1293 let mut previous_step_variable_name: Option<EcoString> = None; 1294 for assignment in std::iter::once(first_value) 1295 .chain(assignments.iter().map(|(assignment, _kind)| assignment)) 1296 { 1297 // A pipeline step always ends up assigned to a variable. 1298 // So we start by generating `_pipe = ...`, followed by the 1299 // expression. 1300 builder.match_operator(); 1301 let name = self.new_erlang_variable(&assignment.name, assignment.location); 1302 builder.variable_pattern(&name); 1303 1304 // In case of a pipe we need to manually pass the previous step to 1305 // echo as an argument. 1306 if let TypedExpr::Echo { 1307 expression: None, 1308 message, 1309 location, 1310 .. 1311 } = assignment.value.as_ref() 1312 { 1313 self.echo( 1314 builder, 1315 *location, 1316 message.as_deref(), 1317 EchoPrintedValue::PipeStep { 1318 name: previous_step_variable_name 1319 .to_owned() 1320 .expect("echo with no previous step in a pipe"), 1321 }, 1322 ); 1323 } else { 1324 self.maybe_block_expr(builder, &assignment.value); 1325 previous_step_variable_name = Some(name); 1326 } 1327 } 1328 1329 // We also need to do the same thing for the final step of the pipeline. 1330 // It's slightly different compared to the other ones so we have to do 1331 // that separately. 1332 if let TypedExpr::Echo { 1333 expression: None, 1334 message, 1335 location, 1336 .. 1337 } = finally 1338 { 1339 self.echo( 1340 builder, 1341 *location, 1342 message.as_deref(), 1343 EchoPrintedValue::PipeStep { 1344 name: previous_step_variable_name 1345 .expect("echo with no previous step in a pipe"), 1346 }, 1347 ); 1348 } else { 1349 self.expression(builder, finally); 1350 } 1351 } 1352 1353 fn assert<Output>( 1354 &mut self, 1355 builder: &mut impl ErlangBuilder<Output>, 1356 assert: &'a TypedAssert, 1357 ) { 1358 let Assert { 1359 value, 1360 location, 1361 message, 1362 } = assert; 1363 1364 match value { 1365 // We're asserting on a binary operator. We want to show the result 1366 // of each side in the error that is produced. 1367 // So we will bind the two sides of the operator to variables and 1368 // shove them in the error map too! 1369 TypedExpr::BinOp { 1370 operator, 1371 left, 1372 right, 1373 .. 1374 } => { 1375 let erlang_operator = match operator { 1376 // Writing asserts on binops requires some extra care, check 1377 // out their docs! 1378 BinOp::And => { 1379 return self.assert_and(builder, left, right, message.as_ref(), *location); 1380 } 1381 BinOp::Or => { 1382 return self.assert_or(builder, left, right, message.as_ref(), *location); 1383 } 1384 1385 BinOp::Eq => "=:=", 1386 BinOp::NotEq => "/=", 1387 BinOp::LtInt | BinOp::LtFloat => "<", 1388 BinOp::LtEqInt | BinOp::LtEqFloat => "=<", 1389 BinOp::GtInt | BinOp::GtFloat => ">", 1390 BinOp::GtEqInt | BinOp::GtEqFloat => ">=", 1391 1392 BinOp::AddInt 1393 | BinOp::AddFloat 1394 | BinOp::SubInt 1395 | BinOp::SubFloat 1396 | BinOp::MultInt 1397 | BinOp::MultFloat 1398 | BinOp::DivInt 1399 | BinOp::DivFloat 1400 | BinOp::RemainderInt 1401 | BinOp::Concatenate => { 1402 panic!("Non-boolean operators cannot appear here in well-typed code") 1403 } 1404 }; 1405 1406 // If the left or right hand side are not simple variables we'll 1407 // need to first assign those to generated variables and keep 1408 // track of those names. 1409 let left = if !left.is_var() { 1410 let name = self.new_generated_variable(); 1411 builder.match_operator(); 1412 builder.variable_pattern(&name); 1413 self.maybe_block_expr(builder, left); 1414 AssertionExpression::from_generated_variable(name, left) 1415 } else { 1416 AssertionExpression::from_expression(left) 1417 }; 1418 1419 let right = if !right.is_var() { 1420 let name = self.new_generated_variable(); 1421 builder.match_operator(); 1422 builder.variable_pattern(&name); 1423 self.maybe_block_expr(builder, right); 1424 AssertionExpression::from_generated_variable(name, right) 1425 } else { 1426 AssertionExpression::from_expression(right) 1427 }; 1428 1429 let case = builder.start_case(); 1430 1431 // Then we need to apply the operator. If any of the two sides 1432 // has been bound to a variable we can use that name directly! 1433 builder.binary_operator(erlang_operator); 1434 self.runtime_value(builder, &left); 1435 self.runtime_value(builder, &right); 1436 let case = builder.end_case_subject(case); 1437 1438 // If the operator evaluates to true the assertion succeeded. 1439 // We can just return nil. 1440 let clause = builder.start_case_clause(); 1441 builder.atom_pattern("true"); 1442 let clause = builder.end_clause_pattern(clause); 1443 let clause = builder.end_clause_guards(clause); 1444 builder.atom("nil"); 1445 builder.end_clause_body(clause); 1446 1447 // Otherwise we want to throw a runtime error! 1448 let clause = builder.start_case_clause(); 1449 builder.atom_pattern("false"); 1450 let clause = builder.end_clause_pattern(clause); 1451 let clause = builder.end_clause_guards(clause); 1452 self.assert_binary_operator_error( 1453 builder, 1454 *operator, 1455 left, 1456 right, 1457 message.as_ref(), 1458 *location, 1459 ); 1460 builder.end_clause_body(clause); 1461 1462 builder.end_case(case); 1463 } 1464 1465 TypedExpr::Call { fun, arguments, .. } => { 1466 // When asserting on a call, we want to include the values of 1467 // each argument in the assertion error in case of failure. 1468 // This means we first have to evaluate each argument and bind 1469 // it to a variable so that we can later reference them from the 1470 // error message without evaluating each argument twice! 1471 let mut call_arguments = Vec::with_capacity(arguments.len()); 1472 for argument in arguments { 1473 let argument = if !argument.value.is_var() { 1474 let name = self.new_generated_variable(); 1475 builder.match_operator(); 1476 builder.variable_pattern(&name); 1477 self.maybe_block_expr(builder, &argument.value); 1478 AssertionExpression::from_generated_variable(name, &argument.value) 1479 } else { 1480 AssertionExpression::from_expression(&argument.value) 1481 }; 1482 call_arguments.push(argument); 1483 } 1484 1485 let case = builder.start_case(); 1486 self.call_in_assert(builder, fun, &call_arguments); 1487 let case = builder.end_case_subject(case); 1488 1489 // If the operator evaluates to true the assertion succeeded. 1490 // We can just return nil. 1491 let clause = builder.start_case_clause(); 1492 builder.atom_pattern("true"); 1493 let clause = builder.end_clause_pattern(clause); 1494 let clause = builder.end_clause_guards(clause); 1495 builder.atom("nil"); 1496 builder.end_clause_body(clause); 1497 1498 // Otherwise we want to throw a runtime error! 1499 let clause = builder.start_case_clause(); 1500 builder.atom_pattern("false"); 1501 let clause = builder.end_clause_pattern(clause); 1502 let clause = builder.end_clause_guards(clause); 1503 self.assert_call_error( 1504 builder, 1505 value, 1506 &call_arguments, 1507 message.as_ref(), 1508 *location, 1509 ); 1510 builder.end_clause_body(clause); 1511 1512 builder.end_case(case); 1513 } 1514 1515 TypedExpr::Int { .. } 1516 | TypedExpr::Float { .. } 1517 | TypedExpr::String { .. } 1518 | TypedExpr::Block { .. } 1519 | TypedExpr::Pipeline { .. } 1520 | TypedExpr::Var { .. } 1521 | TypedExpr::Fn { .. } 1522 | TypedExpr::List { .. } 1523 | TypedExpr::Case { .. } 1524 | TypedExpr::RecordAccess { .. } 1525 | TypedExpr::PositionalAccess { .. } 1526 | TypedExpr::ModuleSelect { .. } 1527 | TypedExpr::Tuple { .. } 1528 | TypedExpr::TupleIndex { .. } 1529 | TypedExpr::Todo { .. } 1530 | TypedExpr::Panic { .. } 1531 | TypedExpr::Echo { .. } 1532 | TypedExpr::BitArray { .. } 1533 | TypedExpr::RecordUpdate { .. } 1534 | TypedExpr::NegateBool { .. } 1535 | TypedExpr::NegateInt { .. } 1536 | TypedExpr::Invalid { .. } => { 1537 let case = builder.start_case(); 1538 self.maybe_block_expr(builder, value); 1539 let case = builder.end_case_subject(case); 1540 1541 // If the expression evaluates to true the assertion succeeded. 1542 // We can just return nil. 1543 let clause = builder.start_case_clause(); 1544 builder.atom_pattern("true"); 1545 let clause = builder.end_clause_pattern(clause); 1546 let clause = builder.end_clause_guards(clause); 1547 builder.atom("nil"); 1548 builder.end_clause_body(clause); 1549 1550 // Otherwise we want to throw a runtime error! 1551 let clause = builder.start_case_clause(); 1552 builder.atom_pattern("false"); 1553 let clause = builder.end_clause_pattern(clause); 1554 let clause = builder.end_clause_guards(clause); 1555 self.assert_expression_error( 1556 builder, 1557 AssertionExpression::from_expression(value).evaluated_to_bool(false), 1558 message.as_ref(), 1559 *location, 1560 ); 1561 builder.end_clause_body(clause); 1562 1563 builder.end_case(case); 1564 } 1565 } 1566 } 1567 1568 /// In Gleam, the `&&` operator is short-circuiting, meaning that we can't 1569 /// pre-evaluate both sides of it, and use them in the exception that is 1570 /// thrown. 1571 /// Instead, we need to implement this short-circuiting logic ourself. 1572 /// 1573 /// If we short-circuit, we must leave the second expression unevaluated, 1574 /// and signal that using the `unevaluated` variant, as detailed in the 1575 /// exception format. For the first expression, we know it must be `false`, 1576 /// otherwise we would have continued by evaluating the second expression. 1577 /// 1578 /// Similarly, if we do evaluate the second expression and fail, we know 1579 /// that the first expression must have evaluated to `true`, and the second 1580 /// to `false`. This way, we avoid needing to evaluate either expression 1581 /// twice. 1582 /// 1583 /// The generated code then looks something like this: 1584 /// ```erlang 1585 /// case expr1 of 1586 /// true -> case expr2 of 1587 /// true -> true; 1588 /// false -> <throw exception> 1589 /// end; 1590 /// false -> <throw exception> 1591 /// end 1592 /// ``` 1593 /// 1594 fn assert_and<Output>( 1595 &mut self, 1596 builder: &mut impl ErlangBuilder<Output>, 1597 left: &'a TypedExpr, 1598 right: &'a TypedExpr, 1599 message: Option<&'a TypedExpr>, 1600 location: SrcSpan, 1601 ) { 1602 let case = builder.start_case(); 1603 self.maybe_block_expr(builder, left); 1604 let case = builder.end_case_subject(case); 1605 1606 // In case the first expression is true, we get to evaluate the second 1607 // one as well, then we will be able to tell if the assertion failed or 1608 // not! 1609 let clause = builder.start_case_clause(); 1610 builder.atom_pattern("true"); 1611 let clause = builder.end_clause_pattern(clause); 1612 let clause = builder.end_clause_guards(clause); 1613 { 1614 // Now we have to match on the right hand side! 1615 let case = builder.start_case(); 1616 self.maybe_block_expr(builder, right); 1617 let case = builder.end_case_subject(case); 1618 1619 // If it's true the assertion succeded! We can return `nil`. 1620 let clause = builder.start_case_clause(); 1621 builder.atom_pattern("true"); 1622 let clause = builder.end_clause_pattern(clause); 1623 let clause = builder.end_clause_guards(clause); 1624 builder.atom("nil"); 1625 builder.end_clause_body(clause); 1626 1627 // If it's false the assertion failed! The left hand side was true 1628 // but this one evaluated to false :( 1629 let clause = builder.start_case_clause(); 1630 builder.atom_pattern("false"); 1631 let clause = builder.end_clause_pattern(clause); 1632 let clause = builder.end_clause_guards(clause); 1633 self.assert_binary_operator_error( 1634 builder, 1635 BinOp::And, 1636 AssertionExpression::from_expression(left).evaluated_to_bool(true), 1637 AssertionExpression::from_expression(right).evaluated_to_bool(false), 1638 message, 1639 location, 1640 ); 1641 builder.end_clause_body(clause); 1642 builder.end_case(case); 1643 } 1644 builder.end_clause_body(clause); 1645 1646 // In case the first expression is false, we want to fail fast. We are 1647 // short circuiting without evaluating the right hand side! This side 1648 // just build an error. 1649 let clause = builder.start_case_clause(); 1650 builder.atom_pattern("false"); 1651 let clause = builder.end_clause_pattern(clause); 1652 let clause = builder.end_clause_guards(clause); 1653 self.assert_binary_operator_error( 1654 builder, 1655 BinOp::And, 1656 AssertionExpression::from_expression(left).evaluated_to_bool(false), 1657 AssertionExpression::from_expression(right).was_unevaluated(), 1658 message, 1659 location, 1660 ); 1661 builder.end_clause_body(clause); 1662 1663 builder.end_case(case); 1664 } 1665 1666 /// Similar to `&&`, `||` is also short-circuiting in Gleam. However, if `||` 1667 /// short-circuits, that's because the first expression evaluated to `true`, 1668 /// meaning the whole assertion succeeds. This allows us to directly use Erlang's 1669 /// `orelse` operator as the subject of the `case` expression. 1670 /// 1671 /// The only difference is that due to the nature of `||`, if the assertion fails, 1672 /// we know that both sides must have evaluated to `false`, so we don't 1673 /// need to store the values of them in variables beforehand. 1674 fn assert_or<Output>( 1675 &mut self, 1676 builder: &mut impl ErlangBuilder<Output>, 1677 left: &'a TypedExpr, 1678 right: &'a TypedExpr, 1679 message: Option<&'a TypedExpr>, 1680 location: SrcSpan, 1681 ) { 1682 let case = builder.start_case(); 1683 builder.binary_operator("orelse"); 1684 self.maybe_block_expr(builder, left); 1685 self.maybe_block_expr(builder, right); 1686 let case = builder.end_case_subject(case); 1687 1688 // If the result is true, then the assertion succeeded, we can return 1689 // nil. 1690 let clause = builder.start_case_clause(); 1691 builder.atom_pattern("true"); 1692 let clause = builder.end_clause_pattern(clause); 1693 let clause = builder.end_clause_guards(clause); 1694 builder.atom("nil"); 1695 builder.end_clause_body(clause); 1696 1697 // But if it fails we know that both sides of the assertion resulted in 1698 // a false value. In that case we throw an error. 1699 1700 let clause = builder.start_case_clause(); 1701 builder.atom_pattern("false"); 1702 let clause = builder.end_clause_pattern(clause); 1703 let clause = builder.end_clause_guards(clause); 1704 self.assert_binary_operator_error( 1705 builder, 1706 BinOp::Or, 1707 AssertionExpression::from_expression(left).evaluated_to_bool(false), 1708 AssertionExpression::from_expression(right).evaluated_to_bool(false), 1709 message, 1710 location, 1711 ); 1712 builder.end_clause_body(clause); 1713 1714 builder.end_case(case); 1715 } 1716 1717 /// This generates the code that throws a runtime error whan an `assert` 1718 /// that is checking the result of a binary operator fails. 1719 fn assert_binary_operator_error<Output>( 1720 &mut self, 1721 builder: &mut impl ErlangBuilder<Output>, 1722 operator: BinOp, 1723 left: AssertionExpression<'a>, 1724 right: AssertionExpression<'a>, 1725 message: Option<&'a TypedExpr>, 1726 location: SrcSpan, 1727 ) { 1728 let error = self.start_runtime_error(builder, RuntimeErrorKind::Assert, location, message); 1729 1730 builder.map_field(); 1731 builder.atom("kind"); 1732 builder.atom("binary_operator"); 1733 1734 builder.map_field(); 1735 builder.atom("operator"); 1736 builder.atom(operator.name()); 1737 1738 builder.map_field(); 1739 builder.atom("left"); 1740 self.assertion_expression_map(builder, &left); 1741 1742 builder.map_field(); 1743 builder.atom("right"); 1744 self.assertion_expression_map(builder, &right); 1745 1746 builder.map_field(); 1747 builder.atom("start"); 1748 builder.int(location.start.into()); 1749 1750 builder.map_field(); 1751 builder.atom("end"); 1752 builder.int(right.location.end.into()); 1753 1754 builder.map_field(); 1755 builder.atom("expression_start"); 1756 builder.int(left.location.start.into()); 1757 1758 self.end_runtime_error(builder, error); 1759 } 1760 1761 /// This generates the code that throws a runtime error whan an `assert` 1762 /// that is checking the result of an arbitrary expression fails. 1763 fn assert_expression_error<Output>( 1764 &mut self, 1765 builder: &mut impl ErlangBuilder<Output>, 1766 expression: AssertionExpression<'a>, 1767 message: Option<&'a TypedExpr>, 1768 location: SrcSpan, 1769 ) { 1770 let error = self.start_runtime_error(builder, RuntimeErrorKind::Assert, location, message); 1771 1772 builder.map_field(); 1773 builder.atom("kind"); 1774 builder.atom("expression"); 1775 1776 // If assert fails on an expression's result then we know it must have 1777 // evaluated to false! 1778 builder.map_field(); 1779 builder.atom("expression"); 1780 self.assertion_expression_map(builder, &expression); 1781 1782 builder.map_field(); 1783 builder.atom("start"); 1784 builder.int(location.start.into()); 1785 1786 builder.map_field(); 1787 builder.atom("end"); 1788 builder.int(expression.location.end.into()); 1789 1790 builder.map_field(); 1791 builder.atom("expression_start"); 1792 builder.int(expression.location.start.into()); 1793 1794 self.end_runtime_error(builder, error); 1795 } 1796 1797 fn assert_call_error<Output>( 1798 &mut self, 1799 builder: &mut impl ErlangBuilder<Output>, 1800 call: &'a TypedExpr, 1801 arguments: &[AssertionExpression<'a>], 1802 message: Option<&'a TypedExpr>, 1803 location: SrcSpan, 1804 ) { 1805 let error = self.start_runtime_error(builder, RuntimeErrorKind::Assert, location, message); 1806 1807 builder.map_field(); 1808 builder.atom("kind"); 1809 builder.atom("function_call"); 1810 1811 builder.map_field(); 1812 builder.atom("arguments"); 1813 for argument in arguments { 1814 builder.cons_list(); 1815 self.assertion_expression_map(builder, argument); 1816 } 1817 builder.empty_list(); 1818 1819 builder.map_field(); 1820 builder.atom("start"); 1821 builder.int(location.start.into()); 1822 1823 builder.map_field(); 1824 builder.atom("end"); 1825 builder.int(call.location().end.into()); 1826 1827 builder.map_field(); 1828 builder.atom("expression_start"); 1829 builder.int(call.location().start.into()); 1830 1831 self.end_runtime_error(builder, error); 1832 } 1833 1834 /// Given an expression being asserted on. This generates the code for an 1835 /// Erlang map that describes it: with a field for its kind, its value, and 1836 /// its location in the source code. 1837 fn assertion_expression_map<Output>( 1838 &mut self, 1839 builder: &mut impl ErlangBuilder<Output>, 1840 expression: &AssertionExpression<'a>, 1841 ) { 1842 let AssertionExpression { 1843 kind, 1844 runtime_value, 1845 location, 1846 } = expression; 1847 1848 let map = builder.start_map(); 1849 1850 builder.map_field(); 1851 builder.atom("kind"); 1852 builder.atom(match kind { 1853 AssertedExpressionKind::Literal => "literal", 1854 AssertedExpressionKind::Expression => "expression", 1855 AssertedExpressionKind::Unevaluated => "unevaluated", 1856 }); 1857 1858 if runtime_value.is_some() { 1859 builder.map_field(); 1860 builder.atom("value"); 1861 self.runtime_value(builder, expression); 1862 } 1863 1864 builder.map_field(); 1865 builder.atom("start"); 1866 builder.int(location.start.into()); 1867 1868 builder.map_field(); 1869 builder.atom("end"); 1870 builder.int(location.end.into()); 1871 1872 builder.end_map(map); 1873 } 1874 1875 /// This takes a value that is in an assertion (and might have been bound 1876 /// to a variable somewhere) and produces the code that will reference 1877 /// such value. 1878 /// 1879 fn runtime_value<Output>( 1880 &mut self, 1881 builder: &mut impl ErlangBuilder<Output>, 1882 expression: &AssertionExpression<'a>, 1883 ) { 1884 match expression 1885 .runtime_value 1886 .as_ref() 1887 .expect("trying to reference unevaluated assert value") 1888 { 1889 AssertedExpressionRuntimeValue::KnownBool(true) => builder.atom("true"), 1890 AssertedExpressionRuntimeValue::KnownBool(false) => builder.atom("false"), 1891 AssertedExpressionRuntimeValue::Variable(name) => builder.variable(name), 1892 AssertedExpressionRuntimeValue::Expression(expr) => { 1893 self.maybe_block_expr(builder, expr); 1894 } 1895 } 1896 } 1897 1898 fn tuple_index<Output>( 1899 &mut self, 1900 builder: &mut impl ErlangBuilder<Output>, 1901 tuple: &'a TypedExpr, 1902 index: u64, 1903 ) { 1904 let call = builder.start_remote_call(ErlangModuleName::erlang(), "element"); 1905 builder.int((index + 1).into()); 1906 self.maybe_block_expr(builder, tuple); 1907 builder.end_call(call); 1908 } 1909 1910 fn var<Output>( 1911 &mut self, 1912 builder: &mut impl ErlangBuilder<Output>, 1913 name: &'a str, 1914 constructor: &'a ValueConstructor, 1915 ) { 1916 match &constructor.variant { 1917 ValueConstructorVariant::Record { 1918 name: record_name, .. 1919 } => match constructor.type_.deref() { 1920 // We have a variable referencing a record: we are either 1921 // referencing a record constructor function, or building a 1922 // record that has no fields: 1923 // 1924 // ```gleam 1925 // type Wibble { 1926 // Wibble 1927 // Wobble(Int) 1928 // } 1929 // 1930 // pub fn main() { 1931 // Wibble 1932 // //^^^^^^ Building record with no fields 1933 // Wobble 1934 // //^^^^^^ Referencing record constructor function 1935 // } 1936 // ``` 1937 Type::Named { .. } | Type::Var { .. } | Type::Tuple { .. } => { 1938 builder.atom(&to_snake_case(record_name)); 1939 } 1940 Type::Fn { arguments, .. } => { 1941 self.record_builder_anonymous_function(builder, record_name, arguments.len()); 1942 } 1943 }, 1944 1945 ValueConstructorVariant::LocalVariable { location, .. } => { 1946 builder.variable(&self.local_var_name(location)); 1947 } 1948 1949 ValueConstructorVariant::ModuleConstant { literal, .. } => { 1950 self.inlined_constant(builder, literal); 1951 } 1952 1953 ValueConstructorVariant::ModuleFn { 1954 arity, 1955 external_erlang: Some((module, name)), 1956 .. 1957 } => { 1958 let name = escape_erlang_existing_name(name); 1959 if *module == self.module_generator.module.name { 1960 builder.function_reference(None, name, *arity); 1961 } else { 1962 builder.function_reference(Some(ErlangModuleName::new(module)), name, *arity); 1963 } 1964 } 1965 1966 ValueConstructorVariant::ModuleFn { arity, module, .. } 1967 if *module == self.module_generator.module.name => 1968 { 1969 builder.function_reference(None, escape_erlang_existing_name(name), *arity); 1970 } 1971 1972 ValueConstructorVariant::ModuleFn { 1973 arity, 1974 module, 1975 name, 1976 .. 1977 } => builder.function_reference( 1978 Some(ErlangModuleName::new(module)), 1979 escape_erlang_existing_name(name), 1980 *arity, 1981 ), 1982 } 1983 } 1984 1985 fn call<Output>( 1986 &mut self, 1987 builder: &mut impl ErlangBuilder<Output>, 1988 fun: &'a TypedExpr, 1989 arguments: &'a [TypedCallArg], 1990 ) { 1991 match how_to_call(fun) { 1992 // If we're building a record then we want to just output a 1993 // tagged tuple, there's no function call at all! 1994 FunctionCall::BuildRecord { name } => self.build_record(builder, name, arguments), 1995 // If we're calling some module function like `io.println`, `main`, 1996 // `list.map` then we can call the function using its name (and 1997 // module name if it comes from a different module). 1998 FunctionCall::Call { module, name } => { 1999 let call = if module != self.module_generator.module.name { 2000 builder.start_remote_call( 2001 ErlangModuleName::new(module), 2002 escape_erlang_existing_name(name), 2003 ) 2004 } else { 2005 let call = builder.start_call(); 2006 builder.atom(escape_erlang_existing_name(name)); 2007 builder.end_called_expression(call) 2008 }; 2009 for argument in arguments { 2010 self.maybe_block_expr(builder, &argument.value); 2011 } 2012 builder.end_call(call); 2013 } 2014 // If we're calling anything else (like an anonymous function, or 2015 // the result of another function call) we generate its code and 2016 // call that result directly. 2017 FunctionCall::DirectCall => { 2018 let call = builder.start_call(); 2019 self.maybe_block_expr(builder, fun); 2020 let call = builder.end_called_expression(call); 2021 for argument in arguments { 2022 self.maybe_block_expr(builder, &argument.value); 2023 } 2024 builder.end_call(call); 2025 } 2026 } 2027 } 2028 2029 /// This generates the code for a call that happens in an `assert`. 2030 /// For example: `assert wibble.wobble(a, b)`. 2031 /// 2032 /// This is a function separate from the regular `self.call` since the call 2033 /// arguments are not just TypedExpressions but values that might have been 2034 /// bound to variables in previous statements. 2035 /// Assert has to do it when a call is asserted so that those arguments can 2036 /// be referenced later in the error thrown at runtime! 2037 /// 2038 fn call_in_assert<Output>( 2039 &mut self, 2040 builder: &mut impl ErlangBuilder<Output>, 2041 fun: &'a TypedExpr, 2042 arguments: &[AssertionExpression<'a>], 2043 ) { 2044 match how_to_call(fun) { 2045 // What comes after `assert` has to produce a boolean, so type 2046 // checking should make it impossible to build a record here. 2047 FunctionCall::BuildRecord { .. } => { 2048 panic!("type checking should make it impossible to call a record in an assert") 2049 } 2050 2051 // If we're calling some module function like `io.println`, `main`, 2052 // `list.map` then we can call the function using its name (and 2053 // module name if it comes from a different module). 2054 FunctionCall::Call { module, name } => { 2055 let call = if module != self.module_generator.module.name { 2056 builder.start_remote_call( 2057 ErlangModuleName::new(module), 2058 escape_erlang_existing_name(name), 2059 ) 2060 } else { 2061 let call = builder.start_call(); 2062 builder.atom(escape_erlang_existing_name(name)); 2063 builder.end_called_expression(call) 2064 }; 2065 for argument in arguments { 2066 self.runtime_value(builder, argument); 2067 } 2068 builder.end_call(call); 2069 } 2070 2071 // If we're calling anything else (like an anonymous function, or 2072 // the result of another function call) we generate its code and 2073 // call that result directly. 2074 FunctionCall::DirectCall => { 2075 let call = builder.start_call(); 2076 self.maybe_block_expr(builder, fun); 2077 let call = builder.end_called_expression(call); 2078 for argument in arguments { 2079 self.runtime_value(builder, argument); 2080 } 2081 builder.end_call(call); 2082 } 2083 } 2084 } 2085 2086 /// Given a Gleam record name and the arguments it's called with, this 2087 /// generates the code to build such record. 2088 /// For example: `Wibble(1, 2)` would be `record_builder("Wibble", [1, 2])`. 2089 /// It would result in a tuple like this: `{wibble, 1, 2}`. 2090 /// 2091 /// Notice how the name you have to specify is the _Gleam name_ of the 2092 /// record. This function will take care of turning it to snake case! 2093 fn build_record<Output>( 2094 &mut self, 2095 builder: &mut impl ErlangBuilder<Output>, 2096 record_name: &str, 2097 arguments: &'a [TypedCallArg], 2098 ) { 2099 if arguments.is_empty() { 2100 builder.atom(&to_snake_case(record_name)); 2101 } else { 2102 let tuple = builder.start_tuple(); 2103 builder.atom(&to_snake_case(record_name)); 2104 for argument in arguments { 2105 self.maybe_block_expr(builder, &argument.value); 2106 } 2107 builder.end_tuple(tuple); 2108 } 2109 } 2110 2111 fn case<Output>( 2112 &mut self, 2113 builder: &mut impl ErlangBuilder<Output>, 2114 subjects: &'a [TypedExpr], 2115 clauses: &'a [TypedClause], 2116 ) { 2117 let case = builder.start_case(); 2118 2119 // If there's more than a single subject we will need to wrap those in a 2120 // tuple and start matching on tuple patterns. That's because Erlang 2121 // doesn't support matching on multiple subjects like Gleam. 2122 match subjects { 2123 [subject] => self.maybe_block_expr(builder, subject), 2124 subjects => { 2125 let tuple = builder.start_tuple(); 2126 for subject in subjects { 2127 self.maybe_block_expr(builder, subject); 2128 } 2129 builder.end_tuple(tuple); 2130 } 2131 } 2132 let case = builder.end_case_subject(case); 2133 2134 for clause in clauses { 2135 let taken_names_before_clause = self.taken_names.clone(); 2136 2137 self.clause_branch(builder, &clause.pattern, clause); 2138 2139 // Erlang doesn't support alternative patterns so we're gonna have 2140 // to turn those into separate branches! 2141 // Since those are going to have the exact same body we don't want 2142 // it to use different updated variable names. 2143 // So they should have the same scope that existed before generating 2144 // the first clause branch. 2145 // For example: 2146 // 2147 // ```gleam 2148 // case x { 2149 // 1 | 2 -> { let a = Nil } 2150 // _ -> Nil 2151 // } 2152 // ``` 2153 // 2154 // We want the generated code to look like this: 2155 // 2156 // ```erl 2157 // case x of 2158 // 1 -> A = nil; 2159 // 2 -> A = nil; 2160 // % ^ We're still using `A`, not `A@1`! 2161 // _ -> nil 2162 // end 2163 // ``` 2164 // 2165 for pattern in &clause.alternative_patterns { 2166 self.taken_names = taken_names_before_clause.clone(); 2167 self.clause_branch(builder, pattern, clause); 2168 } 2169 } 2170 2171 builder.end_case(case); 2172 } 2173 2174 /// Given a pattern and the branch it belongs to this generates an Erlang 2175 /// case clause for that pattern. 2176 fn clause_branch<Output>( 2177 &mut self, 2178 builder: &mut impl ErlangBuilder<Output>, 2179 patterns: &'a Vec<Pattern<Arc<Type>>>, 2180 clause: &'a Clause<TypedExpr, Arc<Type>>, 2181 ) { 2182 let clause_pattern = builder.start_case_clause(); 2183 2184 // We start by generating the case clause pattern. If we're matching on 2185 // multiple subjects (and so patterns has more that a single item) those 2186 // are gonna be wrapped in a tuple pattern. 2187 // That's how we match on multiple things on the Erlang target. 2188 let mut pattern_generator = PatternGenerator::new(self); 2189 match patterns.as_slice() { 2190 [pattern] => pattern_generator.pattern(builder, pattern), 2191 patterns => { 2192 let tuple = builder.start_tuple_pattern(); 2193 for pattern in patterns { 2194 pattern_generator.pattern(builder, pattern); 2195 } 2196 builder.end_tuple_pattern(tuple); 2197 } 2198 } 2199 2200 let variables_to_add_later = pattern_generator.variables_to_add_later; 2201 2202 let clause_guards = builder.end_clause_pattern(clause_pattern); 2203 if let Some(guard) = clause.guard.as_ref() { 2204 let guard_ender = builder.start_clause_guard(); 2205 self.clause_guard(builder, guard, &variables_to_add_later); 2206 builder.end_clause_guard(guard_ender); 2207 } 2208 2209 // Finally we can generate the clause body. If the clause is 2210 // followed by a single block then we want it to be a statements 2211 // sequence (and not wrapped in a begin ... end block as we usually 2212 // would when generating code for a block expression). 2213 let clause_body = builder.end_clause_guards(clause_guards); 2214 self.pattern_assignments(builder, variables_to_add_later); 2215 if let TypedExpr::Block { statements, .. } = &clause.then { 2216 self.statement_sequence(builder, statements); 2217 } else { 2218 self.expression(builder, &clause.then); 2219 } 2220 builder.end_clause_body(clause_body); 2221 } 2222 2223 /// Erlang doesn't have a special constant declaration syntax; so each Gleam 2224 /// constant is simply inlined anywhere it is used. 2225 /// 2226 /// This function produces the code of a constant expression. 2227 /// 2228 fn inlined_constant<Output>( 2229 &mut self, 2230 builder: &mut impl ErlangBuilder<Output>, 2231 literal: &'a TypedConstant, 2232 ) { 2233 match literal { 2234 Constant::Int { int_value, .. } => builder.int(int_value.clone()), 2235 Constant::Float { float_value, .. } => builder.float(float_value.value()), 2236 Constant::String { value, .. } => builder.string(value), 2237 Constant::Var { 2238 name, constructor, .. 2239 } => self.var( 2240 builder, 2241 name, 2242 constructor 2243 .as_ref() 2244 .expect("This is guaranteed to hold a value."), 2245 ), 2246 2247 Constant::Tuple { elements, .. } => { 2248 let tuple = builder.start_tuple(); 2249 for element in elements { 2250 self.inlined_constant(builder, element); 2251 } 2252 builder.end_tuple(tuple); 2253 } 2254 2255 Constant::List { elements, tail, .. } => { 2256 for element in elements { 2257 builder.cons_list(); 2258 self.inlined_constant(builder, element); 2259 } 2260 match tail { 2261 // If there's no tail we simply add an empty list cell to 2262 // end the cons list. 2263 None => builder.empty_list(), 2264 Some(tail) => match tail.list_elements() { 2265 // If there's a tail and we don't statically know the 2266 // elements it's made of, we add it as a regular Erlang 2267 // tail and it will be `[1, 2 | Tail]`. 2268 None => self.inlined_constant(builder, tail), 2269 // But if we can tell it has some fixed amount of 2270 // constant elements, then those are inlined too! 2271 Some(list_elements) => { 2272 for element in list_elements { 2273 builder.cons_list(); 2274 self.inlined_constant(builder, element); 2275 } 2276 builder.empty_list(); 2277 } 2278 }, 2279 } 2280 } 2281 2282 Constant::BitArray { segments, .. } => { 2283 let bit_array = builder.start_bit_array(); 2284 for segment in segments { 2285 self.bit_array_constant_segment(builder, segment); 2286 } 2287 builder.end_bit_array(bit_array); 2288 } 2289 2290 Constant::Record { 2291 type_, arguments, .. 2292 } => { 2293 let tag = literal 2294 .constant_record_tag() 2295 .expect("record without inferred constructor made it to code generation"); 2296 2297 match arguments { 2298 // This is a regular record call, we're building a record as 2299 // usual as a tagged tuple. 2300 Some(arguments) => { 2301 let tuple = builder.start_tuple(); 2302 builder.atom(&to_snake_case(&tag)); 2303 for argument in arguments { 2304 self.inlined_constant(builder, &argument.value); 2305 } 2306 builder.end_tuple(tuple); 2307 } 2308 // Otherwise we are either referencing a record constructor 2309 // function, or building a record that has no fields: 2310 // 2311 // ```gleam 2312 // type Wibble { 2313 // Wibble 2314 // Wobble(Int) 2315 // } 2316 // 2317 // const a = Wibble 2318 // // ^^^^^^ Building record with no fields 2319 // const b = Wobble 2320 // // ^^^^^^ Referencing record constructor function 2321 // ``` 2322 None => match type_::collapse_links(type_.clone()).deref() { 2323 Type::Named { .. } | Type::Var { .. } | Type::Tuple { .. } => { 2324 builder.atom(&to_snake_case(&tag)); 2325 } 2326 Type::Fn { arguments, .. } => { 2327 self.record_builder_anonymous_function(builder, &tag, arguments.len()); 2328 } 2329 }, 2330 } 2331 } 2332 2333 Constant::StringConcatenation { left, right, .. } => { 2334 self.constant_string_concatenate(builder, left, right); 2335 } 2336 2337 Constant::RecordUpdate { .. } => { 2338 panic!("record updates should not reach code generation") 2339 } 2340 Constant::Todo { .. } => panic!("todo constants should not reach code generation"), 2341 Constant::Invalid { .. } => { 2342 panic!("invalid constants should not reach code generation") 2343 } 2344 } 2345 } 2346 2347 fn bit_array_constant_segment<Output>( 2348 &mut self, 2349 builder: &mut impl ErlangBuilder<Output>, 2350 segment: &'a TypedConstantBitArraySegment, 2351 ) { 2352 builder.bit_array_segment(); 2353 self.inlined_constant(builder, &segment.value); 2354 match segment.size() { 2355 Some(TypedConstant::Int { int_value, .. }) if int_value.is_negative() => { 2356 builder.int(BigInt::ZERO); 2357 } 2358 Some(size) => self.inlined_constant(builder, size), 2359 None => builder.atom("default"), 2360 } 2361 self.bit_array_segment_specifiers(builder, segment); 2362 } 2363 2364 fn bit_array_segment_specifiers<Output, Expr>( 2365 &self, 2366 builder: &mut impl ErlangBuilder<Output>, 2367 segment: &'a BitArraySegment<Expr, Arc<Type>>, 2368 ) { 2369 let options = segment.options.iter(); 2370 builder.bit_array_segment_specifiers(options.filter_map(|option| match option { 2371 BitArrayOption::Utf8 { .. } | BitArrayOption::Utf8Codepoint { .. } => { 2372 Some(BitArraySegmentSpecifier::Utf8) 2373 } 2374 BitArrayOption::Utf16 { .. } | BitArrayOption::Utf16Codepoint { .. } => { 2375 Some(BitArraySegmentSpecifier::Utf16) 2376 } 2377 BitArrayOption::Utf32 { .. } | BitArrayOption::Utf32Codepoint { .. } => { 2378 Some(BitArraySegmentSpecifier::Utf32) 2379 } 2380 BitArrayOption::Int { .. } => Some(BitArraySegmentSpecifier::Integer), 2381 BitArrayOption::Float { .. } => Some(BitArraySegmentSpecifier::Float), 2382 BitArrayOption::Bytes { .. } => Some(BitArraySegmentSpecifier::Binary), 2383 BitArrayOption::Bits { .. } => Some(BitArraySegmentSpecifier::Bitstring), 2384 BitArrayOption::Signed { .. } => Some(BitArraySegmentSpecifier::Signed), 2385 BitArrayOption::Unsigned { .. } => Some(BitArraySegmentSpecifier::Unsigned), 2386 BitArrayOption::Big { .. } => Some(BitArraySegmentSpecifier::Big), 2387 BitArrayOption::Little { .. } => Some(BitArraySegmentSpecifier::Little), 2388 BitArrayOption::Native { .. } => Some(BitArraySegmentSpecifier::Native), 2389 BitArrayOption::Unit { value, .. } => Some(BitArraySegmentSpecifier::Unit(*value)), 2390 BitArrayOption::Size { .. } => None, 2391 })); 2392 } 2393 2394 fn constant_string_concatenate<Output>( 2395 &mut self, 2396 builder: &mut impl ErlangBuilder<Output>, 2397 left: &'a TypedConstant, 2398 right: &'a TypedConstant, 2399 ) { 2400 let mut items = VecDeque::new(); 2401 items.push_back(left); 2402 items.push_back(right); 2403 2404 let bit_array = builder.start_bit_array(); 2405 while let Some(segment) = items.pop_front() { 2406 match segment { 2407 // When concatenating constant strings we flatten out all 2408 // strings that are being concatenated: so that 2409 // `"a" <> "b" <> "c"` becomes a single bitstring like this: 2410 // `<<~"a", ~"b", ~"c">>` rather than nested bitstrings: 2411 // `<<<<~"a", ~"b">>/binary, ~"c">>`. 2412 // If we find a string concatenation we push its separate items 2413 // to be printed next! 2414 Constant::StringConcatenation { left, right, .. } => { 2415 items.push_front(right); 2416 items.push_front(left); 2417 continue; 2418 } 2419 // When concatenating constant strings we want all constant 2420 // variables to also be fully expanded, so that if we have 2421 // 2422 // ```gleam 2423 // const a = "one" 2424 // const b = a <> "two" 2425 // ``` 2426 // 2427 // Any use of b will be replaced with `<<~"one", ~"two">>`. 2428 Constant::Var { 2429 constructor: Some(constructor), 2430 .. 2431 } if let ValueConstructorVariant::ModuleConstant { literal, .. } = 2432 &constructor.variant => 2433 { 2434 items.push_front(literal); 2435 continue; 2436 } 2437 2438 Constant::Int { .. } 2439 | Constant::Float { .. } 2440 | Constant::String { .. } 2441 | Constant::Tuple { .. } 2442 | Constant::List { .. } 2443 | Constant::Record { .. } 2444 | Constant::RecordUpdate { .. } 2445 | Constant::BitArray { .. } 2446 | Constant::Var { .. } 2447 | Constant::Invalid { .. } 2448 | Constant::Todo { .. } => (), 2449 } 2450 2451 builder.bit_array_segment(); 2452 self.inlined_constant(builder, segment); 2453 builder.atom("default"); 2454 builder.bit_array_segment_specifiers([BitArraySegmentSpecifier::Utf8]); 2455 } 2456 builder.end_bit_array(bit_array); 2457 } 2458 2459 fn string_concatenate<Output>( 2460 &mut self, 2461 builder: &mut impl ErlangBuilder<Output>, 2462 left: &'a TypedExpr, 2463 right: &'a TypedExpr, 2464 ) { 2465 let bit_array = builder.start_bit_array(); 2466 self.string_concatenate_argument(builder, left); 2467 self.string_concatenate_argument(builder, right); 2468 builder.end_bit_array(bit_array); 2469 } 2470 2471 fn string_concatenate_argument<Output>( 2472 &mut self, 2473 builder: &mut impl ErlangBuilder<Output>, 2474 value: &'a TypedExpr, 2475 ) { 2476 // String concatenation is basically building a bit array with two 2477 // elements. Anything is going to be simply added as a `/binary` segment 2478 // with one exception: if we're dealing with a literal string that needs 2479 // the `/utf8` specifier instead! 2480 // In both cases the size is alwaus automatic, so we generate the 2481 // `default` atom. 2482 builder.bit_array_segment(); 2483 self.maybe_block_expr(builder, value); 2484 builder.atom("default"); 2485 builder.bit_array_segment_specifiers(if produces_literal_string(value) { 2486 [BitArraySegmentSpecifier::Utf8] 2487 } else { 2488 [BitArraySegmentSpecifier::Binary] 2489 }); 2490 } 2491 2492 fn binary_operator<Output>( 2493 &mut self, 2494 builder: &mut impl ErlangBuilder<Output>, 2495 name: &'a BinOp, 2496 left: &'a TypedExpr, 2497 right: &'a TypedExpr, 2498 ) { 2499 let operator = match name { 2500 BinOp::And => "andalso", 2501 BinOp::Or => "orelse", 2502 BinOp::LtInt | BinOp::LtFloat => "<", 2503 BinOp::LtEqInt | BinOp::LtEqFloat => "=<", 2504 BinOp::Eq => "=:=", 2505 BinOp::NotEq => "/=", 2506 BinOp::GtInt | BinOp::GtFloat => ">", 2507 BinOp::GtEqInt | BinOp::GtEqFloat => ">=", 2508 BinOp::AddInt | BinOp::AddFloat => "+", 2509 BinOp::SubInt | BinOp::SubFloat => "-", 2510 BinOp::MultInt | BinOp::MultFloat => "*", 2511 2512 // Division needs some extra case, in Gleam dividing by 0 results 2513 // in 0; while in Erlang that's an exception. 2514 BinOp::DivFloat => return self.float_division(builder, left, right), 2515 BinOp::DivInt => return self.int_division(builder, left, right, "div"), 2516 BinOp::RemainderInt => return self.int_division(builder, left, right, "rem"), 2517 2518 // String concatenation is not a binop at all! It's just building a 2519 // bit array. 2520 BinOp::Concatenate => return self.string_concatenate(builder, left, right), 2521 }; 2522 2523 builder.binary_operator(operator); 2524 self.maybe_block_expr(builder, left); 2525 self.maybe_block_expr(builder, right); 2526 } 2527 2528 fn float_division<Output>( 2529 &mut self, 2530 builder: &mut impl ErlangBuilder<Output>, 2531 left: &'a TypedExpr, 2532 right: &'a TypedExpr, 2533 ) { 2534 match how_to_divide(left, right) { 2535 HowToDivide::ReplaceWithZero => builder.float(0.0), 2536 HowToDivide::EvaluateLeftAndReturnZero => { 2537 // We first evaluate the left hand side, and ignore its return 2538 // value, and then we return zero directly! 2539 self.maybe_block_expr(builder, left); 2540 builder.float(0.0); 2541 } 2542 HowToDivide::PlainErlangDivision => { 2543 builder.binary_operator("/"); 2544 self.maybe_block_expr(builder, left); 2545 self.maybe_block_expr(builder, right); 2546 } 2547 HowToDivide::MatchOnRight { 2548 is_left_hand_side_pure, 2549 } => { 2550 // We first have to evaluate the left hand side, and store its 2551 // result in a variable to use later. 2552 let left_name = if !is_left_hand_side_pure { 2553 let left_name = self.new_generated_variable(); 2554 builder.match_operator(); 2555 builder.variable_pattern(&left_name); 2556 self.maybe_block_expr(builder, left); 2557 Some(left_name) 2558 } else { 2559 None 2560 }; 2561 2562 let case = builder.start_case(); 2563 self.maybe_block_expr(builder, right); 2564 let case = builder.end_case_subject(case); 2565 2566 // +0.0 -> +0.0 2567 let clause = builder.start_case_clause(); 2568 builder.float_pattern(0.0); 2569 let guards = builder.end_clause_pattern(clause); 2570 let body = builder.end_clause_guards(guards); 2571 builder.float(0.0); 2572 builder.end_clause_body(body); 2573 2574 // -0.0 -> -0.0 2575 let clause = builder.start_case_clause(); 2576 builder.float_pattern(-0.0); 2577 let guards = builder.end_clause_pattern(clause); 2578 let body = builder.end_clause_guards(guards); 2579 builder.float(-0.0); 2580 builder.end_clause_body(body); 2581 2582 // _value -> left / _value 2583 let denominator = self.new_generated_variable(); 2584 let clause = builder.start_case_clause(); 2585 builder.variable_pattern(&denominator); 2586 let guards = builder.end_clause_pattern(clause); 2587 let body = builder.end_clause_guards(guards); 2588 builder.binary_operator("/"); 2589 // If we had bound the left hand side to a variabe we just 2590 // reference it, otherwise we will generate the code for the 2591 // numerator. 2592 if let Some(left_name) = left_name { 2593 builder.variable(&left_name); 2594 } else { 2595 self.maybe_block_expr(builder, left); 2596 } 2597 builder.variable(&denominator); 2598 builder.end_clause_body(body); 2599 2600 builder.end_case(case); 2601 } 2602 } 2603 } 2604 2605 fn int_division<Output>( 2606 &mut self, 2607 builder: &mut impl ErlangBuilder<Output>, 2608 left: &'a TypedExpr, 2609 right: &'a TypedExpr, 2610 op: &'static str, 2611 ) { 2612 match how_to_divide(left, right) { 2613 HowToDivide::ReplaceWithZero => builder.int(BigInt::ZERO), 2614 HowToDivide::EvaluateLeftAndReturnZero => { 2615 // We first evaluate the left hand side, and ignore its return 2616 // value, and then we return zero directly! 2617 self.maybe_block_expr(builder, left); 2618 builder.int(BigInt::ZERO); 2619 } 2620 HowToDivide::PlainErlangDivision => { 2621 builder.binary_operator(op); 2622 self.maybe_block_expr(builder, left); 2623 self.maybe_block_expr(builder, right); 2624 } 2625 HowToDivide::MatchOnRight { 2626 is_left_hand_side_pure, 2627 } => { 2628 // If the left hand side is not a pure expression we will have 2629 // to evaluate it before the right hand side of the expression. 2630 // So we assign it to a generated variable that we will then 2631 // reference in the case expression's body. 2632 // It will look something like this: 2633 // 2634 // ```erl 2635 // _value = <left_hand_side>, 2636 // case <right_hand_side> of 2637 // 0 -> 0; 2638 // _value@1 -> _value div _value@1 2639 // end 2640 // ``` 2641 // 2642 let left_name = if !is_left_hand_side_pure { 2643 let left_name = self.new_generated_variable(); 2644 builder.match_operator(); 2645 builder.variable_pattern(&left_name); 2646 self.maybe_block_expr(builder, left); 2647 Some(left_name) 2648 } else { 2649 None 2650 }; 2651 2652 let case = builder.start_case(); 2653 self.maybe_block_expr(builder, right); 2654 let case = builder.end_case_subject(case); 2655 2656 // 0 -> 0 2657 let clause = builder.start_case_clause(); 2658 builder.int_pattern(BigInt::ZERO); 2659 let guards = builder.end_clause_pattern(clause); 2660 let body = builder.end_clause_guards(guards); 2661 builder.int(BigInt::ZERO); 2662 builder.end_clause_body(body); 2663 2664 // _value -> left div _value 2665 let denominator = self.new_generated_variable(); 2666 let clause = builder.start_case_clause(); 2667 builder.variable_pattern(&denominator); 2668 let guards = builder.end_clause_pattern(clause); 2669 let body = builder.end_clause_guards(guards); 2670 builder.binary_operator(op); 2671 // If we had bound the left hand side to a variabe we just 2672 // reference it, otherwise we will generate the code for the 2673 // numerator. 2674 if let Some(left_name) = left_name { 2675 builder.variable(&left_name); 2676 } else { 2677 self.maybe_block_expr(builder, left); 2678 } 2679 builder.variable(&denominator); 2680 builder.end_clause_body(body); 2681 2682 builder.end_case(case); 2683 } 2684 } 2685 } 2686 2687 /// This is used to print segments of a bit array expression. 2688 /// Those are different enough from the constant and pattern ones that it would 2689 /// no longer make sense to try and adapt the `bit_array_segment` generic 2690 /// function to work with the three of them. 2691 /// So you should use this one for printing expression segments, and the generic 2692 /// `bit_array_segment` function for constant and pattern segments instead. 2693 /// 2694 fn bit_array_expression_segment<Output>( 2695 &mut self, 2696 builder: &mut impl ErlangBuilder<Output>, 2697 segment: &'a TypedExprBitArraySegment, 2698 ) { 2699 // Literal strings can have the `utf8`, `utf16`, or `utf32` options just 2700 // fine, and that would be no issue on the Erlang side: 2701 // 2702 // ```erl 2703 // <<"wibble"/utf8>> 2704 // <<"wibble"/utf16>> 2705 // <<"wibble"/utf32>> 2706 // ``` 2707 // 2708 // However there's issues when we try and use those options with _variables_ 2709 // with the string type. That will result in errors on the Erlang target: 2710 // 2711 // ```erl 2712 // % These are all runtime errors!! 2713 // <<SomeString/utf8>> 2714 // <<SomeString/utf16>> 2715 // <<SomeString/utf32>> 2716 // ``` 2717 // 2718 // In Gleam we support those options for all string values, not just 2719 // literals. So we need to do something about them: 2720 // 2721 // - `utf8`: strings are already `utf8` binaries in Gleam, so if we have a 2722 // string value with that option we can put it in the bit array like any 2723 // other binary value: 2724 // ```gleam 2725 // <<some_string:utf8>> 2726 // // becomes <<SomeString/binary>> 2727 // ``` 2728 // - `utf16` and `utf32`: these are a bit tricker since they will require 2729 // some conversion (which is what we also do on the JavaScript target!). 2730 // So in this case we need to use the `unicode:characters_to_binary` 2731 // function that will return a binary value we can then put in the bit 2732 // array: 2733 // ```gleam 2734 // <<some_string:utf16-little>> 2735 // // becomes 2736 // // <<(unicode:characters_to_binary( 2737 // // SomeString, 2738 // // utf8, the current encoding 2739 // // {utf16, little}) the encoding we want 2740 // // )/binary>> 2741 // ``` 2742 // 2743 if segment.type_.is_string() 2744 && !segment.value.is_literal_string() 2745 && let Some(encoding) = expression_segment_string_encoding(segment) 2746 { 2747 let (size, endiannes) = match encoding { 2748 ExpressionSegmentStringEncoding::Utf16 { endiannes } => (16, endiannes), 2749 ExpressionSegmentStringEncoding::Utf32 { endiannes } => (32, endiannes), 2750 ExpressionSegmentStringEncoding::Utf8 => { 2751 // Gleam strings are utf8 encoded binaries, so we just need 2752 // to add the binary option and we can call it a day. 2753 builder.bit_array_segment(); 2754 self.maybe_block_expr(builder, &segment.value); 2755 builder.atom("default"); 2756 builder.bit_array_segment_specifiers([BitArraySegmentSpecifier::Binary]); 2757 return; 2758 } 2759 }; 2760 2761 builder.bit_array_segment(); 2762 2763 // For utf16 and utf32 we need an explicit conversion using erlang's 2764 // `unicode:characters_to_binary`. The segment value will be 2765 // something like this: 2766 // ```erl 2767 // unicode:characters_to_binary(<segment_value>, utf8, {utf16, big}) 2768 // ``` 2769 let call = 2770 builder.start_remote_call(ErlangModuleName::unicode(), "characters_to_binary"); 2771 { 2772 self.maybe_block_expr(builder, &segment.value); 2773 builder.atom("utf8"); 2774 let tuple = builder.start_tuple(); 2775 builder.atom(&format!("utf{size}")); 2776 match endiannes { 2777 Endianness::Big => builder.atom("big"), 2778 Endianness::Little => builder.atom("little"), 2779 } 2780 builder.end_tuple(tuple); 2781 } 2782 builder.end_call(call); 2783 2784 builder.atom("default"); 2785 builder.bit_array_segment_specifiers([BitArraySegmentSpecifier::Binary]); 2786 } else { 2787 // If the bit array segment doesn't need any special handling we use the 2788 // regular printing functions to format its value and options. 2789 builder.bit_array_segment(); 2790 self.maybe_block_expr(builder, &segment.value); 2791 self.bit_array_expression_segment_size(builder, segment); 2792 self.bit_array_segment_specifiers(builder, segment); 2793 } 2794 } 2795 2796 /// This generates the code that will produce the size expression of a bit 2797 /// array segment. 2798 /// 2799 /// Make sure to only call this when you're expected to generate a bit array 2800 /// size! 2801 fn bit_array_expression_segment_size<Output>( 2802 &mut self, 2803 builder: &mut impl ErlangBuilder<Output>, 2804 segment: &'a TypedExprBitArraySegment, 2805 ) { 2806 let Some(size) = segment.size() else { 2807 builder.atom("default"); 2808 return; 2809 }; 2810 2811 // Sizes need some care: in Erlang, having a negative segment size 2812 // results in a runtime error. We can't do that in Gleam! So any 2813 // negative value must be turned to zero instead: 2814 if let TypedExpr::Int { int_value, .. } = &size { 2815 if int_value.is_negative() { 2816 builder.int(BigInt::ZERO); 2817 } else { 2818 builder.int(int_value.clone()); 2819 } 2820 } else { 2821 let call = builder.start_remote_call(ErlangModuleName::erlang(), "max"); 2822 builder.int(BigInt::ZERO); 2823 self.maybe_block_expr(builder, size); 2824 builder.end_call(call); 2825 } 2826 } 2827 2828 fn clause_guard<Output>( 2829 &mut self, 2830 builder: &mut impl ErlangBuilder<Output>, 2831 guard: &'a TypedClauseGuard, 2832 assignments: &HashMap<EcoString, AliasedLiteral>, 2833 ) { 2834 match guard { 2835 ClauseGuard::Invalid { .. } => unreachable!("invalid guard made it to code generation"), 2836 2837 ClauseGuard::ModuleSelect { literal, .. } => self.inlined_constant(builder, literal), 2838 ClauseGuard::Constant(constant) => self.inlined_constant(builder, constant), 2839 2840 ClauseGuard::Block { value, .. } => self.clause_guard(builder, value, assignments), 2841 2842 ClauseGuard::TupleIndex { tuple, index, .. } => { 2843 self.clause_guard_tuple_index(builder, tuple, *index); 2844 } 2845 2846 ClauseGuard::FieldAccess { 2847 container, index, .. 2848 } => self.clause_guard_tuple_index( 2849 builder, 2850 container, 2851 index.expect("Unable to find index") + 1, 2852 ), 2853 ClauseGuard::Not { expression, .. } => { 2854 builder.unary_operator("not"); 2855 self.clause_guard(builder, expression, assignments); 2856 } 2857 2858 ClauseGuard::BinaryOperator { 2859 operator, 2860 left, 2861 right, 2862 .. 2863 } => { 2864 let operator = match operator { 2865 BinOp::Or => "orelse", 2866 BinOp::And => "andalso", 2867 BinOp::Eq => "=:=", 2868 BinOp::NotEq => "=/=", 2869 BinOp::GtInt | BinOp::GtFloat => ">", 2870 BinOp::GtEqInt | BinOp::GtEqFloat => ">=", 2871 BinOp::LtInt | BinOp::LtFloat => "<", 2872 BinOp::LtEqInt | BinOp::LtEqFloat => "=<", 2873 BinOp::AddInt | BinOp::AddFloat => "+", 2874 BinOp::SubInt | BinOp::SubFloat => "-", 2875 BinOp::MultInt | BinOp::MultFloat => "*", 2876 BinOp::DivFloat => "/", 2877 BinOp::DivInt => "div", 2878 BinOp::RemainderInt => "rem", 2879 BinOp::Concatenate => { 2880 return self.clause_guard_string_concatenate( 2881 builder, 2882 left, 2883 right, 2884 assignments, 2885 ); 2886 } 2887 }; 2888 2889 builder.binary_operator(operator); 2890 self.clause_guard(builder, left, assignments); 2891 self.clause_guard(builder, right, assignments); 2892 } 2893 2894 // Only local variables are supported and the typer ensures that all 2895 // ClauseGuard::Vars are local variables 2896 ClauseGuard::Var { 2897 name, 2898 definition_location, 2899 .. 2900 } => { 2901 // If we're referencing a variable introduced by an alias pattern 2902 // we need to replace it with its actual literal value: in the 2903 // generated code the variable is only defined later, so just 2904 // referencing its name would result in an error. 2905 match assignments.get(name) { 2906 Some(AliasedLiteral::String { value, .. }) => builder.string(value), 2907 Some(AliasedLiteral::Int { value, .. }) => builder.int(value.clone()), 2908 Some(AliasedLiteral::Float { value, .. }) => builder.float(value.value()), 2909 None => { 2910 builder.variable(&self.local_var_name(definition_location)); 2911 } 2912 } 2913 } 2914 } 2915 } 2916 2917 fn clause_guard_tuple_index<Output>( 2918 &mut self, 2919 builder: &mut impl ErlangBuilder<Output>, 2920 tuple: &'a TypedClauseGuard, 2921 index: u64, 2922 ) { 2923 let call = builder.start_remote_call(ErlangModuleName::erlang(), "element"); 2924 builder.int((index + 1).into()); 2925 self.clause_guard(builder, tuple, &HashMap::new()); 2926 builder.end_call(call); 2927 } 2928 2929 fn clause_guard_string_concatenate<Output>( 2930 &mut self, 2931 builder: &mut impl ErlangBuilder<Output>, 2932 left: &'a TypedClauseGuard, 2933 right: &'a TypedClauseGuard, 2934 assignments: &HashMap<EcoString, AliasedLiteral>, 2935 ) { 2936 let bit_array = builder.start_bit_array(); 2937 self.clause_guard_string_concatenate_argument(builder, left, assignments); 2938 self.clause_guard_string_concatenate_argument(builder, right, assignments); 2939 builder.end_bit_array(bit_array); 2940 } 2941 2942 fn clause_guard_string_concatenate_argument<Output>( 2943 &mut self, 2944 builder: &mut impl ErlangBuilder<Output>, 2945 guard: &'a TypedClauseGuard, 2946 assignments: &HashMap<EcoString, AliasedLiteral>, 2947 ) { 2948 // String concatenation is basically building a bit array with two 2949 // elements. Anything is going to be simply added as a `/binary` segment 2950 // with one exception: if we're dealing with a literal string that needs 2951 // the `/utf8` specifier instead! 2952 // In both cases the size is alwaus automatic, so we generate the 2953 // `default` atom. 2954 builder.bit_array_segment(); 2955 self.clause_guard(builder, guard, assignments); 2956 builder.atom("default"); 2957 builder.bit_array_segment_specifiers(if guard_produces_literal_string(guard) { 2958 [BitArraySegmentSpecifier::Utf8] 2959 } else { 2960 [BitArraySegmentSpecifier::Binary] 2961 }); 2962 } 2963 2964 /// Given a record name and the number of arguments it accepts, this outputs 2965 /// the code to generate an anonymous function that builds that record. 2966 /// 2967 /// For example, given: 2968 /// 2969 /// ```gleam 2970 /// pub type Wibble { 2971 /// Wibble(Int, String) 2972 /// } 2973 /// 2974 /// pub fn main() { 2975 /// Wibble 2976 /// //^^^^^^ This has to return the builder function! 2977 /// } 2978 /// ``` 2979 /// 2980 /// We will produce the following Erlang code: 2981 /// 2982 /// ```erl 2983 /// main() -> 2984 /// fun(_value, _value@1) -> 2985 /// {wibble, _value, _value@1} 2986 /// end. 2987 /// ``` 2988 /// 2989 fn record_builder_anonymous_function<Output>( 2990 &mut self, 2991 builder: &mut impl ErlangBuilder<Output>, 2992 record_name: &str, 2993 arguments: usize, 2994 ) { 2995 let arguments = (0..arguments) 2996 .map(|_| self.new_generated_variable()) 2997 .collect_vec(); 2998 let function = builder.start_anonymous_function(&arguments); 2999 3000 if arguments.is_empty() { 3001 builder.atom(&to_snake_case(record_name)); 3002 } else { 3003 let tuple = builder.start_tuple(); 3004 builder.atom(&to_snake_case(record_name)); 3005 for argument in arguments { 3006 builder.variable(&argument); 3007 } 3008 builder.end_tuple(tuple); 3009 } 3010 3011 builder.end_function(function); 3012 } 3013 3014 /// After generating a pattern we might have to generate additional variable 3015 /// bindings in the body following a clause pattern. 3016 /// This adds those variable to the current body. 3017 fn pattern_assignments<Output>( 3018 &mut self, 3019 builder: &mut impl ErlangBuilder<Output>, 3020 variables_to_add_later: HashMap<EcoString, AliasedLiteral>, 3021 ) { 3022 let variables_to_add_later = variables_to_add_later 3023 .into_iter() 3024 .sorted_by(|(one, _), (other, _)| one.cmp(other)); 3025 3026 for (gleam_name, value) in variables_to_add_later { 3027 builder.match_operator(); 3028 match value { 3029 AliasedLiteral::String { location, value } => { 3030 builder.variable_pattern(&self.new_erlang_variable(&gleam_name, location)); 3031 builder.string(&value); 3032 } 3033 AliasedLiteral::Float { location, value } => { 3034 builder.variable_pattern(&self.new_erlang_variable(&gleam_name, location)); 3035 builder.float(value.value()); 3036 } 3037 AliasedLiteral::Int { location, value } => { 3038 builder.variable_pattern(&self.new_erlang_variable(&gleam_name, location)); 3039 builder.int(value.clone()); 3040 } 3041 } 3042 } 3043 } 3044} 3045 3046pub fn records(module: &TypedModule) -> Vec<(&str, String)> { 3047 module 3048 .definitions 3049 .custom_types 3050 .iter() 3051 .filter(|custom_type| { 3052 custom_type.publicity.is_public() 3053 && !module 3054 .unused_definition_positions 3055 .contains(&custom_type.location.start) 3056 }) 3057 .flat_map(|custom_type| &custom_type.constructors) 3058 .filter(|constructor| !constructor.arguments.is_empty()) 3059 .filter_map(|constructor| { 3060 constructor 3061 .arguments 3062 .iter() 3063 .map( 3064 |RecordConstructorArg { 3065 label, 3066 ast: _, 3067 location: _, 3068 type_, 3069 .. 3070 }| { 3071 label 3072 .as_ref() 3073 .map(|(_, label)| (label.as_str(), type_.clone())) 3074 }, 3075 ) 3076 .collect::<Option<Vec<_>>>() 3077 .map(|fields| (constructor.name.as_str(), fields)) 3078 }) 3079 .map(|(name, fields)| (name, record_definition(name, &fields))) 3080 .collect() 3081} 3082 3083/// Given an expression, this tells us how we should be calling it as a 3084/// function in the generated erlang code. 3085fn how_to_call<'a>(function: &'a TypedExpr) -> FunctionCall<'a> { 3086 match function { 3087 // This is a record constructor from the current module. 3088 // For example: 3089 // 3090 // ```gleam 3091 // pub type Wibble { Wibble(Int) } 3092 // pub fn main() { 3093 // Wibble(1) 3094 // //^^^^^^^^^ This! 3095 // } 3096 // ``` 3097 // 3098 // On the Erlang side we have to build a tagged tuple 3099 // 3100 TypedExpr::ModuleSelect { 3101 constructor: ModuleValueConstructor::Record { name, .. }, 3102 .. 3103 } => FunctionCall::BuildRecord { name }, 3104 3105 // Notice how whenever we have a function that has an erlang 3106 // external definition we will always directly call that and not go 3107 // through the Gleam function. For example: 3108 // 3109 // ```gleam 3110 // pub fn main() { 3111 // format("hello", []) 3112 // } 3113 // 3114 // @external(erlang, "io", "format") 3115 // fn format(string: String, args: List(String)) -> Nil 3116 // ``` 3117 // 3118 // Will result in: 3119 // 3120 // ```erl 3121 // main() -> 3122 // io:format(~"hello", []). 3123 // ``` 3124 // 3125 // This enables the Erlang compiler to further optimise those calls. 3126 // 3127 TypedExpr::ModuleSelect { 3128 constructor: 3129 ModuleValueConstructor::Fn { 3130 external_erlang: Some((module, name)), 3131 .. 3132 } 3133 | ModuleValueConstructor::Fn { module, name, .. }, 3134 .. 3135 } => FunctionCall::Call { module, name }, 3136 3137 // We're calling a variable as a function. 3138 TypedExpr::Var { constructor, .. } => match &constructor.variant { 3139 // The variable is the constructor for a record. 3140 // That's a tagged tuple. 3141 ValueConstructorVariant::Record { name, .. } => FunctionCall::BuildRecord { name }, 3142 // The variable is a module function, we can call that as usual 3143 // just like we did for `TypedExpr::ModuleSelect`. 3144 ValueConstructorVariant::ModuleFn { 3145 external_erlang: Some((module, name)), 3146 .. 3147 } 3148 | ValueConstructorVariant::ModuleFn { module, name, .. } => { 3149 FunctionCall::Call { module, name } 3150 } 3151 // The variable is a variable defined inside the function, we 3152 // can call it directly: 3153 // 3154 // ```erl 3155 // SomeVariable = fun() -> ... end, 3156 // SomeVariable() 3157 // ``` 3158 ValueConstructorVariant::LocalVariable { .. } => FunctionCall::DirectCall, 3159 // The variable is a module constant, if it refers to a module 3160 // function we want to call it directly. 3161 ValueConstructorVariant::ModuleConstant { literal, .. } => { 3162 if let Constant::Var { 3163 constructor: Some(constructor), 3164 .. 3165 } = literal 3166 && let ValueConstructorVariant::ModuleFn { 3167 external_erlang: Some((module, name)), 3168 .. 3169 } 3170 | ValueConstructorVariant::ModuleFn { module, name, .. } = 3171 &constructor.variant 3172 { 3173 FunctionCall::Call { module, name } 3174 } else { 3175 FunctionCall::DirectCall 3176 } 3177 } 3178 }, 3179 3180 TypedExpr::Fn { .. } 3181 | TypedExpr::Call { .. } 3182 | TypedExpr::Todo { .. } 3183 | TypedExpr::Panic { .. } 3184 | TypedExpr::RecordAccess { .. } 3185 | TypedExpr::TupleIndex { .. } 3186 | TypedExpr::Int { .. } 3187 | TypedExpr::Float { .. } 3188 | TypedExpr::String { .. } 3189 | TypedExpr::Block { .. } 3190 | TypedExpr::Pipeline { .. } 3191 | TypedExpr::List { .. } 3192 | TypedExpr::BinOp { .. } 3193 | TypedExpr::Case { .. } 3194 | TypedExpr::PositionalAccess { .. } 3195 | TypedExpr::ModuleSelect { .. } 3196 | TypedExpr::Tuple { .. } 3197 | TypedExpr::Echo { .. } 3198 | TypedExpr::BitArray { .. } 3199 | TypedExpr::RecordUpdate { .. } 3200 | TypedExpr::NegateBool { .. } 3201 | TypedExpr::NegateInt { .. } 3202 | TypedExpr::Invalid { .. } => FunctionCall::DirectCall, 3203 } 3204} 3205 3206/// This represents the different ways a Gleam division could be turned into an 3207/// Erlang division. 3208/// In Gleam dividing by zero results in a 0, not in an exception. This means 3209/// we can't always just use the plain Erlang division operator. 3210enum HowToDivide { 3211 /// Means we can divide two expression by just doing `One / Other`. 3212 PlainErlangDivision, 3213 3214 /// The entire division can be safely replaced with a literal `0`. 3215 ReplaceWithZero, 3216 3217 /// This means the left hand side could have side effects, but then we're 3218 /// dividing it by zero, so we can just ignore its result and directly 3219 /// return 0. 3220 EvaluateLeftAndReturnZero, 3221 3222 /// This means we have to pattern match on the right hand side to make sure 3223 /// that it is not zero, otherwise we'll have to return zero, or the 3224 /// division operation would result in an exception. 3225 /// 3226 /// It will look something like this: 3227 /// 3228 /// ```erl 3229 /// case right_hand_side of 3230 /// 0 -> 0; 3231 /// _denominator -> left_hand_side / _denominator 3232 /// ``` 3233 /// 3234 MatchOnRight { is_left_hand_side_pure: bool }, 3235} 3236 3237fn how_to_divide(left: &TypedExpr, right: &TypedExpr) -> HowToDivide { 3238 if right.is_non_zero_compile_time_number() { 3239 // Right can't be zero, so it's safe to just divide! 3240 HowToDivide::PlainErlangDivision 3241 } else if left.is_pure_value_constructor() { 3242 if right.is_zero_compile_time_number() { 3243 // Left has no side effects and `right` is `0`, so we can just 3244 // replace the result with zero! 3245 HowToDivide::ReplaceWithZero 3246 } else { 3247 // Left has no side effects, but `right` could still be zero at 3248 // runtime, we need to match on it. 3249 HowToDivide::MatchOnRight { 3250 is_left_hand_side_pure: true, 3251 } 3252 } 3253 } else { 3254 // Left can have side effects, but the right hand side is zero! 3255 // In that case we have to evaluate `left`, but then we can directly 3256 // return 0. 3257 if right.is_zero_compile_time_number() { 3258 HowToDivide::EvaluateLeftAndReturnZero 3259 } else { 3260 // Otherwise we'll have to make sure things are evaluated in the 3261 // correct order. 3262 HowToDivide::MatchOnRight { 3263 is_left_hand_side_pure: false, 3264 } 3265 } 3266 } 3267} 3268 3269pub fn record_definition(record_name: &str, fields: &[(&str, Arc<Type>)]) -> String { 3270 let mut builder = ErlangSourceBuilder::new(None); 3271 3272 let attribute = builder.start_record_attribute(&to_snake_case(record_name)); 3273 3274 let type_printer = TypeGenerator::new("").var_as_any(); 3275 for (field_name, field_type) in fields { 3276 builder.record_field(); 3277 builder.atom(field_name); 3278 type_printer.type_(&mut builder, field_type); 3279 } 3280 3281 builder.end_record_attribute(attribute); 3282 builder.into_output() 3283} 3284 3285pub fn module<'a>( 3286 module: &'a TypedModule, 3287 line_numbers: &'a LineNumbers, 3288 root: &'a Utf8Path, 3289) -> String { 3290 let mut generator = Generator::new(module, line_numbers, root); 3291 let mut builder = ErlangSourceBuilder::new(Some(ErlangModuleName::new(&module.name))); 3292 generator.module_document(&mut builder); 3293 3294 let mut output = builder.into_output(); 3295 if generator.echo_used { 3296 output.push_str(std::include_str!("../templates/echo.erl")); 3297 } 3298 output 3299} 3300 3301/// If the given function should be exported from the current Erlang module then 3302/// this function will return its name and arity to be used when exporting it. 3303/// For example: `pub fn wibble(a, b)` will produce `Some(("wibble", 2))`, so 3304/// we can export `wibble/2`. 3305fn function_export<'a>( 3306 function: &'a TypedFunction, 3307 overridden_publicity: &im::HashSet<EcoString>, 3308) -> Option<(&'a str, usize)> { 3309 let (_, name) = function 3310 .name 3311 .as_ref() 3312 .expect("module function with no name"); 3313 3314 // If the function is not implemented for this target, don't attempt to 3315 // export it. 3316 if !function.implementations.supports(Target::Erlang) { 3317 return None; 3318 } 3319 3320 // If the function is not importable and it's publicity has not been 3321 // overridden, don't attempt to export it. 3322 if !function.publicity.is_importable() && !overridden_publicity.contains(name) { 3323 return None; 3324 } 3325 3326 let name = escape_erlang_existing_name(name); 3327 Some((name, function.arguments.len())) 3328} 3329 3330/// Given a custom type this returns the name it should be used to export it and 3331/// its arity. For example: `pub type Wibble(a, b)` will produce `("wibble", 2)`, 3332/// so we can export `wibble/2`. 3333fn type_export(custom_type: &TypedCustomType) -> (EcoString, usize) { 3334 let name = erl_safe_type_name(to_snake_case(&custom_type.name)); 3335 let arity = custom_type.typed_parameters.len(); 3336 (name, arity) 3337} 3338 3339/// This returns true if the given expression is going to be compiled to a 3340/// single literal Erlang string. 3341/// This is not true just for literal Gleam strings like `"abc"`, but also 3342/// variables referencing string constants (as those are inlined) 3343fn produces_literal_string(value: &TypedExpr) -> bool { 3344 match value { 3345 TypedExpr::String { .. } 3346 // Constants are inlined on the Erlang target, so we need to check if 3347 // those are literal strings too! 3348 | TypedExpr::ModuleSelect { 3349 constructor: 3350 ModuleValueConstructor::Constant { 3351 literal: Constant::String { .. }, 3352 .. 3353 }, 3354 .. 3355 } 3356 | TypedExpr::Var { 3357 constructor: 3358 ValueConstructor { 3359 variant: 3360 ValueConstructorVariant::ModuleConstant { 3361 literal: Constant::String { .. }, 3362 .. 3363 }, 3364 .. 3365 }, 3366 .. 3367 } => true, 3368 3369 TypedExpr::Int { .. } 3370 | TypedExpr::Var { .. } 3371 | TypedExpr::Float { .. } 3372 | TypedExpr::Block { .. } 3373 | TypedExpr::Pipeline { .. } 3374 | TypedExpr::Fn { .. } 3375 | TypedExpr::List { .. } 3376 | TypedExpr::Call { .. } 3377 | TypedExpr::BinOp { .. } 3378 | TypedExpr::Case { .. } 3379 | TypedExpr::RecordAccess { .. } 3380 | TypedExpr::PositionalAccess { .. } 3381 | TypedExpr::ModuleSelect { .. } 3382 | TypedExpr::Tuple { .. } 3383 | TypedExpr::TupleIndex { .. } 3384 | TypedExpr::Todo { .. } 3385 | TypedExpr::Panic { .. } 3386 | TypedExpr::Echo { .. } 3387 | TypedExpr::BitArray { .. } 3388 | TypedExpr::RecordUpdate { .. } 3389 | TypedExpr::NegateBool { .. } 3390 | TypedExpr::NegateInt { .. } 3391 | TypedExpr::Invalid { .. } => false, 3392 } 3393} 3394 3395/// This returns true if the given expression is going to be compiled to a 3396/// single literal Erlang string. 3397/// This is not true just for literal Gleam strings like `"abc"`, but also 3398/// variables referencing string constants (as those are inlined) 3399fn guard_produces_literal_string(guard: &ClauseGuard<Arc<Type>>) -> bool { 3400 match guard { 3401 ClauseGuard::Block { value, .. } => guard_produces_literal_string(value), 3402 3403 ClauseGuard::ModuleSelect { 3404 literal: Constant::String { .. }, 3405 .. 3406 } 3407 | ClauseGuard::Constant(Constant::String { .. }) => true, 3408 3409 ClauseGuard::BinaryOperator { .. } 3410 | ClauseGuard::Constant(..) 3411 | ClauseGuard::ModuleSelect { .. } 3412 | ClauseGuard::Not { .. } 3413 | ClauseGuard::Var { .. } 3414 | ClauseGuard::TupleIndex { .. } 3415 | ClauseGuard::FieldAccess { .. } 3416 | ClauseGuard::Invalid { .. } => false, 3417 } 3418} 3419 3420static ATOM_PATTERN: OnceLock<Regex> = OnceLock::new(); 3421 3422fn atom_pattern() -> &'static Regex { 3423 ATOM_PATTERN.get_or_init(|| Regex::new(r"^[a-z][a-z0-9_@]*$").expect("atom RE regex")) 3424} 3425 3426pub fn escape_atom_string(value: EcoString) -> EcoString { 3427 if is_erlang_reserved_word(&value) { 3428 // Escape because of keyword collision 3429 eco_format!("'{value}'") 3430 } else if atom_pattern().is_match(&value) { 3431 value 3432 } else { 3433 // Escape because of characters contained 3434 eco_format!("'{value}'") 3435 } 3436} 3437 3438enum ExpressionSegmentStringEncoding { 3439 Utf8, 3440 Utf16 { endiannes: Endianness }, 3441 Utf32 { endiannes: Endianness }, 3442} 3443 3444fn expression_segment_string_encoding( 3445 segment: &TypedExprBitArraySegment, 3446) -> Option<ExpressionSegmentStringEncoding> { 3447 let endiannes = segment.endianness(); 3448 segment.options.iter().find_map(|option| match option { 3449 BitArrayOption::Utf8 { .. } => Some(ExpressionSegmentStringEncoding::Utf8), 3450 BitArrayOption::Utf16 { .. } => Some(ExpressionSegmentStringEncoding::Utf16 { endiannes }), 3451 BitArrayOption::Utf32 { .. } => Some(ExpressionSegmentStringEncoding::Utf32 { endiannes }), 3452 3453 BitArrayOption::Bytes { .. } 3454 | BitArrayOption::Int { .. } 3455 | BitArrayOption::Float { .. } 3456 | BitArrayOption::Bits { .. } 3457 | BitArrayOption::Utf8Codepoint { .. } 3458 | BitArrayOption::Utf16Codepoint { .. } 3459 | BitArrayOption::Utf32Codepoint { .. } 3460 | BitArrayOption::Signed { .. } 3461 | BitArrayOption::Unsigned { .. } 3462 | BitArrayOption::Big { .. } 3463 | BitArrayOption::Little { .. } 3464 | BitArrayOption::Native { .. } 3465 | BitArrayOption::Size { .. } 3466 | BitArrayOption::Unit { .. } => None, 3467 }) 3468} 3469 3470fn needs_begin_end_wrapping(expression: &TypedExpr) -> bool { 3471 match expression { 3472 // Record updates are 1 expression if there's no assignment, multiple 3473 // otherwise. 3474 TypedExpr::RecordUpdate { 3475 updated_record_assigned_name, 3476 .. 3477 } => updated_record_assigned_name.is_some(), 3478 3479 // Pipelines are always multiple assignments. 3480 TypedExpr::Pipeline { .. } => true, 3481 3482 // Binary operations that require division might have to be turned into 3483 // multiple statements! 3484 TypedExpr::BinOp { 3485 operator: BinOp::DivFloat | BinOp::DivInt | BinOp::RemainderInt, 3486 left, 3487 right, 3488 .. 3489 } => match how_to_divide(left, right) { 3490 // In these cases we'll have to generate two statements: a variable 3491 // assignment for the left hand side, and one to return the value! 3492 HowToDivide::MatchOnRight { 3493 is_left_hand_side_pure: false, 3494 } 3495 | HowToDivide::EvaluateLeftAndReturnZero => true, 3496 // Here we just generate a single statement. 3497 HowToDivide::PlainErlangDivision 3498 | HowToDivide::ReplaceWithZero 3499 | HowToDivide::MatchOnRight { 3500 is_left_hand_side_pure: true, 3501 } => false, 3502 }, 3503 3504 TypedExpr::Int { .. } 3505 | TypedExpr::Float { .. } 3506 | TypedExpr::String { .. } 3507 | TypedExpr::Var { .. } 3508 | TypedExpr::Fn { .. } 3509 | TypedExpr::List { .. } 3510 | TypedExpr::Call { .. } 3511 | TypedExpr::BinOp { .. } 3512 | TypedExpr::Case { .. } 3513 | TypedExpr::RecordAccess { .. } 3514 | TypedExpr::PositionalAccess { .. } 3515 | TypedExpr::Block { .. } 3516 | TypedExpr::ModuleSelect { .. } 3517 | TypedExpr::Tuple { .. } 3518 | TypedExpr::TupleIndex { .. } 3519 | TypedExpr::Todo { .. } 3520 | TypedExpr::Echo { .. } 3521 | TypedExpr::Panic { .. } 3522 | TypedExpr::BitArray { .. } 3523 | TypedExpr::NegateBool { .. } 3524 | TypedExpr::NegateInt { .. } 3525 | TypedExpr::Invalid { .. } => false, 3526 } 3527} 3528 3529/// This represents an expression that appears in an expression, either because 3530/// it is part of some larger expression (like a binop: `assert a && b`, or a 3531/// call `assert wibble(wobble)`), or because it is being matched against 3532/// directly (like `assert wibble`). 3533struct AssertionExpression<'a> { 3534 /// This tells us the kind of expression we're dealing with: wether that's a 3535 /// literal, an expression that can't be known at compile time, or if it 3536 /// hasn't been evaluated at all! 3537 kind: AssertedExpressionKind, 3538 /// If the expression has been evaluated, this is going to tell us how we 3539 /// can reference its value. 3540 runtime_value: Option<AssertedExpressionRuntimeValue<'a>>, 3541 /// This is the location pointing to where in the Gleam source code this 3542 /// expression comes from. 3543 location: SrcSpan, 3544} 3545 3546impl<'a> AssertionExpression<'a> { 3547 fn from_expression(expression: &'a TypedExpr) -> Self { 3548 Self { 3549 runtime_value: Some(AssertedExpressionRuntimeValue::Expression(expression)), 3550 kind: if expression.is_literal() { 3551 AssertedExpressionKind::Literal 3552 } else { 3553 AssertedExpressionKind::Expression 3554 }, 3555 location: expression.location(), 3556 } 3557 } 3558 3559 fn was_unevaluated(mut self) -> Self { 3560 self.kind = AssertedExpressionKind::Unevaluated; 3561 self.runtime_value = None; 3562 self 3563 } 3564 3565 fn evaluated_to_bool(mut self, result: bool) -> Self { 3566 self.runtime_value = Some(AssertedExpressionRuntimeValue::KnownBool(result)); 3567 self 3568 } 3569 3570 fn from_generated_variable(name: EcoString, original_expression: &'a TypedExpr) -> Self { 3571 Self { 3572 runtime_value: Some(AssertedExpressionRuntimeValue::Variable(name)), 3573 kind: if original_expression.is_literal() { 3574 AssertedExpressionKind::Literal 3575 } else { 3576 AssertedExpressionKind::Expression 3577 }, 3578 location: original_expression.location(), 3579 } 3580 } 3581} 3582 3583/// This describes the kind of expression we're asserting against. 3584/// 3585#[derive(Debug, Clone, Copy)] 3586enum AssertedExpressionKind { 3587 /// The expression being asserted against is a literal value. For example: 3588 /// 3589 /// ```gleam 3590 /// assert True && wibble 3591 /// // ^^^^ This is a literal value. 3592 /// ``` 3593 /// 3594 Literal, 3595 /// The expression being asserted against is anything but a literal, well 3596 /// known, value. 3597 /// For example: 3598 /// 3599 /// ```gleam 3600 /// assert True && wibble 3601 /// // ^^^^^^ This is an expression. 3602 /// ``` 3603 /// 3604 Expression, 3605 /// The expression being asserted against has not been evaluated yet, 3606 /// because of some short circuiting behaviour. 3607 /// 3608 /// ```gleam 3609 /// assert False && something_else() 3610 /// // ^^^^^^^^^^^^^^^^ This will never be evaluated. 3611 /// ``` 3612 Unevaluated, 3613} 3614 3615/// This is telling us what the runtime value of some asserted value is. 3616/// Used to produce the code for such value in runtime errors if an assert 3617/// fails. 3618/// 3619/// For example: 3620/// 3621/// ```gleam 3622/// assert wibble() 3623/// ``` 3624/// 3625/// If the assertion fails we know that `wibble()` must be `false` at runtime. 3626/// It's `AssertedExpressionRuntimeValue` would be 3627/// `AssertedExpressionRuntimeValue::Bool(false)`. 3628/// 3629#[derive(Debug)] 3630enum AssertedExpressionRuntimeValue<'a> { 3631 /// We can tell that the asserted value must be a known bool. 3632 /// That's because we know wether the assertion failed (it must be false), 3633 /// or not (it must be true). 3634 KnownBool(bool), 3635 /// The asserted value was bound to a generated variable with the given 3636 /// name, and we can reference it using that name. 3637 Variable(EcoString), 3638 /// The asserted value is an expression we need to inline in the error. 3639 Expression(&'a TypedExpr), 3640} 3641 3642fn variable_name(name: &str) -> EcoString { 3643 let mut chars = name.chars(); 3644 let first_char = chars.next(); 3645 let first_uppercased = first_char.into_iter().flat_map(char::to_uppercase); 3646 first_uppercased.chain(chars).collect() 3647} 3648 3649/// When rendering a type variable to an erlang type spec we need all type 3650/// variables with the same id to end up with the same name in the generated 3651/// Erlang. 3652/// This function converts a usize into base 26 A-Z for this purpose. 3653fn id_to_type_var_str(id: u64) -> EcoString { 3654 if id < 26 { 3655 let mut name = EcoString::from(""); 3656 name.push(char::from_u32((id % 26 + 65) as u32).expect("id_to_type_var 0")); 3657 return name; 3658 } 3659 let mut name = vec![]; 3660 let mut last_char = id; 3661 while last_char >= 26 { 3662 name.push(char::from_u32((last_char % 26 + 65) as u32).expect("id_to_type_var 1")); 3663 last_char /= 26; 3664 } 3665 name.push(char::from_u32((last_char % 26 + 64) as u32).expect("id_to_type_var 2")); 3666 name.reverse(); 3667 name.into_iter().collect() 3668} 3669 3670pub fn is_erlang_reserved_word(name: &str) -> bool { 3671 match name { 3672 "!" | "receive" | "bnot" | "div" | "rem" | "band" | "bor" | "bxor" | "bsl" | "bsr" 3673 | "not" | "and" | "or" | "xor" | "orelse" | "andalso" | "when" | "end" | "fun" | "try" 3674 | "catch" | "after" | "begin" | "let" | "query" | "cond" | "if" | "of" | "case" 3675 | "maybe" | "else" => true, 3676 _ => false, 3677 } 3678} 3679 3680// Includes shell_default & user_default which are looked for by the erlang shell 3681pub fn is_erlang_standard_library_module(name: &str) -> bool { 3682 match name { 3683 "array" | "base64" | "beam_lib" | "binary" | "c" | "calendar" | "dets" | "dict" 3684 | "digraph" | "digraph_utils" | "epp" | "erl_anno" | "erl_eval" | "erl_expand_records" 3685 | "erl_id_trans" | "erl_internal" | "erl_lint" | "erl_parse" | "erl_pp" | "erl_scan" 3686 | "erl_tar" | "ets" | "file_sorter" | "filelib" | "filename" | "gb_sets" | "gb_trees" 3687 | "gen_event" | "gen_fsm" | "gen_server" | "gen_statem" | "io" | "io_lib" | "lists" 3688 | "log_mf_h" | "maps" | "math" | "ms_transform" | "orddict" | "ordsets" | "pool" 3689 | "proc_lib" | "proplists" | "qlc" | "queue" | "rand" | "random" | "re" | "sets" 3690 | "shell" | "shell_default" | "shell_docs" | "slave" | "sofs" | "string" | "supervisor" 3691 | "supervisor_bridge" | "sys" | "timer" | "unicode" | "uri_string" | "user_default" 3692 | "win32reg" | "zip" => true, 3693 _ => false, 3694 } 3695} 3696 3697// Includes the functions that are autogenerated by Erlang itself 3698pub fn escape_erlang_existing_name(name: &str) -> &str { 3699 match name { 3700 "module_info" => "moduleInfo", 3701 _ => name, 3702 } 3703} 3704 3705/// A TypeVar can either be rendered as an actual type variable such as `A` or `B`, 3706/// or it can be rendered as `any()` depending on how many usages it has. If it 3707/// has only 1 usage it is an `any()` type. If it has more than 1 usage it is a 3708/// type variable. This function gathers usages for this determination. 3709/// 3710/// Examples: 3711/// fn(a) -> String // `a` is `any()` 3712/// fn() -> Result(a, b) // `a` and `b` are `any()` 3713/// fn(a) -> a // `a` is a type var 3714fn collect_type_var_usages<'a>( 3715 mut ids: HashMap<u64, u64>, 3716 types: impl IntoIterator<Item = &'a Arc<Type>>, 3717) -> HashMap<u64, u64> { 3718 for type_ in types { 3719 type_var_ids(type_, &mut ids); 3720 } 3721 ids 3722} 3723 3724fn result_type_var_ids(ids: &mut HashMap<u64, u64>, arg_ok: &Type, arg_err: &Type) { 3725 let mut ok_ids = HashMap::new(); 3726 type_var_ids(arg_ok, &mut ok_ids); 3727 3728 let mut err_ids = HashMap::new(); 3729 type_var_ids(arg_err, &mut err_ids); 3730 3731 let mut result_counts = ok_ids; 3732 for (id, count) in err_ids { 3733 let _ = result_counts 3734 .entry(id) 3735 .and_modify(|current_count| { 3736 if *current_count < count { 3737 *current_count = count; 3738 } 3739 }) 3740 .or_insert(count); 3741 } 3742 for (id, count) in result_counts { 3743 let _ = ids 3744 .entry(id) 3745 .and_modify(|current_count| { 3746 *current_count += count; 3747 }) 3748 .or_insert(count); 3749 } 3750} 3751 3752fn type_var_ids(type_: &Type, ids: &mut HashMap<u64, u64>) { 3753 match type_ { 3754 Type::Var { type_ } => match type_.borrow().deref() { 3755 TypeVar::Generic { id, .. } | TypeVar::Unbound { id, .. } => { 3756 let count = ids.entry(*id).or_insert(0); 3757 *count += 1; 3758 } 3759 TypeVar::Link { type_ } => type_var_ids(type_, ids), 3760 }, 3761 Type::Named { 3762 arguments, 3763 module, 3764 name, 3765 .. 3766 } => match arguments[..] { 3767 [ref arg_ok, ref arg_err] if is_prelude_module(module) && name == "Result" => { 3768 result_type_var_ids(ids, arg_ok, arg_err); 3769 } 3770 _ => { 3771 for argument in arguments { 3772 type_var_ids(argument, ids); 3773 } 3774 } 3775 }, 3776 Type::Fn { arguments, return_ } => { 3777 for argument in arguments { 3778 type_var_ids(argument, ids); 3779 } 3780 type_var_ids(return_, ids); 3781 } 3782 Type::Tuple { elements } => { 3783 for element in elements { 3784 type_var_ids(element, ids); 3785 } 3786 } 3787 } 3788} 3789 3790fn erl_safe_type_name(mut name: EcoString) -> EcoString { 3791 match name.as_str() { 3792 "any" 3793 | "arity" 3794 | "atom" 3795 | "binary" 3796 | "bitstring" 3797 | "boolean" 3798 | "byte" 3799 | "char" 3800 | "dynamic" 3801 | "float" 3802 | "function" 3803 | "identifier" 3804 | "integer" 3805 | "iodata" 3806 | "iolist" 3807 | "list" 3808 | "map" 3809 | "maybe_improper_list" 3810 | "mfa" 3811 | "module" 3812 | "neg_integer" 3813 | "nil" 3814 | "no_return" 3815 | "node" 3816 | "non_neg_integer" 3817 | "none" 3818 | "nonempty_improper_list" 3819 | "nonempty_list" 3820 | "nonempty_string" 3821 | "number" 3822 | "pid" 3823 | "port" 3824 | "pos_integer" 3825 | "reference" 3826 | "string" 3827 | "term" 3828 | "timeout" 3829 | "tuple" => { 3830 name.push('_'); 3831 name 3832 } 3833 3834 _ => name, 3835 } 3836} 3837 3838#[derive(Debug)] 3839struct TypeGenerator<'a> { 3840 /// If this is true, all types that are generic or unbound are going to be 3841 /// treated as `any()`. 3842 /// 3843 var_as_any: bool, 3844 /// A TypeVar can either be rendered as an actual type variable such as `A` 3845 /// or `B`, or it can be rendered as `any()` depending on how many times it 3846 /// is used. 3847 /// If it is only ever used once, it is an `any()` type. 3848 /// If it has more than 1 usage it is a regular type variable. 3849 /// 3850 /// For example: 3851 /// 3852 /// ```gleam 3853 /// fn(a) -> String // `a` is turned into `any()` 3854 /// fn() -> Result(a, b) // `a` and `b` are turned into `any()` 3855 /// fn(a) -> a // `a` is a type var 3856 /// ``` 3857 /// 3858 /// If present, this is a map telling us from generic variable id, to number 3859 /// of times that variable is used. 3860 /// 3861 var_usages: Option<&'a HashMap<u64, u64>>, 3862 current_module: &'a str, 3863} 3864 3865impl<'a> TypeGenerator<'a> { 3866 fn new(current_module: &'a str) -> Self { 3867 Self { 3868 current_module, 3869 var_usages: None, 3870 var_as_any: false, 3871 } 3872 } 3873 3874 /// Records the how type variables are used in order to correctly print 3875 /// the type 3876 pub fn with_var_usages(mut self, var_usages: &'a HashMap<u64, u64>) -> Self { 3877 self.var_usages = Some(var_usages); 3878 self 3879 } 3880 3881 /// Print any type variable as `any()` rather than a generic type (like `A`, 3882 /// `B`, ...). 3883 fn var_as_any(mut self) -> Self { 3884 self.var_as_any = true; 3885 self 3886 } 3887 3888 pub fn type_<Output>(&self, builder: &mut impl ErlangBuilder<Output>, type_: &Type) { 3889 match type_ { 3890 Type::Var { type_ } => self.type_variable(builder, &type_.borrow()), 3891 Type::Named { 3892 name, 3893 module, 3894 arguments, 3895 .. 3896 } if is_prelude_module(module) => self.prelude_type(builder, name, arguments), 3897 Type::Named { 3898 name, 3899 module, 3900 arguments, 3901 .. 3902 } => self.named_type(builder, module.into(), name, arguments), 3903 Type::Fn { arguments, return_ } => { 3904 let function_type = builder.start_function_type(); 3905 for argument in arguments { 3906 self.type_(builder, argument); 3907 } 3908 let function_type = builder.end_function_type_arguments(function_type); 3909 self.type_(builder, return_); 3910 builder.end_function_type(function_type); 3911 } 3912 Type::Tuple { elements } => { 3913 let tuple = builder.start_tuple_type(); 3914 for element in elements { 3915 self.type_(builder, element); 3916 } 3917 builder.end_tuple_type(tuple); 3918 } 3919 } 3920 } 3921 3922 fn type_variable<Output>(&self, builder: &mut impl ErlangBuilder<Output>, type_: &TypeVar) { 3923 match type_ { 3924 TypeVar::Link { type_ } => self.type_(builder, type_), 3925 TypeVar::Generic { id, .. } | TypeVar::Unbound { id, .. } => { 3926 if self.var_as_any || self.type_variable_is_used_exactly_once(*id) { 3927 let any = builder.start_named_type("any"); 3928 builder.end_named_type(any); 3929 } else { 3930 builder.type_variable(&id_to_type_var_str(*id)); 3931 } 3932 } 3933 } 3934 } 3935 3936 /// Given a type variable id, this returns true if the `var_usages` field 3937 /// is set and the variable is used exactly once. 3938 /// 3939 #[must_use] 3940 fn type_variable_is_used_exactly_once(&self, id: u64) -> bool { 3941 match self.var_usages { 3942 Some(usages) => usages.get(&id) == Some(&1), 3943 None => false, 3944 } 3945 } 3946 3947 fn prelude_type<Output>( 3948 &self, 3949 builder: &mut impl ErlangBuilder<Output>, 3950 name: &str, 3951 arguments: &[Arc<Type>], 3952 ) { 3953 match name { 3954 "Nil" => builder.literal_atom_type("nil"), 3955 "Int" | "UtfCodepoint" => { 3956 let integer = builder.start_named_type("integer"); 3957 builder.end_named_type(integer); 3958 } 3959 "String" => { 3960 let string = builder.start_named_type("binary"); 3961 builder.end_named_type(string); 3962 } 3963 "Bool" => { 3964 let boolean = builder.start_named_type("boolean"); 3965 builder.end_named_type(boolean); 3966 } 3967 "Float" => { 3968 let float = builder.start_named_type("float"); 3969 builder.end_named_type(float); 3970 } 3971 "BitArray" => { 3972 let bitstring = builder.start_named_type("bitstring"); 3973 builder.end_named_type(bitstring); 3974 } 3975 "List" => { 3976 let list = builder.start_named_type("list"); 3977 let list_item = arguments 3978 .first() 3979 .expect("prelude type list with no argument"); 3980 self.type_(builder, list_item); 3981 builder.end_named_type(list); 3982 } 3983 "Result" => { 3984 let [ok_type, error_type] = arguments else { 3985 panic!("result type with no ok and err types") 3986 }; 3987 3988 let result = builder.start_union_type(); 3989 3990 let ok = builder.start_tuple_type(); 3991 builder.literal_atom_type("ok"); 3992 self.type_(builder, ok_type); 3993 builder.end_tuple_type(ok); 3994 3995 let error = builder.start_tuple_type(); 3996 builder.literal_atom_type("error"); 3997 self.type_(builder, error_type); 3998 builder.end_tuple_type(error); 3999 4000 builder.end_union_type(result); 4001 } 4002 4003 // Getting here should mean we either forgot a built-in type or there is a 4004 // compiler error 4005 name => panic!("{name} is not a prelude type."), 4006 } 4007 } 4008 4009 fn named_type<Output>( 4010 &self, 4011 builder: &mut impl ErlangBuilder<Output>, 4012 module: EcoString, 4013 name: &str, 4014 arguments: &[Arc<Type>], 4015 ) { 4016 let name = erl_safe_type_name(to_snake_case(name)); 4017 if self.current_module == module { 4018 let type_ = builder.start_named_type(&name); 4019 for argument in arguments { 4020 self.type_(builder, argument); 4021 } 4022 builder.end_named_type(type_); 4023 } else { 4024 let type_ = builder.start_remote_named_type(ErlangModuleName::new(&module), &name); 4025 for argument in arguments { 4026 self.type_(builder, argument); 4027 } 4028 builder.end_remote_named_type(type_); 4029 }; 4030 } 4031} 4032 4033fn find_private_functions_referenced_in_importable_constants( 4034 module: &TypedModule, 4035) -> im::HashSet<EcoString> { 4036 let mut overridden_publicity = im::HashSet::new(); 4037 4038 for constant in &module.definitions.constants { 4039 if constant.publicity.is_importable() { 4040 find_referenced_private_functions(&constant.value, &mut overridden_publicity); 4041 } 4042 } 4043 overridden_publicity 4044} 4045 4046fn find_referenced_private_functions( 4047 constant: &TypedConstant, 4048 already_found: &mut im::HashSet<EcoString>, 4049) { 4050 match constant { 4051 Constant::Todo { .. } => panic!("todo constants should not reach code generation"), 4052 Constant::Invalid { .. } => panic!("invalid constants should not reach code generation"), 4053 Constant::RecordUpdate { .. } => { 4054 panic!("record updates should not reach code generation") 4055 } 4056 4057 Constant::Int { .. } 4058 | Constant::Float { .. } 4059 | Constant::String { .. } 4060 | Constant::BitArray { .. } => (), 4061 4062 TypedConstant::Var { 4063 name, constructor, .. 4064 } => { 4065 if let Some(ValueConstructor { type_, .. }) = constructor.as_deref() 4066 && let Type::Fn { .. } = **type_ 4067 { 4068 let _ = already_found.insert(name.clone()); 4069 } 4070 } 4071 4072 TypedConstant::Record { arguments, .. } => arguments 4073 .iter() 4074 .flatten() 4075 .for_each(|argument| find_referenced_private_functions(&argument.value, already_found)), 4076 4077 TypedConstant::StringConcatenation { left, right, .. } => { 4078 find_referenced_private_functions(left, already_found); 4079 find_referenced_private_functions(right, already_found); 4080 } 4081 4082 Constant::Tuple { elements, .. } => elements 4083 .iter() 4084 .for_each(|element| find_referenced_private_functions(element, already_found)), 4085 4086 Constant::List { elements, tail, .. } => { 4087 elements 4088 .iter() 4089 .for_each(|element| find_referenced_private_functions(element, already_found)); 4090 4091 if let Some(tail) = tail { 4092 find_referenced_private_functions(tail, already_found); 4093 } 4094 } 4095 } 4096}