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