]> git.xonotic.org Git - xonotic/gmqcc.git/blob - lexer.cpp
Eliminate use of vec_ in fold.c
[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  = 0;
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(NULL, "open failed: '%s'\n", file);
85         return NULL;
86     }
87
88     lex = (lex_file*)mem_a(sizeof(*lex));
89     if (!lex) {
90         fclose(in);
91         lexerror(NULL, "out of memory\n");
92         return NULL;
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(NULL, "out of memory\n");
128         return NULL;
129     }
130
131     memset(lex, 0, sizeof(*lex));
132
133     lex->file = NULL;
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)
304 {
305     return isident_start(ch) || util_isdigit(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  = NULL;
333     char *command = NULL;
334     char *param   = NULL;
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->name = util_strdup(param);
396         vec_push(lex_filenames, lex->name);
397     }
398     else if (!strcmp(command, "line")) {
399         line = strtol(param, NULL, 0)-1;
400     }
401     else
402         goto unroll;
403
404     lex->line = line;
405     while (ch != '\n' && ch != EOF)
406         ch = lex_getch(lex);
407     vec_free(command);
408     vec_free(param);
409     vec_free(pragma);
410     return true;
411
412 unroll:
413     if (command) {
414         vec_pop(command);
415         while (vec_size(command)) {
416             lex_ungetch(lex, (unsigned char)vec_last(command));
417             vec_pop(command);
418         }
419         vec_free(command);
420         lex_ungetch(lex, ' ');
421     }
422     if (param) {
423         vec_pop(param);
424         while (vec_size(param)) {
425             lex_ungetch(lex, (unsigned char)vec_last(param));
426             vec_pop(param);
427         }
428         vec_free(param);
429         lex_ungetch(lex, ' ');
430     }
431     if (pragma) {
432         vec_pop(pragma);
433         while (vec_size(pragma)) {
434             lex_ungetch(lex, (unsigned char)vec_last(pragma));
435             vec_pop(pragma);
436         }
437         vec_free(pragma);
438     }
439     lex_ungetch(lex, '#');
440
441     lex->line = line;
442     return false;
443 }
444
445 /* Skip whitespace and comments and return the first
446  * non-white character.
447  * As this makes use of the above getch() ungetch() functions,
448  * we don't need to care at all about line numbering anymore.
449  *
450  * In theory, this function should only be used at the beginning
451  * of lexing, or when we *know* the next character is part of the token.
452  * Otherwise, if the parser throws an error, the linenumber may not be
453  * the line of the error, but the line of the next token AFTER the error.
454  *
455  * This is currently only problematic when using c-like string-continuation,
456  * since comments and whitespaces are allowed between 2 such strings.
457  * Example:
458 printf(   "line one\n"
459 // A comment
460           "A continuation of the previous string"
461 // This line is skipped
462       , foo);
463
464  * In this case, if the parse decides it didn't actually want a string,
465  * and uses lex->line to print an error, it will show the ', foo);' line's
466  * linenumber.
467  *
468  * On the other hand, the parser is supposed to remember the line of the next
469  * token's beginning. In this case we would want skipwhite() to be called
470  * AFTER reading a token, so that the parser, before reading the NEXT token,
471  * doesn't store teh *comment's* linenumber, but the actual token's linenumber.
472  *
473  * THIS SOLUTION
474  *    here is to store the line of the first character after skipping
475  *    the initial whitespace in lex->sline, this happens in lex_do.
476  */
477 static int lex_skipwhite(lex_file *lex, bool hadwhite)
478 {
479     int ch = 0;
480     bool haswhite = hadwhite;
481
482     do
483     {
484         ch = lex_getch(lex);
485         while (ch != EOF && util_isspace(ch)) {
486             if (ch == '\n') {
487                 if (lex_try_pragma(lex))
488                     continue;
489             }
490             if (lex->flags.preprocessing) {
491                 if (ch == '\n') {
492                     /* end-of-line */
493                     /* see if there was whitespace first */
494                     if (haswhite) { /* (vec_size(lex->tok.value)) { */
495                         lex_ungetch(lex, ch);
496                         lex_endtoken(lex);
497                         return TOKEN_WHITE;
498                     }
499                     /* otherwise return EOL */
500                     return TOKEN_EOL;
501                 }
502                 haswhite = true;
503                 lex_tokench(lex, ch);
504             }
505             ch = lex_getch(lex);
506         }
507
508         if (ch == '/') {
509             ch = lex_getch(lex);
510             if (ch == '/')
511             {
512                 /* one line comment */
513                 ch = lex_getch(lex);
514
515                 if (lex->flags.preprocessing) {
516                     haswhite = true;
517                     lex_tokench(lex, ' ');
518                     lex_tokench(lex, ' ');
519                 }
520
521                 while (ch != EOF && ch != '\n') {
522                     if (lex->flags.preprocessing)
523                         lex_tokench(lex, ' '); /* ch); */
524                     ch = lex_getch(lex);
525                 }
526                 if (lex->flags.preprocessing) {
527                     lex_ungetch(lex, '\n');
528                     lex_endtoken(lex);
529                     return TOKEN_WHITE;
530                 }
531                 continue;
532             }
533             if (ch == '*')
534             {
535                 /* multiline comment */
536                 if (lex->flags.preprocessing) {
537                     haswhite = true;
538                     lex_tokench(lex, ' ');
539                     lex_tokench(lex, ' ');
540                 }
541
542                 while (ch != EOF)
543                 {
544                     ch = lex_getch(lex);
545                     if (ch == '*') {
546                         ch = lex_getch(lex);
547                         if (ch == '/') {
548                             if (lex->flags.preprocessing) {
549                                 lex_tokench(lex, ' ');
550                                 lex_tokench(lex, ' ');
551                             }
552                             break;
553                         }
554                         lex_ungetch(lex, ch);
555                     }
556                     if (lex->flags.preprocessing) {
557                         if (ch == '\n')
558                             lex_tokench(lex, '\n');
559                         else
560                             lex_tokench(lex, ' ');
561                     }
562                 }
563                 ch = ' '; /* cause TRUE in the isspace check */
564                 continue;
565             }
566             /* Otherwise roll back to the slash and break out of the loop */
567             lex_ungetch(lex, ch);
568             ch = '/';
569             break;
570         }
571     } while (ch != EOF && util_isspace(ch));
572
573     if (haswhite) {
574         lex_endtoken(lex);
575         lex_ungetch(lex, ch);
576         return TOKEN_WHITE;
577     }
578     return ch;
579 }
580
581 /* Get a token */
582 static bool GMQCC_WARN lex_finish_ident(lex_file *lex)
583 {
584     int ch;
585
586     ch = lex_getch(lex);
587     while (ch != EOF && isident(ch))
588     {
589         lex_tokench(lex, ch);
590         ch = lex_getch(lex);
591     }
592
593     /* last ch was not an ident ch: */
594     lex_ungetch(lex, ch);
595
596     return true;
597 }
598
599 /* read one ident for the frame list */
600 static int lex_parse_frame(lex_file *lex)
601 {
602     int ch;
603
604     lex_token_new(lex);
605
606     ch = lex_getch(lex);
607     while (ch != EOF && ch != '\n' && util_isspace(ch))
608         ch = lex_getch(lex);
609
610     if (ch == '\n')
611         return 1;
612
613     if (!isident_start(ch)) {
614         lexerror(lex, "invalid framename, must start with one of a-z or _, got %c", ch);
615         return -1;
616     }
617
618     lex_tokench(lex, ch);
619     if (!lex_finish_ident(lex))
620         return -1;
621     lex_endtoken(lex);
622     return 0;
623 }
624
625 /* read a list of $frames */
626 static bool lex_finish_frames(lex_file *lex)
627 {
628     do {
629         size_t i;
630         int    rc;
631         frame_macro m;
632
633         rc = lex_parse_frame(lex);
634         if (rc > 0) /* end of line */
635             return true;
636         if (rc < 0) /* error */
637             return false;
638
639         for (i = 0; i < vec_size(lex->frames); ++i) {
640             if (!strcmp(lex->tok.value, lex->frames[i].name)) {
641                 lex->frames[i].value = lex->framevalue++;
642                 if (lexwarn(lex, WARN_FRAME_MACROS, "duplicate frame macro defined: `%s`", lex->tok.value))
643                     return false;
644                 break;
645             }
646         }
647         if (i < vec_size(lex->frames))
648             continue;
649
650         m.value = lex->framevalue++;
651         m.name = util_strdup(lex->tok.value);
652         vec_shrinkto(lex->tok.value, 0);
653         vec_push(lex->frames, m);
654     } while (true);
655
656     return false;
657 }
658
659 static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
660 {
661     utf8ch_t chr = 0;
662     int ch = 0, texttype = 0;
663     int nextch;
664     bool hex;
665     bool oct;
666     char u8buf[8]; /* way more than enough */
667     int  u8len, uc;
668
669     while (ch != EOF)
670     {
671         ch = lex_getch(lex);
672         if (ch == quote)
673             return TOKEN_STRINGCONST;
674
675         if (lex->flags.preprocessing && ch == '\\') {
676             lex_tokench(lex, ch);
677             ch = lex_getch(lex);
678             if (ch == EOF) {
679                 lexerror(lex, "unexpected end of file");
680                 lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
681                 return (lex->tok.ttype = TOKEN_ERROR);
682             }
683             lex_tokench(lex, ch);
684         }
685         else if (ch == '\\') {
686             ch = lex_getch(lex);
687             if (ch == EOF) {
688                 lexerror(lex, "unexpected end of file");
689                 lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
690                 return (lex->tok.ttype = TOKEN_ERROR);
691             }
692
693             switch (ch) {
694             case '\\': break;
695             case '\'': break;
696             case '"':  break;
697             case 'a': ch = '\a'; break;
698             case 'r': ch = '\r'; break;
699             case 'n': ch = '\n'; break;
700             case 't': ch = '\t'; break;
701             case 'f': ch = '\f'; break;
702             case 'v': ch = '\v'; break;
703             case 'x':
704             case 'X':
705                 /* same procedure as in fteqcc */
706                 ch = 0;
707                 nextch = lex_getch(lex);
708                 if      (nextch >= '0' && nextch <= '9')
709                     ch += nextch - '0';
710                 else if (nextch >= 'a' && nextch <= 'f')
711                     ch += nextch - 'a' + 10;
712                 else if (nextch >= 'A' && nextch <= 'F')
713                     ch += nextch - 'A' + 10;
714                 else {
715                     lexerror(lex, "bad character code");
716                     lex_ungetch(lex, nextch);
717                     return (lex->tok.ttype = TOKEN_ERROR);
718                 }
719
720                 ch *= 0x10;
721                 nextch = lex_getch(lex);
722                 if      (nextch >= '0' && nextch <= '9')
723                     ch += nextch - '0';
724                 else if (nextch >= 'a' && nextch <= 'f')
725                     ch += nextch - 'a' + 10;
726                 else if (nextch >= 'A' && nextch <= 'F')
727                     ch += nextch - 'A' + 10;
728                 else {
729                     lexerror(lex, "bad character code");
730                     lex_ungetch(lex, nextch);
731                     return (lex->tok.ttype = TOKEN_ERROR);
732                 }
733                 break;
734
735             /* fteqcc support */
736             case '0': case '1': case '2': case '3':
737             case '4': case '5': case '6': case '7':
738             case '8': case '9':
739                 ch = 18 + ch - '0';
740                 break;
741             case '<':  ch = 29; break;
742             case '-':  ch = 30; break;
743             case '>':  ch = 31; break;
744             case '[':  ch = 16; break;
745             case ']':  ch = 17; break;
746             case '{':
747                 chr = 0;
748                 nextch = lex_getch(lex);
749                 hex = (nextch == 'x');
750                 oct = (nextch == '0');
751                 if (!hex && !oct)
752                     lex_ungetch(lex, nextch);
753                 for (nextch = lex_getch(lex); nextch != '}'; nextch = lex_getch(lex)) {
754                     if (!hex && !oct) {
755                         if (nextch >= '0' && nextch <= '9')
756                             chr = chr * 10 + nextch - '0';
757                         else {
758                             lexerror(lex, "bad character code");
759                             return (lex->tok.ttype = TOKEN_ERROR);
760                         }
761                     } else if (!oct) {
762                         if (nextch >= '0' && nextch <= '9')
763                             chr = chr * 0x10 + nextch - '0';
764                         else if (nextch >= 'a' && nextch <= 'f')
765                             chr = chr * 0x10 + nextch - 'a' + 10;
766                         else if (nextch >= 'A' && nextch <= 'F')
767                             chr = chr * 0x10 + nextch - 'A' + 10;
768                         else {
769                             lexerror(lex, "bad character code");
770                             return (lex->tok.ttype = TOKEN_ERROR);
771                         }
772                     } else {
773                         if (nextch >= '0' && nextch <= '9')
774                             chr = chr * 8 + chr - '0';
775                         else {
776                             lexerror(lex, "bad character code");
777                             return (lex->tok.ttype = TOKEN_ERROR);
778                         }
779                     }
780                     if (chr > 0x10FFFF || (!OPTS_FLAG(UTF8) && chr > 255))
781                     {
782                         lexerror(lex, "character code out of range");
783                         return (lex->tok.ttype = TOKEN_ERROR);
784                     }
785                 }
786                 if (OPTS_FLAG(UTF8) && chr >= 128) {
787                     u8len = utf8_from(u8buf, chr);
788                     if (!u8len)
789                         ch = 0;
790                     else {
791                         --u8len;
792                         lex->column += u8len;
793                         for (uc = 0; uc < u8len; ++uc)
794                             lex_tokench(lex, u8buf[uc]);
795                         /*
796                          * the last character will be inserted with the tokench() call
797                          * below the switch
798                          */
799                         ch = u8buf[uc];
800                     }
801                 }
802                 else
803                     ch = chr;
804                 break;
805
806             /* high bit text */
807             case 'b': case 's':
808                 texttype ^= 128;
809                 continue;
810
811             case '\n':
812                 ch = '\n';
813                 break;
814
815             default:
816                 lexwarn(lex, WARN_UNKNOWN_CONTROL_SEQUENCE, "unrecognized control sequence: \\%c", ch);
817                 /* so we just add the character plus backslash no matter what it actually is */
818                 lex_tokench(lex, '\\');
819             }
820             /* add the character finally */
821             lex_tokench(lex, ch | texttype);
822         }
823         else
824             lex_tokench(lex, ch);
825     }
826     lexerror(lex, "unexpected end of file within string constant");
827     lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
828     return (lex->tok.ttype = TOKEN_ERROR);
829 }
830
831 static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
832 {
833     bool ishex = false;
834
835     int  ch = lastch;
836
837     /* parse a number... */
838     if (ch == '.')
839         lex->tok.ttype = TOKEN_FLOATCONST;
840     else
841         lex->tok.ttype = TOKEN_INTCONST;
842
843     lex_tokench(lex, ch);
844
845     ch = lex_getch(lex);
846     if (ch != '.' && !util_isdigit(ch))
847     {
848         if (lastch != '0' || ch != 'x')
849         {
850             /* end of the number or EOF */
851             lex_ungetch(lex, ch);
852             lex_endtoken(lex);
853
854             lex->tok.constval.i = lastch - '0';
855             return lex->tok.ttype;
856         }
857
858         ishex = true;
859     }
860
861     /* EOF would have been caught above */
862
863     if (ch != '.')
864     {
865         lex_tokench(lex, ch);
866         ch = lex_getch(lex);
867         while (util_isdigit(ch) || (ishex && isxdigit_only(ch)))
868         {
869             lex_tokench(lex, ch);
870             ch = lex_getch(lex);
871         }
872     }
873     /* NOT else, '.' can come from above as well */
874     if (lex->tok.ttype != TOKEN_FLOATCONST && ch == '.' && !ishex)
875     {
876         /* Allow floating comma in non-hex mode */
877         lex->tok.ttype = TOKEN_FLOATCONST;
878         lex_tokench(lex, ch);
879
880         /* continue digits-only */
881         ch = lex_getch(lex);
882         while (util_isdigit(ch))
883         {
884             lex_tokench(lex, ch);
885             ch = lex_getch(lex);
886         }
887     }
888     /* put back the last character */
889     /* but do not put back the trailing 'f' or a float */
890     if (lex->tok.ttype == TOKEN_FLOATCONST && ch == 'f')
891         ch = lex_getch(lex);
892
893     /* generally we don't want words to follow numbers: */
894     if (isident(ch)) {
895         lexerror(lex, "unexpected trailing characters after number");
896         return (lex->tok.ttype = TOKEN_ERROR);
897     }
898     lex_ungetch(lex, ch);
899
900     lex_endtoken(lex);
901     if (lex->tok.ttype == TOKEN_FLOATCONST)
902         lex->tok.constval.f = strtod(lex->tok.value, NULL);
903     else
904         lex->tok.constval.i = strtol(lex->tok.value, NULL, 0);
905     return lex->tok.ttype;
906 }
907
908 int lex_do(lex_file *lex)
909 {
910     int ch, nextch, thirdch;
911     bool hadwhite = false;
912
913     lex_token_new(lex);
914
915     while (true) {
916         ch = lex_skipwhite(lex, hadwhite);
917         hadwhite = true;
918         if (!lex->flags.mergelines || ch != '\\')
919             break;
920         ch = lex_getch(lex);
921         if (ch == '\r')
922             ch = lex_getch(lex);
923         if (ch != '\n') {
924             lex_ungetch(lex, ch);
925             ch = '\\';
926             break;
927         }
928         /* we reached a linemerge */
929         lex_tokench(lex, '\n');
930         continue;
931     }
932
933     if (lex->flags.preprocessing && (ch == TOKEN_WHITE || ch == TOKEN_EOL || ch == TOKEN_FATAL)) {
934         return (lex->tok.ttype = ch);
935     }
936
937     lex->sline = lex->line;
938     lex->tok.ctx.line = lex->sline;
939     lex->tok.ctx.file = lex->name;
940
941     if (lex->eof)
942         return (lex->tok.ttype = TOKEN_FATAL);
943
944     if (ch == EOF) {
945         lex->eof = true;
946         return (lex->tok.ttype = TOKEN_EOF);
947     }
948
949     /* modelgen / spiritgen commands */
950     if (ch == '$' && !lex->flags.preprocessing) {
951         const char *v;
952         size_t frame;
953
954         ch = lex_getch(lex);
955         if (!isident_start(ch)) {
956             lexerror(lex, "hanging '$' modelgen/spritegen command line");
957             return lex_do(lex);
958         }
959         lex_tokench(lex, ch);
960         if (!lex_finish_ident(lex))
961             return (lex->tok.ttype = TOKEN_ERROR);
962         lex_endtoken(lex);
963         /* skip the known commands */
964         v = lex->tok.value;
965
966         if (!strcmp(v, "frame") || !strcmp(v, "framesave"))
967         {
968             /* frame/framesave command works like an enum
969              * similar to fteqcc we handle this in the lexer.
970              * The reason for this is that it is sensitive to newlines,
971              * which the parser is unaware of
972              */
973             if (!lex_finish_frames(lex))
974                  return (lex->tok.ttype = TOKEN_ERROR);
975             return lex_do(lex);
976         }
977
978         if (!strcmp(v, "framevalue"))
979         {
980             ch = lex_getch(lex);
981             while (ch != EOF && util_isspace(ch) && ch != '\n')
982                 ch = lex_getch(lex);
983
984             if (!util_isdigit(ch)) {
985                 lexerror(lex, "$framevalue requires an integer parameter");
986                 return lex_do(lex);
987             }
988
989             lex_token_new(lex);
990             lex->tok.ttype = lex_finish_digit(lex, ch);
991             lex_endtoken(lex);
992             if (lex->tok.ttype != TOKEN_INTCONST) {
993                 lexerror(lex, "$framevalue requires an integer parameter");
994                 return lex_do(lex);
995             }
996             lex->framevalue = lex->tok.constval.i;
997             return lex_do(lex);
998         }
999
1000         if (!strcmp(v, "framerestore"))
1001         {
1002             int rc;
1003
1004             lex_token_new(lex);
1005
1006             rc = lex_parse_frame(lex);
1007
1008             if (rc > 0) {
1009                 lexerror(lex, "$framerestore requires a framename parameter");
1010                 return lex_do(lex);
1011             }
1012             if (rc < 0)
1013                 return (lex->tok.ttype = TOKEN_FATAL);
1014
1015             v = lex->tok.value;
1016             for (frame = 0; frame < vec_size(lex->frames); ++frame) {
1017                 if (!strcmp(v, lex->frames[frame].name)) {
1018                     lex->framevalue = lex->frames[frame].value;
1019                     return lex_do(lex);
1020                 }
1021             }
1022             lexerror(lex, "unknown framename `%s`", v);
1023             return lex_do(lex);
1024         }
1025
1026         if (!strcmp(v, "modelname"))
1027         {
1028             int rc;
1029
1030             lex_token_new(lex);
1031
1032             rc = lex_parse_frame(lex);
1033
1034             if (rc > 0) {
1035                 lexerror(lex, "$modelname requires a parameter");
1036                 return lex_do(lex);
1037             }
1038             if (rc < 0)
1039                 return (lex->tok.ttype = TOKEN_FATAL);
1040
1041             if (lex->modelname) {
1042                 frame_macro m;
1043                 m.value = lex->framevalue;
1044                 m.name = lex->modelname;
1045                 lex->modelname = NULL;
1046                 vec_push(lex->frames, m);
1047             }
1048             lex->modelname = lex->tok.value;
1049             lex->tok.value = NULL;
1050             return lex_do(lex);
1051         }
1052
1053         if (!strcmp(v, "flush"))
1054         {
1055             size_t fi;
1056             for (fi = 0; fi < vec_size(lex->frames); ++fi)
1057                 mem_d(lex->frames[fi].name);
1058             vec_free(lex->frames);
1059             /* skip line (fteqcc does it too) */
1060             ch = lex_getch(lex);
1061             while (ch != EOF && ch != '\n')
1062                 ch = lex_getch(lex);
1063             return lex_do(lex);
1064         }
1065
1066         if (!strcmp(v, "cd") ||
1067             !strcmp(v, "origin") ||
1068             !strcmp(v, "base") ||
1069             !strcmp(v, "flags") ||
1070             !strcmp(v, "scale") ||
1071             !strcmp(v, "skin"))
1072         {
1073             /* skip line */
1074             ch = lex_getch(lex);
1075             while (ch != EOF && ch != '\n')
1076                 ch = lex_getch(lex);
1077             return lex_do(lex);
1078         }
1079
1080         for (frame = 0; frame < vec_size(lex->frames); ++frame) {
1081             if (!strcmp(v, lex->frames[frame].name)) {
1082                 lex->tok.constval.i = lex->frames[frame].value;
1083                 return (lex->tok.ttype = TOKEN_INTCONST);
1084             }
1085         }
1086
1087         lexerror(lex, "invalid frame macro");
1088         return lex_do(lex);
1089     }
1090
1091     /* single-character tokens */
1092     switch (ch)
1093     {
1094         case '[':
1095             nextch = lex_getch(lex);
1096             if (nextch == '[') {
1097                 lex_tokench(lex, ch);
1098                 lex_tokench(lex, nextch);
1099                 lex_endtoken(lex);
1100                 return (lex->tok.ttype = TOKEN_ATTRIBUTE_OPEN);
1101             }
1102             lex_ungetch(lex, nextch);
1103             /* FALL THROUGH */
1104         case '(':
1105         case ':':
1106         case '?':
1107             lex_tokench(lex, ch);
1108             lex_endtoken(lex);
1109             if (lex->flags.noops)
1110                 return (lex->tok.ttype = ch);
1111             else
1112                 return (lex->tok.ttype = TOKEN_OPERATOR);
1113
1114         case ']':
1115             if (lex->flags.noops) {
1116                 nextch = lex_getch(lex);
1117                 if (nextch == ']') {
1118                     lex_tokench(lex, ch);
1119                     lex_tokench(lex, nextch);
1120                     lex_endtoken(lex);
1121                     return (lex->tok.ttype = TOKEN_ATTRIBUTE_CLOSE);
1122                 }
1123                 lex_ungetch(lex, nextch);
1124             }
1125             /* FALL THROUGH */
1126         case ')':
1127         case ';':
1128         case '{':
1129         case '}':
1130
1131         case '#':
1132             lex_tokench(lex, ch);
1133             lex_endtoken(lex);
1134             return (lex->tok.ttype = ch);
1135         default:
1136             break;
1137     }
1138
1139     if (ch == '.') {
1140         nextch = lex_getch(lex);
1141         /* digits starting with a dot */
1142         if (util_isdigit(nextch)) {
1143             lex_ungetch(lex, nextch);
1144             lex->tok.ttype = lex_finish_digit(lex, ch);
1145             lex_endtoken(lex);
1146             return lex->tok.ttype;
1147         }
1148         lex_ungetch(lex, nextch);
1149     }
1150
1151     if (lex->flags.noops)
1152     {
1153         /* Detect characters early which are normally
1154          * operators OR PART of an operator.
1155          */
1156         switch (ch)
1157         {
1158             case '*':
1159             case '/':
1160             case '<':
1161             case '>':
1162             case '=':
1163             case '&':
1164             case '|':
1165             case '^':
1166             case '~':
1167             case ',':
1168             case '!':
1169                 lex_tokench(lex, ch);
1170                 lex_endtoken(lex);
1171                 return (lex->tok.ttype = ch);
1172             default:
1173                 break;
1174         }
1175     }
1176
1177     if (ch == '.')
1178     {
1179         lex_tokench(lex, ch);
1180         /* peak ahead once */
1181         nextch = lex_getch(lex);
1182         if (nextch != '.') {
1183             lex_ungetch(lex, nextch);
1184             lex_endtoken(lex);
1185             if (lex->flags.noops)
1186                 return (lex->tok.ttype = ch);
1187             else
1188                 return (lex->tok.ttype = TOKEN_OPERATOR);
1189         }
1190         /* peak ahead again */
1191         nextch = lex_getch(lex);
1192         if (nextch != '.') {
1193             lex_ungetch(lex, nextch);
1194             lex_ungetch(lex, '.');
1195             lex_endtoken(lex);
1196             if (lex->flags.noops)
1197                 return (lex->tok.ttype = ch);
1198             else
1199                 return (lex->tok.ttype = TOKEN_OPERATOR);
1200         }
1201         /* fill the token to be "..." */
1202         lex_tokench(lex, ch);
1203         lex_tokench(lex, ch);
1204         lex_endtoken(lex);
1205         return (lex->tok.ttype = TOKEN_DOTS);
1206     }
1207
1208     if (ch == ',' || ch == '.') {
1209         lex_tokench(lex, ch);
1210         lex_endtoken(lex);
1211         return (lex->tok.ttype = TOKEN_OPERATOR);
1212     }
1213
1214     if (ch == '+' || ch == '-' || /* ++, --, +=, -=  and -> as well! */
1215         ch == '>' || ch == '<' || /* <<, >>, <=, >=  and >< as well! */
1216         ch == '=' || ch == '!' || /* <=>, ==, !=                     */
1217         ch == '&' || ch == '|' || /* &&, ||, &=, |=                  */
1218         ch == '~' || ch == '^'    /* ~=, ~, ^                        */
1219     )  {
1220         lex_tokench(lex, ch);
1221         nextch = lex_getch(lex);
1222
1223         if ((nextch == '=' && ch != '<') || (nextch == '<' && ch == '>'))
1224             lex_tokench(lex, nextch);
1225         else if (nextch == ch && ch != '!') {
1226             lex_tokench(lex, nextch);
1227             if ((thirdch = lex_getch(lex)) == '=')
1228                 lex_tokench(lex, thirdch);
1229             else
1230                 lex_ungetch(lex, thirdch);
1231         } else if (ch == '<' && nextch == '=') {
1232             lex_tokench(lex, nextch);
1233             if ((thirdch = lex_getch(lex)) == '>')
1234                 lex_tokench(lex, thirdch);
1235             else
1236                 lex_ungetch(lex, thirdch);
1237
1238         } else if (ch == '-' && nextch == '>') {
1239             lex_tokench(lex, nextch);
1240         } else if (ch == '&' && nextch == '~') {
1241             thirdch = lex_getch(lex);
1242             if (thirdch != '=') {
1243                 lex_ungetch(lex, thirdch);
1244                 lex_ungetch(lex, nextch);
1245             }
1246             else {
1247                 lex_tokench(lex, nextch);
1248                 lex_tokench(lex, thirdch);
1249             }
1250         }
1251         else if (lex->flags.preprocessing &&
1252                  ch == '-' && util_isdigit(nextch))
1253         {
1254             lex->tok.ttype = lex_finish_digit(lex, nextch);
1255             if (lex->tok.ttype == TOKEN_INTCONST)
1256                 lex->tok.constval.i = -lex->tok.constval.i;
1257             else
1258                 lex->tok.constval.f = -lex->tok.constval.f;
1259             lex_endtoken(lex);
1260             return lex->tok.ttype;
1261         } else {
1262             lex_ungetch(lex, nextch);
1263         }
1264
1265         lex_endtoken(lex);
1266         return (lex->tok.ttype = TOKEN_OPERATOR);
1267     }
1268
1269     if (ch == '*' || ch == '/') /* *=, /= */
1270     {
1271         lex_tokench(lex, ch);
1272
1273         nextch = lex_getch(lex);
1274         if (nextch == '=' || nextch == '*') {
1275             lex_tokench(lex, nextch);
1276         } else
1277             lex_ungetch(lex, nextch);
1278
1279         lex_endtoken(lex);
1280         return (lex->tok.ttype = TOKEN_OPERATOR);
1281     }
1282
1283     if (ch == '%') {
1284         lex_tokench(lex, ch);
1285         lex_endtoken(lex);
1286         return (lex->tok.ttype = TOKEN_OPERATOR);
1287     }
1288
1289     if (isident_start(ch))
1290     {
1291         const char *v;
1292
1293         lex_tokench(lex, ch);
1294         if (!lex_finish_ident(lex)) {
1295             /* error? */
1296             return (lex->tok.ttype = TOKEN_ERROR);
1297         }
1298         lex_endtoken(lex);
1299         lex->tok.ttype = TOKEN_IDENT;
1300
1301         v = lex->tok.value;
1302         if (!strcmp(v, "void")) {
1303             lex->tok.ttype = TOKEN_TYPENAME;
1304             lex->tok.constval.t = TYPE_VOID;
1305         } else if (!strcmp(v, "int")) {
1306             lex->tok.ttype = TOKEN_TYPENAME;
1307             lex->tok.constval.t = TYPE_INTEGER;
1308         } else if (!strcmp(v, "float")) {
1309             lex->tok.ttype = TOKEN_TYPENAME;
1310             lex->tok.constval.t = TYPE_FLOAT;
1311         } else if (!strcmp(v, "string")) {
1312             lex->tok.ttype = TOKEN_TYPENAME;
1313             lex->tok.constval.t = TYPE_STRING;
1314         } else if (!strcmp(v, "entity")) {
1315             lex->tok.ttype = TOKEN_TYPENAME;
1316             lex->tok.constval.t = TYPE_ENTITY;
1317         } else if (!strcmp(v, "vector")) {
1318             lex->tok.ttype = TOKEN_TYPENAME;
1319             lex->tok.constval.t = TYPE_VECTOR;
1320         } else if (!strcmp(v, "_length")) {
1321             lex->tok.ttype = TOKEN_OPERATOR;
1322         } else {
1323             size_t kw;
1324             for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_qc); ++kw) {
1325                 if (!strcmp(v, keywords_qc[kw]))
1326                     return (lex->tok.ttype = TOKEN_KEYWORD);
1327             }
1328             if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) {
1329                 for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_fg); ++kw) {
1330                     if (!strcmp(v, keywords_fg[kw]))
1331                         return (lex->tok.ttype = TOKEN_KEYWORD);
1332                 }
1333             }
1334         }
1335
1336         return lex->tok.ttype;
1337     }
1338
1339     if (ch == '"')
1340     {
1341         lex->flags.nodigraphs = true;
1342         if (lex->flags.preprocessing)
1343             lex_tokench(lex, ch);
1344         lex->tok.ttype = lex_finish_string(lex, '"');
1345         if (lex->flags.preprocessing)
1346             lex_tokench(lex, ch);
1347         while (!lex->flags.preprocessing && lex->tok.ttype == TOKEN_STRINGCONST)
1348         {
1349             /* Allow c style "string" "continuation" */
1350             ch = lex_skipwhite(lex, false);
1351             if (ch != '"') {
1352                 lex_ungetch(lex, ch);
1353                 break;
1354             }
1355
1356             lex->tok.ttype = lex_finish_string(lex, '"');
1357         }
1358         lex->flags.nodigraphs = false;
1359         lex_endtoken(lex);
1360         return lex->tok.ttype;
1361     }
1362
1363     if (ch == '\'')
1364     {
1365         /* we parse character constants like string,
1366          * but return TOKEN_CHARCONST, or a vector type if it fits...
1367          * Likewise actual unescaping has to be done by the parser.
1368          * The difference is we don't allow 'char' 'continuation'.
1369          */
1370         if (lex->flags.preprocessing)
1371             lex_tokench(lex, ch);
1372         lex->tok.ttype = lex_finish_string(lex, '\'');
1373         if (lex->flags.preprocessing)
1374             lex_tokench(lex, ch);
1375         lex_endtoken(lex);
1376
1377         lex->tok.ttype = TOKEN_CHARCONST;
1378
1379         /* It's a vector if we can successfully scan 3 floats */
1380         if (util_sscanf(lex->tok.value, " %f %f %f ",
1381                    &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
1382
1383         {
1384              lex->tok.ttype = TOKEN_VECTORCONST;
1385         }
1386         else
1387         {
1388             if (!lex->flags.preprocessing && strlen(lex->tok.value) > 1) {
1389                 utf8ch_t u8char;
1390                 /* check for a valid utf8 character */
1391                 if (!OPTS_FLAG(UTF8) || !utf8_to(&u8char, (const unsigned char *)lex->tok.value, 8)) {
1392                     if (lexwarn(lex, WARN_MULTIBYTE_CHARACTER,
1393                                 ( OPTS_FLAG(UTF8) ? "invalid multibyte character sequence `%s`"
1394                                                   : "multibyte character: `%s`" ),
1395                                 lex->tok.value))
1396                         return (lex->tok.ttype = TOKEN_ERROR);
1397                 }
1398                 else
1399                     lex->tok.constval.i = u8char;
1400             }
1401             else
1402                 lex->tok.constval.i = lex->tok.value[0];
1403         }
1404
1405         return lex->tok.ttype;
1406     }
1407
1408     if (util_isdigit(ch))
1409     {
1410         lex->tok.ttype = lex_finish_digit(lex, ch);
1411         lex_endtoken(lex);
1412         return lex->tok.ttype;
1413     }
1414
1415     if (lex->flags.preprocessing) {
1416         lex_tokench(lex, ch);
1417         lex_endtoken(lex);
1418         return (lex->tok.ttype = ch);
1419     }
1420
1421     lexerror(lex, "unknown token: `%c`", ch);
1422     return (lex->tok.ttype = TOKEN_ERROR);
1423 }