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