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