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 }