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