]> git.xonotic.org Git - xonotic/gmqcc.git/blob - lexer.cpp
ir: more aggressively reuse const floats
[xonotic/gmqcc.git] / lexer.cpp
1 #include <string.h>
2 #include <stdlib.h>
3
4 #include "gmqcc.h"
5 #include "lexer.h"
6
7 /*
8  * List of Keywords
9  */
10
11 /* original */
12 static const char *keywords_qc[] = {
13     "for", "do", "while",
14     "if", "else",
15     "local",
16     "return",
17     "const"
18 };
19 /* For fte/gmgqcc */
20 static const char *keywords_fg[] = {
21     "switch", "case", "default",
22     "struct", "union",
23     "break", "continue",
24     "typedef",
25     "goto",
26
27     "__builtin_debug_printtype"
28 };
29
30 /*
31  * Lexer code
32  */
33 static char* *lex_filenames;
34
35 static void lexerror(lex_file *lex, const char *fmt, ...)
36 {
37     va_list ap;
38
39     va_start(ap, fmt);
40     if (lex)
41         con_vprintmsg(LVL_ERROR, lex->name, lex->sline, lex->column, "parse error", fmt, ap);
42     else
43         con_vprintmsg(LVL_ERROR, "", 0, 0, "parse error", fmt, ap);
44     va_end(ap);
45 }
46
47 static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
48 {
49     bool      r;
50     lex_ctx_t ctx;
51     va_list   ap;
52
53     ctx.file   = lex->name;
54     ctx.line   = lex->sline;
55     ctx.column = lex->column;
56
57     va_start(ap, fmt);
58     r = vcompile_warning(ctx, warntype, fmt, ap);
59     va_end(ap);
60     return r;
61 }
62
63 static void lex_token_new(lex_file *lex)
64 {
65     if (lex->tok.value)
66         vec_shrinkto(lex->tok.value, 0);
67
68     lex->tok.constval.t  = TYPE_VOID;
69     lex->tok.ctx.line    = lex->sline;
70     lex->tok.ctx.file    = lex->name;
71     lex->tok.ctx.column  = lex->column;
72 }
73
74 static void lex_ungetch(lex_file *lex, int ch);
75 static int lex_getch(lex_file *lex);
76
77 lex_file* lex_open(const char *file)
78 {
79     lex_file  *lex;
80     FILE *in = fopen(file, "rb");
81     uint32_t   read;
82
83     if (!in) {
84         lexerror(nullptr, "open failed: '%s'\n", file);
85         return nullptr;
86     }
87
88     lex = (lex_file*)mem_a(sizeof(*lex));
89     if (!lex) {
90         fclose(in);
91         lexerror(nullptr, "out of memory\n");
92         return nullptr;
93     }
94
95     memset(lex, 0, sizeof(*lex));
96
97     lex->file    = in;
98     lex->name    = util_strdup(file);
99     lex->line    = 1; /* we start counting at 1 */
100     lex->column  = 0;
101     lex->peekpos = 0;
102     lex->eof     = false;
103
104     /* handle BOM */
105     if ((read = (lex_getch(lex) << 16) | (lex_getch(lex) << 8) | lex_getch(lex)) != 0xEFBBBF) {
106         lex_ungetch(lex, (read & 0x0000FF));
107         lex_ungetch(lex, (read & 0x00FF00) >> 8);
108         lex_ungetch(lex, (read & 0xFF0000) >> 16);
109     } else {
110         /*
111          * otherwise the lexer has advanced 3 bytes for the BOM, we need
112          * to set the column back to 0
113          */
114         lex->column = 0;
115     }
116
117     vec_push(lex_filenames, lex->name);
118     return lex;
119 }
120
121 lex_file* lex_open_string(const char *str, size_t len, const char *name)
122 {
123     lex_file *lex;
124
125     lex = (lex_file*)mem_a(sizeof(*lex));
126     if (!lex) {
127         lexerror(nullptr, "out of memory\n");
128         return nullptr;
129     }
130
131     memset(lex, 0, sizeof(*lex));
132
133     lex->file = nullptr;
134     lex->open_string        = str;
135     lex->open_string_length = len;
136     lex->open_string_pos    = 0;
137
138     lex->name    = util_strdup(name ? name : "<string-source>");
139     lex->line    = 1; /* we start counting at 1 */
140     lex->peekpos = 0;
141     lex->eof     = false;
142     lex->column  = 0;
143
144     vec_push(lex_filenames, lex->name);
145
146     return lex;
147 }
148
149 void lex_cleanup(void)
150 {
151     size_t i;
152     for (i = 0; i < vec_size(lex_filenames); ++i)
153         mem_d(lex_filenames[i]);
154     vec_free(lex_filenames);
155 }
156
157 void lex_close(lex_file *lex)
158 {
159     size_t i;
160     for (i = 0; i < vec_size(lex->frames); ++i)
161         mem_d(lex->frames[i].name);
162     vec_free(lex->frames);
163
164     if (lex->modelname)
165         vec_free(lex->modelname);
166
167     if (lex->file)
168         fclose(lex->file);
169
170     vec_free(lex->tok.value);
171
172     /* mem_d(lex->name); collected in lex_filenames */
173     mem_d(lex);
174 }
175
176
177
178 static int lex_fgetc(lex_file *lex)
179 {
180     if (lex->file) {
181         lex->column++;
182         return fgetc(lex->file);
183     }
184     if (lex->open_string) {
185         if (lex->open_string_pos >= lex->open_string_length)
186             return EOF;
187         lex->column++;
188         return lex->open_string[lex->open_string_pos++];
189     }
190     return EOF;
191 }
192
193 /* Get or put-back data
194  * The following to functions do NOT understand what kind of data they
195  * are working on.
196  * The are merely wrapping get/put in order to count line numbers.
197  */
198 static int lex_try_trigraph(lex_file *lex, int old)
199 {
200     int c2, c3;
201     c2 = lex_fgetc(lex);
202     if (!lex->push_line && c2 == '\n') {
203         lex->line++;
204         lex->column = 0;
205     }
206
207     if (c2 != '?') {
208         lex_ungetch(lex, c2);
209         return old;
210     }
211
212     c3 = lex_fgetc(lex);
213     if (!lex->push_line && c3 == '\n') {
214         lex->line++;
215         lex->column = 0;
216     }
217
218     switch (c3) {
219         case '=': return '#';
220         case '/': return '\\';
221         case '\'': return '^';
222         case '(': return '[';
223         case ')': return ']';
224         case '!': return '|';
225         case '<': return '{';
226         case '>': return '}';
227         case '-': return '~';
228         default:
229             lex_ungetch(lex, c3);
230             lex_ungetch(lex, c2);
231             return old;
232     }
233 }
234
235 static int lex_try_digraph(lex_file *lex, int ch)
236 {
237     int c2;
238     c2 = lex_fgetc(lex);
239     /* we just used fgetc() so count lines
240      * need to offset a \n the ungetch would recognize
241      */
242     if (!lex->push_line && c2 == '\n')
243         lex->line++;
244     if      (ch == '<' && c2 == ':')
245         return '[';
246     else if (ch == ':' && c2 == '>')
247         return ']';
248     else if (ch == '<' && c2 == '%')
249         return '{';
250     else if (ch == '%' && c2 == '>')
251         return '}';
252     else if (ch == '%' && c2 == ':')
253         return '#';
254     lex_ungetch(lex, c2);
255     return ch;
256 }
257
258 static int lex_getch(lex_file *lex)
259 {
260     int ch;
261
262     if (lex->peekpos) {
263         lex->peekpos--;
264         if (!lex->push_line && lex->peek[lex->peekpos] == '\n') {
265             lex->line++;
266             lex->column = 0;
267         }
268         return lex->peek[lex->peekpos];
269     }
270
271     ch = lex_fgetc(lex);
272     if (!lex->push_line && ch == '\n') {
273         lex->line++;
274         lex->column = 0;
275     }
276     else if (ch == '?')
277         return lex_try_trigraph(lex, ch);
278     else if (!lex->flags.nodigraphs && (ch == '<' || ch == ':' || ch == '%'))
279         return lex_try_digraph(lex, ch);
280     return ch;
281 }
282
283 static void lex_ungetch(lex_file *lex, int ch)
284 {
285     lex->peek[lex->peekpos++] = ch;
286     lex->column--;
287     if (!lex->push_line && ch == '\n') {
288         lex->line--;
289         lex->column = 0;
290     }
291 }
292
293 /* classify characters
294  * some additions to the is*() functions of ctype.h
295  */
296
297 /* Idents are alphanumberic, but they start with alpha or _ */
298 static bool isident_start(int ch)
299 {
300     return util_isalpha(ch) || ch == '_';
301 }
302
303 static bool isident(int ch, bool allow_dot)
304 {
305     return isident_start(ch) || util_isdigit(ch) || (allow_dot && ch == '.');
306 }
307
308 /* isxdigit_only is used when we already know it's not a digit
309  * and want to see if it's a hex digit anyway.
310  */
311 static bool isxdigit_only(int ch)
312 {
313     return (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
314 }
315
316 /* Append a character to the token buffer */
317 static void lex_tokench(lex_file *lex, int ch)
318 {
319     vec_push(lex->tok.value, ch);
320 }
321
322 /* Append a trailing null-byte */
323 static void lex_endtoken(lex_file *lex)
324 {
325     vec_push(lex->tok.value, 0);
326     vec_shrinkby(lex->tok.value, 1);
327 }
328
329 static bool lex_try_pragma(lex_file *lex)
330 {
331     int ch;
332     char *pragma  = nullptr;
333     char *command = nullptr;
334     char *param   = nullptr;
335     size_t line;
336
337     if (lex->flags.preprocessing)
338         return false;
339
340     line = lex->line;
341
342     ch = lex_getch(lex);
343     if (ch != '#') {
344         lex_ungetch(lex, ch);
345         return false;
346     }
347
348     for (ch = lex_getch(lex); vec_size(pragma) < 8 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex))
349         vec_push(pragma, ch);
350     vec_push(pragma, 0);
351
352     if (ch != ' ' || strcmp(pragma, "pragma")) {
353         lex_ungetch(lex, ch);
354         goto unroll;
355     }
356
357     for (ch = lex_getch(lex); vec_size(command) < 32 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex))
358         vec_push(command, ch);
359     vec_push(command, 0);
360
361     if (ch != '(') {
362         lex_ungetch(lex, ch);
363         goto unroll;
364     }
365
366     for (ch = lex_getch(lex); vec_size(param) < 1024 && ch != ')' && ch != '\n'; ch = lex_getch(lex))
367         vec_push(param, ch);
368     vec_push(param, 0);
369
370     if (ch != ')') {
371         lex_ungetch(lex, ch);
372         goto unroll;
373     }
374
375     if (!strcmp(command, "push")) {
376         if (!strcmp(param, "line")) {
377             lex->push_line++;
378             if (lex->push_line == 1)
379                 --line;
380         }
381         else
382             goto unroll;
383     }
384     else if (!strcmp(command, "pop")) {
385         if (!strcmp(param, "line")) {
386             if (lex->push_line)
387                 lex->push_line--;
388             if (lex->push_line == 0)
389                 --line;
390         }
391         else
392             goto unroll;
393     }
394     else if (!strcmp(command, "file")) {
395         lex->framevalue = 0;
396         lex->name = util_strdup(param);
397         vec_push(lex_filenames, lex->name);
398     }
399     else if (!strcmp(command, "line")) {
400         line = strtol(param, nullptr, 0)-1;
401     }
402     else
403         goto unroll;
404
405     lex->line = line;
406     while (ch != '\n' && ch != EOF)
407         ch = lex_getch(lex);
408     vec_free(command);
409     vec_free(param);
410     vec_free(pragma);
411     return true;
412
413 unroll:
414     if (command) {
415         vec_pop(command);
416         while (vec_size(command)) {
417             lex_ungetch(lex, (unsigned char)vec_last(command));
418             vec_pop(command);
419         }
420         vec_free(command);
421         lex_ungetch(lex, ' ');
422     }
423     if (param) {
424         vec_pop(param);
425         while (vec_size(param)) {
426             lex_ungetch(lex, (unsigned char)vec_last(param));
427             vec_pop(param);
428         }
429         vec_free(param);
430         lex_ungetch(lex, ' ');
431     }
432     if (pragma) {
433         vec_pop(pragma);
434         while (vec_size(pragma)) {
435             lex_ungetch(lex, (unsigned char)vec_last(pragma));
436             vec_pop(pragma);
437         }
438         vec_free(pragma);
439     }
440     lex_ungetch(lex, '#');
441
442     lex->line = line;
443     return false;
444 }
445
446 /* Skip whitespace and comments and return the first
447  * non-white character.
448  * As this makes use of the above getch() ungetch() functions,
449  * we don't need to care at all about line numbering anymore.
450  *
451  * In theory, this function should only be used at the beginning
452  * of lexing, or when we *know* the next character is part of the token.
453  * Otherwise, if the parser throws an error, the linenumber may not be
454  * the line of the error, but the line of the next token AFTER the error.
455  *
456  * This is currently only problematic when using c-like string-continuation,
457  * since comments and whitespaces are allowed between 2 such strings.
458  * Example:
459 printf(   "line one\n"
460 // A comment
461           "A continuation of the previous string"
462 // This line is skipped
463       , foo);
464
465  * In this case, if the parse decides it didn't actually want a string,
466  * and uses lex->line to print an error, it will show the ', foo);' line's
467  * linenumber.
468  *
469  * On the other hand, the parser is supposed to remember the line of the next
470  * token's beginning. In this case we would want skipwhite() to be called
471  * AFTER reading a token, so that the parser, before reading the NEXT token,
472  * doesn't store teh *comment's* linenumber, but the actual token's linenumber.
473  *
474  * THIS SOLUTION
475  *    here is to store the line of the first character after skipping
476  *    the initial whitespace in lex->sline, this happens in lex_do.
477  */
478 static int lex_skipwhite(lex_file *lex, bool hadwhite)
479 {
480     int ch = 0;
481     bool haswhite = hadwhite;
482
483     do
484     {
485         ch = lex_getch(lex);
486         while (ch != EOF && util_isspace(ch)) {
487             if (ch == '\n') {
488                 if (lex_try_pragma(lex))
489                     continue;
490             }
491             if (lex->flags.preprocessing) {
492                 if (ch == '\n') {
493                     /* end-of-line */
494                     /* see if there was whitespace first */
495                     if (haswhite) { /* (vec_size(lex->tok.value)) { */
496                         lex_ungetch(lex, ch);
497                         lex_endtoken(lex);
498                         return TOKEN_WHITE;
499                     }
500                     /* otherwise return EOL */
501                     return TOKEN_EOL;
502                 }
503                 haswhite = true;
504                 lex_tokench(lex, ch);
505             }
506             ch = lex_getch(lex);
507         }
508
509         if (ch == '/') {
510             ch = lex_getch(lex);
511             if (ch == '/')
512             {
513                 /* one line comment */
514                 ch = lex_getch(lex);
515
516                 if (lex->flags.preprocessing) {
517                     haswhite = true;
518                     lex_tokench(lex, ' ');
519                     lex_tokench(lex, ' ');
520                 }
521
522                 while (ch != EOF && ch != '\n') {
523                     if (lex->flags.preprocessing)
524                         lex_tokench(lex, ' '); /* ch); */
525                     ch = lex_getch(lex);
526                 }
527                 if (lex->flags.preprocessing) {
528                     lex_ungetch(lex, '\n');
529                     lex_endtoken(lex);
530                     return TOKEN_WHITE;
531                 }
532                 continue;
533             }
534             if (ch == '*')
535             {
536                 /* multiline comment */
537                 if (lex->flags.preprocessing) {
538                     haswhite = true;
539                     lex_tokench(lex, ' ');
540                     lex_tokench(lex, ' ');
541                 }
542
543                 while (ch != EOF)
544                 {
545                     ch = lex_getch(lex);
546                     if (ch == '*') {
547                         ch = lex_getch(lex);
548                         if (ch == '/') {
549                             if (lex->flags.preprocessing) {
550                                 lex_tokench(lex, ' ');
551                                 lex_tokench(lex, ' ');
552                             }
553                             break;
554                         }
555                         lex_ungetch(lex, ch);
556                     }
557                     if (lex->flags.preprocessing) {
558                         if (ch == '\n')
559                             lex_tokench(lex, '\n');
560                         else
561                             lex_tokench(lex, ' ');
562                     }
563                 }
564                 ch = ' '; /* cause TRUE in the isspace check */
565                 continue;
566             }
567             /* Otherwise roll back to the slash and break out of the loop */
568             lex_ungetch(lex, ch);
569             ch = '/';
570             break;
571         }
572     } while (ch != EOF && util_isspace(ch));
573
574     if (haswhite) {
575         lex_endtoken(lex);
576         lex_ungetch(lex, ch);
577         return TOKEN_WHITE;
578     }
579     return ch;
580 }
581
582 /* Get a token */
583 static bool GMQCC_WARN lex_finish_ident(lex_file *lex, bool allow_dot)
584 {
585     int ch;
586
587     ch = lex_getch(lex);
588     while (ch != EOF && isident(ch, allow_dot))
589     {
590         lex_tokench(lex, ch);
591         ch = lex_getch(lex);
592     }
593
594     /* last ch was not an ident ch: */
595     lex_ungetch(lex, ch);
596
597     return true;
598 }
599
600 /* read one ident for the frame list */
601 static int lex_parse_frame(lex_file *lex)
602 {
603     int ch;
604
605     lex_token_new(lex);
606
607     ch = lex_getch(lex);
608     while (ch != EOF && ch != '\n' && util_isspace(ch))
609         ch = lex_getch(lex);
610
611     if (ch == '\n')
612         return 1;
613
614     if (!isident_start(ch)) {
615         lexerror(lex, "invalid framename, must start with one of a-z, or _, got %c", ch);
616         return -1;
617     }
618
619     lex_tokench(lex, ch);
620     if (!lex_finish_ident(lex, true))
621         return -1;
622     lex_endtoken(lex);
623     return 0;
624 }
625
626 /* read a list of $frames */
627 static bool lex_finish_frames(lex_file *lex)
628 {
629     do {
630         size_t i;
631         int    rc;
632         frame_macro m;
633
634         rc = lex_parse_frame(lex);
635         if (rc > 0) /* end of line */
636             return true;
637         if (rc < 0) /* error */
638             return false;
639
640         for (i = 0; i < vec_size(lex->frames); ++i) {
641             if (!strcmp(lex->tok.value, lex->frames[i].name)) {
642                 lex->frames[i].value = lex->framevalue++;
643                 if (lexwarn(lex, WARN_FRAME_MACROS, "duplicate frame macro defined: `%s`", lex->tok.value))
644                     return false;
645                 break;
646             }
647         }
648         if (i < vec_size(lex->frames))
649             continue;
650
651         m.value = lex->framevalue++;
652         m.name = util_strdup(lex->tok.value);
653         vec_shrinkto(lex->tok.value, 0);
654         vec_push(lex->frames, m);
655     } while (true);
656
657     return false;
658 }
659
660 static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
661 {
662     utf8ch_t chr = 0;
663     int ch = 0, texttype = 0;
664     int nextch;
665     bool hex;
666     bool oct;
667     char u8buf[8]; /* way more than enough */
668     int  u8len, uc;
669
670     while (ch != EOF)
671     {
672         ch = lex_getch(lex);
673         if (ch == quote)
674             return TOKEN_STRINGCONST;
675
676         if (lex->flags.preprocessing && ch == '\\') {
677             lex_tokench(lex, ch);
678             ch = lex_getch(lex);
679             if (ch == EOF) {
680                 lexerror(lex, "unexpected end of file");
681                 lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
682                 return (lex->tok.ttype = TOKEN_ERROR);
683             }
684             lex_tokench(lex, ch);
685         }
686         else if (ch == '\\') {
687             ch = lex_getch(lex);
688             if (ch == EOF) {
689                 lexerror(lex, "unexpected end of file");
690                 lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
691                 return (lex->tok.ttype = TOKEN_ERROR);
692             }
693
694             switch (ch) {
695             case '\\': break;
696             case '\'': break;
697             case '"':  break;
698             case 'a': ch = '\a'; break;
699             case 'r': ch = '\r'; break;
700             case 'n': ch = '\n'; break;
701             case 't': ch = '\t'; break;
702             case 'f': ch = '\f'; break;
703             case 'v': ch = '\v'; break;
704             case 'x':
705             case 'X':
706                 /* same procedure as in fteqcc */
707                 ch = 0;
708                 nextch = lex_getch(lex);
709                 if      (nextch >= '0' && nextch <= '9')
710                     ch += nextch - '0';
711                 else if (nextch >= 'a' && nextch <= 'f')
712                     ch += nextch - 'a' + 10;
713                 else if (nextch >= 'A' && nextch <= 'F')
714                     ch += nextch - 'A' + 10;
715                 else {
716                     lexerror(lex, "bad character code");
717                     lex_ungetch(lex, nextch);
718                     return (lex->tok.ttype = TOKEN_ERROR);
719                 }
720
721                 ch *= 0x10;
722                 nextch = lex_getch(lex);
723                 if      (nextch >= '0' && nextch <= '9')
724                     ch += nextch - '0';
725                 else if (nextch >= 'a' && nextch <= 'f')
726                     ch += nextch - 'a' + 10;
727                 else if (nextch >= 'A' && nextch <= 'F')
728                     ch += nextch - 'A' + 10;
729                 else {
730                     lexerror(lex, "bad character code");
731                     lex_ungetch(lex, nextch);
732                     return (lex->tok.ttype = TOKEN_ERROR);
733                 }
734                 break;
735
736             /* fteqcc support */
737             case '0': case '1': case '2': case '3':
738             case '4': case '5': case '6': case '7':
739             case '8': case '9':
740                 ch = 18 + ch - '0';
741                 break;
742             case '<':  ch = 29; break;
743             case '-':  ch = 30; break;
744             case '>':  ch = 31; break;
745             case '[':  ch = 16; break;
746             case ']':  ch = 17; break;
747             case '{':
748                 chr = 0;
749                 nextch = lex_getch(lex);
750                 hex = (nextch == 'x');
751                 oct = (nextch == '0');
752                 if (!hex && !oct)
753                     lex_ungetch(lex, nextch);
754                 for (nextch = lex_getch(lex); nextch != '}'; nextch = lex_getch(lex)) {
755                     if (!hex && !oct) {
756                         if (nextch >= '0' && nextch <= '9')
757                             chr = chr * 10 + nextch - '0';
758                         else {
759                             lexerror(lex, "bad character code");
760                             return (lex->tok.ttype = TOKEN_ERROR);
761                         }
762                     } else if (!oct) {
763                         if (nextch >= '0' && nextch <= '9')
764                             chr = chr * 0x10 + nextch - '0';
765                         else if (nextch >= 'a' && nextch <= 'f')
766                             chr = chr * 0x10 + nextch - 'a' + 10;
767                         else if (nextch >= 'A' && nextch <= 'F')
768                             chr = chr * 0x10 + nextch - 'A' + 10;
769                         else {
770                             lexerror(lex, "bad character code");
771                             return (lex->tok.ttype = TOKEN_ERROR);
772                         }
773                     } else {
774                         if (nextch >= '0' && nextch <= '9')
775                             chr = chr * 8 + chr - '0';
776                         else {
777                             lexerror(lex, "bad character code");
778                             return (lex->tok.ttype = TOKEN_ERROR);
779                         }
780                     }
781                     if (chr > 0x10FFFF || (!OPTS_FLAG(UTF8) && chr > 255))
782                     {
783                         lexerror(lex, "character code out of range");
784                         return (lex->tok.ttype = TOKEN_ERROR);
785                     }
786                 }
787                 if (OPTS_FLAG(UTF8) && chr >= 128) {
788                     u8len = utf8_from(u8buf, chr);
789                     if (!u8len)
790                         ch = 0;
791                     else {
792                         --u8len;
793                         lex->column += u8len;
794                         for (uc = 0; uc < u8len; ++uc)
795                             lex_tokench(lex, u8buf[uc]);
796                         /*
797                          * the last character will be inserted with the tokench() call
798                          * below the switch
799                          */
800                         ch = u8buf[uc];
801                     }
802                 }
803                 else
804                     ch = chr;
805                 break;
806
807             /* high bit text */
808             case 'b': case 's':
809                 texttype ^= 128;
810                 continue;
811
812             case '\n':
813                 ch = '\n';
814                 break;
815
816             default:
817                 lexwarn(lex, WARN_UNKNOWN_CONTROL_SEQUENCE, "unrecognized control sequence: \\%c", ch);
818                 /* so we just add the character plus backslash no matter what it actually is */
819                 lex_tokench(lex, '\\');
820             }
821             /* add the character finally */
822             lex_tokench(lex, ch | texttype);
823         }
824         else
825             lex_tokench(lex, ch);
826     }
827     lexerror(lex, "unexpected end of file within string constant");
828     lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
829     return (lex->tok.ttype = TOKEN_ERROR);
830 }
831
832 static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
833 {
834     bool ishex = false;
835     bool isoct = false;
836
837     int  ch = lastch;
838
839     /* parse a number... */
840     if (ch == '.')
841         lex->tok.ttype = TOKEN_FLOATCONST;
842     else
843         lex->tok.ttype = TOKEN_INTCONST;
844
845     lex_tokench(lex, ch);
846
847     ch = lex_getch(lex);
848
849     if (lastch == '0' && util_isdigit(ch)) {
850       if (ch < '0' || ch > '7') {
851         lexerror(lex, "invalid octal constant");
852         return (lex->tok.ttype = TOKEN_ERROR);
853       }
854       isoct = true;
855     }
856
857     if (!isoct && ch != '.' && !util_isdigit(ch))
858     {
859         if (lastch != '0' || ch != 'x')
860         {
861             /* end of the number or EOF */
862             lex_ungetch(lex, ch);
863             lex_endtoken(lex);
864
865             lex->tok.constval.i = lastch - '0';
866             return lex->tok.ttype;
867         }
868
869         ishex = true;
870     }
871
872     /* EOF would have been caught above */
873
874     if (ch != '.')
875     {
876         lex_tokench(lex, ch);
877         ch = lex_getch(lex);
878         while (util_isdigit(ch) || (ishex && isxdigit_only(ch)))
879         {
880             lex_tokench(lex, ch);
881             ch = lex_getch(lex);
882         }
883     }
884     /* NOT else, '.' can come from above as well */
885     if (lex->tok.ttype != TOKEN_FLOATCONST && ch == '.' && !ishex)
886     {
887         /* Allow floating comma in non-hex mode */
888         lex->tok.ttype = TOKEN_FLOATCONST;
889         lex_tokench(lex, ch);
890
891         /* continue digits-only */
892         ch = lex_getch(lex);
893         while (util_isdigit(ch))
894         {
895             lex_tokench(lex, ch);
896             ch = lex_getch(lex);
897         }
898     }
899     /* put back the last character */
900     /* but do not put back the trailing 'f' or a float */
901     if (lex->tok.ttype == TOKEN_FLOATCONST && ch == 'f')
902         ch = lex_getch(lex);
903
904     /* generally we don't want words to follow numbers: */
905     if (isident(ch, false)) {
906         lexerror(lex, "unexpected trailing characters after number");
907         return (lex->tok.ttype = TOKEN_ERROR);
908     }
909     lex_ungetch(lex, ch);
910
911     lex_endtoken(lex);
912     if (lex->tok.ttype == TOKEN_FLOATCONST) {
913         lex->tok.constval.f = strtod(lex->tok.value, nullptr);
914     } else {
915       /* determine base for strtol */
916       int base = 10;
917       if (ishex) base = 16;
918       if (isoct) base = 8;
919       lex->tok.constval.i = strtol(lex->tok.value, nullptr, base);
920     }
921     return lex->tok.ttype;
922 }
923
924 int lex_do(lex_file *lex)
925 {
926     int ch, nextch, thirdch;
927     bool hadwhite = false;
928
929     lex_token_new(lex);
930
931     while (true) {
932         ch = lex_skipwhite(lex, hadwhite);
933         hadwhite = true;
934         if (!lex->flags.mergelines || ch != '\\')
935             break;
936         ch = lex_getch(lex);
937         if (ch == '\r')
938             ch = lex_getch(lex);
939         if (ch != '\n') {
940             lex_ungetch(lex, ch);
941             ch = '\\';
942             break;
943         }
944         /* we reached a linemerge */
945         lex_tokench(lex, '\n');
946         continue;
947     }
948
949     if (lex->flags.preprocessing && (ch == TOKEN_WHITE || ch == TOKEN_EOL || ch == TOKEN_FATAL)) {
950         return (lex->tok.ttype = ch);
951     }
952
953     lex->sline = lex->line;
954     lex->tok.ctx.line = lex->sline;
955     lex->tok.ctx.file = lex->name;
956
957     if (lex->eof)
958         return (lex->tok.ttype = TOKEN_FATAL);
959
960     if (ch == EOF) {
961         lex->eof = true;
962         return (lex->tok.ttype = TOKEN_EOF);
963     }
964
965     /* modelgen / spiritgen commands */
966     if (ch == '$' && !lex->flags.preprocessing) {
967         const char *v;
968         size_t frame;
969
970         ch = lex_getch(lex);
971         if (!isident_start(ch)) {
972             lexerror(lex, "hanging '$' modelgen/spritegen command line");
973             return lex_do(lex);
974         }
975         lex_tokench(lex, ch);
976         if (!lex_finish_ident(lex, true))
977             return (lex->tok.ttype = TOKEN_ERROR);
978         lex_endtoken(lex);
979         /* skip the known commands */
980         v = lex->tok.value;
981
982         if (!strcmp(v, "frame") || !strcmp(v, "framesave"))
983         {
984             /* frame/framesave command works like an enum
985              * similar to fteqcc we handle this in the lexer.
986              * The reason for this is that it is sensitive to newlines,
987              * which the parser is unaware of
988              */
989             if (!lex_finish_frames(lex))
990                  return (lex->tok.ttype = TOKEN_ERROR);
991             return lex_do(lex);
992         }
993
994         if (!strcmp(v, "framevalue"))
995         {
996             ch = lex_getch(lex);
997             while (ch != EOF && util_isspace(ch) && ch != '\n')
998                 ch = lex_getch(lex);
999
1000             if (!util_isdigit(ch)) {
1001                 lexerror(lex, "$framevalue requires an integer parameter");
1002                 return lex_do(lex);
1003             }
1004
1005             lex_token_new(lex);
1006             lex->tok.ttype = lex_finish_digit(lex, ch);
1007             lex_endtoken(lex);
1008             if (lex->tok.ttype != TOKEN_INTCONST) {
1009                 lexerror(lex, "$framevalue requires an integer parameter");
1010                 return lex_do(lex);
1011             }
1012             lex->framevalue = lex->tok.constval.i;
1013             return lex_do(lex);
1014         }
1015
1016         if (!strcmp(v, "framerestore"))
1017         {
1018             int rc;
1019
1020             lex_token_new(lex);
1021
1022             rc = lex_parse_frame(lex);
1023
1024             if (rc > 0) {
1025                 lexerror(lex, "$framerestore requires a framename parameter");
1026                 return lex_do(lex);
1027             }
1028             if (rc < 0)
1029                 return (lex->tok.ttype = TOKEN_FATAL);
1030
1031             v = lex->tok.value;
1032             for (frame = 0; frame < vec_size(lex->frames); ++frame) {
1033                 if (!strcmp(v, lex->frames[frame].name)) {
1034                     lex->framevalue = lex->frames[frame].value;
1035                     return lex_do(lex);
1036                 }
1037             }
1038             lexerror(lex, "unknown framename `%s`", v);
1039             return lex_do(lex);
1040         }
1041
1042         if (!strcmp(v, "modelname"))
1043         {
1044             int rc;
1045
1046             lex_token_new(lex);
1047
1048             rc = lex_parse_frame(lex);
1049
1050             if (rc > 0) {
1051                 lexerror(lex, "$modelname requires a parameter");
1052                 return lex_do(lex);
1053             }
1054             if (rc < 0)
1055                 return (lex->tok.ttype = TOKEN_FATAL);
1056
1057             if (lex->modelname) {
1058                 frame_macro m;
1059                 m.value = lex->framevalue;
1060                 m.name = lex->modelname;
1061                 lex->modelname = nullptr;
1062                 vec_push(lex->frames, m);
1063             }
1064             lex->modelname = lex->tok.value;
1065             lex->tok.value = nullptr;
1066             return lex_do(lex);
1067         }
1068
1069         if (!strcmp(v, "flush"))
1070         {
1071             size_t fi;
1072             for (fi = 0; fi < vec_size(lex->frames); ++fi)
1073                 mem_d(lex->frames[fi].name);
1074             vec_free(lex->frames);
1075             /* skip line (fteqcc does it too) */
1076             ch = lex_getch(lex);
1077             while (ch != EOF && ch != '\n')
1078                 ch = lex_getch(lex);
1079             return lex_do(lex);
1080         }
1081
1082         if (!strcmp(v, "cd") ||
1083             !strcmp(v, "origin") ||
1084             !strcmp(v, "base") ||
1085             !strcmp(v, "flags") ||
1086             !strcmp(v, "scale") ||
1087             !strcmp(v, "skin"))
1088         {
1089             /* skip line */
1090             ch = lex_getch(lex);
1091             while (ch != EOF && ch != '\n')
1092                 ch = lex_getch(lex);
1093             return lex_do(lex);
1094         }
1095
1096         for (frame = 0; frame < vec_size(lex->frames); ++frame) {
1097             if (!strcmp(v, lex->frames[frame].name)) {
1098                 lex->tok.constval.i = lex->frames[frame].value;
1099                 return (lex->tok.ttype = TOKEN_INTCONST);
1100             }
1101         }
1102
1103         lexerror(lex, "invalid frame macro");
1104         return lex_do(lex);
1105     }
1106
1107     /* single-character tokens */
1108     switch (ch)
1109     {
1110         case '[':
1111             nextch = lex_getch(lex);
1112             if (nextch == '[') {
1113                 lex_tokench(lex, ch);
1114                 lex_tokench(lex, nextch);
1115                 lex_endtoken(lex);
1116                 return (lex->tok.ttype = TOKEN_ATTRIBUTE_OPEN);
1117             }
1118             lex_ungetch(lex, nextch);
1119             /* FALL THROUGH */
1120         case '(':
1121         case ':':
1122         case '?':
1123             lex_tokench(lex, ch);
1124             lex_endtoken(lex);
1125             if (lex->flags.noops)
1126                 return (lex->tok.ttype = ch);
1127             else
1128                 return (lex->tok.ttype = TOKEN_OPERATOR);
1129
1130         case ']':
1131             if (lex->flags.noops) {
1132                 nextch = lex_getch(lex);
1133                 if (nextch == ']') {
1134                     lex_tokench(lex, ch);
1135                     lex_tokench(lex, nextch);
1136                     lex_endtoken(lex);
1137                     return (lex->tok.ttype = TOKEN_ATTRIBUTE_CLOSE);
1138                 }
1139                 lex_ungetch(lex, nextch);
1140             }
1141             /* FALL THROUGH */
1142         case ')':
1143         case ';':
1144         case '{':
1145         case '}':
1146
1147         case '#':
1148             lex_tokench(lex, ch);
1149             lex_endtoken(lex);
1150             return (lex->tok.ttype = ch);
1151         default:
1152             break;
1153     }
1154
1155     if (ch == '.') {
1156         nextch = lex_getch(lex);
1157         /* digits starting with a dot */
1158         if (util_isdigit(nextch)) {
1159             lex_ungetch(lex, nextch);
1160             lex->tok.ttype = lex_finish_digit(lex, ch);
1161             lex_endtoken(lex);
1162             return lex->tok.ttype;
1163         }
1164         lex_ungetch(lex, nextch);
1165     }
1166
1167     if (lex->flags.noops)
1168     {
1169         /* Detect characters early which are normally
1170          * operators OR PART of an operator.
1171          */
1172         switch (ch)
1173         {
1174             case '*':
1175             case '/':
1176             case '<':
1177             case '>':
1178             case '=':
1179             case '&':
1180             case '|':
1181             case '^':
1182             case '~':
1183             case ',':
1184             case '!':
1185                 lex_tokench(lex, ch);
1186                 lex_endtoken(lex);
1187                 return (lex->tok.ttype = ch);
1188             default:
1189                 break;
1190         }
1191     }
1192
1193     if (ch == '.')
1194     {
1195         lex_tokench(lex, ch);
1196         /* peak ahead once */
1197         nextch = lex_getch(lex);
1198         if (nextch != '.') {
1199             lex_ungetch(lex, nextch);
1200             lex_endtoken(lex);
1201             if (lex->flags.noops)
1202                 return (lex->tok.ttype = ch);
1203             else
1204                 return (lex->tok.ttype = TOKEN_OPERATOR);
1205         }
1206         /* peak ahead again */
1207         nextch = lex_getch(lex);
1208         if (nextch != '.') {
1209             lex_ungetch(lex, nextch);
1210             lex_ungetch(lex, '.');
1211             lex_endtoken(lex);
1212             if (lex->flags.noops)
1213                 return (lex->tok.ttype = ch);
1214             else
1215                 return (lex->tok.ttype = TOKEN_OPERATOR);
1216         }
1217         /* fill the token to be "..." */
1218         lex_tokench(lex, ch);
1219         lex_tokench(lex, ch);
1220         lex_endtoken(lex);
1221         return (lex->tok.ttype = TOKEN_DOTS);
1222     }
1223
1224     if (ch == ',' || ch == '.') {
1225         lex_tokench(lex, ch);
1226         lex_endtoken(lex);
1227         return (lex->tok.ttype = TOKEN_OPERATOR);
1228     }
1229
1230     if (ch == '+' || ch == '-' || /* ++, --, +=, -=  and -> as well! */
1231         ch == '>' || ch == '<' || /* <<, >>, <=, >=  and >< as well! */
1232         ch == '=' || ch == '!' || /* <=>, ==, !=                     */
1233         ch == '&' || ch == '|' || /* &&, ||, &=, |=                  */
1234         ch == '~' || ch == '^'    /* ~=, ~, ^                        */
1235     )  {
1236         lex_tokench(lex, ch);
1237         nextch = lex_getch(lex);
1238
1239         if ((nextch == '=' && ch != '<') || (nextch == '<' && ch == '>'))
1240             lex_tokench(lex, nextch);
1241         else if (nextch == ch && ch != '!') {
1242             lex_tokench(lex, nextch);
1243             if ((thirdch = lex_getch(lex)) == '=')
1244                 lex_tokench(lex, thirdch);
1245             else
1246                 lex_ungetch(lex, thirdch);
1247         } else if (ch == '<' && nextch == '=') {
1248             lex_tokench(lex, nextch);
1249             if ((thirdch = lex_getch(lex)) == '>')
1250                 lex_tokench(lex, thirdch);
1251             else
1252                 lex_ungetch(lex, thirdch);
1253
1254         } else if (ch == '-' && nextch == '>') {
1255             lex_tokench(lex, nextch);
1256         } else if (ch == '&' && nextch == '~') {
1257             thirdch = lex_getch(lex);
1258             if (thirdch != '=') {
1259                 lex_ungetch(lex, thirdch);
1260                 lex_ungetch(lex, nextch);
1261             }
1262             else {
1263                 lex_tokench(lex, nextch);
1264                 lex_tokench(lex, thirdch);
1265             }
1266         }
1267         else if (lex->flags.preprocessing &&
1268                  ch == '-' && util_isdigit(nextch))
1269         {
1270             lex->tok.ttype = lex_finish_digit(lex, nextch);
1271             if (lex->tok.ttype == TOKEN_INTCONST)
1272                 lex->tok.constval.i = -lex->tok.constval.i;
1273             else
1274                 lex->tok.constval.f = -lex->tok.constval.f;
1275             lex_endtoken(lex);
1276             return lex->tok.ttype;
1277         } else {
1278             lex_ungetch(lex, nextch);
1279         }
1280
1281         lex_endtoken(lex);
1282         return (lex->tok.ttype = TOKEN_OPERATOR);
1283     }
1284
1285     if (ch == '*' || ch == '/') /* *=, /= */
1286     {
1287         lex_tokench(lex, ch);
1288
1289         nextch = lex_getch(lex);
1290         if (nextch == '=' || nextch == '*') {
1291             lex_tokench(lex, nextch);
1292         } else
1293             lex_ungetch(lex, nextch);
1294
1295         lex_endtoken(lex);
1296         return (lex->tok.ttype = TOKEN_OPERATOR);
1297     }
1298
1299     if (ch == '%') {
1300         lex_tokench(lex, ch);
1301         lex_endtoken(lex);
1302         return (lex->tok.ttype = TOKEN_OPERATOR);
1303     }
1304
1305     if (isident_start(ch))
1306     {
1307         const char *v;
1308
1309         lex_tokench(lex, ch);
1310         if (!lex_finish_ident(lex, false)) {
1311             /* error? */
1312             return (lex->tok.ttype = TOKEN_ERROR);
1313         }
1314         lex_endtoken(lex);
1315         lex->tok.ttype = TOKEN_IDENT;
1316
1317         v = lex->tok.value;
1318         if (!strcmp(v, "void")) {
1319             lex->tok.ttype = TOKEN_TYPENAME;
1320             lex->tok.constval.t = TYPE_VOID;
1321         } else if (!strcmp(v, "int")) {
1322             lex->tok.ttype = TOKEN_TYPENAME;
1323             lex->tok.constval.t = TYPE_INTEGER;
1324         } else if (!strcmp(v, "float")) {
1325             lex->tok.ttype = TOKEN_TYPENAME;
1326             lex->tok.constval.t = TYPE_FLOAT;
1327         } else if (!strcmp(v, "string")) {
1328             lex->tok.ttype = TOKEN_TYPENAME;
1329             lex->tok.constval.t = TYPE_STRING;
1330         } else if (!strcmp(v, "entity")) {
1331             lex->tok.ttype = TOKEN_TYPENAME;
1332             lex->tok.constval.t = TYPE_ENTITY;
1333         } else if (!strcmp(v, "vector")) {
1334             lex->tok.ttype = TOKEN_TYPENAME;
1335             lex->tok.constval.t = TYPE_VECTOR;
1336         } else if (!strcmp(v, "_length")) {
1337             lex->tok.ttype = TOKEN_OPERATOR;
1338         } else {
1339             size_t kw;
1340             for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_qc); ++kw) {
1341                 if (!strcmp(v, keywords_qc[kw]))
1342                     return (lex->tok.ttype = TOKEN_KEYWORD);
1343             }
1344             if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) {
1345                 for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_fg); ++kw) {
1346                     if (!strcmp(v, keywords_fg[kw]))
1347                         return (lex->tok.ttype = TOKEN_KEYWORD);
1348                 }
1349             }
1350         }
1351
1352         return lex->tok.ttype;
1353     }
1354
1355     if (ch == '"')
1356     {
1357         lex->flags.nodigraphs = true;
1358         if (lex->flags.preprocessing)
1359             lex_tokench(lex, ch);
1360         lex->tok.ttype = lex_finish_string(lex, '"');
1361         if (lex->flags.preprocessing)
1362             lex_tokench(lex, ch);
1363         while (!lex->flags.preprocessing && lex->tok.ttype == TOKEN_STRINGCONST)
1364         {
1365             /* Allow c style "string" "continuation" */
1366             ch = lex_skipwhite(lex, false);
1367             if (ch != '"') {
1368                 lex_ungetch(lex, ch);
1369                 break;
1370             }
1371
1372             lex->tok.ttype = lex_finish_string(lex, '"');
1373         }
1374         lex->flags.nodigraphs = false;
1375         lex_endtoken(lex);
1376         return lex->tok.ttype;
1377     }
1378
1379     if (ch == '\'')
1380     {
1381         /* we parse character constants like string,
1382          * but return TOKEN_CHARCONST, or a vector type if it fits...
1383          * Likewise actual unescaping has to be done by the parser.
1384          * The difference is we don't allow 'char' 'continuation'.
1385          */
1386         if (lex->flags.preprocessing)
1387             lex_tokench(lex, ch);
1388         lex->tok.ttype = lex_finish_string(lex, '\'');
1389         if (lex->flags.preprocessing)
1390             lex_tokench(lex, ch);
1391         lex_endtoken(lex);
1392
1393         lex->tok.ttype = TOKEN_CHARCONST;
1394
1395         /* It's a vector if we can successfully scan 3 floats */
1396         if (util_sscanf(lex->tok.value, " %f %f %f ",
1397                    &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
1398
1399         {
1400              lex->tok.ttype = TOKEN_VECTORCONST;
1401         }
1402         else
1403         {
1404             if (!lex->flags.preprocessing && strlen(lex->tok.value) > 1) {
1405                 utf8ch_t u8char;
1406                 /* check for a valid utf8 character */
1407                 if (!OPTS_FLAG(UTF8) || !utf8_to(&u8char, (const unsigned char *)lex->tok.value, 8)) {
1408                     if (lexwarn(lex, WARN_MULTIBYTE_CHARACTER,
1409                                 ( OPTS_FLAG(UTF8) ? "invalid multibyte character sequence `%s`"
1410                                                   : "multibyte character: `%s`" ),
1411                                 lex->tok.value))
1412                         return (lex->tok.ttype = TOKEN_ERROR);
1413                 }
1414                 else
1415                     lex->tok.constval.i = u8char;
1416             }
1417             else
1418                 lex->tok.constval.i = lex->tok.value[0];
1419         }
1420
1421         return lex->tok.ttype;
1422     }
1423
1424     if (util_isdigit(ch))
1425     {
1426         lex->tok.ttype = lex_finish_digit(lex, ch);
1427         lex_endtoken(lex);
1428         return lex->tok.ttype;
1429     }
1430
1431     if (lex->flags.preprocessing) {
1432         lex_tokench(lex, ch);
1433         lex_endtoken(lex);
1434         return (lex->tok.ttype = ch);
1435     }
1436
1437     lexerror(lex, "unknown token: `%c`", ch);
1438     return (lex->tok.ttype = TOKEN_ERROR);
1439 }