]> git.sur5r.net Git - cc65/blob - src/cc65/pragma.c
Make the warning "statement has no effect" switchable.
[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-2009, 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
270     /* Try to read an identifier */
271     if (SB_GetSym (B, &Ident, 0)) {
272
273         /* Check if we have a first argument named "pop" */
274         if (SB_CompareStr (&Ident, "pop") == 0) {
275
276             Res = PP_POP;
277
278         /* Check if we have a first argument named "push" */
279         } else if (SB_CompareStr (&Ident, "push") == 0) {
280
281             Res = PP_PUSH;
282
283             /* Skip the following comma */
284             if (!GetComma (B)) {
285                 Res = PP_ERROR;
286             }
287
288         } else {
289
290             /* Unknown keyword */
291             Error ("Invalid pragma arguments");
292             Res = PP_ERROR;
293         }
294     }
295
296     /* Free the string buffer and return the result */
297     SB_Done (&Ident);
298     return Res;
299 }
300
301
302
303 static void PopInt (IntStack* S)
304 /* Pops an integer from an IntStack. Prints an error if the stack is empty */
305 {
306     if (IS_GetCount (S) < 2) {
307         Error ("Cannot pop, stack is empty");
308     } else {
309         IS_Drop (S);
310     }
311 }
312
313
314
315 static void PushInt (IntStack* S, long Val)
316 /* Pushes an integer onto an IntStack. Prints an error if the stack is full */
317 {
318     if (IS_IsFull (S)) {
319         Error ("Cannot push: stack overflow");
320     } else {
321         IS_Push (S, Val);
322     }
323 }
324
325
326
327 static int BoolKeyword (StrBuf* Ident)
328 /* Check if the identifier in Ident is a keyword for a boolean value. Currently
329  * accepted are true/false/on/off.
330  */
331 {
332     if (SB_CompareStr (Ident, "true") == 0) {
333         return 1;
334     }
335     if (SB_CompareStr (Ident, "on") == 0) {
336         return 1;
337     }
338     if (SB_CompareStr (Ident, "false") == 0) {
339         return 0;
340     }
341     if (SB_CompareStr (Ident, "off") == 0) {
342         return 0;
343     }
344
345     /* Error */
346     Error ("Pragma argument must be one of `on', `off', `true' or `false'");
347     return 0;
348 }
349
350
351
352 /*****************************************************************************/
353 /*                         Pragma handling functions                         */
354 /*****************************************************************************/
355
356
357
358 static void StringPragma (StrBuf* B, void (*Func) (const char*))
359 /* Handle a pragma that expects a string parameter */
360 {
361     StrBuf S = AUTO_STRBUF_INITIALIZER;
362
363     /* We expect a string here */
364     if (GetString (B, &S)) {
365         /* Call the given function with the string argument */
366         Func (SB_GetConstBuf (&S));
367     }
368
369     /* Call the string buf destructor */
370     SB_Done (&S);
371 }
372
373
374
375 static void SegNamePragma (StrBuf* B, segment_t Seg)
376 /* Handle a pragma that expects a segment name parameter */
377 {
378     StrBuf      S = AUTO_STRBUF_INITIALIZER;
379     const char* Name;
380
381     /* Check for the "push" or "pop" keywords */
382     int Push = 0;
383     switch (ParsePushPop (B)) {
384
385         case PP_NONE:
386             break;
387
388         case PP_PUSH:
389             Push = 1;
390             break;
391
392         case PP_POP:
393             /* Pop the old value and output it */
394             PopSegName (Seg);
395             g_segname (Seg);
396
397             /* Done */
398             goto ExitPoint;
399
400         case PP_ERROR:
401             /* Bail out */
402             goto ExitPoint;
403
404         default:
405             Internal ("Invalid result from ParsePushPop");
406
407     }
408
409     /* A string argument must follow */
410     if (!GetString (B, &S)) {
411         goto ExitPoint;
412     }
413
414     /* Get the string */
415     Name = SB_GetConstBuf (&S);
416
417     /* Check if the name is valid */
418     if (ValidSegName (Name)) {
419
420         /* Set the new name */
421         if (Push) {
422             PushSegName (Seg, Name);
423         } else {
424             SetSegName (Seg, Name);
425         }
426         g_segname (Seg);
427
428     } else {
429
430         /* Segment name is invalid */
431         Error ("Illegal segment name: `%s'", Name);
432
433     }
434
435 ExitPoint:
436     /* Call the string buf destructor */
437     SB_Done (&S);
438 }
439
440
441
442 static void CharMapPragma (StrBuf* B)
443 /* Change the character map */
444 {
445     long Index, C;
446
447     /* Read the character index */
448     if (!GetNumber (B, &Index)) {
449         return;
450     }
451     if (Index < 1 || Index > 255) {
452         if (Index == 0) {
453             /* For groepaz */
454             Error ("Remapping 0 is not allowed");
455         } else {
456             Error ("Character index out of range");
457         }
458         return;
459     }
460
461     /* Comma follows */
462     if (!GetComma (B)) {
463         return;
464     }
465
466     /* Read the character code */
467     if (!GetNumber (B, &C)) {
468         return;
469     }
470     if (C < 1 || C > 255) {
471         if (C == 0) {
472             /* For groepaz */
473             Error ("Remapping 0 is not allowed");
474         } else {
475             Error ("Character code out of range");
476         }
477         return;
478     }
479
480     /* Remap the character */
481     TgtTranslateSet ((unsigned) Index, (unsigned char) C);
482 }
483
484
485
486 static void WarnPragma (StrBuf* B)
487 /* Enable/disable warnings */
488 {
489     long   Val;
490     int    Push;
491
492     /* A warning name must follow */
493     IntStack* S = GetWarning (B);
494     if (S == 0) {
495         return;
496     }
497
498     /* Comma follows */
499     if (!GetComma (B)) {
500         return;
501     }
502
503     /* Check for the "push" or "pop" keywords */
504     switch (ParsePushPop (B)) {
505
506         case PP_NONE:
507             Push = 0;
508             break;
509
510         case PP_PUSH:
511             Push = 1;
512             break;
513
514         case PP_POP:
515             /* Pop the old value and bail out */
516             PopInt (S);
517             return;
518
519         case PP_ERROR:
520             /* Bail out */
521             return;
522
523         default:
524             Internal ("Invalid result from ParsePushPop");
525     }
526
527     /* Boolean argument follows */
528     if (HasStr (B, "true") || HasStr (B, "on")) {
529         Val = 1;
530     } else if (HasStr (B, "false") || HasStr (B, "off")) {
531         Val = 0;
532     } else if (!SB_GetNumber (B, &Val)) {
533         Error ("Invalid pragma argument");
534         return;
535     }
536
537     /* Set/push the new value */
538     if (Push) {
539         PushInt (S, Val);
540     } else {
541         IS_Set (S, Val);
542     }
543 }
544
545
546
547 static void FlagPragma (StrBuf* B, IntStack* Stack)
548 /* Handle a pragma that expects a boolean paramater */
549 {
550     StrBuf Ident = AUTO_STRBUF_INITIALIZER;
551     long   Val;
552     int    Push;
553
554
555     /* Try to read an identifier */
556     int IsIdent = SB_GetSym (B, &Ident, 0);
557
558     /* Check if we have a first argument named "pop" */
559     if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
560         PopInt (Stack);
561         /* No other arguments allowed */
562         return;
563     }
564
565     /* Check if we have a first argument named "push" */
566     if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
567         Push = 1;
568         if (!GetComma (B)) {
569             goto ExitPoint;
570         }
571         IsIdent = SB_GetSym (B, &Ident, 0);
572     } else {
573         Push = 0;
574     }
575
576     /* Boolean argument follows */
577     if (IsIdent) {
578         Val = BoolKeyword (&Ident);
579     } else if (!GetNumber (B, &Val)) {
580         goto ExitPoint;
581     }
582
583     /* Set/push the new value */
584     if (Push) {
585         PushInt (Stack, Val);
586     } else {
587         IS_Set (Stack, Val);
588     }
589
590 ExitPoint:
591     /* Free the identifier */
592     SB_Done (&Ident);
593 }
594
595
596
597 static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
598 /* Handle a pragma that expects an int paramater */
599 {
600     long  Val;
601     int   Push;
602
603     /* Check for the "push" or "pop" keywords */
604     switch (ParsePushPop (B)) {
605
606         case PP_NONE:
607             Push = 0;
608             break;
609
610         case PP_PUSH:
611             Push = 1;
612             break;
613
614         case PP_POP:
615             /* Pop the old value and bail out */
616             PopInt (Stack);
617             return;
618
619         case PP_ERROR:
620             /* Bail out */
621             return;
622
623         default:
624             Internal ("Invalid result from ParsePushPop");
625
626     }
627
628     /* Integer argument follows */
629     if (!GetNumber (B, &Val)) {
630         return;
631     }
632
633     /* Check the argument */
634     if (Val < Low || Val > High) {
635         Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
636         return;
637     }
638
639     /* Set/push the new value */
640     if (Push) {
641         PushInt (Stack, Val);
642     } else {
643         IS_Set (Stack, Val);
644     }
645 }
646
647
648
649 static void ParsePragma (void)
650 /* Parse the contents of the _Pragma statement */
651 {
652     pragma_t Pragma;
653     StrBuf   Ident = AUTO_STRBUF_INITIALIZER;
654
655     /* Create a string buffer from the string literal */
656     StrBuf B = AUTO_STRBUF_INITIALIZER;
657     SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
658
659     /* Skip the string token */
660     NextToken ();
661
662     /* Get the pragma name from the string */
663     SB_SkipWhite (&B);
664     if (!SB_GetSym (&B, &Ident, "-")) {
665         Error ("Invalid pragma");
666         goto ExitPoint;
667     }
668
669     /* Search for the name */
670     Pragma = FindPragma (&Ident);
671
672     /* Do we know this pragma? */
673     if (Pragma == PRAGMA_ILLEGAL) {
674         /* According to the ANSI standard, we're not allowed to generate errors
675          * for unknown pragmas, but warn about them if enabled (the default).
676          */
677         if (IS_Get (&WarnUnknownPragma)) {
678             Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
679         }
680         goto ExitPoint;
681     }
682
683     /* Check for an open paren */
684     SB_SkipWhite (&B);
685     if (SB_Get (&B) != '(') {
686         Error ("'(' expected");
687         goto ExitPoint;
688     }
689
690     /* Skip white space before the argument */
691     SB_SkipWhite (&B);
692
693     /* Switch for the different pragmas */
694     switch (Pragma) {
695
696         case PRAGMA_BSSSEG:
697             Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
698             /* FALLTHROUGH */
699         case PRAGMA_BSS_NAME:
700             SegNamePragma (&B, SEG_BSS);
701             break;
702
703         case PRAGMA_CHARMAP:
704             CharMapPragma (&B);
705             break;
706
707         case PRAGMA_CHECKSTACK:
708             Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
709             /* FALLTHROUGH */
710         case PRAGMA_CHECK_STACK:
711             FlagPragma (&B, &CheckStack);
712             break;
713
714         case PRAGMA_CODESEG:
715             Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
716             /* FALLTHROUGH */
717         case PRAGMA_CODE_NAME:
718             SegNamePragma (&B, SEG_CODE);
719             break;
720
721         case PRAGMA_CODESIZE:
722             IntPragma (&B, &CodeSizeFactor, 10, 1000);
723             break;
724
725         case PRAGMA_DATASEG:
726             Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
727             /* FALLTHROUGH */
728         case PRAGMA_DATA_NAME:
729             SegNamePragma (&B, SEG_DATA);
730             break;
731
732         case PRAGMA_LOCAL_STRINGS:
733             FlagPragma (&B, &LocalStrings);
734             break;
735
736         case PRAGMA_OPTIMIZE:
737             FlagPragma (&B, &Optimize);
738             break;
739
740         case PRAGMA_REGVARADDR:
741             FlagPragma (&B, &AllowRegVarAddr);
742             break;
743
744         case PRAGMA_REGVARS:
745             Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
746             /* FALLTHROUGH */
747         case PRAGMA_REGISTER_VARS:
748             FlagPragma (&B, &EnableRegVars);
749             break;
750
751         case PRAGMA_RODATASEG:
752             Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
753             /* FALLTHROUGH */
754         case PRAGMA_RODATA_NAME:
755             SegNamePragma (&B, SEG_RODATA);
756             break;
757
758         case PRAGMA_SIGNEDCHARS:
759             Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
760             /* FALLTHROUGH */
761         case PRAGMA_SIGNED_CHARS:
762             FlagPragma (&B, &SignedChars);
763             break;
764
765         case PRAGMA_STATICLOCALS:
766             Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
767             /* FALLTHROUGH */
768         case PRAGMA_STATIC_LOCALS:
769             FlagPragma (&B, &StaticLocals);
770             break;
771
772         case PRAGMA_WARN:
773             WarnPragma (&B);
774             break;
775
776         case PRAGMA_WRITABLE_STRINGS:
777             FlagPragma (&B, &WritableStrings);
778             break;
779
780         case PRAGMA_ZPSYM:
781             StringPragma (&B, MakeZPSym);
782             break;
783
784         default:
785             Internal ("Invalid pragma");
786     }
787
788     /* Closing paren expected */
789     SB_SkipWhite (&B);
790     if (SB_Get (&B) != ')') {
791         Error ("')' expected");
792         goto ExitPoint;
793     }
794     SB_SkipWhite (&B);
795
796     /* Allow an optional semicolon to be compatible with the old syntax */
797     if (SB_Peek (&B) == ';') {
798         SB_Skip (&B);
799         SB_SkipWhite (&B);
800     }
801
802     /* Make sure nothing follows */
803     if (SB_Peek (&B) != '\0') {
804         Error ("Unexpected input following pragma directive");
805     }
806
807 ExitPoint:
808     /* Release the string buffers */
809     SB_Done (&B);
810     SB_Done (&Ident);
811 }
812
813
814
815 void DoPragma (void)
816 /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
817 {
818     /* Skip the token itself */
819     NextToken ();
820
821     /* We expect an opening paren */
822     if (!ConsumeLParen ()) {
823         return;
824     }
825
826     /* String literal */
827     if (CurTok.Tok != TOK_SCONST) {
828
829         /* Print a diagnostic */
830         Error ("String literal expected");
831
832         /* Try some smart error recovery: Skip tokens until we reach the
833          * enclosing paren, or a semicolon.
834          */
835         PragmaErrorSkip ();
836
837     } else {
838
839         /* Parse the _Pragma statement */
840         ParsePragma ();
841     }
842
843     /* Closing paren needed */
844     ConsumeRParen ();
845 }
846
847
848