]> git.sur5r.net Git - cc65/blob - src/cc65/scanner.c
3584a56a138e46d330ddc5e64ed0a6f2c6d4b638
[cc65] / src / cc65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*                      Source file line info structure                      */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2001 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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
42 /* common */
43 #include "chartype.h"
44 #include "tgttrans.h"
45
46 /* cc65 */
47 #include "datatype.h"
48 #include "error.h"
49 #include "function.h"
50 #include "global.h"
51 #include "ident.h"
52 #include "input.h"
53 #include "litpool.h"
54 #include "preproc.h"
55 #include "symtab.h"
56 #include "util.h"
57 #include "scanner.h"
58
59
60
61 /*****************************************************************************/
62 /*                                   data                                    */
63 /*****************************************************************************/
64
65
66
67 Token CurTok;           /* The current token */
68 Token NextTok;          /* The next token */
69
70
71
72 /* Token types */
73 #define TT_C    0               /* ANSI C token */
74 #define TT_EXT  1               /* cc65 extension */
75
76 /* Token table */
77 static const struct Keyword {
78     char*           Key;        /* Keyword name */
79     unsigned char   Tok;        /* The token */
80     unsigned char   Type;       /* Token type */
81 } Keywords [] = {
82     { "__A__",          TOK_A,          TT_C    },
83     { "__AX__",         TOK_AX,         TT_C    },
84     { "__EAX__",        TOK_EAX,        TT_C    },
85     { "__X__",          TOK_X,          TT_C    },
86     { "__Y__",          TOK_Y,          TT_C    },
87     { "__asm__",        TOK_ASM,        TT_C    },
88     { "__attribute__",  TOK_ATTRIBUTE,  TT_C    },
89     { "__far__",        TOK_FAR,        TT_C    },
90     { "__fastcall__",   TOK_FASTCALL,   TT_C    },
91     { "asm",            TOK_ASM,        TT_EXT  },
92     { "auto",           TOK_AUTO,       TT_C    },
93     { "break",          TOK_BREAK,      TT_C    },
94     { "case",           TOK_CASE,       TT_C    },
95     { "char",           TOK_CHAR,       TT_C    },
96     { "const",          TOK_CONST,      TT_C    },
97     { "continue",       TOK_CONTINUE,   TT_C    },
98     { "default",        TOK_DEFAULT,    TT_C    },
99     { "do",             TOK_DO,         TT_C    },
100     { "double",         TOK_DOUBLE,     TT_C    },
101     { "else",           TOK_ELSE,       TT_C    },
102     { "enum",           TOK_ENUM,       TT_C    },
103     { "extern",         TOK_EXTERN,     TT_C    },
104     { "far",            TOK_FAR,        TT_EXT  },
105     { "fastcall",       TOK_FASTCALL,   TT_EXT  },
106     { "float",          TOK_FLOAT,      TT_C    },
107     { "for",            TOK_FOR,        TT_C    },
108     { "goto",           TOK_GOTO,       TT_C    },
109     { "if",             TOK_IF,         TT_C    },
110     { "int",            TOK_INT,        TT_C    },
111     { "long",           TOK_LONG,       TT_C    },
112     { "register",       TOK_REGISTER,   TT_C    },
113     { "return",         TOK_RETURN,     TT_C    },
114     { "short",          TOK_SHORT,      TT_C    },
115     { "signed",         TOK_SIGNED,     TT_C    },
116     { "sizeof",         TOK_SIZEOF,     TT_C    },
117     { "static",         TOK_STATIC,     TT_C    },
118     { "struct",         TOK_STRUCT,     TT_C    },
119     { "switch",         TOK_SWITCH,     TT_C    },
120     { "typedef",        TOK_TYPEDEF,    TT_C    },
121     { "union",          TOK_UNION,      TT_C    },
122     { "unsigned",       TOK_UNSIGNED,   TT_C    },
123     { "void",           TOK_VOID,       TT_C    },
124     { "volatile",       TOK_VOLATILE,   TT_C    },
125     { "while",          TOK_WHILE,      TT_C    },
126 };
127 #define KEY_COUNT       (sizeof (Keywords) / sizeof (Keywords [0]))
128
129
130
131 /* Stuff for determining the type of an integer constant */
132 #define IT_INT          0x01
133 #define IT_UINT         0x02
134 #define IT_LONG         0x04
135 #define IT_ULONG        0x08
136
137
138
139 /*****************************************************************************/
140 /*                                   code                                    */
141 /*****************************************************************************/
142
143
144
145 static int CmpKey (const void* Key, const void* Elem)
146 /* Compare function for bsearch */
147 {
148     return strcmp ((const char*) Key, ((const struct Keyword*) Elem)->Key);
149 }
150
151
152
153 static int FindKey (const char* Key)
154 /* Find a keyword and return the token. Return IDENT if the token is not a
155  * keyword.
156  */
157 {
158     struct Keyword* K;
159     K = bsearch (Key, Keywords, KEY_COUNT, sizeof (Keywords [0]), CmpKey);
160     if (K && (K->Type != TT_EXT || ANSI == 0)) {
161         return K->Tok;
162     } else {
163         return TOK_IDENT;
164     }
165 }
166
167
168
169 static int SkipWhite (void)
170 /* Skip white space in the input stream, reading and preprocessing new lines
171  * if necessary. Return 0 if end of file is reached, return 1 otherwise.
172  */
173 {
174     while (1) {
175         while (CurC == 0) {
176             if (NextLine () == 0) {
177                 return 0;
178             }
179             Preprocess ();
180         }
181         if (IsSpace (CurC)) {
182             NextChar ();
183         } else {
184             return 1;
185         }
186     }
187 }
188
189
190
191 void SymName (char* s)
192 /* Get symbol from input stream */
193 {
194     unsigned k = 0;
195     do {
196         if (k != MAX_IDENTLEN) {
197             ++k;
198             *s++ = CurC;
199         }
200         NextChar ();
201     } while (IsIdent (CurC) || IsDigit (CurC));
202     *s = '\0';
203 }
204
205
206
207 int IsSym (char *s)
208 /* Get symbol from input stream or return 0 if not a symbol. */
209 {
210     if (IsIdent (CurC)) {
211         SymName (s);
212         return 1;
213     } else {
214         return 0;
215     }
216 }
217
218
219
220 static void UnknownChar (char C)
221 /* Error message for unknown character */
222 {
223     Error ("Invalid input character with code %02X", C & 0xFF);
224     NextChar ();                        /* Skip */
225 }
226
227
228
229 static unsigned hexval (int c)
230 /* Convert a hex digit into a value */
231 {
232     if (!IsXDigit (c)) {
233         Error ("Invalid hexadecimal digit: `%c'", c);
234     }
235     if (IsDigit (c)) {
236         return c - '0';
237     } else {
238         return toupper (c) - 'A' + 10;
239     }
240 }
241
242
243
244 static void SetTok (int tok)
245 /* Set NextTok.Tok and bump line ptr */
246 {
247     NextTok.Tok = tok;
248     NextChar ();
249 }
250
251
252
253 static int SignExtendChar (int C)
254 /* Do correct sign extension of a character */
255 {
256     if (SignedChars && (C & 0x80) != 0) {
257         return C | ~0xFF;
258     } else {
259         return C & 0xFF;
260     }
261 }
262
263
264
265 static int ParseChar (void)
266 /* Parse a character. Converts \n into EOL, etc. */
267 {
268     int i;
269     unsigned val;
270     int C;
271
272     /* Check for escape chars */
273     if (CurC == '\\') {
274         NextChar ();
275         switch (CurC) {
276             case 'b':
277                 C = '\b';
278                 break;
279             case 'f':
280                 C = '\f';
281                 break;
282             case 'r':
283                 C = '\r';
284                 break;
285             case 'n':
286                 C = '\n';
287                 break;
288             case 't':
289                 C = '\t';
290                 break;
291             case '\"':
292                 C = '\"';
293                 break;
294             case '\'':
295                 C = '\'';
296                 break;
297             case '\\':
298                 C = '\\';
299                 break;
300             case 'x':
301             case 'X':
302                 /* Hex character constant */
303                 NextChar ();
304                 val = hexval (CurC) << 4;
305                 NextChar ();
306                 C = val | hexval (CurC);        /* Do not translate */
307                 break;
308             case '0':
309             case '1':
310                 /* Octal constant */
311                 i = 0;
312                 C = CurC - '0';
313                 while (NextC >= '0' && NextC <= '7' && i++ < 4) {
314                     NextChar ();
315                     C = (C << 3) | (CurC - '0');
316                 }
317                 break;
318             default:
319                 Error ("Illegal character constant");
320                 C = ' ';
321                 break;
322         }
323     } else {
324         C = CurC;
325     }
326
327     /* Skip the character read */
328     NextChar ();
329
330     /* Do correct sign extension */
331     return SignExtendChar (C);
332 }
333
334
335
336 static void CharConst (void)
337 /* Parse a character constant. */
338 {
339     int C;
340
341     /* Skip the quote */
342     NextChar ();
343
344     /* Get character */
345     C = ParseChar ();
346
347     /* Check for closing quote */
348     if (CurC != '\'') {
349         Error ("`\'' expected");
350     } else {
351         /* Skip the quote */
352         NextChar ();
353     }
354
355     /* Setup values and attributes */
356     NextTok.Tok  = TOK_CCONST;
357
358     /* Translate into target charset */
359     NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
360
361     /* Character constants have type int */
362     NextTok.Type = type_int;
363 }
364
365
366
367 static void StringConst (void)
368 /* Parse a quoted string */
369 {
370     NextTok.IVal = GetLiteralPoolOffs ();
371     NextTok.Tok  = TOK_SCONST;
372
373     /* Be sure to concatenate strings */
374     while (CurC == '\"') {
375
376         /* Skip the quote char */
377         NextChar ();
378
379         while (CurC != '\"') {
380             if (CurC == '\0') {
381                 Error ("Unexpected newline");
382                 break;
383             }
384             AddLiteralChar (ParseChar ());
385         }
386
387         /* Skip closing quote char if there was one */
388         NextChar ();
389
390         /* Skip white space, read new input */
391         SkipWhite ();
392
393     }
394
395     /* Terminate the string */
396     AddLiteralChar ('\0');
397 }
398
399
400
401 void NextToken (void)
402 /* Get next token from input stream */
403 {
404     ident token;
405
406     /* We have to skip white space here before shifting tokens, since the
407      * tokens and the current line info is invalid at startup and will get
408      * initialized by reading the first time from the file. Remember if
409      * we were at end of input and handle that later.
410      */
411     int GotEOF = (SkipWhite() == 0);
412
413     /* Current token is the lookahead token */
414     if (CurTok.LI) {
415         ReleaseLineInfo (CurTok.LI);
416     }
417     CurTok = NextTok;
418
419     /* Remember the starting position of the next token */
420     NextTok.LI = UseLineInfo (GetCurLineInfo ());
421
422     /* Now handle end of input. */
423     if (GotEOF) {
424         /* End of file reached */
425         NextTok.Tok = TOK_CEOF;
426         return;
427     }
428
429     /* Determine the next token from the lookahead */
430     if (IsDigit (CurC)) {
431
432         /* A number */
433         int HaveSuffix;         /* True if we have a type suffix */
434         unsigned types;         /* Possible types */
435         unsigned base;
436         unsigned long k;        /* Value */
437
438         k     = 0;
439         base  = 10;
440         types = IT_INT | IT_LONG | IT_ULONG;
441
442         if (CurC == '0') {
443             /* Octal or hex constants may also be of type unsigned int */
444             types = IT_INT | IT_UINT | IT_LONG | IT_ULONG;
445             /* gobble 0 and examin next char */
446             NextChar ();
447             if (toupper (CurC) == 'X') {
448                 base = 16;
449                 NextTok.Type = type_uint;
450                 NextChar ();    /* gobble "x" */
451             } else {
452                 base = 8;
453             }
454         }
455         while (1) {
456             if (IsDigit (CurC)) {
457                 k = k * base + (CurC - '0');
458             } else if (base == 16 && IsXDigit (CurC)) {
459                 k = (k << 4) + hexval (CurC);
460             } else {
461                 break;          /* not digit */
462             }
463             NextChar ();        /* gobble char */
464         }
465
466         /* Check for a suffix */
467         HaveSuffix = 1;
468         if (CurC == 'u' || CurC == 'U') {
469             /* Unsigned type */
470             NextChar ();
471             if (toupper (CurC) != 'L') {
472                 types = IT_UINT | IT_ULONG;
473             } else {
474                 NextChar ();
475                 types = IT_ULONG;
476             }
477         } else if (CurC == 'l' || CurC == 'L') {
478             /* Long type */
479             NextChar ();
480             if (toupper (CurC) != 'U') {
481                 types = IT_LONG | IT_ULONG;
482             } else {
483                 NextChar ();
484                 types = IT_ULONG;
485             }
486         } else {
487             HaveSuffix = 0;
488         }
489
490         /* Check the range to determine the type */
491         if (k > 0x7FFF) {
492             /* Out of range for int */
493             types &= ~IT_INT;
494             /* If the value is in the range 0x8000..0xFFFF, unsigned int is not
495              * allowed, and we don't have a type specifying suffix, emit a
496              * warning.
497              */
498             if (k <= 0xFFFF && (types & IT_UINT) == 0 && !HaveSuffix) {
499                 Warning ("Constant is long");
500             }
501         }
502         if (k > 0xFFFF) {
503             /* Out of range for unsigned int */
504             types &= ~IT_UINT;
505         }
506         if (k > 0x7FFFFFFF) {
507             /* Out of range for long int */
508             types &= ~IT_LONG;
509         }
510
511         /* Now set the type string to the smallest type in types */
512         if (types & IT_INT) {
513             NextTok.Type = type_int;
514         } else if (types & IT_UINT) {
515             NextTok.Type = type_uint;
516         } else if (types & IT_LONG) {
517             NextTok.Type = type_long;
518         } else {
519             NextTok.Type = type_ulong;
520         }
521
522         /* Set the value and the token */
523         NextTok.IVal = k;
524         NextTok.Tok  = TOK_ICONST;
525         return;
526     }
527
528     if (IsSym (token)) {
529
530         /* Check for a keyword */
531         if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
532             /* Reserved word found */
533             return;
534         }
535         /* No reserved word, check for special symbols */
536         if (token [0] == '_') {
537             /* Special symbols */
538             if (strcmp (token, "__FILE__") == 0) {
539                 NextTok.IVal = AddLiteral (GetCurrentFile());
540                 NextTok.Tok  = TOK_SCONST;
541                 return;
542             } else if (strcmp (token, "__LINE__") == 0) {
543                 NextTok.Tok  = TOK_ICONST;
544                 NextTok.IVal = GetCurrentLine();
545                 NextTok.Type = type_int;
546                 return;
547             } else if (strcmp (token, "__func__") == 0) {
548                 /* __func__ is only defined in functions */
549                 if (CurrentFunc) {
550                     NextTok.IVal = AddLiteral (GetFuncName (CurrentFunc));
551                     NextTok.Tok  = TOK_SCONST;
552                     return;
553                 }
554             }
555         }
556
557         /* No reserved word but identifier */
558         strcpy (NextTok.Ident, token);
559         NextTok.Tok = TOK_IDENT;
560         return;
561     }
562
563     /* Monstrous switch statement ahead... */
564     switch (CurC) {
565
566         case '!':
567             NextChar ();
568             if (CurC == '=') {
569                 SetTok (TOK_NE);
570             } else {
571                 NextTok.Tok = TOK_BOOL_NOT;
572             }
573             break;
574
575         case '\"':
576             StringConst ();
577             break;
578
579         case '%':
580             NextChar ();
581             if (CurC == '=') {
582                 SetTok (TOK_MOD_ASSIGN);
583             } else {
584                 NextTok.Tok = TOK_MOD;
585             }
586             break;
587
588         case '&':
589             NextChar ();
590             switch (CurC) {
591                 case '&':
592                     SetTok (TOK_BOOL_AND);
593                     break;
594                 case '=':
595                     SetTok (TOK_AND_ASSIGN);
596                     break;
597                 default:
598                     NextTok.Tok = TOK_AND;
599             }
600             break;
601
602         case '\'':
603             CharConst ();
604             break;
605
606         case '(':
607             SetTok (TOK_LPAREN);
608             break;
609
610         case ')':
611             SetTok (TOK_RPAREN);
612             break;
613
614         case '*':
615             NextChar ();
616             if (CurC == '=') {
617                 SetTok (TOK_MUL_ASSIGN);
618             } else {
619                 NextTok.Tok = TOK_STAR;
620             }
621             break;
622
623         case '+':
624             NextChar ();
625             switch (CurC) {
626                 case '+':
627                     SetTok (TOK_INC);
628                     break;
629                 case '=':
630                     SetTok (TOK_PLUS_ASSIGN);
631                     break;
632                 default:
633                     NextTok.Tok = TOK_PLUS;
634             }
635             break;
636
637         case ',':
638             SetTok (TOK_COMMA);
639             break;
640
641         case '-':
642             NextChar ();
643             switch (CurC) {
644                 case '-':
645                     SetTok (TOK_DEC);
646                     break;
647                 case '=':
648                     SetTok (TOK_MINUS_ASSIGN);
649                     break;
650                 case '>':
651                     SetTok (TOK_PTR_REF);
652                     break;
653                 default:
654                     NextTok.Tok = TOK_MINUS;
655             }
656             break;
657
658         case '.':
659             NextChar ();
660             if (CurC == '.') {
661                 NextChar ();
662                 if (CurC == '.') {
663                     SetTok (TOK_ELLIPSIS);
664                 } else {
665                     UnknownChar (CurC);
666                 }
667             } else {
668                 NextTok.Tok = TOK_DOT;
669             }
670             break;
671
672         case '/':
673             NextChar ();
674             if (CurC == '=') {
675                 SetTok (TOK_DIV_ASSIGN);
676             } else {
677                 NextTok.Tok = TOK_DIV;
678             }
679             break;
680
681         case ':':
682             SetTok (TOK_COLON);
683             break;
684
685         case ';':
686             SetTok (TOK_SEMI);
687             break;
688
689         case '<':
690             NextChar ();
691             switch (CurC) {
692                 case '=':
693                     SetTok (TOK_LE);
694                     break;
695                 case '<':
696                     NextChar ();
697                     if (CurC == '=') {
698                         SetTok (TOK_SHL_ASSIGN);
699                     } else {
700                         NextTok.Tok = TOK_SHL;
701                     }
702                     break;
703                 default:
704                     NextTok.Tok = TOK_LT;
705             }
706             break;
707
708         case '=':
709             NextChar ();
710             if (CurC == '=') {
711                 SetTok (TOK_EQ);
712             } else {
713                 NextTok.Tok = TOK_ASSIGN;
714             }
715             break;
716
717         case '>':
718             NextChar ();
719             switch (CurC) {
720                 case '=':
721                     SetTok (TOK_GE);
722                     break;
723                 case '>':
724                     NextChar ();
725                     if (CurC == '=') {
726                         SetTok (TOK_SHR_ASSIGN);
727                     } else {
728                         NextTok.Tok = TOK_SHR;
729                     }
730                     break;
731                 default:
732                     NextTok.Tok = TOK_GT;
733             }
734             break;
735
736         case '?':
737             SetTok (TOK_QUEST);
738             break;
739
740         case '[':
741             SetTok (TOK_LBRACK);
742             break;
743
744         case ']':
745             SetTok (TOK_RBRACK);
746             break;
747
748         case '^':
749             NextChar ();
750             if (CurC == '=') {
751                 SetTok (TOK_XOR_ASSIGN);
752             } else {
753                 NextTok.Tok = TOK_XOR;
754             }
755             break;
756
757         case '{':
758             SetTok (TOK_LCURLY);
759             break;
760
761         case '|':
762             NextChar ();
763             switch (CurC) {
764                 case '|':
765                     SetTok (TOK_BOOL_OR);
766                     break;
767                 case '=':
768                     SetTok (TOK_OR_ASSIGN);
769                     break;
770                 default:
771                     NextTok.Tok = TOK_OR;
772             }
773             break;
774
775         case '}':
776             SetTok (TOK_RCURLY);
777             break;
778
779         case '~':
780             SetTok (TOK_COMP);
781             break;
782
783         case '#':
784             /* Skip it and following whitespace */
785             do {
786                 NextChar ();
787             } while (CurC == ' ');
788             if (!IsSym (token) || strcmp (token, "pragma") != 0) {
789                 /* OOPS - should not happen */
790                 Error ("Preprocessor directive expected");
791             }
792             NextTok.Tok = TOK_PRAGMA;
793             break;
794
795         default:
796             UnknownChar (CurC);
797
798     }
799
800 }
801
802
803
804 void SkipTokens (const token_t* TokenList, unsigned TokenCount)
805 /* Skip tokens until we reach TOK_CEOF or a token in the given token list.
806  * This routine is used for error recovery.
807  */
808 {                                                              
809     while (CurTok.Tok != TOK_CEOF) {
810
811         /* Check if the current token is in the token list */
812         unsigned I;
813         for (I = 0; I < TokenCount; ++I) {
814             if (CurTok.Tok == TokenList[I]) {
815                 /* Found a token in the list */
816                 return;
817             }
818         }
819
820         /* Not in the list: Skip it */
821         NextToken ();
822
823     }
824 }
825
826
827
828 void Consume (token_t Token, const char* ErrorMsg)
829 /* Eat token if it is the next in the input stream, otherwise print an error
830  * message.
831  */
832 {
833     if (CurTok.Tok == Token) {
834         NextToken ();
835     } else {
836         Error (ErrorMsg);
837     }
838 }
839
840
841
842 void ConsumeColon (void)
843 /* Check for a colon and skip it. */
844 {
845     Consume (TOK_COLON, "`:' expected");
846 }
847
848
849
850 void ConsumeSemi (void)
851 /* Check for a semicolon and skip it. */
852 {
853     /* Try do be smart about typos... */
854     if (CurTok.Tok == TOK_SEMI) {
855         NextToken ();
856     } else {
857         Error ("`;' expected");
858         if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) {
859             NextToken ();
860         }
861     }
862 }
863
864
865
866 void ConsumeComma (void)
867 /* Check for a comma and skip it. */
868 {
869     /* Try do be smart about typos... */
870     if (CurTok.Tok == TOK_COMMA) {
871         NextToken ();
872     } else {
873         Error ("`,' expected");
874         if (CurTok.Tok == TOK_SEMI) {
875             NextToken ();
876         }
877     }
878 }
879
880
881
882 void ConsumeLParen (void)
883 /* Check for a left parenthesis and skip it */
884 {
885     Consume (TOK_LPAREN, "`(' expected");
886 }
887
888
889
890 void ConsumeRParen (void)
891 /* Check for a right parenthesis and skip it */
892 {
893     Consume (TOK_RPAREN, "`)' expected");
894 }
895
896
897
898 void ConsumeLBrack (void)
899 /* Check for a left bracket and skip it */
900 {
901     Consume (TOK_LBRACK, "`[' expected");
902 }
903
904
905
906 void ConsumeRBrack (void)
907 /* Check for a right bracket and skip it */
908 {
909     Consume (TOK_RBRACK, "`]' expected");
910 }
911
912
913
914 void ConsumeLCurly (void)
915 /* Check for a left curly brace and skip it */
916 {
917     Consume (TOK_LCURLY, "`{' expected");
918 }
919
920
921
922 void ConsumeRCurly (void)
923 /* Check for a right curly brace and skip it */
924 {
925     Consume (TOK_RCURLY, "`}' expected");
926 }
927
928
929