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