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