root/trunk/dlexer.d

Revision 14, 35.2 kB (checked in by Jaymz031602, 7 years ago)

--

  • Property svn:executable set to *
Line 
1 // dlexer - D language lexer
2 // (C) Copyright 2004-2005 James Dunne
3
4 // Please feel free to distribute this module in any form you like.  I only ask that you
5 // give credit where credit is due.  You may make modifications to this module.  If you do
6 // so, you may provide your modifications publicly, but you are not required to.
7
8 module  dlexer;
9
10 import  std.stream;
11 import  std.string;
12 import  std.ctype;
13
14 private import std.c.stdlib;    // for strtod and strtold
15
16 alias std.ctype.isdigit isdigit;
17
18 // Set this version identifier to enable interpretation of escaped characters within parsed strings
19 // If this is disabled, the strings are copied directly, escape sequences and all.
20
21 // version = interpret_slashes;
22
23 // Enumeration of D language tokens taken from DMD's lexer.h
24 enum : uint {
25     TOKreserved,
26
27     // Other
28     TOKlparen,  TOKrparen,
29     TOKlbracket,    TOKrbracket,
30     TOKlcurly,  TOKrcurly,
31     TOKcolon,   TOKneg,
32     TOKsemicolon,   TOKdotdotdot,
33     TOKeof,     TOKcast,
34     TOKnull,    TOKassert,
35     TOKtrue,    TOKfalse,
36     TOKarray,   TOKcall,
37     TOKaddress, TOKtypedot,
38     TOKtype,    TOKthrow,
39     TOKnew,     TOKdelete,
40     TOKstar,    TOKsymoff,
41     TOKvar,     TOKdotvar,
42     TOKdotti,   TOKdotexp,
43     TOKdottype, TOKslice,
44     TOKarraylength, TOKversion,
45     TOKmodule,  TOKdollar,
46     TOKtemplate,    TOKinstance,
47     TOKdeclaration, TOKtypeof,
48     TOKpragma,  TOKdsymbol,
49     TOKtypeid,  TOKuadd,
50
51     // Operators
52     TOKlt,      TOKgt,
53     TOKle,      TOKge,
54     TOKequal,   TOKnotequal,
55     TOKidentity,    TOKnotidentity,
56     TOKindex,
57
58     // NCEG floating point compares
59     // !<>=     <>    <>=    !>     !>=   !<     !<=   !<>
60     TOKunord,TOKlg,TOKleg,TOKule,TOKul,TOKuge,TOKug,TOKue,
61
62     TOKshl,     TOKshr,
63     TOKshlass,  TOKshrass,
64     TOKushr,    TOKushrass,
65     TOKcat,     TOKcatass,  // ~ ~=
66     TOKadd,     TOKmin,     TOKaddass,  TOKminass,
67     TOKmul,     TOKdiv,     TOKmod,
68     TOKmulass,  TOKdivass,  TOKmodass,
69     TOKand,     TOKor,      TOKxor,
70     TOKandass,  TOKorass,   TOKxorass,
71     TOKassign,  TOKnot,     TOKtilde,
72     TOKplusplus,    TOKminusminus,
73     TOKdot,     TOKarrow,   TOKcomma,
74     TOKquestion,    TOKandand,  TOKoror,
75
76     // Numeric literals
77     TOKint32v, TOKuns32v,
78     TOKint64v, TOKuns64v,
79     TOKfloat32v, TOKfloat64v, TOKfloat80v,
80     TOKimaginary32v, TOKimaginary64v, TOKimaginary80v,
81
82     // Char constants
83     TOKcharv, TOKwcharv, TOKdcharv,
84
85     // Leaf operators
86     TOKidentifier,  TOKstring,
87     TOKthis,    TOKsuper,
88
89     // Basic types
90     TOKvoid,
91     TOKint8, TOKuns8,
92     TOKint16, TOKuns16,
93     TOKint32, TOKuns32,
94     TOKint64, TOKuns64,
95     TOKfloat32, TOKfloat64, TOKfloat80,
96     TOKimaginary32, TOKimaginary64, TOKimaginary80,
97     TOKcomplex32, TOKcomplex64, TOKcomplex80,
98     TOKchar, TOKwchar, TOKdchar, TOKbit,
99
100     // Aggregates
101     TOKstruct, TOKclass, TOKinterface, TOKunion, TOKenum, TOKimport,
102     TOKtypedef, TOKalias, TOKoverride, TOKdelegate, TOKfunction,
103     TOKmixin,
104
105     TOKalign, TOKextern, TOKprivate, TOKprotected, TOKpublic, TOKexport,
106     TOKstatic, /*TOKvirtual,*/ TOKfinal, TOKconst, TOKabstract, TOKvolatile,
107     TOKdebug, TOKdeprecated, TOKin, TOKout, TOKinout,
108     TOKauto, TOKpackage,
109
110     // Statements
111     TOKif, TOKelse, TOKwhile, TOKfor, TOKdo, TOKswitch,
112     TOKcase, TOKdefault, TOKbreak, TOKcontinue, TOKwith,
113     TOKsynchronized, TOKreturn, TOKgoto, TOKtry, TOKcatch, TOKfinally,
114     TOKasm, TOKforeach,
115
116     // Contracts
117     TOKbody, TOKinvariant,
118
119     // Testing
120     TOKunittest,
121
122     TOKmax
123 }
124
125 // A table converting token values into string representations:
126 char[]  toktostr[TOKmax] = [
127     TOKreserved : "reserved",
128
129     // Other
130     TOKlparen : "(",
131     TOKrparen : ")",
132     TOKlbracket : "[",
133     TOKrbracket : "]",
134     TOKlcurly : "{",
135     TOKrcurly : "}",
136     TOKcolon : ":",
137     TOKneg : "-",
138     TOKsemicolon : ";",
139     TOKdotdotdot : "...",
140     TOKeof : "EOF",
141     TOKcast : "cast",
142     TOKnull : "null",
143     TOKassert : "assert",
144     TOKtrue : "true",
145     TOKfalse : "false",
146     TOKarray : "[]",
147     TOKcall : "call",
148     TOKaddress : "#",
149     TOKtypedot : "typedot",
150     TOKtype : "type",
151     TOKthrow : "throw",
152     TOKnew : "new",
153     TOKdelete : "delete",
154     TOKstar : "*",
155     TOKsymoff : "symoff",
156     TOKvar : "var",
157     TOKdotvar : "dotvar",
158     TOKdotti : "dotti",
159     TOKdotexp : "dotexp",
160     TOKdottype : "dottype",
161     TOKslice : "..",
162     TOKarraylength : "arraylength",
163     TOKversion : "version",
164     TOKmodule : "module",
165     TOKdollar : "$",
166     TOKtemplate : "template",
167     TOKinstance : "instance",
168     TOKdeclaration : "declaration",
169     TOKtypeof : "typeof",
170     TOKpragma : "pragma",
171     TOKdsymbol : "dsymbol",
172     TOKtypeid : "typeid",
173     TOKuadd : "uadd",
174
175     // Operators
176     TOKlt : "<",
177     TOKgt : ">",
178     TOKle : "<=",
179     TOKge : ">=",
180     TOKequal : "==",
181     TOKnotequal : "!=",
182     TOKidentity : "===",
183     TOKnotidentity : "!==",
184     TOKindex : "[]",
185
186     // NCEG floating point compares
187     // !<>=     <>    <>=    !>     !>=   !<     !<=   !<>
188
189     // NOTE:  These could be horribly wrong
190     TOKunord : "!<>=",
191     TOKue : "!<>",
192     TOKlg : "<>",
193     TOKleg : "<>=",
194     TOKule : "!>",
195     TOKul : "!>=",
196     TOKuge : "!<",
197     TOKug : "!<=",
198
199     TOKshl : "<<",
200     TOKshr : ">>",
201     TOKshlass : "<<=",
202     TOKshrass : ">>=",
203     TOKushr : ">>>",
204     TOKushrass : ">>>=",
205     TOKcat : "~",
206     TOKcatass : "~=",   // ~ ~=
207     TOKadd : "+",
208     TOKmin : "-",
209     TOKaddass : "+=",
210     TOKminass : "-=",
211     TOKmul : "*",
212     TOKdiv : "/",
213     TOKmod : "%",
214     TOKmulass : "*=",
215     TOKdivass : "/=",
216     TOKmodass : "%=",
217     TOKand : "&",
218     TOKor : "|",
219     TOKxor : "^",
220     TOKandass : "&=",
221     TOKorass : "|=",
222     TOKxorass : "^=",
223     TOKassign : "=",
224     TOKnot : "!",
225     TOKtilde : "~",
226     TOKplusplus : "++",
227     TOKminusminus : "--",
228     TOKdot : ".",
229     TOKarrow : "->",
230     TOKcomma : ",",
231     TOKquestion : "?",
232     TOKandand : "&&",
233     TOKoror : "||",
234
235     // Numeric literals
236     TOKint32v : "int32v", TOKuns32v : "uns32v",
237     TOKint64v : "int64v", TOKuns64v : "uns64v",
238     TOKfloat32v : "float32v", TOKfloat64v : "float64v", TOKfloat80v : "float80v",
239     TOKimaginary32v : "imaginary32v", TOKimaginary64v : "imaginary64v", TOKimaginary80v : "imaginary80v",
240
241     // Char constants
242     TOKcharv : "charv", TOKwcharv : "wcharv", TOKdcharv : "dcharv",
243
244     // Leaf operators
245     TOKidentifier : "identifier",   TOKstring : "string",
246     TOKthis : "this",   TOKsuper : "super",
247
248     // Basic types
249     TOKvoid : "void",
250     TOKint8 : "byte", TOKuns8 : "ubyte",
251     TOKint16 : "short", TOKuns16 : "ushort",
252     TOKint32 : "int", TOKuns32 : "uint",
253     TOKint64 : "long", TOKuns64 : "ulong",
254     TOKfloat32 : "float", TOKfloat64 : "double", TOKfloat80 : "real",
255     TOKimaginary32 : "ifloat", TOKimaginary64 : "idouble", TOKimaginary80 : "ireal",
256     TOKcomplex32 : "cfloat", TOKcomplex64 : "cdouble", TOKcomplex80 : "creal",
257     TOKchar : "char", TOKwchar : "wchar", TOKdchar : "dchar", TOKbit : "bit",
258
259     // Aggregates
260     TOKstruct : "struct", TOKclass : "class", TOKinterface : "interface", TOKunion : "union", TOKenum : "enum", TOKimport : "import",
261     TOKtypedef : "typedef", TOKalias : "alias", TOKoverride : "override", TOKdelegate : "delegate", TOKfunction : "function",
262     TOKmixin : "mixin",
263
264     TOKalign : "align", TOKextern : "extern", TOKprivate : "private", TOKprotected : "protected", TOKpublic : "public", TOKexport : "export",
265     TOKstatic : "static", /*TOKvirtual : "virtual",*/ TOKfinal : "final", TOKconst : "const", TOKabstract : "abstract", TOKvolatile : "volatile",
266     TOKdebug : "debug", TOKdeprecated : "deprecated", TOKin : "in", TOKout : "out", TOKinout : "inout",
267     TOKauto : "auto", TOKpackage : "package",
268
269     // Statements
270     TOKif : "if", TOKelse : "else", TOKwhile : "while", TOKfor : "for", TOKdo : "do", TOKswitch : "switch",
271     TOKcase : "case", TOKdefault : "default", TOKbreak : "break", TOKcontinue : "continue", TOKwith : "with",
272     TOKsynchronized : "synchronized", TOKreturn : "return", TOKgoto : "goto", TOKtry : "try", TOKcatch : "catch", TOKfinally : "finally",
273     TOKasm : "asm", TOKforeach : "foreach",
274
275     // Contracts
276     TOKbody : "body", TOKinvariant : "invariant",
277
278     // Testing
279     TOKunittest : "unittest",
280 ];
281
282 class Identifier {
283     int     value;
284     char[]  string;
285
286     this(char[] string, int value) {
287         this.value = value;
288         this.string = string;
289     };
290 };
291
292 // A language token:
293 class Token {
294     // The kind of token:
295     uint    value;
296     Token   next;
297
298     // The value for the token:
299     union {
300         // The identifier or value of the token:
301         Identifier  ident;
302
303         char[]      ustring;
304
305         // Integers
306         int         int32value;
307         uint        uns32value;
308         long        int64value;
309         ulong       uns64value;
310
311         // Floats
312         real        float80value;
313     };
314
315     char[] toString() {
316         return toktostr[value];
317     };
318
319     static char[] toChars(uint value) {
320         return toktostr[value];
321     };
322 }
323
324 // Exception thrown during parsing of D language:
325 class DLexerException : Error {
326     public:
327         this(DLexer dlx, char[] msg) {
328             // Construct an error message with the filename and line number:
329             super(dlx.filename ~ "(" ~ format("%d", dlx.line) ~ ")" ~ ": " ~ msg);
330         }
331 }
332
333 // Check for octal digit:
334 int isodigit(dchar x) {
335     if (!isdigit(x) || (x == '8') || (x == '9')) return 0;
336     return -1;
337 }
338
339 class Loc {
340     public:
341         int linnum;
342
343         this() {
344         }
345 }
346
347 // The D language lexer (tokenizer):
348 class DLexer {
349         static uint[char[]] keywords;
350         // Initialize the keyword->tokenvalue AA:
351         static this() {
352             // Add all the keywords' values into the AA:
353             keywords["this"] = TOKthis;
354             keywords["super"] = TOKsuper;
355             keywords["assert"] = TOKassert;
356             keywords["null"] = TOKnull;
357             keywords["true"] = TOKtrue;
358             keywords["false"] = TOKfalse;
359             keywords["cast"] = TOKcast;
360             keywords["new"] = TOKnew;
361             keywords["delete"] = TOKdelete;
362             keywords["throw"] = TOKthrow;
363             keywords["module"] = TOKmodule;
364             keywords["pragma"] = TOKpragma;
365             keywords["typeof"] = TOKtypeof;
366             keywords["typeid"] = TOKtypeid;
367
368             keywords["template"] = TOKtemplate;
369             keywords["instance"] = TOKinstance;
370
371             keywords["void"] = TOKvoid;
372             keywords["byte"] = TOKint8;
373             keywords["ubyte"] = TOKuns8;
374             keywords["short"] = TOKint16;
375             keywords["ushort"] = TOKuns16;
376             keywords["int"] = TOKint32;
377             keywords["uint"] = TOKuns32;
378             keywords["long"] = TOKint64;
379             keywords["ulong"] = TOKuns64;
380             keywords["float"] = TOKfloat32;
381             keywords["double"] = TOKfloat64;
382             keywords["real"] = TOKfloat80;
383
384             keywords["bit"] = TOKbit;
385             keywords["char"] = TOKchar;
386             keywords["wchar"] = TOKwchar;
387             keywords["dchar"] = TOKdchar;
388
389             keywords["ifloat"] = TOKimaginary32;
390             keywords["idouble"] = TOKimaginary64;
391             keywords["ireal"] = TOKimaginary80;
392
393             keywords["cfloat"] = TOKcomplex32;
394             keywords["cdouble"] = TOKcomplex64;
395             keywords["creal"] = TOKcomplex80;
396
397             keywords["delegate"] = TOKdelegate;
398             keywords["function"] = TOKfunction;
399
400             keywords["is"] = TOKidentity;
401             keywords["if"] = TOKif;
402             keywords["else"] = TOKelse;
403             keywords["while"] = TOKwhile;
404             keywords["for"] = TOKfor;
405             keywords["do"] = TOKdo;
406             keywords["switch"] = TOKswitch;
407             keywords["case"] = TOKcase;
408             keywords["default"] = TOKdefault;
409             keywords["break"] = TOKbreak;
410             keywords["continue"] = TOKcontinue;
411             keywords["synchronized"] = TOKsynchronized;
412             keywords["return"] = TOKreturn;
413             keywords["goto"] = TOKgoto;
414             keywords["try"] = TOKtry;
415             keywords["catch"] = TOKcatch;
416             keywords["finally"] = TOKfinally;
417             keywords["with"] = TOKwith;
418             keywords["asm"] = TOKasm;
419             keywords["foreach"] = TOKforeach;
420
421             keywords["struct"] = TOKstruct;
422             keywords["class"] = TOKclass;
423             keywords["interface"] = TOKinterface;
424             keywords["union"] = TOKunion;
425             keywords["enum"] = TOKenum;
426             keywords["import"] = TOKimport;
427             keywords["mixin"] = TOKmixin;
428             keywords["static"] = TOKstatic;
429             /*keywords["virtual"] = TOKvirtual;*/
430             keywords["final"] = TOKfinal;
431             keywords["const"] = TOKconst;
432             keywords["typedef"] = TOKtypedef;
433             keywords["alias"] = TOKalias;
434             keywords["override"] = TOKoverride;
435             keywords["abstract"] = TOKabstract;
436             keywords["volatile"] = TOKvolatile;
437             keywords["debug"] = TOKdebug;
438             keywords["deprecated"] = TOKdeprecated;
439             keywords["in"] = TOKin;
440             keywords["out"] = TOKout;
441             keywords["inout"] = TOKinout;
442             keywords["auto"] = TOKauto;
443
444             keywords["align"] = TOKalign;
445             keywords["extern"] = TOKextern;
446             keywords["private"] = TOKprivate;
447             keywords["package"] = TOKpackage;
448             keywords["protected"] = TOKprotected;
449             keywords["public"] = TOKpublic;
450             keywords["export"] = TOKexport;
451
452             keywords["body"] = TOKbody;
453             keywords["invariant"] = TOKinvariant;
454             keywords["unittest"] = TOKunittest;
455             keywords["version"] = TOKversion;
456         }
457
458     protected:
459         Token               token;
460         Identifier[char[]]  identifiers;
461         Loc                 loc;
462
463     private:
464         char[]  file;       // The input file
465         uint    p;          // Current character
466
467         char[]  wysiwygString(char tc) {
468             char    c;
469             char[]  s;
470             uint    i;
471
472             i = 0;
473             ++p;
474             s.length = 16;
475             while (p < file.length) {
476                 c = file[p++];
477                 switch (c) {
478                     case '\n':
479                         ++line;
480                         break;
481
482                     case '\r':
483                         if (file[p] == '\n')
484                             continue;   // ignore
485                         c = '\n';   // treat EndOfLine as \n character
486                         ++line;
487                         break;
488
489                     case 0x1A:
490                         error("unterminated string constant starting at %s", s);
491                         return null;
492
493                     case '"', '`':
494                         if (c == tc) {
495                             s.length = i;
496                             return s;
497                         }
498                         break;
499
500                     default:
501                         break;
502                 }
503
504                 if (i == s.length) s.length = s.length * 2;
505                 s[i++] = c;
506             }
507             s.length = i;
508             return s;
509         }
510
511         // Test this!
512         char[]  hexString() {
513             char    c;
514             uint    n = 0, i = 0;
515             char[]  s;
516             ubyte   v;
517
518             p++;
519             s.length = 16;
520             while (p < file.length) {
521                 c = file[p++];
522                 switch (c) {
523                     case ' ', '\t', '\v', '\f':
524                         continue;           // skip white space
525
526                     case '\r':
527                         if (file[p] == '\n')
528                             continue;           // ignore
529                         // Treat isolated '\r' as if it were a '\n'
530                     case '\n':
531                         ++line;
532                         continue;
533
534                     case 0x1A:
535                         error("unterminated string constant starting at %s", s);
536                         return null;
537
538                     case '"':
539                         if (n & 1) {
540                             error("odd number (%d) of hex characters in hex string", n);
541                             return null;
542                         }
543                         s.length = i;
544                         return s;
545
546                     default:
547                         if (c >= '0' && c <= '9')
548                             c -= '0';
549                         else if (c >= 'a' && c <= 'f')
550                             c -= 'a' - 10;
551                         else if (c >= 'A' && c <= 'F')
552                             c -= 'A' - 10;
553                         else
554                             error("non-hex character '%c'", c);
555                         if (n & 1) {
556                             v = (v << 4) | c;
557                             if (i == s.length) s.length = s.length * 2;
558                             s[i++] = v;
559                         } else
560                             v = c;
561                         ++n;
562                         break;
563                 }
564             }
565             return null;
566         }
567
568     public:
569         char[]  filename;   // Filename
570         uint    line;       // Current line
571
572         // Initialize the lexer with the full source code as a string:
573         this(char[] filename, char[] src) {
574             file = src;
575             this.filename = filename;
576             restart();
577         }
578
579         // Possible to buffer up errors until some limit n.
580         void error(char[] msg, ...) {
581             throw new DLexerException(this, format(msg, _arguments));
582         }
583
584         // This function consumes a full D language token and returns it in the structure Token:
585         // null is returned if the end of the file is reached.
586         uint    scan(Token t) {
587             uint    start;
588
589             t.ident = null;
590             t.uns64value = 0;
591             t.value = TOKidentifier;
592
593             // Past the end of file? Return an EOF token:
594             if (p >= file.length) {
595                 t.value = TOKeof;
596                 return t.value;
597             }
598
599             // Read up to the next white-space char:
600             while (p < file.length) {
601                 switch (file[p]) {
602                     case ' ', '\n', '\r', '\v', '\t', '\f':
603                         if (file[p] == '\n') ++line;
604                         ++p;
605                         break;
606
607                     case '.':
608                         ++p;
609                         if (file[p] == '.') {
610                             ++p;
611                             if (file[p] == '.') {
612                                 ++p;
613                                 t.value = TOKdotdotdot;
614                             } else
615                                 t.value = TOKslice;
616                         } else
617                             t.value = TOKdot;
618                         return t.value;
619
620                     case '&':
621                         ++p;
622                         if (file[p] == '=') {
623                             ++p;
624                             t.value = TOKandass;
625                         } else if (file[p] == '&') {
626                             ++p;
627                             t.value = TOKandand;
628                         } else
629                             t.value = TOKand;
630                         return t.value;
631
632                     case '|':
633                         ++p;
634                         if (file[p] == '=') {
635                             ++p;
636                             t.value = TOKorass;
637                         } else if (file[p] == '|') {
638                             ++p;
639                             t.value = TOKoror;
640                         } else
641                             t.value = TOKor;
642                         return t.value;
643
644                     case '-':
645                         ++p;
646                         if (file[p] == '=') {
647                             ++p;
648                             t.value = TOKminass;
649                         } else if (file[p] == '-') {
650                             ++p;
651                             t.value = TOKminusminus;
652                         } else
653                             t.value = TOKmin;
654                         return t.value;
655
656                     case '+':
657                         ++p;
658                         if (file[p] == '=') {
659                             ++p;
660                             t.value = TOKaddass;
661                         } else if (file[p] == '+') {
662                             ++p;
663                             t.value = TOKplusplus;
664                         } else
665                             t.value = TOKadd;
666                         return t.value;
667
668                     case '=':
669                         ++p;
670                         if (file[p] == '=') {
671                             ++p;
672                             if (file[p] == '=') {
673                                 ++p;
674                                 t.value = TOKidentity;
675                             } else
676                                 t.value = TOKequal;
677                         } else
678                             t.value = TOKassign;
679                         return t.value;
680
681                     case '<':
682                         ++p;
683                         if (file[p] == '=') {
684                             ++p;
685                             t.value = TOKle;            // <=
686                         } else if (file[p] == '<') {
687                             ++p;
688                             if (file[p] == '=') {
689                                 ++p;
690                                 t.value = TOKshlass;        // <<=
691                             } else
692                                 t.value = TOKshl;       // <<
693                         } else if (file[p] == '>') {
694                             ++p;
695                             if (file[p] == '=') {
696                                 ++p;
697                                 t.value = TOKleg;       // <>=
698                             } else
699                                 t.value = TOKlg;        // <>
700                         } else
701                             t.value = TOKlt;            // <
702                         return t.value;
703
704                     case '>':
705                         ++p;
706                         if (file[p] == '=') {
707                             ++p;
708                             t.value = TOKge;            // >=
709                         } else if (file[p] == '>') {
710                             ++p;
711                             if (file[p] == '=') {
712                                 ++p;
713                                 t.value = TOKshrass;        // >>=
714                             } else if (file[p] == '>') {
715                                 ++p;
716                                 if (file[p] == '=') {
717                                     ++p;
718                                     t.value = TOKushrass;   // >>>=
719                                 } else
720                                     t.value = TOKushr;      // >>>
721                             } else
722                                 t.value = TOKshr;       // >>
723                         } else
724                             t.value = TOKgt;            // >
725                         return t.value;
726
727                     case '!':
728                         ++p;
729                         if (file[p] == '=') {
730                             ++p;
731                             if (file[p] == '=') {
732                                 ++p;
733                                 t.value = TOKnotidentity;   // !==
734                             } else
735                                 t.value = TOKnotequal;      // !=
736                         } else if (file[p] == '<') {
737                             ++p;
738                             if (file[p] == '>') {
739                                 ++p;
740                                 if (file[p] == '=') {
741                                     ++p;
742                                     t.value = TOKunord; // !<>=
743                                 } else
744                                     t.value = TOKue;    // !<>
745                             } else if (file[p] == '=') {
746                                 ++p;
747                                 t.value = TOKug;    // !<=
748                             } else
749                                 t.value = TOKuge;   // !<
750                         } else if (file[p] == '>') {
751                             ++p;
752                             if (file[p] == '=') {
753                                 ++p;
754                                 t.value = TOKul;    // !>=
755                             } else
756                                 t.value = TOKule;   // !>
757                         } else
758                             t.value = TOKnot;       // !
759                         return t.value;
760
761                     case '*':
762                         ++p;
763                         if (file[p] == '=') {
764                             ++p;
765                             t.value = TOKmulass;
766                         } else
767                             t.value = TOKmul;
768                         return t.value;
769                     case '%':
770                         ++p;
771                         if (file[p] == '=') {
772                             ++p;
773                             t.value = TOKmodass;
774                         } else
775                             t.value = TOKmod;
776                         return t.value;
777                     case '^':
778                         ++p;
779                         if (file[p] == '=') {
780                             ++p;
781                             t.value = TOKxorass;
782                         } else
783                             t.value = TOKxor;
784                         return t.value;
785                     case '~':
786                         ++p;
787                         if (file[p] == '=') {
788                             ++p;
789                             t.value = TOKcatass;
790                         } else
791                             t.value = TOKtilde;
792                         return t.value;
793
794                     case '(': ++p; t.value = TOKlparen; return t.value;
795                     case ')': ++p; t.value = TOKrparen; return t.value;
796                     case '[':
797                         ++p;
798                         if (file[p] == ']') {
799                             ++p;
800                             t.value = TOKarray;
801                         } else
802                             t.value = TOKlbracket;
803                         return t.value;
804                     case ']': ++p; t.value = TOKrbracket; return t.value;
805                     case '{': ++p; t.value = TOKlcurly; return t.value;
806                     case '}': ++p; t.value = TOKrcurly; return t.value;
807                     case ':': ++p; t.value = TOKcolon; return t.value;
808                     case ';': ++p; t.value = TOKsemicolon; return t.value;
809                     case '?': ++p; t.value = TOKquestion; return t.value;
810                     case ',': ++p; t.value = TOKcomma; return t.value;
811
812                     case '/':
813                         ++p;
814                         switch (file[p]) {
815                             case '=':
816                                 t.value = TOKdivass;
817                                 return t.value;
818
819                             case '/':
820                                 // single-line // comment:
821                                 ++p;
822                                 while (p < file.length) {
823                                     if (file[p] == '\n') {
824                                         ++line;
825                                         ++p;
826                                         break;
827                                     }
828                                     ++p;
829                                 }
830                                 break;
831
832                             case '+': {
833                                 int nest = 0;
834                                 // nested code comment /+:
835                                 ++p;
836                                 while (p < file.length) {
837                                     if (file[p] == '\n') ++line;
838                                    
839                                     if (file[p] == '+') {
840                                         ++p;
841                                         if (file[p] == '/') {
842                                             ++p;
843                                             --nest;
844                                             if (nest < 0) break;
845                                         }
846                                     } else if (file[p] == '/') {
847                                         ++p;
848                                         if (file[p] == '+') {
849                                             ++p;
850                                             ++nest;
851                                         }
852                                     } else ++p;
853                                 }
854                                 break;
855                             }
856
857                             case '*':
858                                 // multi-line comment /*:
859                                 ++p;
860                                 while (p < file.length) {
861                                     if (file[p] == '\n') ++line;
862                                    
863                                     if (file[p] == '*') {
864                                         ++p;
865                                         if (file[p] == '/') {
866                                             ++p;
867                                             break;
868                                         }
869                                     } else ++p;
870                                 }
871                                 break;
872
873                             default:
874                                 t.value = TOKdiv;
875                                 return t.value;
876                         }
877                         break;
878
879                     case '\'': {
880                         char[]  tok;
881                         version (interpret_slashes) {
882                             // Interpret the escaped characters and insert them:
883                             ++p;
884                             if (file[p] == '\\') {
885                                 tok.length = 1;
886                                 ++p;
887                                 switch (file[p]) {
888                                     case 'n': tok[0] = '\n'; break;
889                                     case 'r': tok[0] = '\r'; break;
890                                     case 'v': tok[0] = '\v'; break;
891                                     case 't': tok[0] = '\t'; break;
892                                     case 'f': tok[0] = '\f'; break;
893                                     case '\'': tok[0] = '\''; break;
894                                     case '\"': tok[0] = '\"'; break;
895                                     case '\\': tok[0] = '\\'; break;
896                                     default: break;
897                                 }
898                                 ++p;
899                                 ++p;
900                                 t.value = TOKcharv;
901                                 t.ustring = tok;
902                                 return t.value;
903                             } else {
904                                 tok.length = 1;
905                                 tok[0] = file[p];
906                                 ++p;
907                                 ++p;
908                                 t.value = TOKcharv;
909                                 t.ustring = tok;
910                                 return t.value;
911                             }
912                         } else {
913                             // Just copy over the escape strings:
914                             ++p;
915                             start = p;
916                             while (p < file.length) {
917                                 if (file[p] == '\\') {
918                                     ++p;
919                                 } else if (file[p] == '\'') {
920                                     break;
921                                 }
922                                 ++p;
923                             }
924                             t.ustring = file[start .. p];
925                             t.value = TOKcharv;
926                             ++p;
927                             return t.value;
928                         }
929                     }
930
931                     case '\"': {
932                         char[]  tok;
933                         version (interpret_slashes) {
934                             // Interpret the escaped characters and insert them:
935                             uint    l = 0;
936                             tok.length = 64;
937                             ++p;
938                             while (p < file.length) {
939                                 if (file[p] == '\\') {
940                                     ++p;
941                                     if (p >= file.length) break;
942                                     switch (file[p]) {
943                                         case 'n': tok[l] = '\n'; break;
944                                         case 'r': tok[l] = '\r'; break;
945                                         case 't': tok[l] = '\t'; break;
946                                         case 'v': tok[l] = '\v'; break;
947                                         case 'f': tok[l] = '\f'; break;
948                                         case '\'': tok[l] = '\''; break;
949                                         case '\"': tok[l] = '\"'; break;
950                                         case '\\': tok[l] = '\\'; break;
951                                         default:
952                                     }
953                                     // Grow the token length:
954                                     if (++l >= tok.length) tok.length = tok.length * 2;
955                                     ++p;
956                                 } else if (file[p] == '\"') {
957                                     break;
958                                 } else {
959                                     tok[l] = file[p];
960                                     // Grow the token length:
961                                     if (++l >= tok.length) tok.length = tok.length * 2;
962                                     ++p;
963                                 }
964                             }
965                             t.value = TOKstring;
966                             tok.length = l;
967                             t.ustring = tok;
968                             ++p;
969                             return t.value;
970                         } else {
971                             // Just copy over the escape strings:
972                             ++p;
973                             start = p;
974                             while (p < file.length) {
975                                 if (file[p] == '\\') {
976                                     ++p;
977                                 } else if (file[p] == '\"') {
978                                     break;
979                                 }
980                                 ++p;
981                             }
982                             t.value = TOKstring;
983                             t.ustring = file[start .. p];
984                             ++p;
985                             return t.value;
986                         }
987                     }
988
989                     case 'r':
990                         // WYSIWYG string?
991                         ++p;
992                         if (file[p] != '\"') {
993                             // Nope, just an identifier:
994                             start = p - 1;
995                             goto case_ident;
996                         }
997                     case '`':
998                         t.value = TOKstring;
999                         t.ustring = wysiwygString(file[p]);
1000                         return t.value;
1001
1002                     case 'x':
1003                         // HEX string?
1004                         ++p;
1005                         if (file[p] != '\"') {
1006                             start = p - 1;
1007                             goto case_ident;
1008                         }
1009                         t.value = TOKstring;
1010                         t.ustring = hexString();
1011                         return t.value;
1012
1013                     // Identifier start with _ or a-z,A-Z:
1014                     case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
1015                          'm', 'n', 'o', 'p', 'q',      's', 't', 'u', 'v', 'w',
1016                          'y', 'z':
1017                     case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
1018                          'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
1019                          'Y', 'Z', '_':
1020                         // Set the starting point at this character:
1021                         start = p;
1022                     case_ident:
1023                         // Extract the identifier:
1024                         while (p < file.length) {
1025                             if (isalnum(file[p]) || (file[p] == '_'))
1026                                 ++p;
1027                             else
1028                                 break;
1029                         }
1030                         // Checks for the identifier in the keyword list, returns null if not there:
1031                         char[] tok = file[start .. p];
1032                         if (tok in keywords) {
1033                             t.value = keywords[tok];
1034                         } else {
1035                             t.value = TOKidentifier;
1036                             // Check for the identifier in the list:
1037                             if (tok in identifiers) {
1038                                 // Use that.
1039                                 t.ident = identifiers[tok];
1040                             } else {
1041                                 // Make a new one.
1042                                 t.ident = new Identifier(tok, 0);
1043                                 identifiers[tok] = t.ident;
1044                             }
1045                         }
1046                         return t.value;
1047
1048                     // Numeric literal:
1049                     case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': {
1050                         return number(t);
1051                     }
1052
1053                     default:
1054                         ++p;
1055                 }
1056             }
1057
1058             return 0;
1059         }
1060
1061         // Parse a real number:
1062         uint    inreal(Token t) {
1063             printf("inreal\n");
1064
1065             int     dblstate, i;
1066             char[]  s;
1067             char    c;
1068             char    hex;            // is this a hexadecimal-floating-constant?
1069             uint    result;
1070
1071             //printf("Lexer::inreal()\n");
1072             i = 0;
1073             s.length = 16;
1074             dblstate = 0;
1075             hex = 0;
1076         Lnext:
1077             while (p < file.length) {
1078                 // Get next char from input
1079                 c = file[p++];
1080                 while (1) {
1081                     switch (dblstate) {
1082                         case 0:         // opening state
1083                             if (c == '0')
1084                                 dblstate = 9;
1085                             else
1086                                 dblstate = 1;
1087                             break;
1088                
1089                         case 9:
1090                             dblstate = 1;
1091                             if (c == 'X' || c == 'x') {
1092                                 hex++;
1093                                 break;
1094                             }
1095                         case 1:         // digits to left of .
1096                         case 3:         // digits to right of .
1097                         case 7:         // continuing exponent digits
1098                             if (!isdigit(c) && !(hex && isxdigit(c))) {
1099                                 if (c == '_')
1100                                     goto Lnext; // ignore embedded '_'
1101                                 dblstate++;
1102                                 continue;
1103                             }
1104                             break;
1105                
1106                         case 2:         // no more digits to left of .
1107                             if (c == '.') {
1108                                 dblstate++;
1109                                 break;
1110                             }
1111                         case 4:         // no more digits to right of .
1112                             if ((c == 'E' || c == 'e') || hex && (c == 'P' || c == 'p')) {
1113                                 dblstate = 5;
1114                                 hex = 0;    // exponent is always decimal
1115                                 break;
1116                             }
1117                             if (hex)
1118                                 error("binary-exponent-part required");
1119                             goto done;
1120                
1121                         case 5:         // looking immediately to right of E
1122                             dblstate++;
1123                             if (c == '-' || c == '+')
1124                                 break;
1125                         case 6:         // 1st exponent digit expected
1126                             if (!isdigit(c))
1127                                 error("exponent expected");
1128                             dblstate++;
1129                             break;
1130                
1131                         case 8:         // past end of exponent digits
1132                             goto done;
1133                     }
1134                     break;
1135                 }
1136                 if (i == s.length) s.length = s.length * 2;
1137                 s[i++] = c;
1138             }
1139         done:
1140             p--;
1141             s.length = i;
1142
1143             setErrno(0);
1144
1145             switch (file[p]) {
1146                 case 'F':
1147                 case 'f':
1148                     t.float80value = strtod(s.ptr, null);
1149                     result = TOKfloat32v;
1150                     p++;
1151                     break;
1152
1153                 default:
1154                     t.float80value = strtod(s.ptr, null);
1155                     result = TOKfloat64v;
1156                     break;
1157
1158                 case 'L':
1159                 case 'l':
1160                     t.float80value = strtold(s.ptr, null);
1161                     result = TOKfloat80v;
1162                     p++;
1163                     break;
1164             }
1165             // Imaginary value:
1166             if (file[p] == 'i' || file[p] == 'I') {
1167                 p++;
1168                 switch (result)
1169                 {
1170                     case TOKfloat32v:
1171                         result = TOKimaginary32v;
1172                         break;
1173                     case TOKfloat64v:
1174                         result = TOKimaginary64v;
1175                         break;
1176                     case TOKfloat80v:
1177                         result = TOKimaginary80v;
1178                         break;
1179                 }
1180             }
1181             // Standard C:
1182             if (getErrno() != 0)    // == ERANGE
1183                 error("number is not representable");
1184             return result;
1185         }
1186
1187         // Parse a number and return the associated value:
1188         uint    number(Token t) {
1189             // We use a state machine to collect numbers
1190             enum : uint { STATE_initial, STATE_0, STATE_decimal, STATE_octal, STATE_octale,
1191             STATE_hex, STATE_binary, STATE_hex0, STATE_binary0,
1192             STATE_hexh, STATE_error };
1193             uint    state;
1194
1195             enum : uint {
1196                 FLAGS_decimal  = 1,     // decimal
1197                 FLAGS_unsigned = 2,     // u or U suffix
1198                 FLAGS_long     = 4,     // l or L suffix
1199             };
1200             uint    flags = FLAGS_decimal;
1201
1202             uint    i;
1203             int     base;
1204             char    c;
1205             char[]  s;
1206
1207             uint    start;
1208             ulong   n;
1209
1210             state = STATE_initial;
1211             base = 0;
1212
1213             s.length = 8;
1214
1215             start = p;
1216             i = 0;
1217             while (p < file.length) {
1218                 c = file[p];
1219                 switch (state) {
1220                     case STATE_initial:     // opening state
1221                         if (c == '0')
1222                             state = STATE_0;
1223                         else
1224                             state = STATE_decimal;
1225                         break;
1226
1227                     case STATE_0:
1228                         flags = (flags & ~FLAGS_decimal);
1229                         switch (c) {
1230                             case 'X':
1231                             case 'x':
1232                                 state = STATE_hex0;
1233                                 break;
1234                             case '.':
1235                                 if (file[p+1] == '.')   // .. is a separate token
1236                                     goto done;
1237                             case 'i':
1238                             case 'f':
1239                             case 'F':
1240                                 goto realnum;
1241                             case 'B':
1242                             case 'b':
1243                                 state = STATE_binary0;
1244                                 break;
1245
1246                             case '0': case '1': case '2': case '3':
1247                             case '4': case '5': case '6': case '7':
1248                                 state = STATE_octal;
1249                                 break;
1250
1251                             case '_':
1252                                 state = STATE_octal;
1253                                 ++p;
1254                                 continue;
1255
1256                             default:
1257                                 goto done;
1258                         }
1259                         break;
1260
1261                     case STATE_decimal:     // reading decimal number
1262                         if (!isdigit(c)) {
1263                             if (c == '_') { // ignore embedded _
1264                                 ++p;
1265                                 continue;
1266                             }
1267                             if (c == '.' && file[p+1] != '.')
1268                                 goto realnum;
1269                             else if (c == 'i' || c == 'f' || c == 'F' ||
1270                                  c == 'e' || c == 'E') {
1271                         realnum:    // It's a real number. Back up and rescan as a real
1272                                 p = start;
1273                                 return inreal(t);
1274                             }
1275                             goto done;
1276                         }
1277                         break;
1278
1279                     case STATE_hex0:        // reading hex number
1280                     case STATE_hex:
1281                         if (!isxdigit(c)) {
1282                             if (c == '_') { // ignore embedded _
1283                                 ++p;
1284                                 continue;
1285                             }
1286                             if (c == '.' && file[p+1] != '.')
1287                                 goto realnum;
1288                             if (c == 'P' || c == 'p' || c == 'i')
1289                                 goto realnum;
1290                             if (state == STATE_hex0)
1291                                 error("Hex digit expected, not '%c'", c);
1292                             goto done;
1293                         }
1294                         state = STATE_hex;
1295                         break;
1296
1297                     case STATE_octal:       // reading octal number
1298                     case STATE_octale:      // reading octal number with non-octal digits
1299                         if (!isodigit(c)) {
1300                             if (c == '_') { // ignore embedded _
1301                                 ++p;
1302                                 continue;
1303                             }
1304                             if (c == '.' && file[p+1] != '.')
1305                                 goto realnum;
1306                             if (c == 'i')
1307                                 goto realnum;
1308                             if (isdigit(c))
1309                                 state = STATE_octale;
1310                             else
1311                                 goto done;
1312                         }
1313                         break;
1314
1315                     case STATE_binary0:     // starting binary number
1316                     case STATE_binary:      // reading binary number
1317                         if (c != '0' && c != '1') {
1318                             if (c == '_') { // ignore embedded _
1319                                 ++p;
1320                                 continue;
1321                             }
1322                             if (state == STATE_binary0) {
1323                                 error("binary digit expected");
1324                                 state = STATE_error;
1325                                 break;
1326                             } else
1327                                 goto done;
1328                         }
1329                         state = STATE_binary;
1330                         break;
1331
1332                     case STATE_error:       // for error recovery
1333                         if (!isdigit(c))    // scan until non-digit
1334                             goto done;
1335                         break;
1336
1337                     default:
1338                         assert(0);
1339                 }
1340
1341                 if (i == s.length) s.length = s.length * 2;
1342                 s[i++] = c;
1343                 ++p;
1344             }
1345
1346 done:
1347             s.length = i;
1348             if (state == STATE_octale)
1349                 error("Octal digit expected");
1350
1351             if ((i == 1) && (state == STATE_decimal || state == STATE_0))
1352                 n = s[0] - '0';
1353             else {
1354                 // Convert string to integer
1355                 int q = 0;
1356                 int r = 10, d;
1357
1358                 if (s[q] == '0') {
1359                     if (s[q+1] == 'x' || s[q+1] == 'X') {
1360                         q += 2, r = 16;
1361                     } else if (s[q+1] == 'b' || s[q+1] == 'B') {
1362                         q += 2, r = 2;
1363                     } else if (isdigit(s[q+1])) {
1364                         q += 1, r = 8;
1365                     }
1366                 }
1367
1368                 n = 0;
1369                 while (q < s.length) {
1370                     if (s[q] >= '0' && s[q] <= '9')
1371                         d = s[q] - '0';
1372                     else if (s[q] >= 'a' && s[q] <= 'z')
1373                         d = s[q] - 'a' + 10;
1374                     else if (s[q] >= 'A' && s[q] <= 'Z')
1375                         d = s[q] - 'A' + 10;
1376                     else
1377                         break;
1378                     if (d >= r)
1379                         break;
1380                     if (n * r + d < n) {
1381                         error("integer overflow");
1382                         break;
1383                     }
1384
1385                     n = n * r + d;
1386                     ++q;
1387                 }
1388             }
1389
1390             // Parse trailing 'u', 'U', 'l' or 'L' in any combination
1391             while (p < file.length) {
1392                 uint    f;
1393
1394                 switch (file[p]) {
1395                     case 'U':
1396                     case 'u':
1397                         f = FLAGS_unsigned;
1398                         goto L1;
1399                     case 'L':
1400                     case 'l':
1401                         f = FLAGS_long;
1402                 L1:
1403                         ++p;
1404                         if (flags & f)
1405                             error("unrecognized token");
1406                         flags = (flags | f);
1407                         continue;
1408
1409                     default:
1410                         break;
1411                 }
1412                 break;
1413             }
1414
1415             switch (flags) {
1416                 case 0:
1417                     if (n & 0x8000000000000000L)
1418                         t.value = TOKuns64v;
1419                     else if (n & 0xFFFFFFFF00000000L)
1420                         t.value = TOKint64v;
1421                     else if (n & 0x80000000)
1422                         t.value = TOKuns32v;
1423                     else
1424                         t.value = TOKint32v;
1425                     break;
1426
1427                 case FLAGS_decimal:
1428                     if (n & 0x8000000000000000L) {
1429                         error("signed integer overflow");
1430                         t.value = TOKuns64v;
1431                     }
1432                     else if (n & 0xFFFFFFFF80000000L)
1433                         t.value = TOKint64v;
1434                     else
1435                         t.value = TOKint32v;
1436                     break;
1437
1438                 case FLAGS_unsigned:
1439                 case FLAGS_decimal | FLAGS_unsigned:
1440                     if (n & 0xFFFFFFFF00000000L)
1441                         t.value = TOKuns64v;
1442                     else
1443                         t.value = TOKuns32v;
1444                     break;
1445
1446                 case FLAGS_decimal | FLAGS_long:
1447                     if (n & 0x8000000000000000L) {
1448                         error("signed integer overflow");
1449                         t.value = TOKuns64v;
1450                     } else
1451                         t.value = TOKint64v;
1452                     break;
1453
1454                 case FLAGS_long:
1455                     if (n & 0x8000000000000000L)
1456                         t.value = TOKuns64v;
1457                     else
1458                         t.value = TOKint64v;
1459                     break;
1460
1461                 case FLAGS_unsigned | FLAGS_long:
1462                 case FLAGS_decimal | FLAGS_unsigned | FLAGS_long:
1463                     t.value = TOKuns64v;
1464                     break;
1465
1466                 default:
1467                     assert(0);
1468             }
1469
1470             t.uns64value = n;
1471             return t.value;
1472         }
1473
1474         uint nextToken() {
1475             Token   t;
1476
1477             if (token.next) {
1478                 t = token.next;
1479                 token = t;
1480                 t.next = null;
1481             } else
1482                 scan(token);
1483
1484             return token.value;
1485         }
1486
1487         // Only peek at the next token, don't consume it:
1488         Token peek(Token ct) {
1489             Token   t;
1490
1491             if (ct.next) {
1492                 t = ct.next;
1493             } else {
1494                 t = new Token();
1495                 scan(t);
1496                 t.next = null;
1497                 ct.next = t;
1498             }
1499
1500             return t;
1501         }
1502
1503         // Start the parsing at the beginning of the module:
1504         void restart() {
1505             p = 0;
1506             line = 1;
1507         }
1508 }
Note: See TracBrowser for help on using the browser.