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