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