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