1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module grimoire.compiler.lexer; 7 8 import std.stdio; 9 import std..string; 10 import std.array; 11 import std.conv; 12 import std.math; 13 import std.file; 14 import std.algorithm : canFind; 15 import grimoire.assembly; 16 import grimoire.compiler.error; 17 18 /** 19 Kinds of valid token. 20 */ 21 enum GrLexemeType { 22 leftBracket, 23 rightBracket, 24 leftParenthesis, 25 rightParenthesis, 26 leftCurlyBrace, 27 rightCurlyBrace, 28 period, 29 semicolon, 30 colon, 31 doubleColon, 32 comma, 33 at, 34 pointer, 35 as, 36 try_, 37 catch_, 38 raise_, 39 defer, 40 assign, 41 addAssign, 42 substractAssign, 43 multiplyAssign, 44 divideAssign, 45 concatenateAssign, 46 remainderAssign, 47 powerAssign, 48 plus, 49 minus, 50 add, 51 substract, 52 multiply, 53 divide, 54 concatenate, 55 remainder, 56 power, 57 equal, 58 doubleEqual, 59 threeWayComparison, 60 notEqual, 61 greaterOrEqual, 62 greater, 63 lesserOrEqual, 64 lesser, 65 leftShift, 66 rightShift, 67 and, 68 or, 69 xor, 70 not, 71 increment, 72 decrement, 73 identifier, 74 integer, 75 float_, 76 boolean, 77 string_, 78 null_, 79 public_, 80 main_, 81 type_, 82 event_, 83 class_, 84 enum_, 85 template_, 86 new_, 87 copy, 88 send, 89 receive, 90 intType, 91 floatType, 92 boolType, 93 stringType, 94 arrayType, 95 chanType, 96 functionType, 97 taskType, 98 autoType, 99 if_, 100 unless, 101 else_, 102 switch_, 103 select, 104 case_, 105 while_, 106 do_, 107 until, 108 for_, 109 loop, 110 return_, 111 self, 112 kill, 113 killAll, 114 yield, 115 break_, 116 continue_, 117 } 118 119 /** 120 Describe the smallest element found in a source _file. 121 */ 122 struct GrLexeme { 123 /// Default. 124 this(GrLexer _lexer) { 125 _line = _lexer._line; 126 _column = _lexer._current - _lexer._positionOfLine; 127 _fileId = _lexer._fileId; 128 lexer = _lexer; 129 } 130 131 private { 132 /// Parent lexer. 133 GrLexer lexer; 134 135 /// Id of the file the token is in. 136 uint _fileId; 137 138 /// Position information in case of errors. 139 uint _line, _column, _textLength = 1; 140 } 141 142 @property { 143 /// Line position 144 uint line() const { 145 return _line; 146 } 147 /// Column position 148 uint column() const { 149 return _column; 150 } 151 /// Text length 152 uint textLength() const { 153 return _textLength; 154 } 155 /// File id 156 uint fileId() const { 157 return _fileId; 158 } 159 } 160 161 /// Kind of token. 162 GrLexemeType type; 163 164 /// Whether the lexeme is a constant value. 165 bool isLiteral; 166 167 /// Whether the lexeme is an operator. 168 bool isOperator; 169 170 /// is this a reserved grimoire word ? 171 bool isKeyword; 172 173 /// Only describe first class type such as `int`, `string` or `func`. 174 /// Structure or other custom type are not. 175 bool isType; 176 177 /// Integral value of the constant. 178 /// isLiteral will be true and type set to integer. 179 GrInt ivalue; 180 181 /// Floating point value of the constant. 182 /// isLiteral will be true and type set to float_. 183 GrFloat fvalue; 184 185 /// boolean value of the constant. 186 /// isLiteral will be true and type set to boolean. 187 bool bvalue; 188 189 /// Can either describe a literal value like `"myString"` or an identifier. 190 GrString svalue; 191 192 /// Returns the entire _line from where the token is located. 193 string getLine() { 194 return lexer.getLine(this); 195 } 196 197 string getFile() { 198 return lexer.getFile(this); 199 } 200 } 201 202 /** 203 The lexer scans the entire file and all the imported files it references. 204 */ 205 package final class GrLexer { 206 private { 207 string[] _filesToImport, _filesImported; 208 string[] _lines; 209 string _file, _text; 210 uint _line, _current, _positionOfLine, _fileId; 211 GrLexeme[] _lexemes; 212 } 213 214 @property { 215 /// Generated tokens. 216 GrLexeme[] lexemes() { 217 return _lexemes; 218 } 219 } 220 221 /// Start scanning the root file and all its dependencies. 222 void scanFile(string fileName) { 223 import std.path : buildNormalizedPath, absolutePath; 224 225 string filePath = to!string(fileName); 226 filePath = buildNormalizedPath(convertPathToImport(filePath)); 227 filePath = absolutePath(filePath); 228 fileName = to!string(filePath); 229 230 _filesToImport ~= fileName; 231 232 while (_filesToImport.length) { 233 _file = _filesToImport[$ - 1]; 234 _filesImported ~= _file; 235 _text = to!string(readText(to!string(_file))); 236 _filesToImport.length--; 237 238 _line = 0u; 239 _current = 0u; 240 _positionOfLine = 0u; 241 _lines = split(_text, "\n"); 242 243 scanScript(); 244 245 _fileId++; 246 } 247 } 248 249 /** 250 Fetch the entire line where a lexeme is. 251 */ 252 package string getLine(GrLexeme lex) { 253 if (lex._fileId >= _filesImported.length) 254 raiseError("Lexeme file id out of bounds"); 255 auto _text = to!string(readText(to!string(_filesImported[lex._fileId]))); 256 _lines = split(_text, "\n"); 257 if (lex._line >= _lines.length) 258 raiseError("Lexeme line count out of bounds"); 259 return _lines[lex._line]; 260 } 261 262 /** 263 Fetch the file where a lexeme is. 264 */ 265 package string getFile(GrLexeme lex) { 266 if (lex._fileId >= _filesImported.length) 267 raiseError("Lexeme file id out of bounds"); 268 return _filesImported[lex._fileId]; 269 } 270 /// Ditto 271 package string getFile(size_t fileId) { 272 if (fileId >= _filesImported.length) 273 raiseError("file id out of bounds"); 274 return _filesImported[fileId]; 275 } 276 277 private dchar get(int offset = 0) { 278 const uint position = to!int(_current) + offset; 279 if (position < 0 || position >= _text.length) 280 raiseError("Unexpected end of script"); 281 return _text[position]; 282 } 283 284 /// Advance the current character pointer and skips whitespaces and comments. 285 private bool advance(bool startFromCurrent = false) { 286 if (!startFromCurrent) 287 _current++; 288 289 if (_current >= _text.length) 290 return false; 291 292 dchar symbol = _text[_current]; 293 294 whileLoop: while (symbol <= 0x20 || symbol == '/' || symbol == '#') { 295 if (_current >= _text.length) 296 return false; 297 298 symbol = _text[_current]; 299 300 if (symbol == '\n') { 301 _positionOfLine = _current; 302 _line++; 303 } 304 else if (symbol == '#') { 305 do { 306 if (_current >= _text.length) 307 return false; 308 _current++; 309 } 310 while (_text[_current] != '\n'); 311 _positionOfLine = _current; 312 _line++; 313 } 314 else if (symbol == '/') { 315 if ((_current + 1) >= _text.length) 316 return false; 317 318 switch (_text[_current + 1]) { 319 case '/': 320 do { 321 if (_current >= _text.length) 322 return false; 323 _current++; 324 } 325 while (_current < _text.length && _text[_current] != '\n'); 326 _positionOfLine = _current; 327 _line++; 328 break; 329 case '*': 330 for (;;) { 331 if ((_current + 1) >= _text.length) { 332 _current++; 333 return false; 334 } 335 336 if (_text[_current] == '\n') { 337 _positionOfLine = _current; 338 _line++; 339 } 340 341 if (_text[_current] == '*' && _text[_current + 1] == '/') { 342 _current++; 343 break; 344 } 345 346 _current++; 347 } 348 349 break; 350 default: 351 break whileLoop; 352 } 353 } 354 _current++; 355 356 if (_current >= _text.length) 357 return false; 358 359 symbol = _text[_current]; 360 } 361 return true; 362 } 363 364 /// Scan the content of a single file. 365 private void scanScript() { 366 //Skip the first escape characters. 367 advance(true); 368 369 do { 370 if (_current >= _text.length) 371 break; 372 switch (get()) { 373 case '0': .. case '9': 374 scanNumber(); 375 break; 376 case '.': 377 if (get(1) >= '0' && get(1) <= '9') 378 scanNumber(); 379 else 380 goto case '!'; 381 break; 382 case '!': 383 case '#': .. case '&': 384 case '(': .. case '-': 385 case '/': 386 case ':': 387 .. case '@': 388 case '[': .. case '^': 389 case '{': .. case '~': 390 scanOperator(); 391 break; 392 case '\"': 393 scanString(); 394 break; 395 default: 396 scanWord(); 397 break; 398 } 399 } 400 while (advance()); 401 } 402 403 /** 404 Scan either a integer or a floating point number. \ 405 Floats can start with a `.` \ 406 A number finishing with `f` will be scanned as a float. \ 407 Underscores `_` are ignored inside a number. 408 */ 409 private void scanNumber() { 410 GrLexeme lex = GrLexeme(this); 411 lex.isLiteral = true; 412 413 bool isFloat; 414 string buffer; 415 for (;;) { 416 dchar symbol = get(); 417 418 if (symbol >= '0' && symbol <= '9') 419 buffer ~= symbol; 420 else if (symbol == '_') { 421 // Do nothing, only cosmetic (e.g. 1_000_000). 422 } 423 else if (symbol == '.') { 424 if (isFloat) 425 break; 426 isFloat = true; 427 buffer ~= symbol; 428 } 429 else if (symbol == 'f') { 430 isFloat = true; 431 break; 432 } 433 else { 434 if (_current) 435 _current--; 436 break; 437 } 438 439 _current++; 440 441 if (_current >= _text.length) 442 break; 443 } 444 445 lex._textLength = cast(uint) buffer.length; 446 447 if (isFloat) { 448 lex.type = GrLexemeType.float_; 449 lex.fvalue = to!GrFloat(buffer); 450 } 451 else { 452 lex.type = GrLexemeType.integer; 453 lex.ivalue = to!GrInt(buffer); 454 } 455 _lexemes ~= lex; 456 } 457 458 /** 459 Scan a `"` delimited string. 460 */ 461 void scanString() { 462 GrLexeme lex = GrLexeme(this); 463 lex.type = GrLexemeType.string_; 464 lex.isLiteral = true; 465 466 if (get() != '\"') 467 raiseError("Expected \'\"\' at the beginning of the string."); 468 _current++; 469 470 string buffer; 471 bool escape = false; 472 bool wasEscape = false; 473 for (;;) { 474 if (_current >= _text.length) 475 raiseError("Missing \'\"\' character."); 476 const dchar symbol = get(); 477 478 if (symbol == '\n') { 479 _positionOfLine = _current; 480 _line++; 481 } 482 else if (symbol == '\"' && (!wasEscape)) 483 break; 484 else if (symbol == '\\' && (!wasEscape)) { 485 escape = true; 486 } 487 488 if (!escape) { 489 if (!wasEscape) { 490 buffer ~= symbol; 491 } 492 else { 493 if (symbol == 'n') 494 buffer ~= '\n'; 495 else 496 buffer ~= symbol; 497 } 498 } 499 wasEscape = escape; 500 escape = false; 501 502 _current++; 503 } 504 505 lex._textLength = cast(uint) buffer.length + 2u; 506 lex.svalue = buffer; 507 _lexemes ~= lex; 508 } 509 510 /** 511 Scan a symbol-based operator. 512 */ 513 private void scanOperator() { 514 GrLexeme lex = GrLexeme(this); 515 lex.isOperator = true; 516 517 switch (get()) { 518 case '{': 519 lex.type = GrLexemeType.leftCurlyBrace; 520 break; 521 case '}': 522 lex.type = GrLexemeType.rightCurlyBrace; 523 break; 524 case '(': 525 lex.type = GrLexemeType.leftParenthesis; 526 break; 527 case ')': 528 lex.type = GrLexemeType.rightParenthesis; 529 break; 530 case '[': 531 lex.type = GrLexemeType.leftBracket; 532 break; 533 case ']': 534 lex.type = GrLexemeType.rightBracket; 535 break; 536 case '.': 537 lex.type = GrLexemeType.period; 538 break; 539 case ';': 540 lex.type = GrLexemeType.semicolon; 541 break; 542 case ':': 543 lex.type = GrLexemeType.colon; 544 if (_current + 1 >= _text.length) 545 break; 546 if (get(1) == ':') { 547 lex.type = GrLexemeType.doubleColon; 548 lex._textLength = 2; 549 _current++; 550 } 551 break; 552 case ',': 553 lex.type = GrLexemeType.comma; 554 break; 555 case '^': 556 lex.type = GrLexemeType.power; 557 if (_current + 1 >= _text.length) 558 break; 559 if (get(1) == '=') { 560 lex.type = GrLexemeType.powerAssign; 561 lex._textLength = 2; 562 _current++; 563 } 564 break; 565 case '@': 566 lex.type = GrLexemeType.at; 567 break; 568 case '&': 569 lex.type = GrLexemeType.pointer; 570 break; 571 case '~': 572 lex.type = GrLexemeType.concatenate; 573 if (_current + 1 >= _text.length) 574 break; 575 if (get(1) == '=') { 576 lex.type = GrLexemeType.concatenateAssign; 577 lex._textLength = 2; 578 _current++; 579 } 580 break; 581 case '+': 582 lex.type = GrLexemeType.add; 583 if (_current + 1 >= _text.length) 584 break; 585 switch (get(1)) { 586 case '=': 587 lex.type = GrLexemeType.addAssign; 588 lex._textLength = 2; 589 _current++; 590 break; 591 case '+': 592 lex.type = GrLexemeType.increment; 593 lex._textLength = 2; 594 _current++; 595 break; 596 default: 597 break; 598 } 599 break; 600 case '-': 601 lex.type = GrLexemeType.substract; 602 if (_current + 1 >= _text.length) 603 break; 604 switch (get(1)) { 605 case '=': 606 lex.type = GrLexemeType.substractAssign; 607 lex._textLength = 2; 608 _current++; 609 break; 610 case '-': 611 lex.type = GrLexemeType.decrement; 612 lex._textLength = 2; 613 _current++; 614 break; 615 default: 616 break; 617 } 618 break; 619 case '*': 620 lex.type = GrLexemeType.multiply; 621 if (_current + 1 >= _text.length) 622 break; 623 if (get(1) == '=') { 624 lex.type = GrLexemeType.multiplyAssign; 625 lex._textLength = 2; 626 _current++; 627 } 628 break; 629 case '/': 630 lex.type = GrLexemeType.divide; 631 if (_current + 1 >= _text.length) 632 break; 633 if (get(1) == '=') { 634 lex.type = GrLexemeType.divideAssign; 635 lex._textLength = 2; 636 _current++; 637 } 638 break; 639 case '%': 640 lex.type = GrLexemeType.remainder; 641 if (_current + 1 >= _text.length) 642 break; 643 if (get(1) == '=') { 644 lex.type = GrLexemeType.remainderAssign; 645 lex._textLength = 2; 646 _current++; 647 } 648 break; 649 case '=': 650 lex.type = GrLexemeType.assign; 651 if (_current + 1 >= _text.length) 652 break; 653 if (get(1) == '=') { 654 lex.type = GrLexemeType.equal; 655 lex._textLength = 2; 656 _current++; 657 if (_current + 1 >= _text.length) 658 break; 659 if (get(1) == '=') { 660 lex.type = GrLexemeType.doubleEqual; 661 lex._textLength = 3; 662 _current++; 663 } 664 } 665 break; 666 case '<': 667 lex.type = GrLexemeType.lesser; 668 if (_current + 1 >= _text.length) 669 break; 670 if (get(1) == '=') { 671 lex.type = GrLexemeType.lesserOrEqual; 672 lex._textLength = 2; 673 _current++; 674 if (_current + 1 >= _text.length) 675 break; 676 if (get(1) == '>') { 677 lex.type = GrLexemeType.threeWayComparison; 678 lex._textLength = 3; 679 _current++; 680 } 681 } 682 else if (get(1) == '-') { 683 lex.type = GrLexemeType.send; 684 lex._textLength = 2; 685 _current++; 686 } 687 else if (get(1) == '<') { 688 lex.type = GrLexemeType.leftShift; 689 lex._textLength = 2; 690 _current++; 691 } 692 break; 693 case '>': 694 lex.type = GrLexemeType.greater; 695 if (_current + 1 >= _text.length) 696 break; 697 if (get(1) == '=') { 698 lex.type = GrLexemeType.greaterOrEqual; 699 lex._textLength = 2; 700 _current++; 701 } 702 else if (get(1) == '>') { 703 lex.type = GrLexemeType.rightShift; 704 lex._textLength = 2; 705 _current++; 706 } 707 break; 708 case '!': 709 lex.type = GrLexemeType.not; 710 if (_current + 1 >= _text.length) 711 break; 712 if (get(1) == '=') { 713 lex.type = GrLexemeType.notEqual; 714 lex._textLength = 2; 715 _current++; 716 } 717 break; 718 default: 719 raiseError("GrLexer: invalid operator"); 720 } 721 722 _lexemes ~= lex; 723 } 724 725 /** 726 Scan a known keyword or an identifier otherwise. 727 */ 728 private void scanWord() { 729 GrLexeme lex = GrLexeme(this); 730 lex.isKeyword = true; 731 732 string buffer; 733 for (;;) { 734 if (_current >= _text.length) 735 break; 736 737 const dchar symbol = get(); 738 if (symbol == '!' || symbol == '?') { 739 buffer ~= symbol; 740 _current++; 741 break; 742 } 743 if (symbol <= '&' || (symbol >= '(' && symbol <= '/') || (symbol >= ':' 744 && symbol <= '@') || (symbol >= '[' && symbol <= '^') 745 || (symbol >= '{' && symbol <= 0x7F)) 746 break; 747 748 buffer ~= symbol; 749 _current++; 750 } 751 _current--; 752 753 lex._textLength = cast(uint) buffer.length; 754 755 switch (buffer) { 756 case "use": 757 scanUse(); 758 return; 759 case "pub": 760 lex.type = GrLexemeType.public_; 761 break; 762 case "main": 763 lex.type = GrLexemeType.main_; 764 break; 765 case "type": 766 lex.type = GrLexemeType.type_; 767 break; 768 case "event": 769 lex.type = GrLexemeType.event_; 770 break; 771 case "class": 772 lex.type = GrLexemeType.class_; 773 break; 774 case "enum": 775 lex.type = GrLexemeType.enum_; 776 break; 777 case "template": 778 lex.type = GrLexemeType.template_; 779 break; 780 case "if": 781 lex.type = GrLexemeType.if_; 782 break; 783 case "unless": 784 lex.type = GrLexemeType.unless; 785 break; 786 case "else": 787 lex.type = GrLexemeType.else_; 788 break; 789 case "switch": 790 lex.type = GrLexemeType.switch_; 791 break; 792 case "select": 793 lex.type = GrLexemeType.select; 794 break; 795 case "case": 796 lex.type = GrLexemeType.case_; 797 break; 798 case "while": 799 lex.type = GrLexemeType.while_; 800 break; 801 case "do": 802 lex.type = GrLexemeType.do_; 803 break; 804 case "until": 805 lex.type = GrLexemeType.until; 806 break; 807 case "for": 808 lex.type = GrLexemeType.for_; 809 break; 810 case "loop": 811 lex.type = GrLexemeType.loop; 812 break; 813 case "return": 814 lex.type = GrLexemeType.return_; 815 break; 816 case "self": 817 lex.type = GrLexemeType.self; 818 break; 819 case "kill": 820 lex.type = GrLexemeType.kill; 821 break; 822 case "killall": 823 lex.type = GrLexemeType.killAll; 824 break; 825 case "yield": 826 lex.type = GrLexemeType.yield; 827 break; 828 case "break": 829 lex.type = GrLexemeType.break_; 830 break; 831 case "continue": 832 lex.type = GrLexemeType.continue_; 833 break; 834 case "as": 835 lex.type = GrLexemeType.as; 836 break; 837 case "try": 838 lex.type = GrLexemeType.try_; 839 break; 840 case "catch": 841 lex.type = GrLexemeType.catch_; 842 break; 843 case "raise": 844 lex.type = GrLexemeType.raise_; 845 break; 846 case "defer": 847 lex.type = GrLexemeType.defer; 848 break; 849 case "task": 850 lex.type = GrLexemeType.taskType; 851 lex.isType = true; 852 break; 853 case "func": 854 lex.type = GrLexemeType.functionType; 855 lex.isType = true; 856 break; 857 case "int": 858 lex.type = GrLexemeType.intType; 859 lex.isType = true; 860 break; 861 case "float": 862 lex.type = GrLexemeType.floatType; 863 lex.isType = true; 864 break; 865 case "bool": 866 lex.type = GrLexemeType.boolType; 867 lex.isType = true; 868 break; 869 case "string": 870 lex.type = GrLexemeType.stringType; 871 lex.isType = true; 872 break; 873 case "array": 874 lex.type = GrLexemeType.arrayType; 875 lex.isType = true; 876 break; 877 case "chan": 878 lex.type = GrLexemeType.chanType; 879 lex.isType = true; 880 break; 881 case "new": 882 lex.type = GrLexemeType.new_; 883 lex.isType = false; 884 break; 885 case "let": 886 lex.type = GrLexemeType.autoType; 887 lex.isType = false; 888 break; 889 case "true": 890 lex.type = GrLexemeType.boolean; 891 lex.isKeyword = false; 892 lex.isLiteral = true; 893 lex.bvalue = true; 894 break; 895 case "false": 896 lex.type = GrLexemeType.boolean; 897 lex.isKeyword = false; 898 lex.isLiteral = true; 899 lex.bvalue = false; 900 break; 901 case "null": 902 lex.type = GrLexemeType.null_; 903 lex.isKeyword = false; 904 lex.isLiteral = true; 905 break; 906 case "not": 907 lex.type = GrLexemeType.not; 908 lex.isKeyword = false; 909 lex.isOperator = true; 910 break; 911 case "and": 912 lex.type = GrLexemeType.and; 913 lex.isKeyword = false; 914 lex.isOperator = true; 915 break; 916 case "or": 917 lex.type = GrLexemeType.or; 918 lex.isKeyword = false; 919 lex.isOperator = true; 920 break; 921 case "xor": 922 lex.type = GrLexemeType.xor; 923 lex.isKeyword = false; 924 lex.isOperator = true; 925 break; 926 default: 927 lex.isKeyword = false; 928 lex.type = GrLexemeType.identifier; 929 lex.svalue = buffer; 930 break; 931 } 932 933 _lexemes ~= lex; 934 } 935 936 /// Transform the path in your path system. 937 private string convertPathToImport(string path) { 938 import std.regex : replaceAll, regex; 939 import std.path : dirSeparator; 940 941 return replaceAll(path, regex(r"\\/|/|\\"), dirSeparator); 942 } 943 944 /// add a single _file path delimited by `" "` to the import list. 945 private void scanFilePath() { 946 import std.path : dirName, buildNormalizedPath, absolutePath; 947 948 if (get() != '\"') 949 raiseError("Expected \'\"\' at the beginning of the import."); 950 _current++; 951 952 string buffer; 953 for (;;) { 954 if (_current >= _text.length) 955 raiseError("Missing \'\"\' character."); 956 const dchar symbol = get(); 957 if (symbol == '\n') { 958 _positionOfLine = _current; 959 _line++; 960 } 961 else if (symbol == '\"') 962 break; 963 964 buffer ~= symbol; 965 _current++; 966 } 967 string filePath = to!string(buffer); 968 filePath = buildNormalizedPath(dirName(to!string(_file)), convertPathToImport(filePath)); 969 filePath = absolutePath(filePath); 970 buffer = to!string(filePath); 971 if (_filesImported.canFind(buffer) || _filesToImport.canFind(buffer)) 972 return; 973 _filesToImport ~= buffer; 974 } 975 976 /// Scan a `use` directive. \ 977 /// Syntax: \ 978 /// `use "FILEPATH"` or \ 979 /// `use { "FILEPATH1", "FILEPATH2", "FILEPATH3" }` \ 980 /// ___ 981 /// add a file to the list of files to import. 982 private void scanUse() { 983 advance(); 984 985 // Multiple files import. 986 if (get() == '{') { 987 advance(); 988 bool isFirst = true; 989 for (;;) { 990 if (isFirst) 991 isFirst = false; 992 else if (get() == '\"') 993 advance(); 994 else 995 raiseError("Missing \'\"\' character."); 996 // EOF 997 if (_current >= _text.length) 998 raiseError("Missing \'}\' after import list."); 999 // End of the import list. 1000 if (get() == '}') 1001 break; 1002 // Scan 1003 scanFilePath(); 1004 } 1005 } 1006 else { 1007 scanFilePath(); 1008 } 1009 } 1010 1011 /** 1012 Lexical error 1013 */ 1014 private void raiseError(string message) { 1015 GrError error = new GrError; 1016 error.type = GrError.Type.lexer; 1017 1018 error.message = message; 1019 error.info = ""; 1020 1021 if (_lexemes.length) { 1022 GrLexeme lexeme = _lexemes[$ - 1]; 1023 error.filePath = to!string(lexeme.getFile()); 1024 error.lineText = to!string(lexeme.getLine()).replace("\t", " "); 1025 error.line = lexeme._line + 1u; // By convention, the first line is 1, not 0. 1026 error.column = lexeme._column; 1027 error.textLength = lexeme._textLength; 1028 } 1029 else { 1030 error.filePath = to!string(_file); 1031 error.lineText = to!string(_lines[_line]); 1032 error.line = _line + 1u; // By convention, the first line is 1, not 0. 1033 error.column = _current - _positionOfLine; 1034 error.textLength = 0u; 1035 } 1036 1037 throw new GrLexerException(error); 1038 } 1039 } 1040 1041 /// Returns a displayable version of a token type. 1042 string grGetPrettyLexemeType(GrLexemeType operator) { 1043 immutable string[] lexemeTypeStrTable = [ 1044 "[", "]", "(", ")", "{", "}", ".", ";", ":", "::", ",", "@", "&", "as", 1045 "try", "catch", "raise", "defer", "=", "+=", "-=", "*=", "/=", "~=", 1046 "%=", "^=", "+", "-", "+", "-", "*", "/", "~", "%", "^", "==", "===", 1047 "<=>", "!=", ">=", ">", "<=", "<", "<<", ">>", "and", "or", "xor", 1048 "not", "++", "--", "identifier", "const_int", "const_float", "const_bool", 1049 "const_str", "null", "pub", "main", "type", "event", "class", "enum", 1050 "template", "new", "copy", "send", "receive", "int", "float", "bool", 1051 "string", "array", "chan", "func", "task", "let", "if", "unless", 1052 "else", "switch", "select", "case", "while", "do", "until", "for", "loop", 1053 "return", "self", "kill", "killall", "yield", "break", "continue" 1054 ]; 1055 return lexemeTypeStrTable[operator]; 1056 } 1057 1058 /** 1059 Lexical error during tokenization 1060 */ 1061 package final class GrLexerException : Exception { 1062 GrError error; 1063 1064 /// Ctor 1065 this(GrError error_, string _file = __FILE__, size_t _line = __LINE__) { 1066 super(error_.message, _file, _line); 1067 error = error_; 1068 } 1069 }