⭐️ A friendly language for building type-safe, scalable systems!
at main 30 kB view raw
1use crate::ast::{SrcSpan, TypeAst}; 2use crate::diagnostic::{ExtraLabel, Label}; 3use crate::error::wrap; 4use crate::parse::Token; 5use ecow::EcoString; 6use itertools::Itertools; 7 8#[derive(Debug, PartialEq, Eq, Clone, Copy)] 9pub struct LexicalError { 10 pub error: LexicalErrorType, 11 pub location: SrcSpan, 12} 13 14#[derive(Debug, PartialEq, Eq, Clone, Copy)] 15pub enum InvalidUnicodeEscapeError { 16 MissingOpeningBrace, // Expected '{' 17 ExpectedHexDigitOrCloseBrace, // Expected hex digit or '}' 18 InvalidNumberOfHexDigits, // Expected between 1 and 6 hex digits 19 InvalidCodepoint, // Invalid Unicode codepoint 20} 21 22#[derive(Debug, PartialEq, Eq, Clone, Copy)] 23pub enum LexicalErrorType { 24 BadStringEscape, // string contains an unescaped slash 25 InvalidUnicodeEscape(InvalidUnicodeEscapeError), // \u{...} escape sequence is invalid 26 DigitOutOfRadix, // 0x012 , 2 is out of radix 27 NumTrailingUnderscore, // 1_000_ is not allowed 28 RadixIntNoValue, // 0x, 0b, 0o without a value 29 MissingExponent, // 1.0e, for example, where there is no exponent 30 UnexpectedStringEnd, // Unterminated string literal 31 UnrecognizedToken { tok: char }, 32 InvalidTripleEqual, 33} 34 35#[derive(Debug, Clone, PartialEq, Eq)] 36pub struct ParseError { 37 pub error: ParseErrorType, 38 pub location: SrcSpan, 39} 40 41#[derive(Debug, Clone, PartialEq, Eq)] 42pub enum ParseErrorType { 43 ExpectedEqual, // expect "=" 44 ExpectedExpr, // after "->" in a case clause 45 ExpectedName, // any token used when a Name was expected 46 ExpectedPattern, // after ':' where a pattern is expected 47 ExpectedType, // after ':' or '->' where a type annotation is expected 48 ExpectedUpName, // any token used when a UpName was expected 49 ExpectedValue, // no value after "=" 50 ExpectedDefinition, // after attributes 51 ExpectedDeprecationMessage, // after "deprecated" 52 ExpectedFunctionDefinition, // after function-only attributes 53 ExpectedTargetName, // after "@target(" 54 ExprLparStart, // it seems "(" was used to start an expression 55 ExtraSeparator, // #(1,,) <- the 2nd comma is an extra separator 56 IncorrectName, // UpName or DiscardName used when Name was expected 57 IncorrectUpName, // Name or DiscardName used when UpName was expected 58 InvalidBitArraySegment, // <<7:hello>> `hello` is an invalid BitArray segment 59 InvalidBitArrayUnit, // in <<1:unit(x)>> x must be 1 <= x <= 256 60 InvalidTailPattern, // only name and _name are allowed after ".." in list pattern 61 InvalidTupleAccess, // only positive int literals for tuple access 62 LexError { 63 error: LexicalError, 64 }, 65 NestedBitArrayPattern, // <<<<1>>, 2>>, <<1>> is not allowed in there 66 NoLetBinding, // Bindings and rebinds always require let and must always bind to a value. 67 NoValueAfterEqual, // = <something other than a value> 68 NotConstType, // :fn(), name, _ are not valid const types 69 OpNakedRight, // Operator with no value to the right 70 OpaqueTypeAlias, // Type aliases cannot be opaque 71 TooManyArgHoles, // a function call can have at most 1 arg hole 72 DuplicateAttribute, // an attribute was used more than once 73 UnknownAttribute, // an attribute was used that is not known 74 UnknownTarget, // an unknown target was used 75 ListSpreadWithoutElements, // Pointless spread: `[..xs]` 76 ListSpreadFollowedByElements, // trying to append something after the spread: `[..xs, x]` 77 ListSpreadWithAnotherSpread { 78 first_spread_location: SrcSpan, 79 }, // trying to use multiple spreads: `[..xs, ..ys]` 80 LowcaseBooleanPattern, // most likely user meant True or False in patterns 81 UnexpectedLabel, // argument labels were provided, but are not supported in this context 82 UnexpectedEof, 83 UnexpectedReservedWord, // reserved word used when a name was expected 84 UnexpectedToken { 85 token: Token, 86 expected: Vec<EcoString>, 87 hint: Option<EcoString>, 88 }, 89 UnexpectedFunction, // a function was used called outside of another function 90 // A variable was assigned or discarded on the left hand side of a <> pattern 91 ConcatPatternVariableLeftHandSide, 92 ListSpreadWithoutTail, // let x = [1, ..] 93 ExpectedFunctionBody, // let x = fn() 94 RedundantInternalAttribute, // for a private definition marked as internal 95 InvalidModuleTypePattern, // for patterns that have a dot like: `name.thing` 96 ListPatternSpreadFollowedByElements, // When there is a pattern after a spread [..rest, pattern] 97 ExpectedRecordConstructor { 98 name: EcoString, 99 public: bool, 100 opaque: bool, 101 field: EcoString, 102 field_type: Option<Box<TypeAst>>, 103 }, 104 CallInClauseGuard, // case x { _ if f() -> 1 } 105 IfExpression, 106 ConstantRecordConstructorNoArguments, // const x = Record() 107 TypeConstructorNoArguments, // let a : Int() 108 TypeDefinitionNoArguments, // pub type Wibble() { ... } 109 UnknownAttributeRecordVariant, // an attribute was used that is not know for a custom type variant 110 // a Python-like import was written, such as `import gleam.io`, instead of `import gleam/io` 111 IncorrectImportModuleSeparator { 112 module: EcoString, 113 item: EcoString, 114 }, 115} 116 117pub(crate) struct ParseErrorDetails { 118 pub text: String, 119 pub label_text: EcoString, 120 pub extra_labels: Vec<ExtraLabel>, 121 pub hint: Option<String>, 122} 123 124impl ParseErrorType { 125 pub(crate) fn details(&self) -> ParseErrorDetails { 126 match self { 127 ParseErrorType::ExpectedEqual => ParseErrorDetails { 128 text: "".into(), 129 hint: None, 130 label_text: "I was expecting a '=' after this".into(), 131 extra_labels: vec![], 132 }, 133 134 ParseErrorType::ExpectedExpr => ParseErrorDetails { 135 text: "".into(), 136 hint: None, 137 label_text: "I was expecting an expression after this".into(), 138 extra_labels: vec![], 139 }, 140 141 ParseErrorType::ExpectedName => ParseErrorDetails { 142 text: "".into(), 143 hint: None, 144 label_text: "I was expecting a name here".into(), 145 extra_labels: vec![], 146 }, 147 148 ParseErrorType::ExpectedPattern => ParseErrorDetails { 149 text: "".into(), 150 hint: None, 151 label_text: "I was expecting a pattern after this".into(), 152 extra_labels: vec![], 153 }, 154 155 ParseErrorType::ExpectedType => ParseErrorDetails { 156 text: "See: https://tour.gleam.run/basics/assignments/".into(), 157 hint: None, 158 label_text: "I was expecting a type after this".into(), 159 extra_labels: vec![], 160 }, 161 162 ParseErrorType::ExpectedUpName => ParseErrorDetails { 163 text: "".into(), 164 hint: None, 165 label_text: "I was expecting a type name here".into(), 166 extra_labels: vec![], 167 }, 168 169 ParseErrorType::ExpectedValue => ParseErrorDetails { 170 text: "".into(), 171 hint: None, 172 label_text: "I was expecting a value after this".into(), 173 extra_labels: vec![], 174 }, 175 176 ParseErrorType::ExpectedDefinition => ParseErrorDetails { 177 text: "".into(), 178 hint: None, 179 label_text: "I was expecting a definition after this".into(), 180 extra_labels: vec![], 181 }, 182 183 ParseErrorType::ExpectedDeprecationMessage => ParseErrorDetails { 184 text: "".into(), 185 hint: None, 186 label_text: "A deprecation attribute must have a string message.".into(), 187 extra_labels: vec![], 188 }, 189 190 ParseErrorType::ExpectedFunctionDefinition => ParseErrorDetails { 191 text: "".into(), 192 hint: None, 193 label_text: "I was expecting a function definition after this".into(), 194 extra_labels: vec![], 195 }, 196 197 ParseErrorType::ExpectedTargetName => ParseErrorDetails { 198 text: "Try `erlang`, `javascript`.".into(), 199 hint: None, 200 label_text: "I was expecting a target name after this".into(), 201 extra_labels: vec![], 202 }, 203 204 ParseErrorType::ExtraSeparator => ParseErrorDetails { 205 text: "".into(), 206 hint: Some("Try removing it?".into()), 207 label_text: "This is an extra delimiter".into(), 208 extra_labels: vec![], 209 }, 210 211 ParseErrorType::ExprLparStart => ParseErrorDetails { 212 text: "".into(), 213 hint: Some( 214 "To group expressions in Gleam, use \"{\" and \"}\"; \ 215tuples are created with `#(` and `)`." 216 .into(), 217 ), 218 label_text: "This parenthesis cannot be understood here".into(), 219 extra_labels: vec![], 220 }, 221 222 ParseErrorType::IncorrectName => ParseErrorDetails { 223 text: "".into(), 224 hint: Some(wrap( 225 "Variable and module names start with a lowercase letter, \ 226and can contain a-z, 0-9, or _.", 227 )), 228 label_text: "I'm expecting a lowercase name here".into(), 229 extra_labels: vec![], 230 }, 231 232 ParseErrorType::IncorrectUpName => ParseErrorDetails { 233 text: "".into(), 234 hint: Some(wrap( 235 "Type names start with a uppercase letter, and can \ 236contain a-z, A-Z, or 0-9.", 237 )), 238 label_text: "I'm expecting a type name here".into(), 239 extra_labels: vec![], 240 }, 241 242 ParseErrorType::InvalidBitArraySegment => ParseErrorDetails { 243 text: "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 244 hint: Some(format!( 245 "Valid BitArray segment options are:\n{}", 246 wrap( 247 "bits, bytes, int, float, utf8, utf16, utf32, utf8_codepoint, \ 248utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, unit.", 249 ) 250 )), 251 label_text: "This is not a valid BitArray segment option".into(), 252 extra_labels: vec![], 253 }, 254 255 ParseErrorType::InvalidBitArrayUnit => ParseErrorDetails { 256 text: "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 257 hint: Some("Unit must be an integer literal >= 1 and <= 256.".into()), 258 label_text: "This is not a valid BitArray unit value".into(), 259 extra_labels: vec![], 260 }, 261 262 ParseErrorType::InvalidTailPattern => ParseErrorDetails { 263 text: "".into(), 264 hint: None, 265 label_text: "This part of a list pattern can only be a name or a discard".into(), 266 extra_labels: vec![], 267 }, 268 269 ParseErrorType::InvalidTupleAccess => ParseErrorDetails { 270 text: "".into(), 271 hint: Some( 272 "Only non negative integer literals like 0, or 1_000 can be used.".into(), 273 ), 274 label_text: "This integer is not valid for tuple access".into(), 275 extra_labels: vec![], 276 }, 277 278 ParseErrorType::LexError { error: lex_err } => { 279 let (label_text, text_lines) = lex_err.to_parse_error_info(); 280 let text = text_lines.join("\n"); 281 ParseErrorDetails { 282 text, 283 hint: None, 284 label_text: label_text.into(), 285 extra_labels: vec![], 286 } 287 } 288 289 ParseErrorType::NestedBitArrayPattern => ParseErrorDetails { 290 text: "".into(), 291 hint: None, 292 label_text: "BitArray patterns cannot be nested".into(), 293 extra_labels: vec![], 294 }, 295 296 ParseErrorType::NotConstType => ParseErrorDetails { 297 text: "See: https://tour.gleam.run/basics/constants/".into(), 298 hint: None, 299 label_text: "This type is not allowed in module constants".into(), 300 extra_labels: vec![], 301 }, 302 303 ParseErrorType::NoLetBinding => ParseErrorDetails { 304 text: "See: https://tour.gleam.run/basics/assignments/".into(), 305 hint: Some("Use let for binding.".into()), 306 label_text: "There must be a 'let' to bind variable to value".into(), 307 extra_labels: vec![], 308 }, 309 310 ParseErrorType::NoValueAfterEqual => ParseErrorDetails { 311 text: "".into(), 312 hint: None, 313 label_text: "I was expecting to see a value after this equals sign".into(), 314 extra_labels: vec![], 315 }, 316 317 ParseErrorType::OpaqueTypeAlias => ParseErrorDetails { 318 text: "See: https://tour.gleam.run/basics/type-aliases/".into(), 319 hint: None, 320 label_text: "Type Aliases cannot be opaque".into(), 321 extra_labels: vec![], 322 }, 323 324 ParseErrorType::OpNakedRight => ParseErrorDetails { 325 text: "".into(), 326 hint: Some("Remove it or put a value after it.".into()), 327 label_text: "This operator has no value on its right side".into(), 328 extra_labels: vec![], 329 }, 330 331 ParseErrorType::TooManyArgHoles => ParseErrorDetails { 332 text: "See: https://tour.gleam.run/functions/functions/".into(), 333 hint: Some("Function calls can have at most one argument hole.".into()), 334 label_text: "There is more than 1 argument hole in this function call".into(), 335 extra_labels: vec![], 336 }, 337 338 ParseErrorType::UnexpectedEof => ParseErrorDetails { 339 text: "".into(), 340 hint: None, 341 label_text: "The module ended unexpectedly".into(), 342 extra_labels: vec![], 343 }, 344 345 ParseErrorType::ListSpreadWithoutElements => ParseErrorDetails { 346 text: "See: https://tour.gleam.run/basics/lists/".into(), 347 hint: Some("Try prepending some elements [1, 2, ..list].".into()), 348 label_text: "This spread does nothing".into(), 349 extra_labels: vec![], 350 }, 351 352 ParseErrorType::ListSpreadWithAnotherSpread { 353 first_spread_location, 354 } => ParseErrorDetails { 355 text: [ 356 "Lists are immutable and singly-linked, so to join two or more lists", 357 "all the elements of the lists would need to be copied into a new list.", 358 "This would be slow, so there is no built-in syntax for it.", 359 ] 360 .join("\n"), 361 hint: None, 362 label_text: "I wasn't expecting a second spread here".into(), 363 extra_labels: vec![ExtraLabel { 364 src_info: None, 365 label: Label { 366 text: Some("You're using a spread here".into()), 367 span: *first_spread_location, 368 }, 369 }], 370 }, 371 372 ParseErrorType::ListSpreadFollowedByElements => ParseErrorDetails { 373 text: [ 374 "Lists are immutable and singly-linked, so to append items to them", 375 "all the elements of a list would need to be copied into a new list.", 376 "This would be slow, so there is no built-in syntax for it.", 377 "", 378 ] 379 .join("\n"), 380 hint: Some( 381 "Prepend items to the list and then reverse it once you are done.".into(), 382 ), 383 label_text: "I wasn't expecting elements after this".into(), 384 extra_labels: vec![], 385 }, 386 387 ParseErrorType::ListPatternSpreadFollowedByElements => ParseErrorDetails { 388 text: [ 389 "Lists are immutable and singly-linked, so to match on the end", 390 "of a list would require the whole list to be traversed. This", 391 "would be slow, so there is no built-in syntax for it. Pattern", 392 "match on the start of the list instead.", 393 ] 394 .join("\n"), 395 hint: None, 396 label_text: "I wasn't expecting elements after this".into(), 397 extra_labels: vec![], 398 }, 399 400 ParseErrorType::UnexpectedReservedWord => ParseErrorDetails { 401 text: "".into(), 402 hint: Some("I was expecting to see a name here.".into()), 403 label_text: "This is a reserved word".into(), 404 extra_labels: vec![], 405 }, 406 407 ParseErrorType::LowcaseBooleanPattern => ParseErrorDetails { 408 text: "See: https://tour.gleam.run/basics/bools/".into(), 409 hint: Some("In Gleam boolean literals are `True` and `False`.".into()), 410 label_text: "Did you want a Bool instead of a variable?".into(), 411 extra_labels: vec![], 412 }, 413 414 ParseErrorType::UnexpectedLabel => ParseErrorDetails { 415 text: "Please remove the argument label.".into(), 416 hint: None, 417 label_text: "Argument labels are not allowed for anonymous functions".into(), 418 extra_labels: vec![], 419 }, 420 421 ParseErrorType::UnexpectedToken { 422 token, 423 expected, 424 hint, 425 } => { 426 let found = match token { 427 Token::Int { .. } => "an Int".to_string(), 428 Token::Float { .. } => "a Float".to_string(), 429 Token::String { .. } => "a String".to_string(), 430 Token::CommentDoc { .. } => "a comment".to_string(), 431 Token::DiscardName { .. } => "a discard name".to_string(), 432 Token::Name { .. } | Token::UpName { .. } => "a name".to_string(), 433 _ if token.is_reserved_word() => format!("the keyword {token}"), 434 _ => token.to_string(), 435 }; 436 437 let messages = std::iter::once(format!("Found {found}, expected one of: ")) 438 .chain(expected.iter().map(|s| format!("- {s}"))); 439 440 let messages = match hint { 441 Some(hint_text) => messages 442 .chain(std::iter::once(format!("Hint: {hint_text}"))) 443 .collect_vec(), 444 _ => messages.collect(), 445 }; 446 447 ParseErrorDetails { 448 text: messages.join("\n"), 449 hint: None, 450 label_text: "I was not expecting this".into(), 451 extra_labels: vec![], 452 } 453 } 454 455 ParseErrorType::ConcatPatternVariableLeftHandSide => ParseErrorDetails { 456 text: [ 457 "We can't tell what size this prefix should be so we don't know", 458 "how to handle this pattern.", 459 "", 460 "If you want to match one character consider using `pop_grapheme`", 461 "from the stdlib's `gleam/string` module.", 462 ] 463 .join("\n"), 464 hint: None, 465 label_text: "This must be a string literal".into(), 466 extra_labels: vec![], 467 }, 468 469 ParseErrorType::UnexpectedFunction => ParseErrorDetails { 470 text: "".into(), 471 hint: None, 472 label_text: "Functions can only be called within other functions".into(), 473 extra_labels: vec![], 474 }, 475 476 ParseErrorType::ListSpreadWithoutTail => ParseErrorDetails { 477 text: "If a list expression has a spread then a tail must also be given.".into(), 478 hint: None, 479 label_text: "I was expecting a value after this spread".into(), 480 extra_labels: vec![], 481 }, 482 483 ParseErrorType::UnknownAttribute => ParseErrorDetails { 484 text: "".into(), 485 hint: Some("Try `deprecated`, `external` or `target` instead.".into()), 486 label_text: "I don't recognise this attribute".into(), 487 extra_labels: vec![], 488 }, 489 490 ParseErrorType::DuplicateAttribute => ParseErrorDetails { 491 text: "This attribute has already been given.".into(), 492 hint: None, 493 label_text: "Duplicate attribute".into(), 494 extra_labels: vec![], 495 }, 496 497 ParseErrorType::UnknownTarget => ParseErrorDetails { 498 text: "Try `erlang`, `javascript`.".into(), 499 hint: None, 500 label_text: "I don't recognise this target".into(), 501 extra_labels: vec![], 502 }, 503 504 ParseErrorType::ExpectedFunctionBody => ParseErrorDetails { 505 text: "".into(), 506 hint: None, 507 label_text: "This function does not have a body".into(), 508 extra_labels: vec![], 509 }, 510 511 ParseErrorType::RedundantInternalAttribute => ParseErrorDetails { 512 text: "Only a public definition can be annotated as internal.".into(), 513 hint: Some("Remove the `@internal` annotation.".into()), 514 label_text: "Redundant internal attribute".into(), 515 extra_labels: vec![], 516 }, 517 518 ParseErrorType::InvalidModuleTypePattern => ParseErrorDetails { 519 text: [ 520 "I'm expecting a pattern here", 521 "Hint: A pattern can be a constructor name, a literal value", 522 "or a variable to bind a value to, etc.", 523 "See: https://tour.gleam.run/flow-control/case-expressions/", 524 ] 525 .join("\n"), 526 hint: None, 527 label_text: "Invalid pattern".into(), 528 extra_labels: vec![], 529 }, 530 531 ParseErrorType::ExpectedRecordConstructor { 532 name, 533 public, 534 opaque, 535 field, 536 field_type, 537 } => { 538 let (accessor, opaque) = match *public { 539 true if *opaque => ("pub ", "opaque "), 540 true => ("pub ", ""), 541 false => ("", ""), 542 }; 543 544 let mut annotation = EcoString::new(); 545 match field_type { 546 Some(t) => t.print(&mut annotation), 547 None => annotation.push_str("Type"), 548 }; 549 550 ParseErrorDetails { 551 text: [ 552 "Each custom type variant must have a constructor:\n".into(), 553 format!("{accessor}{opaque}type {name} {{"), 554 format!(" {name}("), 555 format!(" {field}: {annotation},"), 556 " )".into(), 557 "}".into(), 558 ] 559 .join("\n"), 560 hint: None, 561 label_text: "I was not expecting this".into(), 562 extra_labels: vec![], 563 } 564 } 565 566 ParseErrorType::CallInClauseGuard => ParseErrorDetails { 567 text: "Functions cannot be called in clause guards.".into(), 568 hint: None, 569 label_text: "Unsupported expression".into(), 570 extra_labels: vec![], 571 }, 572 573 ParseErrorType::IfExpression => ParseErrorDetails { 574 text: [ 575 "If you want to write a conditional expression you can use a `case`:", 576 "", 577 " case condition {", 578 " True -> todo", 579 " False -> todo", 580 " }", 581 "", 582 "See: https://tour.gleam.run/flow-control/case-expressions/", 583 ] 584 .join("\n"), 585 hint: None, 586 label_text: "Gleam doesn't have if expressions".into(), 587 extra_labels: vec![], 588 }, 589 590 ParseErrorType::ConstantRecordConstructorNoArguments => ParseErrorDetails { 591 text: "A record must be passed arguments when constructed.".into(), 592 hint: None, 593 label_text: "I was expecting arguments here".into(), 594 extra_labels: vec![], 595 }, 596 597 ParseErrorType::TypeConstructorNoArguments => ParseErrorDetails { 598 text: "A type constructor must be passed arguments.".into(), 599 hint: None, 600 label_text: "I was expecting arguments here".into(), 601 extra_labels: vec![], 602 }, 603 604 ParseErrorType::TypeDefinitionNoArguments => ParseErrorDetails { 605 text: "A generic type must have at least a generic parameter.".into(), 606 hint: Some("If a type is not generic you should omit the `()`.".into()), 607 label_text: "I was expecting generic parameters here".into(), 608 extra_labels: vec![], 609 }, 610 611 ParseErrorType::UnknownAttributeRecordVariant => ParseErrorDetails { 612 text: "".into(), 613 hint: Some("Did you mean `@deprecated`?".into()), 614 label_text: "This attribute cannot be used on a variant.".into(), 615 extra_labels: vec![], 616 }, 617 618 ParseErrorType::IncorrectImportModuleSeparator { module, item } => ParseErrorDetails { 619 text: [ 620 "Perhaps you meant one of:".into(), 621 "".into(), 622 format!(" import {module}/{item}"), 623 format!(" import {module}.{{item}}"), 624 ] 625 .join("\n"), 626 hint: None, 627 label_text: "I was expecting either `/` or `.{` here.".into(), 628 extra_labels: vec![], 629 }, 630 } 631 } 632} 633 634impl LexicalError { 635 pub fn to_parse_error_info(&self) -> (&'static str, Vec<String>) { 636 match &self.error { 637 LexicalErrorType::BadStringEscape => ( 638 "I don't understand this escape code", 639 vec![ 640 "Hint: Add another backslash before it.".into(), 641 "See: https://tour.gleam.run/basics/strings".into(), 642 ], 643 ), 644 LexicalErrorType::DigitOutOfRadix => { 645 ("This digit is too big for the specified radix", vec![]) 646 } 647 LexicalErrorType::NumTrailingUnderscore => ( 648 "Numbers cannot have a trailing underscore", 649 vec!["Hint: remove it.".into()], 650 ), 651 LexicalErrorType::RadixIntNoValue => ("This integer has no value", vec![]), 652 LexicalErrorType::MissingExponent => ( 653 "This float is missing an exponent", 654 vec!["Hint: Add an exponent or remove the trailing `e`".into()], 655 ), 656 LexicalErrorType::UnexpectedStringEnd => { 657 ("The string starting here was left open", vec![]) 658 } 659 LexicalErrorType::UnrecognizedToken { tok } if *tok == ';' => ( 660 "Remove this semicolon", 661 vec![ 662 "Hint: Semicolons used to be whitespace and did nothing.".into(), 663 "You can safely remove them without your program changing.".into(), 664 ], 665 ), 666 LexicalErrorType::UnrecognizedToken { tok } if *tok == '\'' => ( 667 "Unexpected single quote", 668 vec!["Hint: Strings are written with double quotes.".into()], 669 ), 670 LexicalErrorType::UnrecognizedToken { .. } => ( 671 "I can't figure out what to do with this character", 672 vec!["Hint: Is it a typo?".into()], 673 ), 674 LexicalErrorType::InvalidUnicodeEscape( 675 InvalidUnicodeEscapeError::MissingOpeningBrace, 676 ) => ( 677 "Expected '{' in Unicode escape sequence", 678 vec!["Hint: Add it.".into()], 679 ), 680 LexicalErrorType::InvalidUnicodeEscape( 681 InvalidUnicodeEscapeError::ExpectedHexDigitOrCloseBrace, 682 ) => ( 683 "Expected hex digit or '}' in Unicode escape sequence", 684 vec![ 685 "Hint: Hex digits are digits from 0 to 9 and letters from a to f or A to F." 686 .into(), 687 ], 688 ), 689 LexicalErrorType::InvalidUnicodeEscape( 690 InvalidUnicodeEscapeError::InvalidNumberOfHexDigits, 691 ) => ( 692 "Expected between 1 and 6 hex digits in Unicode escape sequence", 693 vec![], 694 ), 695 LexicalErrorType::InvalidUnicodeEscape(InvalidUnicodeEscapeError::InvalidCodepoint) => { 696 ("Invalid Unicode codepoint", vec![]) 697 } 698 LexicalErrorType::InvalidTripleEqual => ( 699 "Did you mean `==`?", 700 vec![ 701 "Gleam uses `==` to check for equality between two values.".into(), 702 "See: https://tour.gleam.run/basics/equality".into(), 703 ], 704 ), 705 } 706 } 707}