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