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