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