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