2 // Compiler implementation of the D programming language
3 // Copyright (c) 1999-2008 by Digital Mars
5 // written by Walter Bright
6 // http://www.digitalmars.com
7 // License for redistribution is by either the Artistic License
8 // in artistic.txt, or the GNU General Public License in gnu.txt.
9 // See the included readme.txt for details.
11 /* NOTE: This file has been patched from the original DMD distribution to
12 work with the GDC compiler.
14 Modified by David Friedman, December 2006
17 /* Lexical Analyzer */
41 #include "..\root\mem.h"
43 #include "../root/mem.h"
47 #include "stringtable.h"
51 #include "identifier.h"
56 // from \dm\src\include\setlocal.h
57 extern "C" char * __cdecl __locale_decpoint
;
60 extern int HtmlNamedEntity(unsigned char *p
, int length
);
62 #define LS 0x2028 // UTF line separator
63 #define PS 0x2029 // UTF paragraph separator
65 /********************************************
66 * Do our own char maps
69 static unsigned char cmtable
[256];
71 const int CMoctal
= 0x1;
72 const int CMhex
= 0x2;
73 const int CMidchar
= 0x4;
75 inline unsigned char isoctal (unsigned char c
) { return cmtable
[c
] & CMoctal
; }
76 inline unsigned char ishex (unsigned char c
) { return cmtable
[c
] & CMhex
; }
77 inline unsigned char isidchar(unsigned char c
) { return cmtable
[c
] & CMidchar
; }
79 static void cmtable_init()
81 for (unsigned c
= 0; c
< sizeof(cmtable
) / sizeof(cmtable
[0]); c
++)
83 if ('0' <= c
&& c
<= '7')
84 cmtable
[c
] |= CMoctal
;
85 if (isdigit(c
) || ('a' <= c
&& c
<= 'f') || ('A' <= c
&& c
<= 'F'))
87 if (isalnum(c
) || c
== '_')
88 cmtable
[c
] |= CMidchar
;
93 /************************* Token **********************************************/
95 char *Token::tochars
[TOKMAX
];
97 void *Token::operator new(size_t size
)
103 Lexer::freelist
= t
->next
;
107 return ::operator new(size
);
113 fprintf(stdmsg
, "%s\n", toChars());
117 char *Token::toChars()
119 static char buffer
[3 + 3 * sizeof(value
) + 1];
126 sprintf(buffer
,"%d",(d_int32
)int64value
);
128 sprintf(buffer
,"%d",int32value
);
137 sprintf(buffer
,"%uU",(d_uns32
)uns64value
);
139 sprintf(buffer
,"%uU",uns32value
);
144 sprintf(buffer
,"%"PRIdMAX
"L",int64value
);
148 sprintf(buffer
,"%"PRIuMAX
"UL",uns64value
);
155 float80value
.format(buffer
, sizeof(buffer
));
157 case TOKimaginary32v
:
158 case TOKimaginary64v
:
159 case TOKimaginary80v
:
160 float80value
.format(buffer
, sizeof(buffer
));
166 sprintf(buffer
,"%Lgf", float80value
);
170 sprintf(buffer
,"%Lg", float80value
);
174 sprintf(buffer
,"%LgL", float80value
);
177 case TOKimaginary32v
:
178 sprintf(buffer
,"%Lgfi", float80value
);
181 case TOKimaginary64v
:
182 sprintf(buffer
,"%Lgi", float80value
);
185 case TOKimaginary80v
:
186 sprintf(buffer
,"%LgLi", float80value
);
198 for (size_t i
= 0; i
< len
; )
201 utf_decodeChar((unsigned char *)ustring
, len
, &i
, &c
);
214 buf
.printf("\\x%02x", c
);
215 else if (c
<= 0xFFFF)
216 buf
.printf("\\u%04x", c
);
218 buf
.printf("\\U%08x", c
);
227 p
= (char *)buf
.extractData();
237 p
= ident
->toChars();
247 char *Token::toChars(enum TOK value
)
249 static char buffer
[3 + 3 * sizeof(value
) + 1];
253 { sprintf(buffer
,"TOK%d",value
);
259 /*************************** Lexer ********************************************/
261 Token
*Lexer::freelist
= NULL
;
262 StringTable
Lexer::stringtable
;
263 OutBuffer
Lexer::stringbuffer
;
265 Lexer::Lexer(Module
*mod
,
266 unsigned char *base
, unsigned begoffset
, unsigned endoffset
,
267 int doDocComment
, int commentToken
, bool dltSyntax
)
268 : loc(mod
, 1), dltSyntax(dltSyntax
)
270 //printf("Lexer::Lexer(%p,%d)\n",base,length);
271 //printf("lexer.mod = %p, %p\n", mod, this->loc.mod);
272 memset(&token
,0,sizeof(token
));
274 this->end
= base
+ endoffset
;
275 p
= base
+ begoffset
;
277 this->doDocComment
= doDocComment
;
279 this->commentToken
= commentToken
;
282 this->atStartOfLine
= 1;
286 /* If first line starts with '#!', ignore the line
289 if (p
[0] == '#' && p
[1] =='!')
293 { unsigned char c
= *p
;
312 { unsigned u
= decodeUTF();
313 if (u
== PS
|| u
== LS
)
326 void Lexer::error(const char *format
, ...)
328 if (mod
&& !global
.gag
)
330 char *p
= loc
.toChars();
332 fprintf(stdmsg
, "%s: ", p
);
336 va_start(ap
, format
);
337 vfprintf(stdmsg
, format
, ap
);
340 fprintf(stdmsg
, "\n");
343 if (global
.errors
>= 20) // moderate blizzard of cascading messages
349 void Lexer::error(Loc loc
, const char *format
, ...)
351 if (mod
&& !global
.gag
)
353 char *p
= loc
.toChars();
355 fprintf(stdmsg
, "%s: ", p
);
359 va_start(ap
, format
);
360 vfprintf(stdmsg
, format
, ap
);
363 fprintf(stdmsg
, "\n");
366 if (global
.errors
>= 20) // moderate blizzard of cascading messages
372 TOK
Lexer::nextToken()
378 memcpy(&token
,t
,sizeof(Token
));
390 Token
*Lexer::peek(Token
*ct
)
405 /*********************************
406 * tk is on the opening (.
407 * Look ahead and return token that is past the closing ).
410 Token
*Lexer::peekPastParen(Token
*tk
)
412 //printf("peekPastParen()\n");
437 if (--curlynest
>= 0)
456 /**********************************
457 * Determine if string is a valid Identifier.
458 * Placed here because of commonality with Lexer functionality.
463 int Lexer::isValidIdentifier(char *p
)
471 if (*p
>= '0' && *p
<= '9') // beware of isdigit() on signed chars
479 char *q
= utf_decodeChar((unsigned char *)p
, len
, &idx
, &dc
);
483 if (!((dc
>= 0x80 && isUniAlpha(dc
)) || isalnum(dc
) || dc
== '_'))
492 /****************************
493 * Turn next token in buffer into a token.
496 void Lexer::scan(Token
*t
)
498 unsigned lastLine
= loc
.linnum
;
501 // Delayed line-number updating
504 assert(incLineno
== 1);
509 t
->blockComment
= NULL
;
510 t
->lineComment
= NULL
;
515 if (dltSyntax
&& atStartOfLine
) {
518 for (i
= 0; p
[i
] == '\t'; i
++) {
521 error("Whitespace error: use tabs to indent!");
526 } else if (p
[i
] != '\n' && p
[i
] != '\r') {
528 i
= 0; // End-of-file always has no indent
530 error("unexpected indentation (expected %d tabs, not %d)",
532 } else if (i
< indent
) {
534 t
->value
= TOKrcurly
;
538 } /* else ignore blank line */
541 //printf("p = %p, *p = '%c'\n",p,*p);
546 t
->value
= TOKeof
; // end of file
554 continue; // skip white space
557 if (p
[1] == '\n') { // if CRLF
566 // Delay incrementing the line number until after sending
567 // the TOKendline, for better error messages
575 t
->value
= TOKendline
;
581 continue; // Ignore newlines inside brackets
582 case '0': case '1': case '2': case '3': case '4':
583 case '5': case '6': case '7': case '8': case '9':
584 t
->value
= number(t
);
589 t
->value
= charConstant(t
, 0);
593 t
->value
= stringConstant(t
,0);
601 t
->value
= charConstant(t
, 1);
604 else if (p
[1] == '"')
607 t
->value
= stringConstant(t
, 1);
612 t
->value
= charConstant(t
,0);
620 t
->value
= wysiwygStringConstant(t
, *p
);
627 t
->value
= hexStringConstant(t
);
635 t
->value
= delimitedStringConstant(t
);
638 else if (p
[1] == '{')
641 t
->value
= tokenStringConstant(t
);
649 t
->value
= escapeStringConstant(t
,0);
652 case '\\': // escaped string literal
655 stringbuffer
.reset();
664 c
= escapeSequence();
665 stringbuffer
.writeUTF8(c
);
669 c
= escapeSequence();
670 stringbuffer
.writeByte(c
);
673 } while (*p
== '\\');
674 t
->len
= stringbuffer
.offset
;
675 stringbuffer
.writeByte(0);
676 t
->ustring
= (unsigned char *)mem
.malloc(stringbuffer
.offset
);
677 memcpy(t
->ustring
, stringbuffer
.data
, stringbuffer
.offset
);
679 t
->value
= TOKstring
;
686 case 'a': case 'b': case 'c': case 'd': case 'e':
687 case 'f': case 'g': case 'h': case 'i': case 'j':
688 case 'k': case 'm': case 'n': case 'o':
690 case 'p': /*case 'q': case 'r':*/ case 's': case 't':
692 case 'p': case 'q': /*case 'r':*/ case 's': case 't':
694 case 'u': case 'v': case 'w': /*case 'x':*/ case 'y':
696 case 'A': case 'B': case 'C': case 'D': case 'E':
697 case 'F': case 'G': case 'H': case 'I': case 'J':
698 case 'K': case 'M': case 'N': case 'O':
699 case 'P': case 'Q': case 'R': case 'S': case 'T':
700 case 'U': case 'V': case 'W': case 'X': case 'Y':
711 } while (isidchar(c
) || (c
& 0x80 && isUniAlpha(decodeUTF())));
712 sv
= stringtable
.update((char *)t
->ptr
, p
- t
->ptr
);
713 id
= (Identifier
*) sv
->ptrvalue
;
715 { id
= new Identifier(sv
->lstring
.string
,TOKidentifier
);
719 t
->value
= (enum TOK
) id
->value
;
722 if (t
->value
== TOKand
||
726 t
->value
= TOKidentifier
;
730 if (*t
->ptr
== '_') // if special identifier token
732 static char date
[11+1];
733 static char time
[8+1];
734 static char timestamp
[24+1];
736 if (!date
[0]) // lazy evaluation
743 sprintf(date
, "%.6s %.4s", p
+ 4, p
+ 20);
744 sprintf(time
, "%.8s", p
+ 11);
745 sprintf(timestamp
, "%.24s", p
);
749 if (mod
&& id
== Id::FILE)
751 t
->ustring
= (unsigned char *)(loc
.filename
? loc
.filename
: mod
->ident
->toChars());
754 else if (mod
&& id
== Id::LINE
)
756 t
->value
= TOKint64v
;
757 t
->uns64value
= loc
.linnum
;
763 t
->ustring
= (unsigned char *)date
;
766 else if (id
== Id::TIME
)
768 t
->ustring
= (unsigned char *)time
;
771 else if (id
== Id::VENDOR
)
774 t
->ustring
= (unsigned char *)"GDC";
776 t
->ustring
= (unsigned char *)"Digital Mars D";
780 else if (id
== Id::TIMESTAMP
)
782 t
->ustring
= (unsigned char *)timestamp
;
784 t
->value
= TOKstring
;
787 t
->len
= strlen((char *)t
->ustring
);
789 else if (id
== Id::VERSIONX
)
790 { unsigned major
= 0;
793 for (char *p
= global
.version
+ 1; 1; p
++)
797 minor
= minor
* 10 + c
- '0';
805 t
->value
= TOKint64v
;
806 t
->uns64value
= major
* 1000 + minor
;
809 else if (id
== Id::EOFX
)
812 // Advance scanner to end of file
813 while (!(*p
== 0 || *p
== 0x1A))
818 //printf("t->value = %d\n",t->value);
828 t
->value
= TOKdivass
;
837 { unsigned char c
= *p
;
856 error("unterminated /* */ comment");
863 { unsigned u
= decodeUTF();
864 if (u
== PS
|| u
== LS
)
873 if (p
[-2] == '*' && p
- 3 != t
->ptr
)
878 t
->value
= TOKcomment
;
881 else if (doDocComment
&& t
->ptr
[2] == '*' && p
- 4 != t
->ptr
)
882 { // if /** but not /**/
883 getDocComment(t
, lastLine
== linnum
);
887 case '/': // do // style comments
890 { unsigned char c
= *++p
;
906 t
->value
= TOKcomment
;
909 if (doDocComment
&& t
->ptr
[2] == '/')
910 getDocComment(t
, lastLine
== linnum
);
917 { unsigned u
= decodeUTF();
918 if (u
== PS
|| u
== LS
)
930 t
->value
= TOKcomment
;
933 if (doDocComment
&& t
->ptr
[2] == '/')
934 getDocComment(t
, lastLine
== linnum
);
947 { unsigned char c
= *p
;
982 error("unterminated /+ +/ comment");
989 { unsigned u
= decodeUTF();
990 if (u
== PS
|| u
== LS
)
1000 t
->value
= TOKcomment
;
1003 if (doDocComment
&& t
->ptr
[2] == '+' && p
- 4 != t
->ptr
)
1004 { // if /++ but not /++/
1005 getDocComment(t
, lastLine
== linnum
);
1016 { /* Note that we don't allow ._1 and ._ as being
1017 * valid floating point numbers.
1020 t
->value
= inreal(t
);
1022 else if (p
[0] == '.')
1026 t
->value
= TOKdotdotdot
;
1030 t
->value
= TOKslice
;
1041 t
->value
= TOKandass
;
1045 t
->value
= TOKandand
;
1047 error("Use 'and' instead of '&&'");
1057 t
->value
= TOKorass
;
1063 error("Use 'or' instead of '||'");
1073 t
->value
= TOKminass
;
1078 t
->value
= TOKarrow
;
1083 t
->value
= TOKminusminus
;
1093 t
->value
= TOKaddass
;
1097 t
->value
= TOKplusplus
;
1107 t
->value
= TOKle
; // <=
1113 t
->value
= TOKshlass
; // <<=
1116 t
->value
= TOKshl
; // <<
1122 t
->value
= TOKleg
; // <>=
1125 t
->value
= TOKlg
; // <>
1128 t
->value
= TOKlt
; // <
1135 t
->value
= TOKge
; // >=
1141 t
->value
= TOKshrass
; // >>=
1147 t
->value
= TOKushrass
; // >>>=
1150 t
->value
= TOKushr
; // >>>
1153 t
->value
= TOKshr
; // >>
1156 t
->value
= TOKgt
; // >
1163 if (*p
== '=' && global
.params
.Dversion
== 1)
1165 t
->value
= TOKnotidentity
; // !==
1168 t
->value
= TOKnotequal
; // !=
1176 t
->value
= TOKunord
; // !<>=
1179 t
->value
= TOKue
; // !<>
1183 t
->value
= TOKug
; // !<=
1186 t
->value
= TOKuge
; // !<
1192 t
->value
= TOKul
; // !>=
1195 t
->value
= TOKule
; // !>
1198 t
->value
= TOKnot
; // !
1205 if (*p
== '=' && global
.params
.Dversion
== 1)
1207 t
->value
= TOKidentity
; // ===
1210 t
->value
= TOKequal
; // ==
1213 t
->value
= TOKassign
; // =
1220 t
->value
= TOKcatass
; // ~=
1223 t
->value
= TOKtilde
; // ~
1226 #define NESTED(cin,tokin,cout,tokout) \
1227 case cin: nesting++; p++; t->value = tokin; return;\
1228 case cout: if (nesting == 0) {error("Unexpected '%c'", cout);} else {nesting--;} p++; t->value = tokout; return;
1230 NESTED('(', TOKlparen
, ')', TOKrparen
)
1231 NESTED('[', TOKlbracket
, ']', TOKrbracket
)
1232 NESTED('{', TOKlcurly
, '}', TOKrcurly
)
1235 #define SINGLE(c,tok) case c: p++; t->value = tok; return;
1236 SINGLE('?', TOKquestion
)
1237 SINGLE(',', TOKcomma
)
1238 SINGLE(';', TOKsemicolon
)
1239 SINGLE('$', TOKdollar
)
1248 t
->value
= TOKcolon
;
1251 #define DOUBLE(c1,tok1,c2,tok2) \
1262 DOUBLE('*', TOKmul
, '=', TOKmulass
)
1263 DOUBLE('%', TOKmod
, '=', TOKmodass
)
1264 DOUBLE('^', TOKxor
, '=', TOKxorass
)
1268 case '#': // do # style comments and pragmas
1271 do { p
++; } while (*p
!= '\n');
1281 { unsigned char c
= *p
;
1284 { unsigned u
= decodeUTF();
1286 // Check for start of unicode identifier
1290 if (u
== PS
|| u
== LS
)
1298 error("unsupported char '%c'", c
);
1300 error("unsupported char 0x%02x", c
);
1308 /*******************************************
1309 * Parse escape sequence.
1312 unsigned Lexer::escapeSequence()
1328 case 'a': c
= 7; goto Lconsume
;
1329 case 'b': c
= 8; goto Lconsume
;
1330 case 'f': c
= 12; goto Lconsume
;
1331 case 'n': c
= 10; goto Lconsume
;
1332 case 'r': c
= 13; goto Lconsume
;
1333 case 't': c
= 9; goto Lconsume
;
1334 case 'v': c
= 11; goto Lconsume
;
1356 else if (islower(c
))
1365 { error("escape hex sequence has %d hex digits instead of %d", n
, ndigits
);
1369 if (ndigits
!= 2 && !utf_isValidDchar(v
))
1370 error("invalid UTF character \\U%08x", v
);
1374 error("undefined escape hex sequence \\%c\n",c
);
1377 case '&': // named character entity
1378 for (unsigned char *idstart
= ++p
; 1; p
++)
1383 c
= HtmlNamedEntity(idstart
, p
- idstart
);
1385 { error("unnamed character entity &%.*s;", (int)(p
- idstart
), idstart
);
1393 (p
!= idstart
+ 1 && isdigit(*p
)))
1395 error("unterminated named entity");
1403 case 0x1A: // end of file
1415 v
= v
* 8 + (c
- '0');
1417 } while (++n
< 3 && isoctal(c
));
1420 error("0%03o is larger than a byte", c
);
1423 error("undefined escape sequence \\%c\n",c
);
1429 /**************************************
1432 TOK
Lexer::wysiwygStringConstant(Token
*t
, int tc
)
1437 stringbuffer
.reset();
1450 c
= '\n'; // treat EndOfLine as \n character
1456 error("unterminated string constant starting at %s", start
.toChars());
1457 t
->ustring
= (unsigned char *)"";
1466 t
->len
= stringbuffer
.offset
;
1467 stringbuffer
.writeByte(0);
1468 t
->ustring
= (unsigned char *)mem
.malloc(stringbuffer
.offset
);
1469 memcpy(t
->ustring
, stringbuffer
.data
, stringbuffer
.offset
);
1478 unsigned u
= decodeUTF();
1480 if (u
== PS
|| u
== LS
)
1482 stringbuffer
.writeUTF8(u
);
1487 stringbuffer
.writeByte(c
);
1491 /**************************************
1496 TOK
Lexer::hexStringConstant(Token
*t
)
1503 stringbuffer
.reset();
1513 continue; // skip white space
1518 // Treat isolated '\r' as if it were a '\n'
1525 error("unterminated string constant starting at %s", start
.toChars());
1526 t
->ustring
= (unsigned char *)"";
1533 { error("odd number (%d) of hex characters in hex string", n
);
1534 stringbuffer
.writeByte(v
);
1536 t
->len
= stringbuffer
.offset
;
1537 stringbuffer
.writeByte(0);
1538 t
->ustring
= (unsigned char *)mem
.malloc(stringbuffer
.offset
);
1539 memcpy(t
->ustring
, stringbuffer
.data
, stringbuffer
.offset
);
1544 if (c
>= '0' && c
<= '9')
1546 else if (c
>= 'a' && c
<= 'f')
1548 else if (c
>= 'A' && c
<= 'F')
1552 unsigned u
= decodeUTF();
1554 if (u
== PS
|| u
== LS
)
1557 error("non-hex character \\u%x", u
);
1560 error("non-hex character '%c'", c
);
1563 stringbuffer
.writeByte(v
);
1575 /**************************************
1576 * Lex delimited strings:
1577 * q"(foo(xxx))" // "foo(xxx)"
1578 * q"[foo(]" // "foo("
1579 * q"/foo]/" // "foo]"
1587 TOK
Lexer::delimitedStringConstant(Token
*t
)
1590 unsigned delimleft
= 0;
1591 unsigned delimright
= 0;
1594 Identifier
*hereid
= NULL
;
1595 unsigned blankrol
= 0;
1596 unsigned startline
= 0;
1599 stringbuffer
.reset();
1603 //printf("c = '%c'\n", c);
1616 stringbuffer
.writeUTF8(c
);
1624 c
= '\n'; // treat EndOfLine as \n character
1636 if (c
== PS
|| c
== LS
)
1653 else if (isalpha(c
) || c
== '_' || (c
>= 0x80 && isUniAlpha(c
)))
1654 { // Start of identifier; must be a heredoc
1657 scan(&t
); // read in heredoc identifier
1658 if (t
.value
!= TOKidentifier
)
1659 { error("identifier expected for heredoc, not %s", t
.toChars());
1664 //printf("hereid = '%s'\n", hereid->toChars());
1677 { error("heredoc rest of line should be blank");
1685 else if (c
== delimright
)
1691 else if (c
== delimright
)
1693 if (startline
&& isalpha(c
))
1695 unsigned char *psave
= p
;
1697 scan(&t
); // read in possible heredoc identifier
1698 //printf("endid = '%s'\n", t.ident->toChars());
1699 if (t
.value
== TOKidentifier
&& t
.ident
->equals(hereid
))
1700 { /* should check that rest of line is blank
1706 stringbuffer
.writeUTF8(c
);
1715 error("delimited string must end in %c\"", delimright
);
1716 t
->len
= stringbuffer
.offset
;
1717 stringbuffer
.writeByte(0);
1718 t
->ustring
= (unsigned char *)mem
.malloc(stringbuffer
.offset
);
1719 memcpy(t
->ustring
, stringbuffer
.data
, stringbuffer
.offset
);
1724 error("unterminated string constant starting at %s", start
.toChars());
1725 t
->ustring
= (unsigned char *)"";
1731 /**************************************
1732 * Lex delimited strings:
1733 * q{ foo(xxx) } // " foo(xxx) "
1735 * q{{foo}"}"} // "{foo}"}""
1740 TOK
Lexer::tokenStringConstant(Token
*t
)
1744 unsigned char *pstart
= ++p
;
1771 t
->len
= p
- 1 - pstart
;
1772 t
->ustring
= (unsigned char *)mem
.malloc(t
->len
+ 1);
1773 memcpy(t
->ustring
, pstart
, t
->len
);
1774 t
->ustring
[t
->len
] = 0;
1779 error("unterminated token string constant starting at %s", start
.toChars());
1780 t
->ustring
= (unsigned char *)"";
1789 /**************************************
1792 TOK
Lexer::escapeStringConstant(Token
*t
, int wide
)
1797 stringbuffer
.reset();
1809 c
= escapeSequence();
1810 stringbuffer
.writeUTF8(c
);
1814 c
= escapeSequence();
1826 c
= '\n'; // treat EndOfLine as \n character
1831 t
->len
= stringbuffer
.offset
;
1832 stringbuffer
.writeByte(0);
1833 t
->ustring
= (unsigned char *)mem
.malloc(stringbuffer
.offset
);
1834 memcpy(t
->ustring
, stringbuffer
.data
, stringbuffer
.offset
);
1841 error("unterminated string constant starting at %s", start
.toChars());
1842 t
->ustring
= (unsigned char *)"";
1852 if (c
== LS
|| c
== PS
)
1857 stringbuffer
.writeUTF8(c
);
1862 stringbuffer
.writeByte(c
);
1866 /**************************************
1869 TOK
Lexer::charConstant(Token
*t
, int wide
)
1874 //printf("Lexer::charConstant\n");
1883 t
->uns64value
= escapeSequence();
1889 t
->uns64value
= escapeSequence();
1894 t
->uns64value
= escapeSequence();
1906 error("unterminated character constant");
1915 if (c
== LS
|| c
== PS
)
1917 if (c
< 0xD800 || (c
>= 0xE000 && c
< 0xFFFE))
1927 { error("unterminated character constant");
1934 /***************************************
1935 * Get postfix of string literal.
1938 void Lexer::stringPostfix(Token
*t
)
1955 /***************************************
1956 * Read \u or \U unicode sequence
1962 unsigned Lexer::wchar(unsigned u
)
1969 nchars
= (u
== 'U') ? 8 : 4;
1978 { error("\\%c sequence must be followed by %d hex characters", u
, nchars
);
1983 else if (islower(c
))
1994 /**************************************
1996 * If it's an integer, store it in tok.TKutok.Vlong.
1997 * integers can be decimal, octal or hex
1998 * Handle the suffixes U, UL, LU, L, etc.
1999 * If it's double, store it in tok.TKutok.Vdouble.
2005 TOK
Lexer::number(Token
*t
)
2007 // We use a state machine to collect numbers
2008 enum STATE
{ STATE_initial
, STATE_0
, STATE_decimal
, STATE_octal
, STATE_octale
,
2009 STATE_hex
, STATE_binary
, STATE_hex0
, STATE_binary0
,
2010 STATE_hexh
, STATE_error
};
2014 { FLAGS_decimal
= 1, // decimal
2015 FLAGS_unsigned
= 2, // u or U suffix
2016 FLAGS_long
= 4, // l or L suffix
2018 enum FLAGS flags
= FLAGS_decimal
;
2023 unsigned char *start
;
2026 //printf("Lexer::number()\n");
2027 state
= STATE_initial
;
2029 stringbuffer
.reset();
2036 case STATE_initial
: // opening state
2040 state
= STATE_decimal
;
2044 flags
= (FLAGS
) (flags
& ~FLAGS_decimal
);
2058 if (p
[1] == '.') // .. is a separate token
2071 state
= STATE_binary0
;
2074 case '0': case '1': case '2': case '3':
2075 case '4': case '5': case '6': case '7':
2076 state
= STATE_octal
;
2080 case '8': case '9': case 'A':
2081 case 'C': case 'D': case 'F':
2082 case 'a': case 'c': case 'd': case 'f':
2088 state
= STATE_octal
;
2102 case STATE_decimal
: // reading decimal number
2107 || c
== 'H' || c
== 'h'
2111 if (c
== '_') // ignore embedded _
2115 if (c
== '.' && p
[1] != '.')
2117 else if (c
== 'i' || c
== 'f' || c
== 'F' ||
2118 c
== 'e' || c
== 'E')
2120 real
: // It's a real number. Back up and rescan as a real
2124 else if (c
== 'L' && p
[1] == 'i')
2130 case STATE_hex0
: // reading hex number
2134 if (c
== '_') // ignore embedded _
2138 if (c
== '.' && p
[1] != '.')
2140 if (c
== 'P' || c
== 'p' || c
== 'i')
2142 if (state
== STATE_hex0
)
2143 error("Hex digit expected, not '%c'", c
);
2152 case STATE_hexh
: // parse numbers like 0FFh
2155 if (c
== 'H' || c
== 'h')
2163 // Check for something like 1E3 or 0E24
2164 if (memchr((char *)stringbuffer
.data
, 'E', stringbuffer
.offset
) ||
2165 memchr((char *)stringbuffer
.data
, 'e', stringbuffer
.offset
))
2167 error("Hex digit expected, not '%c'", c
);
2174 case STATE_octal
: // reading octal number
2175 case STATE_octale
: // reading octal number with non-octal digits
2180 || c
== 'H' || c
== 'h'
2184 if (c
== '_') // ignore embedded _
2188 if (c
== '.' && p
[1] != '.')
2194 state
= STATE_octale
;
2201 case STATE_binary0
: // starting binary number
2202 case STATE_binary
: // reading binary number
2203 if (c
!= '0' && c
!= '1')
2207 || c
== 'H' || c
== 'h'
2211 if (c
== '_') // ignore embedded _
2215 if (state
== STATE_binary0
)
2216 { error("binary digit expected");
2217 state
= STATE_error
;
2223 state
= STATE_binary
;
2226 case STATE_error
: // for error recovery
2227 if (!isdigit(c
)) // scan until non-digit
2234 stringbuffer
.writeByte(c
);
2238 stringbuffer
.writeByte(0); // terminate string
2239 if (state
== STATE_octale
)
2240 error("Octal digit expected");
2242 uinteger_t n
; // unsigned >=64 bit integer type
2244 if (stringbuffer
.offset
== 2 && (state
== STATE_decimal
|| state
== STATE_0
))
2245 n
= stringbuffer
.data
[0] - '0';
2248 // Convert string to integer
2251 n
= strtoull((char *)stringbuffer
.data
,NULL
,base
);
2252 if (errno
== ERANGE
)
2253 error("integer overflow");
2255 // Not everybody implements strtoull()
2256 char *p
= (char *)stringbuffer
.data
;
2261 if (p
[1] == 'x' || p
[1] == 'X')
2263 else if (p
[1] == 'b' || p
[1] == 'B')
2265 else if (isdigit(p
[1]))
2272 if (*p
>= '0' && *p
<= '9')
2274 else if (*p
>= 'a' && *p
<= 'z')
2276 else if (*p
>= 'A' && *p
<= 'Z')
2282 if (n
&& n
* r
+ d
<= n
)
2284 error ("integer overflow");
2292 if (sizeof(n
) > 8 &&
2293 n
> 0xFFFFFFFFFFFFFFFFULL
) // if n needs more than 64 bits
2294 error("integer overflow");
2297 // Parse trailing 'u', 'U', 'l' or 'L' in any combination
2308 if (1 || !global
.params
.useDeprecated
)
2309 error("'l' suffix is deprecated, use 'L' instead");
2315 error("unrecognized token");
2316 flags
= (FLAGS
) (flags
| f
);
2327 /* Octal or Hexadecimal constant.
2328 * First that fits: int, uint, long, ulong
2330 if (n
& 0x8000000000000000LL
)
2332 else if (n
& 0xFFFFFFFF00000000LL
)
2334 else if (n
& 0x80000000)
2341 /* First that fits: int, long, long long
2343 if (n
& 0x8000000000000000LL
)
2344 { error("signed integer overflow");
2347 else if (n
& 0xFFFFFFFF80000000LL
)
2353 case FLAGS_unsigned
:
2354 case FLAGS_decimal
| FLAGS_unsigned
:
2355 /* First that fits: uint, ulong
2357 if (n
& 0xFFFFFFFF00000000LL
)
2363 case FLAGS_decimal
| FLAGS_long
:
2364 if (n
& 0x8000000000000000LL
)
2365 { error("signed integer overflow");
2373 if (n
& 0x8000000000000000LL
)
2379 case FLAGS_unsigned
| FLAGS_long
:
2380 case FLAGS_decimal
| FLAGS_unsigned
| FLAGS_long
:
2386 printf("%x\n",flags
);
2394 /**************************************
2395 * Read in characters, converting them to real.
2397 * Exponent overflow not detected.
2398 * Too much requested precision is not detected.
2401 TOK
Lexer::inreal(Token
*t
)
2405 assert(*p
== '.' || isdigit(*p
));
2414 case TOKimaginary32v
:
2415 case TOKimaginary64v
:
2416 case TOKimaginary80v
:
2424 #endif /* __DMC__ */
2427 char hex
; // is this a hexadecimal-floating-constant?
2430 //printf("Lexer::inreal()\n");
2431 stringbuffer
.reset();
2437 // Get next char from input
2439 //printf("dblstate = %d, c = '%c'\n", dblstate, c);
2444 case 0: // opening state
2455 if (c
== 'X' || c
== 'x')
2459 case 1: // digits to left of .
2460 case 3: // digits to right of .
2461 case 7: // continuing exponent digits
2462 if (!isdigit(c
) && !(hex
&& isxdigit(c
)))
2465 goto Lnext
; // ignore embedded '_'
2471 case 2: // no more digits to left of .
2476 case 4: // no more digits to right of .
2477 if ((c
== 'E' || c
== 'e') ||
2478 hex
&& (c
== 'P' || c
== 'p'))
2480 hex
= 0; // exponent is always decimal
2484 error("binary-exponent-part required");
2487 case 5: // looking immediately to right of E
2489 if (c
== '-' || c
== '+')
2491 case 6: // 1st exponent digit expected
2493 error("exponent expected");
2497 case 8: // past end of exponent digits
2502 stringbuffer
.writeByte(c
);
2507 stringbuffer
.writeByte(0);
2509 #if _WIN32 && __DMC__
2510 char *save
= __locale_decpoint
;
2511 __locale_decpoint
= ".";
2514 t
->float80value
= real_t::parse((char *)stringbuffer
.data
, real_t::LongDouble
);
2516 t
->float80value
= strtold((char *)stringbuffer
.data
, NULL
);
2524 real_t::parse((char *)stringbuffer
.data
, real_t::Float
);
2526 strtof((char *)stringbuffer
.data
, NULL
);
2528 result
= TOKfloat32v
;
2534 real_t::parse((char *)stringbuffer
.data
, real_t::Double
);
2536 strtod((char *)stringbuffer
.data
, NULL
);
2538 result
= TOKfloat64v
;
2542 if (!global
.params
.useDeprecated
)
2543 error("'l' suffix is deprecated, use 'L' instead");
2545 result
= TOKfloat80v
;
2549 if (*p
== 'i' || *p
== 'I')
2551 if (!global
.params
.useDeprecated
&& *p
== 'I')
2552 error("'I' suffix is deprecated, use 'i' instead");
2557 result
= TOKimaginary32v
;
2560 result
= TOKimaginary64v
;
2563 result
= TOKimaginary80v
;
2567 #if _WIN32 && __DMC__
2568 __locale_decpoint
= save
;
2570 if (errno
== ERANGE
)
2571 error("number is not representable");
2575 /*********************************************
2577 * Currently, the only pragma supported is:
2578 * #line linnum [filespec]
2581 void Lexer::pragma()
2585 char *filespec
= NULL
;
2586 Loc loc
= this->loc
;
2588 while (isblank(*p
)) p
++;
2593 if (tok
.value
!= TOKidentifier
|| tok
.ident
!= Id::line
)
2597 if (tok
.value
== TOKint32v
|| tok
.value
== TOKint64v
)
2598 linnum
= tok
.uns64value
- 1;
2610 this->loc
.linnum
= linnum
;
2612 this->loc
.filename
= filespec
;
2628 continue; // skip white space
2631 if (mod
&& memcmp(p
, "__FILE__", 8) == 0)
2634 filespec
= mem
.strdup(loc
.filename
? loc
.filename
: mod
->ident
->toChars());
2641 stringbuffer
.reset();
2656 stringbuffer
.writeByte(0);
2657 filespec
= mem
.strdup((char *)stringbuffer
.data
);
2663 { unsigned u
= decodeUTF();
2664 if (u
== PS
|| u
== LS
)
2667 stringbuffer
.writeByte(c
);
2677 { unsigned u
= decodeUTF();
2678 if (u
== PS
|| u
== LS
)
2686 error(loc
, "#line integer [\"filespec\"]\\n expected");
2690 /********************************************
2691 * Decode UTF character.
2692 * Issue error messages for invalid sequences.
2693 * Return decoded character, advance p to last character in UTF sequence.
2696 unsigned Lexer::decodeUTF()
2700 unsigned char *s
= p
;
2708 // Check length of remaining string up to 6 UTF-8 characters
2709 for (len
= 1; len
< 6 && s
[len
]; len
++)
2713 msg
= utf_decodeChar(s
, len
, &idx
, &u
);
2723 /***************************************************
2724 * Parse doc comment embedded between t->ptr and p.
2725 * Remove trailing blanks and tabs from lines.
2726 * Replace all newlines with \n.
2727 * Remove leading comment character from each line.
2728 * Decide if it's a lineComment or a blockComment.
2729 * Append to previous one for this token.
2732 void Lexer::getDocComment(Token
*t
, unsigned lineComment
)
2735 unsigned char ct
= t
->ptr
[2];
2736 unsigned char *q
= t
->ptr
+ 3; // start of comment text
2739 unsigned char *qend
= p
;
2740 if (ct
== '*' || ct
== '+')
2743 /* Scan over initial row of ****'s or ++++'s or ////'s
2745 for (; q
< qend
; q
++)
2751 /* Remove trailing row of ****'s or ++++'s
2755 for (; q
< qend
; qend
--)
2762 for (; q
< qend
; q
++)
2764 unsigned char c
= *q
;
2770 if (linestart
&& c
== ct
)
2772 /* Trim preceding whitespace up to preceding \n
2774 while (buf
.offset
&& (buf
.data
[buf
.offset
- 1] == ' ' || buf
.data
[buf
.offset
- 1] == '\t'))
2786 continue; // skip the \r
2794 (q
[2] == 168 || q
[2] == 169))
2804 c
= '\n'; // replace all newlines with \n
2808 /* Trim trailing whitespace
2810 while (buf
.offset
&& (buf
.data
[buf
.offset
- 1] == ' ' || buf
.data
[buf
.offset
- 1] == '\t'))
2818 // Always end with a newline
2819 if (!buf
.offset
|| buf
.data
[buf
.offset
- 1] != '\n')
2820 buf
.writeByte('\n');
2824 // It's a line comment if the start of the doc comment comes
2825 // after other non-whitespace on the same line.
2826 unsigned char** dc
= (lineComment
&& anyToken
)
2830 // Combine with previous doc comment, if any
2832 *dc
= combineComments(*dc
, (unsigned char *)buf
.data
);
2834 *dc
= (unsigned char *)buf
.extractData();
2837 /********************************************
2838 * Combine two document comments into one.
2841 unsigned char *Lexer::combineComments(unsigned char *c1
, unsigned char *c2
)
2843 unsigned char *c
= c2
;
2848 { size_t len1
= strlen((char *)c1
);
2849 size_t len2
= strlen((char *)c2
);
2851 c
= (unsigned char *)mem
.malloc(len1
+ 1 + len2
+ 1);
2852 memcpy(c
, c1
, len1
);
2854 memcpy(c
+ len1
+ 1, c2
, len2
);
2855 c
[len1
+ 1 + len2
] = 0;
2861 /********************************************
2862 * Create an identifier in the string table.
2865 Identifier
*Lexer::idPool(const char *s
)
2867 size_t len
= strlen(s
);
2868 StringValue
*sv
= stringtable
.update(s
, len
);
2869 Identifier
*id
= (Identifier
*) sv
->ptrvalue
;
2872 id
= new Identifier(sv
->lstring
.string
, TOKidentifier
);
2878 /*********************************************
2879 * Create a unique identifier using the prefix s.
2882 Identifier
*Lexer::uniqueId(const char *s
, int num
)
2884 size_t slen
= strlen(s
);
2886 assert(slen
+ sizeof(num
) * 3 + 1 <= sizeof(buffer
));
2887 sprintf(buffer
, "%s%d", s
, num
);
2888 return idPool(buffer
);
2891 Identifier
*Lexer::uniqueId(const char *s
)
2894 return uniqueId(s
, ++num
);
2897 /****************************************
2905 static Keyword keywords
[] =
2909 { "this", TOKthis
},
2910 { "super", TOKsuper
},
2911 { "assert", TOKassert
},
2912 { "null", TOKnull
},
2913 { "true", TOKtrue
},
2914 { "false", TOKfalse
},
2915 { "cast", TOKcast
},
2917 { "delete", TOKdelete
},
2918 { "throw", TOKthrow
},
2919 { "module", TOKmodule
},
2920 { "pragma", TOKpragma
},
2921 { "typeof", TOKtypeof
},
2922 { "typeid", TOKtypeid
},
2924 { "template", TOKtemplate
},
2926 { "void", TOKvoid
},
2927 { "byte", TOKint8
},
2928 { "ubyte", TOKuns8
},
2929 { "short", TOKint16
},
2930 { "ushort", TOKuns16
},
2931 { "int", TOKint32
},
2932 { "uint", TOKuns32
},
2933 { "long", TOKint64
},
2934 { "ulong", TOKuns64
},
2935 { "cent", TOKcent
, },
2936 { "ucent", TOKucent
, },
2937 { "float", TOKfloat32
},
2938 { "double", TOKfloat64
},
2939 { "real", TOKfloat80
},
2941 { "bool", TOKbool
},
2942 { "char", TOKchar
},
2943 { "wchar", TOKwchar
},
2944 { "dchar", TOKdchar
},
2946 { "ifloat", TOKimaginary32
},
2947 { "idouble", TOKimaginary64
},
2948 { "ireal", TOKimaginary80
},
2950 { "cfloat", TOKcomplex32
},
2951 { "cdouble", TOKcomplex64
},
2952 { "creal", TOKcomplex80
},
2954 { "delegate", TOKdelegate
},
2955 { "function", TOKfunction
},
2959 { "else", TOKelse
},
2960 { "while", TOKwhile
},
2963 { "switch", TOKswitch
},
2964 { "case", TOKcase
},
2965 { "default", TOKdefault
},
2966 { "break", TOKbreak
},
2967 { "continue", TOKcontinue
},
2968 { "synchronized", TOKsynchronized
},
2969 { "return", TOKreturn
},
2970 { "goto", TOKgoto
},
2972 { "catch", TOKcatch
},
2973 { "finally", TOKfinally
},
2974 { "with", TOKwith
},
2976 { "foreach", TOKforeach
},
2977 { "foreach_reverse", TOKforeach_reverse
},
2978 { "reversed", TOKreversed
},
2979 { "scope", TOKscope
},
2981 { "struct", TOKstruct
},
2982 { "class", TOKclass
},
2983 { "interface", TOKinterface
},
2984 { "union", TOKunion
},
2985 { "enum", TOKenum
},
2986 { "import", TOKimport
},
2987 { "mixin", TOKmixin
},
2988 { "static", TOKstatic
},
2989 { "final", TOKfinal
},
2990 { "const", TOKconst
},
2991 { "typedef", TOKtypedef
},
2992 { "alias", TOKalias
},
2993 { "override", TOKoverride
},
2994 { "abstract", TOKabstract
},
2995 { "volatile", TOKvolatile
},
2996 { "debug", TOKdebug
},
2997 { "deprecated", TOKdeprecated
},
3000 { "inout", TOKinout
},
3001 { "lazy", TOKlazy
},
3002 { "auto", TOKauto
},
3004 { "align", TOKalign
},
3005 { "extern", TOKextern
},
3006 { "private", TOKprivate
},
3007 { "package", TOKpackage
},
3008 { "protected", TOKprotected
},
3009 { "public", TOKpublic
},
3010 { "export", TOKexport
},
3012 { "body", TOKbody
},
3013 { "invariant", TOKinvariant
},
3014 { "unittest", TOKunittest
},
3015 { "version", TOKversion
},
3016 //{ "manifest", TOKmanifest },
3020 { "macro", TOKmacro
},
3024 { "and", TOKandand
},
3027 { "extends", TOKextends
},
3028 { "implements", TOKimplements
},
3029 { "log_error", TOKlog_error
},
3030 { "log_warning", TOKlog_warning
},
3031 { "log_info", TOKlog_info
},
3032 { "log_trace", TOKlog_trace
},
3034 { "pure", TOKpure
},
3035 { "nothrow", TOKnothrow
},
3036 { "__thread", TOKtls
},
3037 { "__traits", TOKtraits
},
3038 { "__overloadset", TOKoverloadset
},
3039 { "__FILE__", TOKfile
},
3040 { "__LINE__", TOKline
},
3044 int Token::isKeyword()
3046 for (unsigned u
= 0; u
< sizeof(keywords
) / sizeof(keywords
[0]); u
++)
3048 if (keywords
[u
].value
== value
)
3054 void Lexer::initKeywords()
3058 unsigned nkeywords
= sizeof(keywords
) / sizeof(keywords
[0]);
3060 if (global
.params
.Dversion
== 1)
3065 for (u
= 0; u
< nkeywords
; u
++)
3068 //printf("keyword[%d] = '%s'\n",u, keywords[u].name);
3069 s
= keywords
[u
].name
;
3070 v
= keywords
[u
].value
;
3071 sv
= stringtable
.insert(s
, strlen(s
));
3072 sv
->ptrvalue
= (void *) new Identifier(sv
->lstring
.string
,v
);
3074 //printf("tochars[%d] = '%s'\n",v, s);
3075 Token::tochars
[v
] = s
;
3078 Token::tochars
[TOKeof
] = "EOF";
3079 Token::tochars
[TOKlcurly
] = "{";
3080 Token::tochars
[TOKrcurly
] = "}";
3081 Token::tochars
[TOKlparen
] = "(";
3082 Token::tochars
[TOKrparen
] = ")";
3083 Token::tochars
[TOKlbracket
] = "[";
3084 Token::tochars
[TOKrbracket
] = "]";
3085 Token::tochars
[TOKsemicolon
] = ";";
3086 Token::tochars
[TOKcolon
] = ":";
3087 Token::tochars
[TOKcomma
] = ",";
3088 Token::tochars
[TOKdot
] = ".";
3089 Token::tochars
[TOKxor
] = "^";
3090 Token::tochars
[TOKxorass
] = "^=";
3091 Token::tochars
[TOKassign
] = "=";
3092 Token::tochars
[TOKconstruct
] = "=";
3094 Token::tochars
[TOKblit
] = "=";
3096 Token::tochars
[TOKlt
] = "<";
3097 Token::tochars
[TOKgt
] = ">";
3098 Token::tochars
[TOKle
] = "<=";
3099 Token::tochars
[TOKge
] = ">=";
3100 Token::tochars
[TOKequal
] = "==";
3101 Token::tochars
[TOKnotequal
] = "!=";
3102 Token::tochars
[TOKnotidentity
] = "!is";
3103 Token::tochars
[TOKtobool
] = "!!";
3104 Token::tochars
[TOKat
] = "@";
3106 Token::tochars
[TOKunord
] = "!<>=";
3107 Token::tochars
[TOKue
] = "!<>";
3108 Token::tochars
[TOKlg
] = "<>";
3109 Token::tochars
[TOKleg
] = "<>=";
3110 Token::tochars
[TOKule
] = "!>";
3111 Token::tochars
[TOKul
] = "!>=";
3112 Token::tochars
[TOKuge
] = "!<";
3113 Token::tochars
[TOKug
] = "!<=";
3115 Token::tochars
[TOKnot
] = "!";
3116 Token::tochars
[TOKtobool
] = "!!";
3117 Token::tochars
[TOKshl
] = "<<";
3118 Token::tochars
[TOKshr
] = ">>";
3119 Token::tochars
[TOKushr
] = ">>>";
3120 Token::tochars
[TOKadd
] = "+";
3121 Token::tochars
[TOKmin
] = "-";
3122 Token::tochars
[TOKmul
] = "*";
3123 Token::tochars
[TOKdiv
] = "/";
3124 Token::tochars
[TOKmod
] = "%";
3125 Token::tochars
[TOKslice
] = "..";
3126 Token::tochars
[TOKdotdotdot
] = "...";
3127 Token::tochars
[TOKand
] = "&";
3128 Token::tochars
[TOKandand
] = "&&";
3129 Token::tochars
[TOKor
] = "|";
3130 Token::tochars
[TOKoror
] = "||";
3131 Token::tochars
[TOKarray
] = "[]";
3132 Token::tochars
[TOKindex
] = "[i]";
3133 Token::tochars
[TOKaddress
] = "&";
3134 Token::tochars
[TOKstar
] = "*";
3135 Token::tochars
[TOKtilde
] = "~";
3136 Token::tochars
[TOKdollar
] = "$";
3137 Token::tochars
[TOKcast
] = "cast";
3138 Token::tochars
[TOKplusplus
] = "++";
3139 Token::tochars
[TOKminusminus
] = "--";
3140 Token::tochars
[TOKtype
] = "type";
3141 Token::tochars
[TOKquestion
] = "?";
3142 Token::tochars
[TOKneg
] = "-";
3143 Token::tochars
[TOKuadd
] = "+";
3144 Token::tochars
[TOKvar
] = "var";
3145 Token::tochars
[TOKaddass
] = "+=";
3146 Token::tochars
[TOKminass
] = "-=";
3147 Token::tochars
[TOKmulass
] = "*=";
3148 Token::tochars
[TOKdivass
] = "/=";
3149 Token::tochars
[TOKmodass
] = "%=";
3150 Token::tochars
[TOKshlass
] = "<<=";
3151 Token::tochars
[TOKshrass
] = ">>=";
3152 Token::tochars
[TOKushrass
] = ">>>=";
3153 Token::tochars
[TOKandass
] = "&=";
3154 Token::tochars
[TOKorass
] = "|=";
3155 Token::tochars
[TOKcatass
] = "~=";
3156 Token::tochars
[TOKcat
] = "~";
3157 Token::tochars
[TOKcall
] = "call";
3158 Token::tochars
[TOKidentity
] = "is";
3159 Token::tochars
[TOKnotidentity
] = "!is";
3160 Token::tochars
[TOKendline
] = "\\n";
3162 Token::tochars
[TOKorass
] = "|=";
3163 Token::tochars
[TOKidentifier
] = "identifier";
3166 Token::tochars
[TOKdotexp
] = "dotexp";
3167 Token::tochars
[TOKdotti
] = "dotti";
3168 Token::tochars
[TOKdotvar
] = "dotvar";
3169 Token::tochars
[TOKdottype
] = "dottype";
3170 Token::tochars
[TOKsymoff
] = "symoff";
3171 Token::tochars
[TOKtypedot
] = "typedot";
3172 Token::tochars
[TOKarraylength
] = "arraylength";
3173 Token::tochars
[TOKarrayliteral
] = "arrayliteral";
3174 Token::tochars
[TOKassocarrayliteral
] = "assocarrayliteral";
3175 Token::tochars
[TOKstructliteral
] = "structliteral";
3176 Token::tochars
[TOKstring
] = "string";
3177 Token::tochars
[TOKdsymbol
] = "symbol";
3178 Token::tochars
[TOKtuple
] = "tuple";
3179 Token::tochars
[TOKdeclaration
] = "declaration";
3180 Token::tochars
[TOKdottd
] = "dottd";
3181 Token::tochars
[TOKlogger
] = "logger";
3182 Token::tochars
[TOKon_scope_exit
] = "scope(exit)";