]> git.sur5r.net Git - cc65/blob - src/cc65/scanner.c
6516a396c10bb46e792f4c9030fa97ea97e92533
[cc65] / src / cc65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*                      Source file line info structure                      */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2004 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <ctype.h>
41 #include <math.h>
42
43 /* common */
44 #include "chartype.h"
45 #include "tgttrans.h"
46
47 /* cc65 */
48 #include "datatype.h"
49 #include "error.h"
50 #include "function.h"
51 #include "global.h"
52 #include "hexval.h"
53 #include "ident.h"
54 #include "input.h"
55 #include "litpool.h"
56 #include "preproc.h"
57 #include "scanner.h"
58 #include "standard.h"
59 #include "symtab.h"
60
61
62
63 /*****************************************************************************/
64 /*                                   data                                    */
65 /*****************************************************************************/
66
67
68
69 Token CurTok;           /* The current token */
70 Token NextTok;          /* The next token */
71
72
73
74 /* Token types */
75 enum {
76     TT_C89      = 0x01 << STD_C89,      /* Token valid in C89 */
77     TT_C99      = 0x01 << STD_C99,      /* Token valid in C99 */
78     TT_CC65     = 0x01 << STD_CC65      /* Token valid in cc65 */
79 };
80
81 /* Token table */
82 static const struct Keyword {
83     char*           Key;        /* Keyword name */
84     unsigned char   Tok;        /* The token */
85     unsigned char   Std;        /* Token supported in which standards? */
86 } Keywords [] = {
87     { "_Pragma",        TOK_PRAGMA,     TT_C89 | TT_C99 | TT_CC65  },   /* !! */
88     { "__AX__",         TOK_AX,         TT_C89 | TT_C99 | TT_CC65  },
89     { "__A__",          TOK_A,          TT_C89 | TT_C99 | TT_CC65  },
90     { "__EAX__",        TOK_EAX,        TT_C89 | TT_C99 | TT_CC65  },
91     { "__X__",          TOK_X,          TT_C89 | TT_C99 | TT_CC65  },
92     { "__Y__",          TOK_Y,          TT_C89 | TT_C99 | TT_CC65  },
93     { "__asm__",        TOK_ASM,        TT_C89 | TT_C99 | TT_CC65  },
94     { "__attribute__",  TOK_ATTRIBUTE,  TT_C89 | TT_C99 | TT_CC65  },
95     { "__far__",        TOK_FAR,        TT_C89 | TT_C99 | TT_CC65  },
96     { "__fastcall__",   TOK_FASTCALL,   TT_C89 | TT_C99 | TT_CC65  },
97     { "__inline__",     TOK_INLINE,     TT_C89 | TT_C99 | TT_CC65  },
98     { "__near__",       TOK_NEAR,       TT_C89 | TT_C99 | TT_CC65  },
99     { "asm",            TOK_ASM,                          TT_CC65  },
100     { "auto",           TOK_AUTO,       TT_C89 | TT_C99 | TT_CC65  },
101     { "break",          TOK_BREAK,      TT_C89 | TT_C99 | TT_CC65  },
102     { "case",           TOK_CASE,       TT_C89 | TT_C99 | TT_CC65  },
103     { "char",           TOK_CHAR,       TT_C89 | TT_C99 | TT_CC65  },
104     { "const",          TOK_CONST,      TT_C89 | TT_C99 | TT_CC65  },
105     { "continue",       TOK_CONTINUE,   TT_C89 | TT_C99 | TT_CC65  },
106     { "default",        TOK_DEFAULT,    TT_C89 | TT_C99 | TT_CC65  },
107     { "do",             TOK_DO,         TT_C89 | TT_C99 | TT_CC65  },
108     { "double",         TOK_DOUBLE,     TT_C89 | TT_C99 | TT_CC65  },
109     { "else",           TOK_ELSE,       TT_C89 | TT_C99 | TT_CC65  },
110     { "enum",           TOK_ENUM,       TT_C89 | TT_C99 | TT_CC65  },
111     { "extern",         TOK_EXTERN,     TT_C89 | TT_C99 | TT_CC65  },
112     { "far",            TOK_FAR,                          TT_CC65  },
113     { "fastcall",       TOK_FASTCALL,                     TT_CC65  },
114     { "float",          TOK_FLOAT,      TT_C89 | TT_C99 | TT_CC65  },
115     { "for",            TOK_FOR,        TT_C89 | TT_C99 | TT_CC65  },
116     { "goto",           TOK_GOTO,       TT_C89 | TT_C99 | TT_CC65  },
117     { "if",             TOK_IF,         TT_C89 | TT_C99 | TT_CC65  },
118     { "inline",         TOK_INLINE,              TT_C99 | TT_CC65  },
119     { "int",            TOK_INT,        TT_C89 | TT_C99 | TT_CC65  },
120     { "long",           TOK_LONG,       TT_C89 | TT_C99 | TT_CC65  },
121     { "near",           TOK_NEAR,                         TT_CC65  },
122     { "register",       TOK_REGISTER,   TT_C89 | TT_C99 | TT_CC65  },
123     { "restrict",       TOK_RESTRICT,            TT_C99 | TT_CC65  },
124     { "return",         TOK_RETURN,     TT_C89 | TT_C99 | TT_CC65  },
125     { "short",          TOK_SHORT,      TT_C89 | TT_C99 | TT_CC65  },
126     { "signed",         TOK_SIGNED,     TT_C89 | TT_C99 | TT_CC65  },
127     { "sizeof",         TOK_SIZEOF,     TT_C89 | TT_C99 | TT_CC65  },
128     { "static",         TOK_STATIC,     TT_C89 | TT_C99 | TT_CC65  },
129     { "struct",         TOK_STRUCT,     TT_C89 | TT_C99 | TT_CC65  },
130     { "switch",         TOK_SWITCH,     TT_C89 | TT_C99 | TT_CC65  },
131     { "typedef",        TOK_TYPEDEF,    TT_C89 | TT_C99 | TT_CC65  },
132     { "union",          TOK_UNION,      TT_C89 | TT_C99 | TT_CC65  },
133     { "unsigned",       TOK_UNSIGNED,   TT_C89 | TT_C99 | TT_CC65  },
134     { "void",           TOK_VOID,       TT_C89 | TT_C99 | TT_CC65  },
135     { "volatile",       TOK_VOLATILE,   TT_C89 | TT_C99 | TT_CC65  },
136     { "while",          TOK_WHILE,      TT_C89 | TT_C99 | TT_CC65  },
137 };
138 #define KEY_COUNT       (sizeof (Keywords) / sizeof (Keywords [0]))
139
140
141
142 /* Stuff for determining the type of an integer constant */
143 #define IT_INT          0x01
144 #define IT_UINT         0x02
145 #define IT_LONG         0x04
146 #define IT_ULONG        0x08
147
148
149
150 /*****************************************************************************/
151 /*                                   code                                    */
152 /*****************************************************************************/
153
154
155
156 static int CmpKey (const void* Key, const void* Elem)
157 /* Compare function for bsearch */
158 {
159     return strcmp ((const char*) Key, ((const struct Keyword*) Elem)->Key);
160 }
161
162
163
164 static token_t FindKey (const char* Key)
165 /* Find a keyword and return the token. Return IDENT if the token is not a
166  * keyword.
167  */
168 {
169     struct Keyword* K;
170     K = bsearch (Key, Keywords, KEY_COUNT, sizeof (Keywords [0]), CmpKey);
171     if (K && (K->Std & (0x01 << IS_Get (&Standard))) != 0) {
172         return K->Tok;
173     } else {
174         return TOK_IDENT;
175     }
176 }
177
178
179
180 static int SkipWhite (void)
181 /* Skip white space in the input stream, reading and preprocessing new lines
182  * if necessary. Return 0 if end of file is reached, return 1 otherwise.
183  */
184 {
185     while (1) {
186         while (CurC == '\0') {
187             if (NextLine () == 0) {
188                 return 0;
189             }
190             Preprocess ();
191         }
192         if (IsSpace (CurC)) {
193             NextChar ();
194         } else {
195             return 1;
196         }
197     }
198 }
199
200
201
202 void SymName (char* S)
203 /* Read a symbol from the input stream. The first character must have been
204  * checked before calling this function. The buffer is expected to be at
205  * least of size MAX_IDENTLEN+1.
206  */
207 {
208     unsigned Len = 0;
209     do {
210         if (Len < MAX_IDENTLEN) {
211             ++Len;
212             *S++ = CurC;
213         }
214         NextChar ();
215     } while (IsIdent (CurC) || IsDigit (CurC));
216     *S = '\0';
217 }
218
219
220
221 int IsSym (char* S)
222 /* If a symbol follows, read it and return 1, otherwise return 0 */
223 {
224     if (IsIdent (CurC)) {
225         SymName (S);
226         return 1;
227     } else {
228         return 0;
229     }
230 }
231
232
233
234 static void UnknownChar (char C)
235 /* Error message for unknown character */
236 {
237     Error ("Invalid input character with code %02X", C & 0xFF);
238     NextChar ();                        /* Skip */
239 }
240
241
242
243 static void SetTok (int tok)
244 /* Set NextTok.Tok and bump line ptr */
245 {
246     NextTok.Tok = tok;
247     NextChar ();
248 }
249
250
251
252 static int ParseChar (void)
253 /* Parse a character. Converts escape chars into character codes. */
254 {
255     int C;
256     int HadError;
257
258     /* Check for escape chars */
259     if (CurC == '\\') {
260         NextChar ();
261         switch (CurC) {
262             case '?':
263                 C = '\?';
264                 break;
265             case 'a':
266                 C = '\a';
267                 break;
268             case 'b':
269                 C = '\b';
270                 break;
271             case 'f':
272                 C = '\f';
273                 break;
274             case 'r':
275                 C = '\r';
276                 break;
277             case 'n':
278                 C = '\n';
279                 break;
280             case 't':
281                 C = '\t';
282                 break;
283             case 'v':
284                 C = '\v';
285                 break;
286             case '\"':
287                 C = '\"';
288                 break;
289             case '\'':
290                 C = '\'';
291                 break;
292             case '\\':
293                 C = '\\';
294                 break;
295             case 'x':
296             case 'X':
297                 /* Hex character constant */
298                 if (!IsXDigit (NextC)) {
299                     Error ("\\x used with no following hex digits");
300                     C = ' ';
301                 } else {
302                     HadError = 0;
303                     C = 0;
304                     while (IsXDigit (NextC)) {
305                         if ((C << 4) >= 256) {
306                             if (!HadError) {
307                                 Error ("Hex character constant out of range");
308                                 HadError = 1;
309                             }
310                         } else {
311                             C = (C << 4) | HexVal (NextC);
312                         }
313                         NextChar ();
314                     }
315                 }
316                 break;
317             case '0':
318             case '1':
319             case '2':
320             case '3':
321             case '4':
322             case '5':
323             case '6':
324             case '7':
325                 /* Octal constant */
326                 HadError = 0;
327                 C = HexVal (CurC);
328                 while (IsODigit (NextC)) {
329                     if ((C << 3) >= 256) {
330                         if (!HadError) {
331                             Error ("Octal character constant out of range");
332                             HadError = 1;
333                         }
334                     } else {
335                         C = (C << 3) | HexVal (NextC);
336                     }
337                     NextChar ();
338                 }
339                 break;
340             default:
341                 Error ("Illegal character constant");
342                 C = ' ';
343                 /* Try to do error recovery, otherwise the compiler will spit
344                  * out thousands of errors in this place and abort.
345                  */
346                 if (CurC != '\'' && CurC != '\0') {
347                     while (NextC != '\'' && NextC != '\"' && NextC != '\0') {
348                         NextChar ();
349                     }
350                 }
351                 break;
352         }
353     } else {
354         C = CurC;
355     }
356
357     /* Skip the character read */
358     NextChar ();
359
360     /* Do correct sign extension */
361     return SignExtendChar (C);
362 }
363
364
365
366 static void CharConst (void)
367 /* Parse a character constant. */
368 {
369     int C;
370
371     /* Skip the quote */
372     NextChar ();
373
374     /* Get character */
375     C = ParseChar ();
376
377     /* Check for closing quote */
378     if (CurC != '\'') {
379         Error ("`\'' expected");
380     } else {
381         /* Skip the quote */
382         NextChar ();
383     }
384
385     /* Setup values and attributes */
386     NextTok.Tok  = TOK_CCONST;
387
388     /* Translate into target charset */
389     NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
390
391     /* Character constants have type int */
392     NextTok.Type = type_int;
393 }
394
395
396
397 static void StringConst (void)
398 /* Parse a quoted string */
399 {
400     NextTok.IVal = GetLiteralPoolOffs ();
401     NextTok.Tok  = TOK_SCONST;
402
403     /* Be sure to concatenate strings */
404     while (CurC == '\"') {
405
406         /* Skip the quote char */
407         NextChar ();
408
409         while (CurC != '\"') {
410             if (CurC == '\0') {
411                 Error ("Unexpected newline");
412                 break;
413             }
414             AddLiteralChar (ParseChar ());
415         }
416
417         /* Skip closing quote char if there was one */
418         NextChar ();
419
420         /* Skip white space, read new input */
421         SkipWhite ();
422
423     }
424
425     /* Terminate the string */
426     AddLiteralChar ('\0');
427 }
428
429
430
431 static void NumericConst (void)
432 /* Parse a numeric constant */
433 {
434     unsigned Base;              /* Temporary number base */
435     unsigned Prefix;            /* Base according to prefix */
436     StrBuf   S;
437     int      IsFloat;
438     char     C;
439     unsigned DigitVal;
440     unsigned long IVal;         /* Value */
441
442     /* Check for a leading hex or octal prefix and determine the possible
443      * integer types.
444      */
445     if (CurC == '0') {
446         /* Gobble 0 and examine next char */
447         NextChar ();
448         if (toupper (CurC) == 'X') {
449             Base = Prefix = 16;
450             NextChar ();        /* gobble "x" */
451         } else {
452             Base = 10;          /* Assume 10 for now - see below */
453             Prefix = 8;         /* Actual prefix says octal */
454         }
455     } else {
456         Base  = Prefix = 10;
457     }
458
459     /* Because floating point numbers don't have octal prefixes (a number
460      * with a leading zero is decimal), we first have to read the number
461      * before converting it, so we can determine if it's a float or an
462      * integer.
463      */
464     InitStrBuf (&S);
465     while (IsXDigit (CurC) && HexVal (CurC) < Base) {
466         SB_AppendChar (&S, CurC);
467         NextChar ();
468     }
469     SB_Terminate (&S);
470
471     /* The following character tells us if we have an integer or floating
472      * point constant. Note: Hexadecimal floating point constants aren't
473      * supported in C89.
474      */
475     IsFloat = (CurC == '.' ||
476                (Base == 10 && toupper (CurC) == 'E') ||
477                (Base == 16 && toupper (CurC) == 'P' && IS_Get (&Standard) >= STD_C99));
478
479     /* If we don't have a floating point type, an octal prefix results in an
480      * octal base.
481      */
482     if (!IsFloat && Prefix == 8) {
483         Base = 8;
484     }
485
486     /* Since we do now know the correct base, convert the remembered input
487      * into a number.
488      */
489     SB_Reset (&S);
490     IVal = 0;
491     while ((C = SB_Get (&S)) != '\0') {
492         DigitVal = HexVal (C);
493         if (DigitVal >= Base) {
494             Error ("Numeric constant contains digits beyond the radix");
495         }
496         IVal = (IVal * Base) + DigitVal;
497     }
498
499     /* We don't need the string buffer any longer */
500     DoneStrBuf (&S);
501
502     /* Distinguish between integer and floating point constants */
503     if (!IsFloat) {
504
505         unsigned Types;
506         int      HaveSuffix;
507
508         /* Check for a suffix and determine the possible types */
509         HaveSuffix = 1;
510         if (toupper (CurC) == 'U') {
511             /* Unsigned type */
512             NextChar ();
513             if (toupper (CurC) != 'L') {
514                 Types = IT_UINT | IT_ULONG;
515             } else {
516                 NextChar ();
517                 Types = IT_ULONG;
518             }
519         } else if (toupper (CurC) == 'L') {
520             /* Long type */
521             NextChar ();
522             if (toupper (CurC) != 'U') {
523                 Types = IT_LONG | IT_ULONG;
524             } else {
525                 NextChar ();
526                 Types = IT_ULONG;
527             }
528         } else {
529             HaveSuffix = 0;
530             if (Prefix == 10) {
531                 /* Decimal constants are of any type but uint */
532                 Types = IT_INT | IT_LONG | IT_ULONG;
533             } else {
534                 /* Octal or hex constants are of any type */
535                 Types = IT_INT | IT_UINT | IT_LONG | IT_ULONG;
536             }
537         }
538
539         /* Check the range to determine the type */
540         if (IVal > 0x7FFF) {
541             /* Out of range for int */
542             Types &= ~IT_INT;
543             /* If the value is in the range 0x8000..0xFFFF, unsigned int is not
544              * allowed, and we don't have a type specifying suffix, emit a
545              * warning, because the constant is of type long.
546              */
547             if (IVal <= 0xFFFF && (Types & IT_UINT) == 0 && !HaveSuffix) {
548                 Warning ("Constant is long");
549             }
550         }
551         if (IVal > 0xFFFF) {
552             /* Out of range for unsigned int */
553             Types &= ~IT_UINT;
554         }
555         if (IVal > 0x7FFFFFFF) {
556             /* Out of range for long int */
557             Types &= ~IT_LONG;
558         }
559
560         /* Now set the type string to the smallest type in types */
561         if (Types & IT_INT) {
562             NextTok.Type = type_int;
563         } else if (Types & IT_UINT) {
564             NextTok.Type = type_uint;
565         } else if (Types & IT_LONG) {
566             NextTok.Type = type_long;
567         } else {
568             NextTok.Type = type_ulong;
569         }
570
571         /* Set the value and the token */
572         NextTok.IVal = IVal;
573         NextTok.Tok  = TOK_ICONST;
574
575     } else {
576
577         /* Float constant */
578         double FVal = IVal;             /* Convert to float */
579
580         /* Check for a fractional part and read it */
581         if (CurC == '.') {
582
583             unsigned Digits;
584             unsigned long Frac;
585             unsigned long Scale;
586
587             /* Skip the dot */
588             NextChar ();
589
590             /* Read fractional digits. Since we support only 32 bit floats
591              * with a maximum of 7 fractional digits, we read the fractional
592              * part as integer with up to 8 digits and drop the remainder.
593              * This avoids an overflow of Frac and Scale.
594              */
595             Digits = 0;
596             Frac   = 0;
597             Scale  = 1;
598             while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) {
599                 if (Digits < 8) {
600                     Frac = Frac * Base + DigitVal;
601                     ++Digits;
602                     Scale *= Base;
603                 }
604                 NextChar ();
605             }
606
607             /* Scale the fractional part and add it */
608             if (Frac) {
609                 FVal += ((double) Frac) / ((double) Scale);
610             }
611         }
612
613         /* Check for an exponent and read it */
614         if ((Base == 16 && toupper (CurC) == 'F') ||
615             (Base == 10 && toupper (CurC) == 'E')) {
616
617             int Sign;
618             unsigned Digits;
619             unsigned Exp;
620
621             /* Skip the exponent notifier */
622             NextChar ();
623
624             /* Read an optional sign */
625             Sign = 1;
626             if (CurC == '-') {
627                 Sign = -1;
628                 NextChar ();
629             }
630
631             /* Read exponent digits. Since we support only 32 bit floats
632              * with a maximum exponent of +-/127, we read the exponent
633              * part as integer with up to 3 digits and drop the remainder.
634              * This avoids an overflow of Exp. The exponent is always
635              * decimal, even for hex float consts.
636              */
637             Digits = 0;
638             Exp    = 0;
639             while (IsDigit (CurC)) {
640                 if (++Digits <= 3) {
641                     Exp = Exp * 10 + HexVal (CurC);
642                 }
643                 NextChar ();
644             }
645
646             /* Check for errors: We must have exponent digits, and not more
647              * than three.
648              */
649             if (Digits == 0) {
650                 Error ("Floating constant exponent has no digits");
651             } else if (Digits > 3) {
652                 Warning ("Floating constant exponent is too large");
653             }
654
655             /* Scale the exponent and adjust the value accordingly */
656             if (Exp) {
657                 FVal *= pow (10, Exp);
658             }
659         }
660
661         /* Check for a suffix and determine the type of the constant */
662         if (toupper (CurC) == 'F') {
663             NextChar ();
664             NextTok.Type = type_float;
665         } else {
666             NextTok.Type = type_double;
667         }
668
669         /* Set the value and the token */
670         NextTok.FVal = FVal;
671         NextTok.Tok  = TOK_FCONST;
672
673     }
674 }
675
676
677
678 void NextToken (void)
679 /* Get next token from input stream */
680 {
681     ident token;
682
683     /* We have to skip white space here before shifting tokens, since the
684      * tokens and the current line info is invalid at startup and will get
685      * initialized by reading the first time from the file. Remember if
686      * we were at end of input and handle that later.
687      */
688     int GotEOF = (SkipWhite() == 0);
689
690     /* Current token is the lookahead token */
691     if (CurTok.LI) {
692         ReleaseLineInfo (CurTok.LI);
693     }
694     CurTok = NextTok;
695
696     /* When reading the first time from the file, the line info in NextTok,
697      * which was copied to CurTok is invalid. Since the information from
698      * the token is used for error messages, we must make it valid.
699      */
700     if (CurTok.LI == 0) {
701         CurTok.LI = UseLineInfo (GetCurLineInfo ());
702     }
703
704     /* Remember the starting position of the next token */
705     NextTok.LI = UseLineInfo (GetCurLineInfo ());
706
707     /* Now handle end of input. */
708     if (GotEOF) {
709         /* End of file reached */
710         NextTok.Tok = TOK_CEOF;
711         return;
712     }
713
714     /* Determine the next token from the lookahead */
715     if (IsDigit (CurC) || (CurC == '.' && IsDigit (NextC))) {
716         /* A number */
717         NumericConst ();
718         return;
719     }
720
721     if (IsSym (token)) {
722
723         /* Check for a keyword */
724         if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
725             /* Reserved word found */
726             return;
727         }
728         /* No reserved word, check for special symbols */
729         if (token[0] == '_' && token[1] == '_') {
730             /* Special symbols */
731             if (strcmp (token+2, "FILE__") == 0) {
732                 NextTok.IVal = AddLiteral (GetCurrentFile());
733                 NextTok.Tok  = TOK_SCONST;
734                 return;
735             } else if (strcmp (token+2, "LINE__") == 0) {
736                 NextTok.Tok  = TOK_ICONST;
737                 NextTok.IVal = GetCurrentLine();
738                 NextTok.Type = type_int;
739                 return;
740             } else if (strcmp (token+2, "func__") == 0) {
741                 /* __func__ is only defined in functions */
742                 if (CurrentFunc) {
743                     NextTok.IVal = AddLiteral (F_GetFuncName (CurrentFunc));
744                     NextTok.Tok  = TOK_SCONST;
745                     return;
746                 }
747             }
748         }
749
750         /* No reserved word but identifier */
751         strcpy (NextTok.Ident, token);
752         NextTok.Tok = TOK_IDENT;
753         return;
754     }
755
756     /* Monstrous switch statement ahead... */
757     switch (CurC) {
758
759         case '!':
760             NextChar ();
761             if (CurC == '=') {
762                 SetTok (TOK_NE);
763             } else {
764                 NextTok.Tok = TOK_BOOL_NOT;
765             }
766             break;
767
768         case '\"':
769             StringConst ();
770             break;
771
772         case '%':
773             NextChar ();
774             if (CurC == '=') {
775                 SetTok (TOK_MOD_ASSIGN);
776             } else {
777                 NextTok.Tok = TOK_MOD;
778             }
779             break;
780
781         case '&':
782             NextChar ();
783             switch (CurC) {
784                 case '&':
785                     SetTok (TOK_BOOL_AND);
786                     break;
787                 case '=':
788                     SetTok (TOK_AND_ASSIGN);
789                     break;
790                 default:
791                     NextTok.Tok = TOK_AND;
792             }
793             break;
794
795         case '\'':
796             CharConst ();
797             break;
798
799         case '(':
800             SetTok (TOK_LPAREN);
801             break;
802
803         case ')':
804             SetTok (TOK_RPAREN);
805             break;
806
807         case '*':
808             NextChar ();
809             if (CurC == '=') {
810                 SetTok (TOK_MUL_ASSIGN);
811             } else {
812                 NextTok.Tok = TOK_STAR;
813             }
814             break;
815
816         case '+':
817             NextChar ();
818             switch (CurC) {
819                 case '+':
820                     SetTok (TOK_INC);
821                     break;
822                 case '=':
823                     SetTok (TOK_PLUS_ASSIGN);
824                     break;
825                 default:
826                     NextTok.Tok = TOK_PLUS;
827             }
828             break;
829
830         case ',':
831             SetTok (TOK_COMMA);
832             break;
833
834         case '-':
835             NextChar ();
836             switch (CurC) {
837                 case '-':
838                     SetTok (TOK_DEC);
839                     break;
840                 case '=':
841                     SetTok (TOK_MINUS_ASSIGN);
842                     break;
843                 case '>':
844                     SetTok (TOK_PTR_REF);
845                     break;
846                 default:
847                     NextTok.Tok = TOK_MINUS;
848             }
849             break;
850
851         case '.':
852             NextChar ();
853             if (CurC == '.') {
854                 NextChar ();
855                 if (CurC == '.') {
856                     SetTok (TOK_ELLIPSIS);
857                 } else {
858                     UnknownChar (CurC);
859                 }
860             } else {
861                 NextTok.Tok = TOK_DOT;
862             }
863             break;
864
865         case '/':
866             NextChar ();
867             if (CurC == '=') {
868                 SetTok (TOK_DIV_ASSIGN);
869             } else {
870                 NextTok.Tok = TOK_DIV;
871             }
872             break;
873
874         case ':':
875             SetTok (TOK_COLON);
876             break;
877
878         case ';':
879             SetTok (TOK_SEMI);
880             break;
881
882         case '<':
883             NextChar ();
884             switch (CurC) {
885                 case '=':
886                     SetTok (TOK_LE);
887                     break;
888                 case '<':
889                     NextChar ();
890                     if (CurC == '=') {
891                         SetTok (TOK_SHL_ASSIGN);
892                     } else {
893                         NextTok.Tok = TOK_SHL;
894                     }
895                     break;
896                 default:
897                     NextTok.Tok = TOK_LT;
898             }
899             break;
900
901         case '=':
902             NextChar ();
903             if (CurC == '=') {
904                 SetTok (TOK_EQ);
905             } else {
906                 NextTok.Tok = TOK_ASSIGN;
907             }
908             break;
909
910         case '>':
911             NextChar ();
912             switch (CurC) {
913                 case '=':
914                     SetTok (TOK_GE);
915                     break;
916                 case '>':
917                     NextChar ();
918                     if (CurC == '=') {
919                         SetTok (TOK_SHR_ASSIGN);
920                     } else {
921                         NextTok.Tok = TOK_SHR;
922                     }
923                     break;
924                 default:
925                     NextTok.Tok = TOK_GT;
926             }
927             break;
928
929         case '?':
930             SetTok (TOK_QUEST);
931             break;
932
933         case '[':
934             SetTok (TOK_LBRACK);
935             break;
936
937         case ']':
938             SetTok (TOK_RBRACK);
939             break;
940
941         case '^':
942             NextChar ();
943             if (CurC == '=') {
944                 SetTok (TOK_XOR_ASSIGN);
945             } else {
946                 NextTok.Tok = TOK_XOR;
947             }
948             break;
949
950         case '{':
951             SetTok (TOK_LCURLY);
952             break;
953
954         case '|':
955             NextChar ();
956             switch (CurC) {
957                 case '|':
958                     SetTok (TOK_BOOL_OR);
959                     break;
960                 case '=':
961                     SetTok (TOK_OR_ASSIGN);
962                     break;
963                 default:
964                     NextTok.Tok = TOK_OR;
965             }
966             break;
967
968         case '}':
969             SetTok (TOK_RCURLY);
970             break;
971
972         case '~':
973             SetTok (TOK_COMP);
974             break;
975
976         default:
977             UnknownChar (CurC);
978
979     }
980
981 }
982
983
984
985 void SkipTokens (const token_t* TokenList, unsigned TokenCount)
986 /* Skip tokens until we reach TOK_CEOF or a token in the given token list.
987  * This routine is used for error recovery.
988  */
989 {
990     while (CurTok.Tok != TOK_CEOF) {
991
992         /* Check if the current token is in the token list */
993         unsigned I;
994         for (I = 0; I < TokenCount; ++I) {
995             if (CurTok.Tok == TokenList[I]) {
996                 /* Found a token in the list */
997                 return;
998             }
999         }
1000
1001         /* Not in the list: Skip it */
1002         NextToken ();
1003
1004     }
1005 }
1006
1007
1008
1009 int Consume (token_t Token, const char* ErrorMsg)
1010 /* Eat token if it is the next in the input stream, otherwise print an error
1011  * message. Returns true if the token was found and false otherwise.
1012  */
1013 {
1014     if (CurTok.Tok == Token) {
1015         NextToken ();
1016         return 1;
1017     } else {
1018         Error (ErrorMsg);
1019         return 0;
1020     }
1021 }
1022
1023
1024
1025 int ConsumeColon (void)
1026 /* Check for a colon and skip it. */
1027 {
1028     return Consume (TOK_COLON, "`:' expected");
1029 }
1030
1031
1032
1033 int ConsumeSemi (void)
1034 /* Check for a semicolon and skip it. */
1035 {
1036     /* Try do be smart about typos... */
1037     if (CurTok.Tok == TOK_SEMI) {
1038         NextToken ();
1039         return 1;
1040     } else {
1041         Error ("`;' expected");
1042         if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) {
1043             NextToken ();
1044         }
1045         return 0;
1046     }
1047 }
1048
1049
1050
1051 int ConsumeComma (void)
1052 /* Check for a comma and skip it. */
1053 {
1054     /* Try do be smart about typos... */
1055     if (CurTok.Tok == TOK_COMMA) {
1056         NextToken ();
1057         return 1;
1058     } else {
1059         Error ("`,' expected");
1060         if (CurTok.Tok == TOK_SEMI) {
1061             NextToken ();
1062         }
1063         return 0;
1064     }
1065 }
1066
1067
1068
1069 int ConsumeLParen (void)
1070 /* Check for a left parenthesis and skip it */
1071 {
1072     return Consume (TOK_LPAREN, "`(' expected");
1073 }
1074
1075
1076
1077 int ConsumeRParen (void)
1078 /* Check for a right parenthesis and skip it */
1079 {
1080     return Consume (TOK_RPAREN, "`)' expected");
1081 }
1082
1083
1084
1085 int ConsumeLBrack (void)
1086 /* Check for a left bracket and skip it */
1087 {
1088     return Consume (TOK_LBRACK, "`[' expected");
1089 }
1090
1091
1092
1093 int ConsumeRBrack (void)
1094 /* Check for a right bracket and skip it */
1095 {
1096     return Consume (TOK_RBRACK, "`]' expected");
1097 }
1098
1099
1100
1101 int ConsumeLCurly (void)
1102 /* Check for a left curly brace and skip it */
1103 {
1104     return Consume (TOK_LCURLY, "`{' expected");
1105 }
1106
1107
1108
1109 int ConsumeRCurly (void)
1110 /* Check for a right curly brace and skip it */
1111 {
1112     return Consume (TOK_RCURLY, "`}' expected");
1113 }
1114
1115
1116