]> git.xonotic.org Git - xonotic/gmqcc.git/blob - lexer.c
cache filenames as such instead of using code_cachedstring
[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 int lex_getch(lex_file *lex)
201 {
202     int ch;
203
204     if (lex->peekpos) {
205         lex->peekpos--;
206         if (lex->peek[lex->peekpos] == '\n')
207             lex->line++;
208         return lex->peek[lex->peekpos];
209     }
210
211     ch = fgetc(lex->file);
212     if (ch == '\n')
213         lex->line++;
214     return ch;
215 }
216
217 static void lex_ungetch(lex_file *lex, int ch)
218 {
219     lex->peek[lex->peekpos++] = ch;
220     if (ch == '\n')
221         lex->line--;
222 }
223
224 /* classify characters
225  * some additions to the is*() functions of ctype.h
226  */
227
228 /* Idents are alphanumberic, but they start with alpha or _ */
229 static bool isident_start(int ch)
230 {
231     return isalpha(ch) || ch == '_';
232 }
233
234 static bool isident(int ch)
235 {
236     return isident_start(ch) || isdigit(ch);
237 }
238
239 /* isxdigit_only is used when we already know it's not a digit
240  * and want to see if it's a hex digit anyway.
241  */
242 static bool isxdigit_only(int ch)
243 {
244     return (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
245 }
246
247 /* Skip whitespace and comments and return the first
248  * non-white character.
249  * As this makes use of the above getch() ungetch() functions,
250  * we don't need to care at all about line numbering anymore.
251  *
252  * In theory, this function should only be used at the beginning
253  * of lexing, or when we *know* the next character is part of the token.
254  * Otherwise, if the parser throws an error, the linenumber may not be
255  * the line of the error, but the line of the next token AFTER the error.
256  *
257  * This is currently only problematic when using c-like string-continuation,
258  * since comments and whitespaces are allowed between 2 such strings.
259  * Example:
260 printf(   "line one\n"
261 // A comment
262           "A continuation of the previous string"
263 // This line is skipped
264       , foo);
265
266  * In this case, if the parse decides it didn't actually want a string,
267  * and uses lex->line to print an error, it will show the ', foo);' line's
268  * linenumber.
269  *
270  * On the other hand, the parser is supposed to remember the line of the next
271  * token's beginning. In this case we would want skipwhite() to be called
272  * AFTER reading a token, so that the parser, before reading the NEXT token,
273  * doesn't store teh *comment's* linenumber, but the actual token's linenumber.
274  *
275  * THIS SOLUTION
276  *    here is to store the line of the first character after skipping
277  *    the initial whitespace in lex->sline, this happens in lex_do.
278  */
279 static int lex_skipwhite(lex_file *lex)
280 {
281     int ch = 0;
282
283     do
284     {
285         ch = lex_getch(lex);
286         while (ch != EOF && isspace(ch)) ch = lex_getch(lex);
287
288         if (ch == '/') {
289             ch = lex_getch(lex);
290             if (ch == '/')
291             {
292                 /* one line comment */
293                 ch = lex_getch(lex);
294
295                 /* check for special: '/', '/', '*', '/' */
296                 if (ch == '*') {
297                     ch = lex_getch(lex);
298                     if (ch == '/') {
299                         ch = ' ';
300                         continue;
301                     }
302                 }
303
304                 while (ch != EOF && ch != '\n') {
305                     ch = lex_getch(lex);
306                 }
307                 continue;
308             }
309             if (ch == '*')
310             {
311                 /* multiline comment */
312                 while (ch != EOF)
313                 {
314                     ch = lex_getch(lex);
315                     if (ch == '*') {
316                         ch = lex_getch(lex);
317                         if (ch == '/') {
318                             ch = lex_getch(lex);
319                             break;
320                         }
321                     }
322                 }
323                 if (ch == '/') /* allow *//* direct following comment */
324                 {
325                     lex_ungetch(lex, ch);
326                     ch = ' '; /* cause TRUE in the isspace check */
327                 }
328                 continue;
329             }
330             /* Otherwise roll back to the slash and break out of the loop */
331             lex_ungetch(lex, ch);
332             ch = '/';
333             break;
334         }
335     } while (ch != EOF && isspace(ch));
336
337     return ch;
338 }
339
340 /* Append a character to the token buffer */
341 static bool GMQCC_WARN lex_tokench(lex_file *lex, int ch)
342 {
343     if (!token_value_add(&lex->tok, ch)) {
344         lexerror(lex, "out of memory");
345         return false;
346     }
347     return true;
348 }
349
350 /* Append a trailing null-byte */
351 static bool GMQCC_WARN lex_endtoken(lex_file *lex)
352 {
353     if (!token_value_add(&lex->tok, 0)) {
354         lexerror(lex, "out of memory");
355         return false;
356     }
357     lex->tok.value_count--;
358     return true;
359 }
360
361 /* Get a token */
362 static bool GMQCC_WARN lex_finish_ident(lex_file *lex)
363 {
364     int ch;
365
366     ch = lex_getch(lex);
367     while (ch != EOF && isident(ch))
368     {
369         if (!lex_tokench(lex, ch))
370             return (lex->tok.ttype = TOKEN_FATAL);
371         ch = lex_getch(lex);
372     }
373
374     /* last ch was not an ident ch: */
375     lex_ungetch(lex, ch);
376
377     return true;
378 }
379
380 /* read one ident for the frame list */
381 static int lex_parse_frame(lex_file *lex)
382 {
383     int ch;
384
385     lex_token_new(lex);
386
387     ch = lex_getch(lex);
388     while (ch != EOF && ch != '\n' && isspace(ch))
389         ch = lex_getch(lex);
390
391     if (ch == '\n')
392         return 1;
393
394     if (!isident_start(ch)) {
395         lexerror(lex, "invalid framename, must start with one of a-z or _, got %c", ch);
396         return -1;
397     }
398
399     if (!lex_tokench(lex, ch))
400         return -1;
401     if (!lex_finish_ident(lex))
402         return -1;
403     if (!lex_endtoken(lex))
404         return -1;
405     return 0;
406 }
407
408 /* read a list of $frames */
409 static bool lex_finish_frames(lex_file *lex)
410 {
411     do {
412         size_t i;
413         int    rc;
414         frame_macro m;
415
416         rc = lex_parse_frame(lex);
417         if (rc > 0) /* end of line */
418             return true;
419         if (rc < 0) /* error */
420             return false;
421
422         for (i = 0; i < lex->frames_count; ++i) {
423             if (!strcmp(lex->tok.value, lex->frames[i].name)) {
424                 lex->frames[i].value = lex->framevalue++;
425                 if (lexwarn(lex, WARN_FRAME_MACROS, "duplicate frame macro defined: `%s`", lex->tok.value))
426                     return false;
427                 break;
428             }
429         }
430         if (i < lex->frames_count)
431             continue;
432
433         m.value = lex->framevalue++;
434         m.name = lex->tok.value;
435         lex->tok.value = NULL;
436         lex->tok.value_alloc = lex->tok.value_count = 0;
437         if (!lex_file_frames_add(lex, m)) {
438             lexerror(lex, "out of memory");
439             return false;
440         }
441     } while (true);
442 }
443
444 static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
445 {
446     int ch = 0;
447
448     while (ch != EOF)
449     {
450         ch = lex_getch(lex);
451         if (ch == quote)
452             return TOKEN_STRINGCONST;
453
454         if (ch == '\\') {
455             ch = lex_getch(lex);
456             if (ch == EOF) {
457                 lexerror(lex, "unexpected end of file");
458                 lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
459                 return (lex->tok.ttype = TOKEN_ERROR);
460             }
461
462             switch (ch) {
463             case '\\': break;
464             case 'a':  ch = '\a'; break;
465             case 'b':  ch = '\b'; break;
466             case 'r':  ch = '\r'; break;
467             case 'n':  ch = '\n'; break;
468             case 't':  ch = '\t'; break;
469             case 'f':  ch = '\f'; break;
470             case 'v':  ch = '\v'; break;
471             default:
472                 lexwarn(lex, WARN_UNKNOWN_CONTROL_SEQUENCE, "unrecognized control sequence: \\%c", ch);
473                 /* so we just add the character plus backslash no matter what it actually is */
474                 if (!lex_tokench(lex, '\\'))
475                     return (lex->tok.ttype = TOKEN_FATAL);
476             }
477             /* add the character finally */
478             if (!lex_tokench(lex, ch))
479                 return (lex->tok.ttype = TOKEN_FATAL);
480         }
481         else if (!lex_tokench(lex, ch))
482             return (lex->tok.ttype = TOKEN_FATAL);
483     }
484     lexerror(lex, "unexpected end of file within string constant");
485     lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
486     return (lex->tok.ttype = TOKEN_ERROR);
487 }
488
489 static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
490 {
491     bool ishex = false;
492
493     int  ch = lastch;
494
495     /* parse a number... */
496     lex->tok.ttype = TOKEN_INTCONST;
497
498     if (!lex_tokench(lex, ch))
499         return (lex->tok.ttype = TOKEN_FATAL);
500
501     ch = lex_getch(lex);
502     if (ch != '.' && !isdigit(ch))
503     {
504         if (lastch != '0' || ch != 'x')
505         {
506             /* end of the number or EOF */
507             lex_ungetch(lex, ch);
508             if (!lex_endtoken(lex))
509                 return (lex->tok.ttype = TOKEN_FATAL);
510
511             lex->tok.constval.i = lastch - '0';
512             return lex->tok.ttype;
513         }
514
515         ishex = true;
516     }
517
518     /* EOF would have been caught above */
519
520     if (ch != '.')
521     {
522         if (!lex_tokench(lex, ch))
523             return (lex->tok.ttype = TOKEN_FATAL);
524         ch = lex_getch(lex);
525         while (isdigit(ch) || (ishex && isxdigit_only(ch)))
526         {
527             if (!lex_tokench(lex, ch))
528                 return (lex->tok.ttype = TOKEN_FATAL);
529             ch = lex_getch(lex);
530         }
531     }
532     /* NOT else, '.' can come from above as well */
533     if (ch == '.' && !ishex)
534     {
535         /* Allow floating comma in non-hex mode */
536         lex->tok.ttype = TOKEN_FLOATCONST;
537         if (!lex_tokench(lex, ch))
538             return (lex->tok.ttype = TOKEN_FATAL);
539
540         /* continue digits-only */
541         ch = lex_getch(lex);
542         while (isdigit(ch))
543         {
544             if (!lex_tokench(lex, ch))
545                 return (lex->tok.ttype = TOKEN_FATAL);
546             ch = lex_getch(lex);
547         }
548     }
549     /* put back the last character */
550     /* but do not put back the trailing 'f' or a float */
551     if (lex->tok.ttype == TOKEN_FLOATCONST && ch == 'f')
552         ch = lex_getch(lex);
553
554     /* generally we don't want words to follow numbers: */
555     if (isident(ch)) {
556         lexerror(lex, "unexpected trailing characters after number");
557         return (lex->tok.ttype = TOKEN_ERROR);
558     }
559     lex_ungetch(lex, ch);
560
561     if (!lex_endtoken(lex))
562         return (lex->tok.ttype = TOKEN_FATAL);
563     if (lex->tok.ttype == TOKEN_FLOATCONST)
564         lex->tok.constval.f = strtod(lex->tok.value, NULL);
565     else
566         lex->tok.constval.i = strtol(lex->tok.value, NULL, 0);
567     return lex->tok.ttype;
568 }
569
570 int lex_do(lex_file *lex)
571 {
572     int ch, nextch;
573
574     lex_token_new(lex);
575 #if 0
576     if (!lex->tok)
577         return TOKEN_FATAL;
578 #endif
579
580     ch = lex_skipwhite(lex);
581     lex->sline = lex->line;
582     lex->tok.ctx.line = lex->sline;
583     lex->tok.ctx.file = lex->name;
584
585     if (lex->eof)
586         return (lex->tok.ttype = TOKEN_FATAL);
587
588     if (ch == EOF) {
589         lex->eof = true;
590         return (lex->tok.ttype = TOKEN_EOF);
591     }
592
593     /* modelgen / spiritgen commands */
594     if (ch == '$') {
595         const char *v;
596         size_t frame;
597
598         ch = lex_getch(lex);
599         if (!isident_start(ch)) {
600             lexerror(lex, "hanging '$' modelgen/spritegen command line");
601             return lex_do(lex);
602         }
603         if (!lex_tokench(lex, ch))
604             return (lex->tok.ttype = TOKEN_FATAL);
605         if (!lex_finish_ident(lex))
606             return (lex->tok.ttype = TOKEN_ERROR);
607         if (!lex_endtoken(lex))
608             return (lex->tok.ttype = TOKEN_FATAL);
609         /* skip the known commands */
610         v = lex->tok.value;
611
612         if (!strcmp(v, "frame") || !strcmp(v, "framesave"))
613         {
614             /* frame/framesave command works like an enum
615              * similar to fteqcc we handle this in the lexer.
616              * The reason for this is that it is sensitive to newlines,
617              * which the parser is unaware of
618              */
619             if (!lex_finish_frames(lex))
620                  return (lex->tok.ttype = TOKEN_ERROR);
621             return lex_do(lex);
622         }
623
624         if (!strcmp(v, "framevalue"))
625         {
626             ch = lex_getch(lex);
627             while (ch != EOF && isspace(ch) && ch != '\n')
628                 ch = lex_getch(lex);
629
630             if (!isdigit(ch)) {
631                 lexerror(lex, "$framevalue requires an integer parameter");
632                 return lex_do(lex);
633             }
634
635             lex_token_new(lex);
636             lex->tok.ttype = lex_finish_digit(lex, ch);
637             if (!lex_endtoken(lex))
638                 return (lex->tok.ttype = TOKEN_FATAL);
639             if (lex->tok.ttype != TOKEN_INTCONST) {
640                 lexerror(lex, "$framevalue requires an integer parameter");
641                 return lex_do(lex);
642             }
643             lex->framevalue = lex->tok.constval.i;
644             return lex_do(lex);
645         }
646
647         if (!strcmp(v, "framerestore"))
648         {
649             int rc;
650
651             lex_token_new(lex);
652
653             rc = lex_parse_frame(lex);
654
655             if (rc > 0) {
656                 lexerror(lex, "$framerestore requires a framename parameter");
657                 return lex_do(lex);
658             }
659             if (rc < 0)
660                 return (lex->tok.ttype = TOKEN_FATAL);
661
662             v = lex->tok.value;
663             for (frame = 0; frame < lex->frames_count; ++frame) {
664                 if (!strcmp(v, lex->frames[frame].name)) {
665                     lex->framevalue = lex->frames[frame].value;
666                     return lex_do(lex);
667                 }
668             }
669             lexerror(lex, "unknown framename `%s`", v);
670             return lex_do(lex);
671         }
672
673         if (!strcmp(v, "modelname"))
674         {
675             int rc;
676
677             lex_token_new(lex);
678
679             rc = lex_parse_frame(lex);
680
681             if (rc > 0) {
682                 lexerror(lex, "$framerestore requires a framename parameter");
683                 return lex_do(lex);
684             }
685             if (rc < 0)
686                 return (lex->tok.ttype = TOKEN_FATAL);
687
688             v = lex->tok.value;
689             if (lex->modelname) {
690                 frame_macro m;
691                 m.value = lex->framevalue;
692                 m.name = lex->modelname;
693                 lex->modelname = NULL;
694                 if (!lex_file_frames_add(lex, m)) {
695                     lexerror(lex, "out of memory");
696                     return (lex->tok.ttype = TOKEN_FATAL);
697                 }
698             }
699             lex->modelname = lex->tok.value;
700             lex->tok.value = NULL;
701             lex->tok.value_alloc = lex->tok.value_count = 0;
702             for (frame = 0; frame < lex->frames_count; ++frame) {
703                 if (!strcmp(v, lex->frames[frame].name)) {
704                     lex->framevalue = lex->frames[frame].value;
705                     break;
706                 }
707             }
708             return lex_do(lex);
709         }
710
711         if (!strcmp(v, "flush"))
712         {
713             size_t frame;
714             for (frame = 0; frame < lex->frames_count; ++frame)
715                 mem_d(lex->frames[frame].name);
716             MEM_VECTOR_CLEAR(lex, frames);
717             /* skip line (fteqcc does it too) */
718             ch = lex_getch(lex);
719             while (ch != EOF && ch != '\n')
720                 ch = lex_getch(lex);
721             return lex_do(lex);
722         }
723
724         if (!strcmp(v, "cd") ||
725             !strcmp(v, "origin") ||
726             !strcmp(v, "base") ||
727             !strcmp(v, "flags") ||
728             !strcmp(v, "scale") ||
729             !strcmp(v, "skin"))
730         {
731             /* skip line */
732             ch = lex_getch(lex);
733             while (ch != EOF && ch != '\n')
734                 ch = lex_getch(lex);
735             return lex_do(lex);
736         }
737
738         for (frame = 0; frame < lex->frames_count; ++frame) {
739             if (!strcmp(v, lex->frames[frame].name)) {
740                 lex->tok.constval.i = lex->frames[frame].value;
741                 return (lex->tok.ttype = TOKEN_INTCONST);
742             }
743         }
744
745         lexerror(lex, "invalid frame macro");
746         return lex_do(lex);
747     }
748
749     /* single-character tokens */
750     switch (ch)
751     {
752         case '(':
753             if (!lex_tokench(lex, ch) ||
754                 !lex_endtoken(lex))
755             {
756                 return (lex->tok.ttype = TOKEN_FATAL);
757             }
758             if (lex->flags.noops)
759                 return (lex->tok.ttype = ch);
760             else
761                 return (lex->tok.ttype = TOKEN_OPERATOR);
762         case ')':
763         case ';':
764         case '{':
765         case '}':
766         case '[':
767         case ']':
768
769         case '#':
770             if (!lex_tokench(lex, ch) ||
771                 !lex_endtoken(lex))
772             {
773                 return (lex->tok.ttype = TOKEN_FATAL);
774             }
775             return (lex->tok.ttype = ch);
776         default:
777             break;
778     }
779
780     if (lex->flags.noops)
781     {
782         /* Detect characters early which are normally
783          * operators OR PART of an operator.
784          */
785         switch (ch)
786         {
787             case '+':
788             case '-':
789             case '*':
790             case '/':
791             case '<':
792             case '>':
793             case '=':
794             case '&':
795             case '|':
796             case '^':
797             case '~':
798             case ',':
799             case '!':
800                 if (!lex_tokench(lex, ch) ||
801                     !lex_endtoken(lex))
802                 {
803                     return (lex->tok.ttype = TOKEN_FATAL);
804                 }
805                 return (lex->tok.ttype = ch);
806             default:
807                 break;
808         }
809
810         if (ch == '.')
811         {
812             if (!lex_tokench(lex, ch))
813                 return (lex->tok.ttype = TOKEN_FATAL);
814             /* peak ahead once */
815             nextch = lex_getch(lex);
816             if (nextch != '.') {
817                 lex_ungetch(lex, nextch);
818                 if (!lex_endtoken(lex))
819                     return (lex->tok.ttype = TOKEN_FATAL);
820                 return (lex->tok.ttype = ch);
821             }
822             /* peak ahead again */
823             nextch = lex_getch(lex);
824             if (nextch != '.') {
825                 lex_ungetch(lex, nextch);
826                 lex_ungetch(lex, nextch);
827                 if (!lex_endtoken(lex))
828                     return (lex->tok.ttype = TOKEN_FATAL);
829                 return (lex->tok.ttype = ch);
830             }
831             /* fill the token to be "..." */
832             if (!lex_tokench(lex, ch) ||
833                 !lex_tokench(lex, ch) ||
834                 !lex_endtoken(lex))
835             {
836                 return (lex->tok.ttype = TOKEN_FATAL);
837             }
838             return (lex->tok.ttype = TOKEN_DOTS);
839         }
840     }
841
842     if (ch == ',' || ch == '.') {
843         if (!lex_tokench(lex, ch) ||
844             !lex_endtoken(lex))
845         {
846             return (lex->tok.ttype = TOKEN_FATAL);
847         }
848         return (lex->tok.ttype = TOKEN_OPERATOR);
849     }
850
851     if (ch == '+' || ch == '-' || /* ++, --, +=, -=  and -> as well! */
852         ch == '>' || ch == '<' || /* <<, >>, <=, >= */
853         ch == '=' || ch == '!' || /* ==, != */
854         ch == '&' || ch == '|')   /* &&, ||, &=, |= */
855     {
856         if (!lex_tokench(lex, ch))
857             return (lex->tok.ttype = TOKEN_FATAL);
858
859         nextch = lex_getch(lex);
860         if (nextch == ch || nextch == '=') {
861             if (!lex_tokench(lex, nextch))
862                 return (lex->tok.ttype = TOKEN_FATAL);
863         } else if (ch == '-' && nextch == '>') {
864             if (!lex_tokench(lex, nextch))
865                 return (lex->tok.ttype = TOKEN_FATAL);
866         } else
867             lex_ungetch(lex, nextch);
868
869         if (!lex_endtoken(lex))
870             return (lex->tok.ttype = TOKEN_FATAL);
871         return (lex->tok.ttype = TOKEN_OPERATOR);
872     }
873
874     /*
875     if (ch == '^' || ch == '~' || ch == '!')
876     {
877         if (!lex_tokench(lex, ch) ||
878             !lex_endtoken(lex))
879         {
880             return (lex->tok.ttype = TOKEN_FATAL);
881         }
882         return (lex->tok.ttype = TOKEN_OPERATOR);
883     }
884     */
885
886     if (ch == '*' || ch == '/') /* *=, /= */
887     {
888         if (!lex_tokench(lex, ch))
889             return (lex->tok.ttype = TOKEN_FATAL);
890
891         nextch = lex_getch(lex);
892         if (nextch == '=') {
893             if (!lex_tokench(lex, nextch))
894                 return (lex->tok.ttype = TOKEN_FATAL);
895         } else
896             lex_ungetch(lex, nextch);
897
898         if (!lex_endtoken(lex))
899             return (lex->tok.ttype = TOKEN_FATAL);
900         return (lex->tok.ttype = TOKEN_OPERATOR);
901     }
902
903     if (isident_start(ch))
904     {
905         const char *v;
906
907         if (!lex_tokench(lex, ch))
908             return (lex->tok.ttype = TOKEN_FATAL);
909         if (!lex_finish_ident(lex)) {
910             /* error? */
911             return (lex->tok.ttype = TOKEN_ERROR);
912         }
913         if (!lex_endtoken(lex))
914             return (lex->tok.ttype = TOKEN_FATAL);
915         lex->tok.ttype = TOKEN_IDENT;
916
917         v = lex->tok.value;
918         if (!strcmp(v, "void")) {
919             lex->tok.ttype = TOKEN_TYPENAME;
920             lex->tok.constval.t = TYPE_VOID;
921         } else if (!strcmp(v, "int")) {
922             lex->tok.ttype = TOKEN_TYPENAME;
923             lex->tok.constval.t = TYPE_INTEGER;
924         } else if (!strcmp(v, "float")) {
925             lex->tok.ttype = TOKEN_TYPENAME;
926             lex->tok.constval.t = TYPE_FLOAT;
927         } else if (!strcmp(v, "string")) {
928             lex->tok.ttype = TOKEN_TYPENAME;
929             lex->tok.constval.t = TYPE_STRING;
930         } else if (!strcmp(v, "entity")) {
931             lex->tok.ttype = TOKEN_TYPENAME;
932             lex->tok.constval.t = TYPE_ENTITY;
933         } else if (!strcmp(v, "vector")) {
934             lex->tok.ttype = TOKEN_TYPENAME;
935             lex->tok.constval.t = TYPE_VECTOR;
936         } else if (!strcmp(v, "for")  ||
937                  !strcmp(v, "while")  ||
938                  !strcmp(v, "do")     ||
939                  !strcmp(v, "if")     ||
940                  !strcmp(v, "else")   ||
941                  !strcmp(v, "local")  ||
942                  !strcmp(v, "return") ||
943                  !strcmp(v, "const"))
944             lex->tok.ttype = TOKEN_KEYWORD;
945
946         return lex->tok.ttype;
947     }
948
949     if (ch == '"')
950     {
951         lex->tok.ttype = lex_finish_string(lex, '"');
952         while (lex->tok.ttype == TOKEN_STRINGCONST)
953         {
954             /* Allow c style "string" "continuation" */
955             ch = lex_skipwhite(lex);
956             if (ch != '"') {
957                 lex_ungetch(lex, ch);
958                 break;
959             }
960
961             lex->tok.ttype = lex_finish_string(lex, '"');
962         }
963         if (!lex_endtoken(lex))
964             return (lex->tok.ttype = TOKEN_FATAL);
965         return lex->tok.ttype;
966     }
967
968     if (ch == '\'')
969     {
970         /* we parse character constants like string,
971          * but return TOKEN_CHARCONST, or a vector type if it fits...
972          * Likewise actual unescaping has to be done by the parser.
973          * The difference is we don't allow 'char' 'continuation'.
974          */
975          lex->tok.ttype = lex_finish_string(lex, '\'');
976          if (!lex_endtoken(lex))
977               return (lex->tok.ttype = TOKEN_FATAL);
978
979          /* It's a vector if we can successfully scan 3 floats */
980 #ifdef WIN32
981          if (sscanf_s(lex->tok.value, " %f %f %f ",
982                     &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
983 #else
984          if (sscanf(lex->tok.value, " %f %f %f ",
985                     &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
986 #endif
987          {
988               lex->tok.ttype = TOKEN_VECTORCONST;
989          }
990
991          return lex->tok.ttype;
992     }
993
994     if (isdigit(ch))
995     {
996         lex->tok.ttype = lex_finish_digit(lex, ch);
997         if (!lex_endtoken(lex))
998             return (lex->tok.ttype = TOKEN_FATAL);
999         return lex->tok.ttype;
1000     }
1001
1002     lexerror(lex, "unknown token");
1003     return (lex->tok.ttype = TOKEN_ERROR);
1004 }