1 /*****************************************************************************/
5 /* Pragma handling for the cc65 C compiler */
9 /* (C) 1998-2011, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
51 #include "scanstrbuf.h"
57 /*****************************************************************************/
59 /*****************************************************************************/
63 /* Tokens for the #pragmas */
68 PRAGMA_BSSSEG, /* obsolete */
71 PRAGMA_CHECKSTACK, /* obsolete */
73 PRAGMA_CODESEG, /* obsolete */
76 PRAGMA_DATASEG, /* obsolete */
81 PRAGMA_REGVARS, /* obsolete */
83 PRAGMA_RODATASEG, /* obsolete */
85 PRAGMA_SIGNEDCHARS, /* obsolete */
87 PRAGMA_STATICLOCALS, /* obsolete */
89 PRAGMA_WRITABLE_STRINGS,
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 },
126 /* Result of ParsePushPop */
136 /*****************************************************************************/
137 /* Helper functions */
138 /*****************************************************************************/
142 static void PragmaErrorSkip (void)
143 /* Called in case of an error, skips tokens until the closing paren or a
144 * semicolon is reached.
147 static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
148 SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
153 static int CmpKey (const void* Key, const void* Elem)
154 /* Compare function for bsearch */
156 return strcmp ((const char*) Key, ((const struct Pragma*) Elem)->Key);
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.
167 P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
168 return P? P->Tok : PRAGMA_ILLEGAL;
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.
179 if (SB_Get (B) != ',') {
180 Error ("Comma expected");
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.
194 if (!SB_GetString (B, S)) {
195 Error ("String literal expected");
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.
208 if (!SB_GetNumber (B, Val)) {
209 Error ("Constant integer expected");
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.
224 StrBuf W = AUTO_STRBUF_INITIALIZER;
226 /* The warning name is a symbol but the '-' char is allowed within */
227 if (SB_GetSym (B, &W, "-")) {
229 /* Map the warning name to an IntStack that contains its state */
230 S = FindWarning (SB_GetConstBuf (&W));
234 Error ("Pragma expects a warning name as first argument");
238 /* Deallocate the string */
247 static int HasStr (StrBuf* B, const char* E)
248 /* Checks if E follows in B. If so, skips it and returns true */
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) {
254 SB_SkipMultiple (B, Len);
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.
268 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
269 PushPopResult Res = PP_NONE;
271 /* Remember the current string index, so we can go back in case of errors */
272 unsigned Index = SB_GetIndex (B);
274 /* Try to read an identifier */
275 if (SB_GetSym (B, &Ident, 0)) {
277 /* Check if we have a first argument named "pop" */
278 if (SB_CompareStr (&Ident, "pop") == 0) {
282 /* Check if we have a first argument named "push" */
283 } else if (SB_CompareStr (&Ident, "push") == 0) {
287 /* Skip the following comma */
289 /* Error already flagged by GetComma */
295 /* Unknown keyword, roll back */
296 SB_SetIndex (B, Index);
300 /* Free the string buffer and return the result */
307 static void PopInt (IntStack* S)
308 /* Pops an integer from an IntStack. Prints an error if the stack is empty */
310 if (IS_GetCount (S) < 2) {
311 Error ("Cannot pop, stack is empty");
319 static void PushInt (IntStack* S, long Val)
320 /* Pushes an integer onto an IntStack. Prints an error if the stack is full */
323 Error ("Cannot push: stack overflow");
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.
336 if (SB_CompareStr (Ident, "true") == 0) {
339 if (SB_CompareStr (Ident, "on") == 0) {
342 if (SB_CompareStr (Ident, "false") == 0) {
345 if (SB_CompareStr (Ident, "off") == 0) {
350 Error ("Pragma argument must be one of `on', `off', `true' or `false'");
356 /*****************************************************************************/
357 /* Pragma handling functions */
358 /*****************************************************************************/
362 static void StringPragma (StrBuf* B, void (*Func) (const char*))
363 /* Handle a pragma that expects a string parameter */
365 StrBuf S = AUTO_STRBUF_INITIALIZER;
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));
373 /* Call the string buf destructor */
379 static void SegNamePragma (StrBuf* B, segment_t Seg)
380 /* Handle a pragma that expects a segment name parameter */
382 StrBuf S = AUTO_STRBUF_INITIALIZER;
385 /* Check for the "push" or "pop" keywords */
387 switch (ParsePushPop (B)) {
397 /* Pop the old value and output it */
409 Internal ("Invalid result from ParsePushPop");
413 /* A string argument must follow */
414 if (!GetString (B, &S)) {
419 Name = SB_GetConstBuf (&S);
421 /* Check if the name is valid */
422 if (ValidSegName (Name)) {
424 /* Set the new name */
426 PushSegName (Seg, Name);
428 SetSegName (Seg, Name);
434 /* Segment name is invalid */
435 Error ("Illegal segment name: `%s'", Name);
440 /* Call the string buf destructor */
446 static void CharMapPragma (StrBuf* B)
447 /* Change the character map */
451 /* Read the character index */
452 if (!GetNumber (B, &Index)) {
455 if (Index < 1 || Index > 255) {
458 Error ("Remapping 0 is not allowed");
460 Error ("Character index out of range");
470 /* Read the character code */
471 if (!GetNumber (B, &C)) {
474 if (C < 1 || C > 255) {
477 Error ("Remapping 0 is not allowed");
479 Error ("Character code out of range");
484 /* Remap the character */
485 TgtTranslateSet ((unsigned) Index, (unsigned char) C);
490 static void WarnPragma (StrBuf* B)
491 /* Enable/disable warnings */
496 /* A warning name must follow */
497 IntStack* S = GetWarning (B);
507 /* Check for the "push" or "pop" keywords */
508 switch (ParsePushPop (B)) {
519 /* Pop the old value and bail out */
528 Internal ("Invalid result from ParsePushPop");
531 /* Boolean argument follows */
532 if (HasStr (B, "true") || HasStr (B, "on")) {
534 } else if (HasStr (B, "false") || HasStr (B, "off")) {
536 } else if (!SB_GetNumber (B, &Val)) {
537 Error ("Invalid pragma argument");
541 /* Set/push the new value */
551 static void FlagPragma (StrBuf* B, IntStack* Stack)
552 /* Handle a pragma that expects a boolean paramater */
554 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
559 /* Try to read an identifier */
560 int IsIdent = SB_GetSym (B, &Ident, 0);
562 /* Check if we have a first argument named "pop" */
563 if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
565 /* No other arguments allowed */
569 /* Check if we have a first argument named "push" */
570 if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
575 IsIdent = SB_GetSym (B, &Ident, 0);
580 /* Boolean argument follows */
582 Val = BoolKeyword (&Ident);
583 } else if (!GetNumber (B, &Val)) {
587 /* Set/push the new value */
589 PushInt (Stack, Val);
595 /* Free the identifier */
601 static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
602 /* Handle a pragma that expects an int paramater */
607 /* Check for the "push" or "pop" keywords */
608 switch (ParsePushPop (B)) {
619 /* Pop the old value and bail out */
628 Internal ("Invalid result from ParsePushPop");
632 /* Integer argument follows */
633 if (!GetNumber (B, &Val)) {
637 /* Check the argument */
638 if (Val < Low || Val > High) {
639 Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
643 /* Set/push the new value */
645 PushInt (Stack, Val);
653 static void ParsePragma (void)
654 /* Parse the contents of the _Pragma statement */
657 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
659 /* Create a string buffer from the string literal */
660 StrBuf B = AUTO_STRBUF_INITIALIZER;
661 SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
663 /* Skip the string token */
666 /* Get the pragma name from the string */
668 if (!SB_GetSym (&B, &Ident, "-")) {
669 Error ("Invalid pragma");
673 /* Search for the name */
674 Pragma = FindPragma (&Ident);
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).
681 if (IS_Get (&WarnUnknownPragma)) {
682 Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
687 /* Check for an open paren */
689 if (SB_Get (&B) != '(') {
690 Error ("'(' expected");
694 /* Skip white space before the argument */
697 /* Switch for the different pragmas */
701 IntPragma (&B, &DataAlignment, 1, 4096);
705 Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
707 case PRAGMA_BSS_NAME:
708 SegNamePragma (&B, SEG_BSS);
715 case PRAGMA_CHECKSTACK:
716 Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
718 case PRAGMA_CHECK_STACK:
719 FlagPragma (&B, &CheckStack);
723 Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
725 case PRAGMA_CODE_NAME:
726 SegNamePragma (&B, SEG_CODE);
729 case PRAGMA_CODESIZE:
730 IntPragma (&B, &CodeSizeFactor, 10, 1000);
734 Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
736 case PRAGMA_DATA_NAME:
737 SegNamePragma (&B, SEG_DATA);
740 case PRAGMA_LOCAL_STRINGS:
741 FlagPragma (&B, &LocalStrings);
744 case PRAGMA_OPTIMIZE:
745 FlagPragma (&B, &Optimize);
748 case PRAGMA_REGVARADDR:
749 FlagPragma (&B, &AllowRegVarAddr);
753 Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
755 case PRAGMA_REGISTER_VARS:
756 FlagPragma (&B, &EnableRegVars);
759 case PRAGMA_RODATASEG:
760 Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
762 case PRAGMA_RODATA_NAME:
763 SegNamePragma (&B, SEG_RODATA);
766 case PRAGMA_SIGNEDCHARS:
767 Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
769 case PRAGMA_SIGNED_CHARS:
770 FlagPragma (&B, &SignedChars);
773 case PRAGMA_STATICLOCALS:
774 Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
776 case PRAGMA_STATIC_LOCALS:
777 FlagPragma (&B, &StaticLocals);
784 case PRAGMA_WRITABLE_STRINGS:
785 FlagPragma (&B, &WritableStrings);
789 StringPragma (&B, MakeZPSym);
793 Internal ("Invalid pragma");
796 /* Closing paren expected */
798 if (SB_Get (&B) != ')') {
799 Error ("')' expected");
804 /* Allow an optional semicolon to be compatible with the old syntax */
805 if (SB_Peek (&B) == ';') {
810 /* Make sure nothing follows */
811 if (SB_Peek (&B) != '\0') {
812 Error ("Unexpected input following pragma directive");
816 /* Release the string buffers */
824 /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
826 /* Skip the token itself */
829 /* We expect an opening paren */
830 if (!ConsumeLParen ()) {
835 if (CurTok.Tok != TOK_SCONST) {
837 /* Print a diagnostic */
838 Error ("String literal expected");
840 /* Try some smart error recovery: Skip tokens until we reach the
841 * enclosing paren, or a semicolon.
847 /* Parse the _Pragma statement */
851 /* Closing paren needed */