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