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