]> git.sur5r.net Git - cc65/blob - src/cc65/pragma.c
Fix parsing of push/pop so that #pragma warn works without.
[cc65] / src / cc65 / pragma.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 pragma.c                                  */
4 /*                                                                           */
5 /*                  Pragma handling for the cc65 C compiler                  */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2010, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 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 <stdlib.h>
37 #include <string.h>
38
39 /* common */
40 #include "chartype.h"
41 #include "segnames.h"
42 #include "tgttrans.h"
43
44 /* cc65 */
45 #include "codegen.h"
46 #include "error.h"
47 #include "expr.h"
48 #include "global.h"
49 #include "litpool.h"
50 #include "scanner.h"
51 #include "scanstrbuf.h"
52 #include "symtab.h"
53 #include "pragma.h"
54
55
56
57 /*****************************************************************************/
58 /*                                   data                                    */
59 /*****************************************************************************/
60
61
62
63 /* Tokens for the #pragmas */
64 typedef enum {
65     PRAGMA_ILLEGAL = -1,
66     PRAGMA_BSS_NAME,
67     PRAGMA_BSSSEG,                                      /* obsolete */
68     PRAGMA_CHARMAP,
69     PRAGMA_CHECK_STACK,
70     PRAGMA_CHECKSTACK,                                  /* obsolete */
71     PRAGMA_CODE_NAME,
72     PRAGMA_CODESEG,                                     /* obsolete */
73     PRAGMA_CODESIZE,
74     PRAGMA_DATA_NAME,
75     PRAGMA_DATASEG,                                     /* obsolete */
76     PRAGMA_LOCAL_STRINGS,
77     PRAGMA_OPTIMIZE,
78     PRAGMA_REGVARADDR,
79     PRAGMA_REGISTER_VARS,
80     PRAGMA_REGVARS,                                     /* obsolete */
81     PRAGMA_RODATA_NAME,
82     PRAGMA_RODATASEG,                                   /* obsolete */
83     PRAGMA_SIGNED_CHARS,
84     PRAGMA_SIGNEDCHARS,                                 /* obsolete */
85     PRAGMA_STATIC_LOCALS,
86     PRAGMA_STATICLOCALS,                                /* obsolete */
87     PRAGMA_WARN,
88     PRAGMA_WRITABLE_STRINGS,
89     PRAGMA_ZPSYM,
90     PRAGMA_COUNT
91 } pragma_t;
92
93 /* Pragma table */
94 static const struct Pragma {
95     const char* Key;            /* Keyword */
96     pragma_t    Tok;            /* Token */
97 } Pragmas[PRAGMA_COUNT] = {
98     { "bss-name",               PRAGMA_BSS_NAME         },
99     { "bssseg",                 PRAGMA_BSSSEG           },      /* obsolete */
100     { "charmap",                PRAGMA_CHARMAP          },
101     { "check-stack",            PRAGMA_CHECK_STACK      },
102     { "checkstack",             PRAGMA_CHECKSTACK       },      /* obsolete */
103     { "code-name",              PRAGMA_CODE_NAME        },
104     { "codeseg",                PRAGMA_CODESEG          },      /* obsolete */
105     { "codesize",               PRAGMA_CODESIZE         },
106     { "data-name",              PRAGMA_DATA_NAME        },
107     { "dataseg",                PRAGMA_DATASEG          },      /* obsolete */
108     { "local-strings",          PRAGMA_LOCAL_STRINGS    },
109     { "optimize",               PRAGMA_OPTIMIZE         },
110     { "register-vars",          PRAGMA_REGISTER_VARS    },
111     { "regvaraddr",             PRAGMA_REGVARADDR       },
112     { "regvars",                PRAGMA_REGVARS          },      /* obsolete */
113     { "rodata-name",            PRAGMA_RODATA_NAME      },
114     { "rodataseg",              PRAGMA_RODATASEG        },      /* obsolete */
115     { "signed-chars",           PRAGMA_SIGNED_CHARS     },
116     { "signedchars",            PRAGMA_SIGNEDCHARS      },      /* obsolete */
117     { "static-locals",          PRAGMA_STATIC_LOCALS    },
118     { "staticlocals",           PRAGMA_STATICLOCALS     },      /* obsolete */
119     { "warn",                   PRAGMA_WARN             },
120     { "writable-strings",       PRAGMA_WRITABLE_STRINGS },
121     { "zpsym",                  PRAGMA_ZPSYM            },
122 };
123
124 /* Result of ParsePushPop */
125 typedef enum {
126     PP_NONE,
127     PP_POP,
128     PP_PUSH,
129     PP_ERROR,
130 } PushPopResult;
131
132
133
134 /*****************************************************************************/
135 /*                             Helper functions                              */
136 /*****************************************************************************/
137
138
139
140 static void PragmaErrorSkip (void)
141 /* Called in case of an error, skips tokens until the closing paren or a
142  * semicolon is reached.
143  */
144 {
145     static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
146     SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
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 Pragma*) Elem)->Key);
155 }
156
157
158
159 static pragma_t FindPragma (const StrBuf* Key)
160 /* Find a pragma and return the token. Return PRAGMA_ILLEGAL if the keyword is
161  * not a valid pragma.
162  */
163 {
164     struct Pragma* P;
165     P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
166     return P? P->Tok : PRAGMA_ILLEGAL;
167 }
168
169
170
171 static int GetComma (StrBuf* B)
172 /* Expects and skips a comma in B. Prints an error and returns zero if no
173  * comma is found. Return a value <> 0 otherwise.
174  */
175 {
176     SB_SkipWhite (B);
177     if (SB_Get (B) != ',') {
178         Error ("Comma expected");
179         return 0;
180     }
181     SB_SkipWhite (B);
182     return 1;
183 }
184
185
186
187 static int GetString (StrBuf* B, StrBuf* S)
188 /* Expects and skips a string in B. Prints an error and returns zero if no
189  * string is found. Returns a value <> 0 otherwise.
190  */
191 {
192     if (!SB_GetString (B, S)) {
193         Error ("String literal expected");
194         return 0;
195     }
196     return 1;
197 }
198
199
200
201 static int GetNumber (StrBuf* B, long* Val)
202 /* Expects and skips a number in B. Prints an eror and returns zero if no
203  * number is found. Returns a value <> 0 otherwise.
204  */
205 {
206     if (!SB_GetNumber (B, Val)) {
207         Error ("Constant integer expected");
208         return 0;
209     }
210     return 1;
211 }
212
213
214
215 static IntStack* GetWarning (StrBuf* B)
216 /* Get a warning name from the string buffer. Returns a pointer to the intstack
217  * that holds the state of the warning, and NULL in case of errors. The
218  * function will output error messages in case of problems.
219  */
220 {
221     IntStack* S = 0;
222     StrBuf W = AUTO_STRBUF_INITIALIZER;
223
224     /* The warning name is a symbol but the '-' char is allowed within */
225     if (SB_GetSym (B, &W, "-")) {
226
227         /* Map the warning name to an IntStack that contains its state */
228         S = FindWarning (SB_GetConstBuf (&W));
229
230         /* Handle errors */
231         if (S == 0) {
232             Error ("Pragma expects a warning name as first argument");
233         }
234     }
235
236     /* Deallocate the string */
237     SB_Done (&W);
238
239     /* Done */
240     return S;
241 }
242
243
244
245 static int HasStr (StrBuf* B, const char* E)
246 /* Checks if E follows in B. If so, skips it and returns true */
247 {
248     unsigned Len = strlen (E);
249     if (SB_GetLen (B) - SB_GetIndex (B) >= Len) {
250         if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) {
251             /* Found */
252             SB_SkipMultiple (B, Len);
253             return 1;
254         }
255     }
256     return 0;
257 }
258
259
260
261 static PushPopResult ParsePushPop (StrBuf* B)
262 /* Check for and parse the "push" and "pop" keywords. In case of "push", a
263  * following comma is expected and skipped.
264  */
265 {
266     StrBuf Ident      = AUTO_STRBUF_INITIALIZER;
267     PushPopResult Res = PP_NONE;
268
269     /* Remember the current string index, so we can go back in case of errors */
270     unsigned Index = SB_GetIndex (B);
271
272     /* Try to read an identifier */
273     if (SB_GetSym (B, &Ident, 0)) {
274
275         /* Check if we have a first argument named "pop" */
276         if (SB_CompareStr (&Ident, "pop") == 0) {
277
278             Res = PP_POP;
279
280         /* Check if we have a first argument named "push" */
281         } else if (SB_CompareStr (&Ident, "push") == 0) {
282
283             Res = PP_PUSH;
284
285             /* Skip the following comma */
286             if (!GetComma (B)) {
287                 /* Error already flagged by GetComma */
288                 Res = PP_ERROR;
289             }
290
291         } else {
292
293             /* Unknown keyword, roll back */
294             SB_SetIndex (B, Index);
295         }
296     }
297
298     /* Free the string buffer and return the result */
299     SB_Done (&Ident);
300     return Res;
301 }
302
303
304
305 static void PopInt (IntStack* S)
306 /* Pops an integer from an IntStack. Prints an error if the stack is empty */
307 {
308     if (IS_GetCount (S) < 2) {
309         Error ("Cannot pop, stack is empty");
310     } else {
311         IS_Drop (S);
312     }
313 }
314
315
316
317 static void PushInt (IntStack* S, long Val)
318 /* Pushes an integer onto an IntStack. Prints an error if the stack is full */
319 {
320     if (IS_IsFull (S)) {
321         Error ("Cannot push: stack overflow");
322     } else {
323         IS_Push (S, Val);
324     }
325 }
326
327
328
329 static int BoolKeyword (StrBuf* Ident)
330 /* Check if the identifier in Ident is a keyword for a boolean value. Currently
331  * accepted are true/false/on/off.
332  */
333 {
334     if (SB_CompareStr (Ident, "true") == 0) {
335         return 1;
336     }
337     if (SB_CompareStr (Ident, "on") == 0) {
338         return 1;
339     }
340     if (SB_CompareStr (Ident, "false") == 0) {
341         return 0;
342     }
343     if (SB_CompareStr (Ident, "off") == 0) {
344         return 0;
345     }
346
347     /* Error */
348     Error ("Pragma argument must be one of `on', `off', `true' or `false'");
349     return 0;
350 }
351
352
353
354 /*****************************************************************************/
355 /*                         Pragma handling functions                         */
356 /*****************************************************************************/
357
358
359
360 static void StringPragma (StrBuf* B, void (*Func) (const char*))
361 /* Handle a pragma that expects a string parameter */
362 {
363     StrBuf S = AUTO_STRBUF_INITIALIZER;
364
365     /* We expect a string here */
366     if (GetString (B, &S)) {
367         /* Call the given function with the string argument */
368         Func (SB_GetConstBuf (&S));
369     }
370
371     /* Call the string buf destructor */
372     SB_Done (&S);
373 }
374
375
376
377 static void SegNamePragma (StrBuf* B, segment_t Seg)
378 /* Handle a pragma that expects a segment name parameter */
379 {
380     StrBuf      S = AUTO_STRBUF_INITIALIZER;
381     const char* Name;
382
383     /* Check for the "push" or "pop" keywords */
384     int Push = 0;
385     switch (ParsePushPop (B)) {
386
387         case PP_NONE:
388             break;
389
390         case PP_PUSH:
391             Push = 1;
392             break;
393
394         case PP_POP:
395             /* Pop the old value and output it */
396             PopSegName (Seg);
397             g_segname (Seg);
398
399             /* Done */
400             goto ExitPoint;
401
402         case PP_ERROR:
403             /* Bail out */
404             goto ExitPoint;
405
406         default:
407             Internal ("Invalid result from ParsePushPop");
408
409     }
410
411     /* A string argument must follow */
412     if (!GetString (B, &S)) {
413         goto ExitPoint;
414     }
415
416     /* Get the string */
417     Name = SB_GetConstBuf (&S);
418
419     /* Check if the name is valid */
420     if (ValidSegName (Name)) {
421
422         /* Set the new name */
423         if (Push) {
424             PushSegName (Seg, Name);
425         } else {
426             SetSegName (Seg, Name);
427         }
428         g_segname (Seg);
429
430     } else {
431
432         /* Segment name is invalid */
433         Error ("Illegal segment name: `%s'", Name);
434
435     }
436
437 ExitPoint:
438     /* Call the string buf destructor */
439     SB_Done (&S);
440 }
441
442
443
444 static void CharMapPragma (StrBuf* B)
445 /* Change the character map */
446 {
447     long Index, C;
448
449     /* Read the character index */
450     if (!GetNumber (B, &Index)) {
451         return;
452     }
453     if (Index < 1 || Index > 255) {
454         if (Index == 0) {
455             /* For groepaz */
456             Error ("Remapping 0 is not allowed");
457         } else {
458             Error ("Character index out of range");
459         }
460         return;
461     }
462
463     /* Comma follows */
464     if (!GetComma (B)) {
465         return;
466     }
467
468     /* Read the character code */
469     if (!GetNumber (B, &C)) {
470         return;
471     }
472     if (C < 1 || C > 255) {
473         if (C == 0) {
474             /* For groepaz */
475             Error ("Remapping 0 is not allowed");
476         } else {
477             Error ("Character code out of range");
478         }
479         return;
480     }
481
482     /* Remap the character */
483     TgtTranslateSet ((unsigned) Index, (unsigned char) C);
484 }
485
486
487
488 static void WarnPragma (StrBuf* B)
489 /* Enable/disable warnings */
490 {
491     long   Val;
492     int    Push;
493
494     /* A warning name must follow */
495     IntStack* S = GetWarning (B);
496     if (S == 0) {
497         return;
498     }
499
500     /* Comma follows */
501     if (!GetComma (B)) {
502         return;
503     }
504
505     /* Check for the "push" or "pop" keywords */
506     switch (ParsePushPop (B)) {
507
508         case PP_NONE:
509             Push = 0;
510             break;
511
512         case PP_PUSH:
513             Push = 1;
514             break;
515
516         case PP_POP:
517             /* Pop the old value and bail out */
518             PopInt (S);
519             return;
520
521         case PP_ERROR:
522             /* Bail out */
523             return;
524
525         default:
526             Internal ("Invalid result from ParsePushPop");
527     }
528
529     /* Boolean argument follows */
530     if (HasStr (B, "true") || HasStr (B, "on")) {
531         Val = 1;
532     } else if (HasStr (B, "false") || HasStr (B, "off")) {
533         Val = 0;
534     } else if (!SB_GetNumber (B, &Val)) {
535         Error ("Invalid pragma argument");
536         return;
537     }
538
539     /* Set/push the new value */
540     if (Push) {
541         PushInt (S, Val);
542     } else {
543         IS_Set (S, Val);
544     }
545 }
546
547
548
549 static void FlagPragma (StrBuf* B, IntStack* Stack)
550 /* Handle a pragma that expects a boolean paramater */
551 {
552     StrBuf Ident = AUTO_STRBUF_INITIALIZER;
553     long   Val;
554     int    Push;
555
556
557     /* Try to read an identifier */
558     int IsIdent = SB_GetSym (B, &Ident, 0);
559
560     /* Check if we have a first argument named "pop" */
561     if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
562         PopInt (Stack);
563         /* No other arguments allowed */
564         return;
565     }
566
567     /* Check if we have a first argument named "push" */
568     if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
569         Push = 1;
570         if (!GetComma (B)) {
571             goto ExitPoint;
572         }
573         IsIdent = SB_GetSym (B, &Ident, 0);
574     } else {
575         Push = 0;
576     }
577
578     /* Boolean argument follows */
579     if (IsIdent) {
580         Val = BoolKeyword (&Ident);
581     } else if (!GetNumber (B, &Val)) {
582         goto ExitPoint;
583     }
584
585     /* Set/push the new value */
586     if (Push) {
587         PushInt (Stack, Val);
588     } else {
589         IS_Set (Stack, Val);
590     }
591
592 ExitPoint:
593     /* Free the identifier */
594     SB_Done (&Ident);
595 }
596
597
598
599 static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
600 /* Handle a pragma that expects an int paramater */
601 {
602     long  Val;
603     int   Push;
604
605     /* Check for the "push" or "pop" keywords */
606     switch (ParsePushPop (B)) {
607
608         case PP_NONE:
609             Push = 0;
610             break;
611
612         case PP_PUSH:
613             Push = 1;
614             break;
615
616         case PP_POP:
617             /* Pop the old value and bail out */
618             PopInt (Stack);
619             return;
620
621         case PP_ERROR:
622             /* Bail out */
623             return;
624
625         default:
626             Internal ("Invalid result from ParsePushPop");
627
628     }
629
630     /* Integer argument follows */
631     if (!GetNumber (B, &Val)) {
632         return;
633     }
634
635     /* Check the argument */
636     if (Val < Low || Val > High) {
637         Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
638         return;
639     }
640
641     /* Set/push the new value */
642     if (Push) {
643         PushInt (Stack, Val);
644     } else {
645         IS_Set (Stack, Val);
646     }
647 }
648
649
650
651 static void ParsePragma (void)
652 /* Parse the contents of the _Pragma statement */
653 {
654     pragma_t Pragma;
655     StrBuf   Ident = AUTO_STRBUF_INITIALIZER;
656
657     /* Create a string buffer from the string literal */
658     StrBuf B = AUTO_STRBUF_INITIALIZER;
659     SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
660
661     /* Skip the string token */
662     NextToken ();
663
664     /* Get the pragma name from the string */
665     SB_SkipWhite (&B);
666     if (!SB_GetSym (&B, &Ident, "-")) {
667         Error ("Invalid pragma");
668         goto ExitPoint;
669     }
670
671     /* Search for the name */
672     Pragma = FindPragma (&Ident);
673
674     /* Do we know this pragma? */
675     if (Pragma == PRAGMA_ILLEGAL) {
676         /* According to the ANSI standard, we're not allowed to generate errors
677          * for unknown pragmas, but warn about them if enabled (the default).
678          */
679         if (IS_Get (&WarnUnknownPragma)) {
680             Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
681         }
682         goto ExitPoint;
683     }
684
685     /* Check for an open paren */
686     SB_SkipWhite (&B);
687     if (SB_Get (&B) != '(') {
688         Error ("'(' expected");
689         goto ExitPoint;
690     }
691
692     /* Skip white space before the argument */
693     SB_SkipWhite (&B);
694
695     /* Switch for the different pragmas */
696     switch (Pragma) {
697
698         case PRAGMA_BSSSEG:
699             Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
700             /* FALLTHROUGH */
701         case PRAGMA_BSS_NAME:
702             SegNamePragma (&B, SEG_BSS);
703             break;
704
705         case PRAGMA_CHARMAP:
706             CharMapPragma (&B);
707             break;
708
709         case PRAGMA_CHECKSTACK:
710             Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
711             /* FALLTHROUGH */
712         case PRAGMA_CHECK_STACK:
713             FlagPragma (&B, &CheckStack);
714             break;
715
716         case PRAGMA_CODESEG:
717             Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
718             /* FALLTHROUGH */
719         case PRAGMA_CODE_NAME:
720             SegNamePragma (&B, SEG_CODE);
721             break;
722
723         case PRAGMA_CODESIZE:
724             IntPragma (&B, &CodeSizeFactor, 10, 1000);
725             break;
726
727         case PRAGMA_DATASEG:
728             Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
729             /* FALLTHROUGH */
730         case PRAGMA_DATA_NAME:
731             SegNamePragma (&B, SEG_DATA);
732             break;
733
734         case PRAGMA_LOCAL_STRINGS:
735             FlagPragma (&B, &LocalStrings);
736             break;
737
738         case PRAGMA_OPTIMIZE:
739             FlagPragma (&B, &Optimize);
740             break;
741
742         case PRAGMA_REGVARADDR:
743             FlagPragma (&B, &AllowRegVarAddr);
744             break;
745
746         case PRAGMA_REGVARS:
747             Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
748             /* FALLTHROUGH */
749         case PRAGMA_REGISTER_VARS:
750             FlagPragma (&B, &EnableRegVars);
751             break;
752
753         case PRAGMA_RODATASEG:
754             Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
755             /* FALLTHROUGH */
756         case PRAGMA_RODATA_NAME:
757             SegNamePragma (&B, SEG_RODATA);
758             break;
759
760         case PRAGMA_SIGNEDCHARS:
761             Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
762             /* FALLTHROUGH */
763         case PRAGMA_SIGNED_CHARS:
764             FlagPragma (&B, &SignedChars);
765             break;
766
767         case PRAGMA_STATICLOCALS:
768             Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
769             /* FALLTHROUGH */
770         case PRAGMA_STATIC_LOCALS:
771             FlagPragma (&B, &StaticLocals);
772             break;
773
774         case PRAGMA_WARN:
775             WarnPragma (&B);
776             break;
777
778         case PRAGMA_WRITABLE_STRINGS:
779             FlagPragma (&B, &WritableStrings);
780             break;
781
782         case PRAGMA_ZPSYM:
783             StringPragma (&B, MakeZPSym);
784             break;
785
786         default:
787             Internal ("Invalid pragma");
788     }
789
790     /* Closing paren expected */
791     SB_SkipWhite (&B);
792     if (SB_Get (&B) != ')') {
793         Error ("')' expected");
794         goto ExitPoint;
795     }
796     SB_SkipWhite (&B);
797
798     /* Allow an optional semicolon to be compatible with the old syntax */
799     if (SB_Peek (&B) == ';') {
800         SB_Skip (&B);
801         SB_SkipWhite (&B);
802     }
803
804     /* Make sure nothing follows */
805     if (SB_Peek (&B) != '\0') {
806         Error ("Unexpected input following pragma directive");
807     }
808
809 ExitPoint:
810     /* Release the string buffers */
811     SB_Done (&B);
812     SB_Done (&Ident);
813 }
814
815
816
817 void DoPragma (void)
818 /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
819 {
820     /* Skip the token itself */
821     NextToken ();
822
823     /* We expect an opening paren */
824     if (!ConsumeLParen ()) {
825         return;
826     }
827
828     /* String literal */
829     if (CurTok.Tok != TOK_SCONST) {
830
831         /* Print a diagnostic */
832         Error ("String literal expected");
833
834         /* Try some smart error recovery: Skip tokens until we reach the
835          * enclosing paren, or a semicolon.
836          */
837         PragmaErrorSkip ();
838
839     } else {
840
841         /* Parse the _Pragma statement */
842         ParsePragma ();
843     }
844
845     /* Closing paren needed */
846     ConsumeRParen ();
847 }
848
849
850