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"
54 #include "wrappedcall.h"
58 /*****************************************************************************/
60 /*****************************************************************************/
64 /* Tokens for the #pragmas */
68 PRAGMA_ALLOW_EAGER_INLINE,
70 PRAGMA_BSSSEG, /* obsolete */
73 PRAGMA_CHECKSTACK, /* obsolete */
75 PRAGMA_CODESEG, /* obsolete */
78 PRAGMA_DATASEG, /* obsolete */
79 PRAGMA_INLINE_STDFUNCS,
84 PRAGMA_REGVARS, /* obsolete */
86 PRAGMA_RODATASEG, /* obsolete */
88 PRAGMA_SIGNEDCHARS, /* obsolete */
90 PRAGMA_STATICLOCALS, /* obsolete */
93 PRAGMA_WRITABLE_STRINGS,
99 static const struct Pragma {
100 const char* Key; /* Keyword */
101 pragma_t Tok; /* Token */
102 } Pragmas[PRAGMA_COUNT] = {
103 { "align", PRAGMA_ALIGN },
104 { "allow-eager-inline", PRAGMA_ALLOW_EAGER_INLINE },
105 { "bss-name", PRAGMA_BSS_NAME },
106 { "bssseg", PRAGMA_BSSSEG }, /* obsolete */
107 { "charmap", PRAGMA_CHARMAP },
108 { "check-stack", PRAGMA_CHECK_STACK },
109 { "checkstack", PRAGMA_CHECKSTACK }, /* obsolete */
110 { "code-name", PRAGMA_CODE_NAME },
111 { "codeseg", PRAGMA_CODESEG }, /* obsolete */
112 { "codesize", PRAGMA_CODESIZE },
113 { "data-name", PRAGMA_DATA_NAME },
114 { "dataseg", PRAGMA_DATASEG }, /* obsolete */
115 { "inline-stdfuncs", PRAGMA_INLINE_STDFUNCS },
116 { "local-strings", PRAGMA_LOCAL_STRINGS },
117 { "optimize", PRAGMA_OPTIMIZE },
118 { "register-vars", PRAGMA_REGISTER_VARS },
119 { "regvaraddr", PRAGMA_REGVARADDR },
120 { "regvars", PRAGMA_REGVARS }, /* obsolete */
121 { "rodata-name", PRAGMA_RODATA_NAME },
122 { "rodataseg", PRAGMA_RODATASEG }, /* obsolete */
123 { "signed-chars", PRAGMA_SIGNED_CHARS },
124 { "signedchars", PRAGMA_SIGNEDCHARS }, /* obsolete */
125 { "static-locals", PRAGMA_STATIC_LOCALS },
126 { "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */
127 { "warn", PRAGMA_WARN },
128 { "wrapped-call", PRAGMA_WRAPPED_CALL },
129 { "writable-strings", PRAGMA_WRITABLE_STRINGS },
130 { "zpsym", PRAGMA_ZPSYM },
133 /* Result of ParsePushPop */
143 /*****************************************************************************/
144 /* Helper functions */
145 /*****************************************************************************/
149 static void PragmaErrorSkip (void)
150 /* Called in case of an error, skips tokens until the closing paren or a
151 ** semicolon is reached.
154 static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
155 SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
160 static int CmpKey (const void* Key, const void* Elem)
161 /* Compare function for bsearch */
163 return strcmp ((const char*) Key, ((const struct Pragma*) Elem)->Key);
168 static pragma_t FindPragma (const StrBuf* Key)
169 /* Find a pragma and return the token. Return PRAGMA_ILLEGAL if the keyword is
170 ** not a valid pragma.
174 P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey);
175 return P? P->Tok : PRAGMA_ILLEGAL;
180 static int GetComma (StrBuf* B)
181 /* Expects and skips a comma in B. Prints an error and returns zero if no
182 ** comma is found. Return a value <> 0 otherwise.
186 if (SB_Get (B) != ',') {
187 Error ("Comma expected");
196 static int GetString (StrBuf* B, StrBuf* S)
197 /* Expects and skips a string in B. Prints an error and returns zero if no
198 ** string is found. Returns a value <> 0 otherwise.
201 if (!SB_GetString (B, S)) {
202 Error ("String literal expected");
210 static int GetNumber (StrBuf* B, long* Val)
211 /* Expects and skips a number in B. Prints an eror and returns zero if no
212 ** number is found. Returns a value <> 0 otherwise.
215 if (!SB_GetNumber (B, Val)) {
216 Error ("Constant integer expected");
224 static IntStack* GetWarning (StrBuf* B)
225 /* Get a warning name from the string buffer. Returns a pointer to the intstack
226 ** that holds the state of the warning, and NULL in case of errors. The
227 ** function will output error messages in case of problems.
231 StrBuf W = AUTO_STRBUF_INITIALIZER;
233 /* The warning name is a symbol but the '-' char is allowed within */
234 if (SB_GetSym (B, &W, "-")) {
236 /* Map the warning name to an IntStack that contains its state */
237 S = FindWarning (SB_GetConstBuf (&W));
241 Error ("Pragma expects a warning name as first argument");
245 /* Deallocate the string */
254 static int HasStr (StrBuf* B, const char* E)
255 /* Checks if E follows in B. If so, skips it and returns true */
257 unsigned Len = strlen (E);
258 if (SB_GetLen (B) - SB_GetIndex (B) >= Len) {
259 if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) {
261 SB_SkipMultiple (B, Len);
270 static PushPopResult ParsePushPop (StrBuf* B)
271 /* Check for and parse the "push" and "pop" keywords. In case of "push", a
272 ** following comma is expected and skipped.
275 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
276 PushPopResult Res = PP_NONE;
278 /* Remember the current string index, so we can go back in case of errors */
279 unsigned Index = SB_GetIndex (B);
281 /* Try to read an identifier */
282 if (SB_GetSym (B, &Ident, 0)) {
284 /* Check if we have a first argument named "pop" */
285 if (SB_CompareStr (&Ident, "pop") == 0) {
289 /* Check if we have a first argument named "push" */
290 } else if (SB_CompareStr (&Ident, "push") == 0) {
294 /* Skip the following comma */
296 /* Error already flagged by GetComma */
302 /* Unknown keyword, roll back */
303 SB_SetIndex (B, Index);
307 /* Free the string buffer and return the result */
314 static void PopInt (IntStack* S)
315 /* Pops an integer from an IntStack. Prints an error if the stack is empty */
317 if (IS_GetCount (S) < 2) {
318 Error ("Cannot pop, stack is empty");
326 static void PushInt (IntStack* S, long Val)
327 /* Pushes an integer onto an IntStack. Prints an error if the stack is full */
330 Error ("Cannot push: stack overflow");
338 static int BoolKeyword (StrBuf* Ident)
339 /* Check if the identifier in Ident is a keyword for a boolean value. Currently
340 ** accepted are true/false/on/off.
343 if (SB_CompareStr (Ident, "true") == 0) {
346 if (SB_CompareStr (Ident, "on") == 0) {
349 if (SB_CompareStr (Ident, "false") == 0) {
352 if (SB_CompareStr (Ident, "off") == 0) {
357 Error ("Pragma argument must be one of `on', `off', `true' or `false'");
363 /*****************************************************************************/
364 /* Pragma handling functions */
365 /*****************************************************************************/
369 static void StringPragma (StrBuf* B, void (*Func) (const char*))
370 /* Handle a pragma that expects a string parameter */
372 StrBuf S = AUTO_STRBUF_INITIALIZER;
374 /* We expect a string here */
375 if (GetString (B, &S)) {
376 /* Call the given function with the string argument */
377 Func (SB_GetConstBuf (&S));
380 /* Call the string buf destructor */
386 static void SegNamePragma (StrBuf* B, segment_t Seg)
387 /* Handle a pragma that expects a segment name parameter */
390 StrBuf S = AUTO_STRBUF_INITIALIZER;
393 /* Check for the "push" or "pop" keywords */
394 switch (ParsePushPop (B)) {
404 /* Pop the old value and output it */
407 /* BSS variables are output at the end of the compilation. Don't
408 ** bother to change their segment, now.
410 if (Seg != SEG_BSS) {
422 Internal ("Invalid result from ParsePushPop");
426 /* A string argument must follow */
427 if (!GetString (B, &S)) {
432 Name = SB_GetConstBuf (&S);
434 /* Check if the name is valid */
435 if (ValidSegName (Name)) {
437 /* Set the new name */
439 PushSegName (Seg, Name);
441 SetSegName (Seg, Name);
444 /* BSS variables are output at the end of the compilation. Don't
445 ** bother to change their segment, now.
447 if (Seg != SEG_BSS) {
453 /* Segment name is invalid */
454 Error ("Illegal segment name: `%s'", Name);
459 /* Call the string buf destructor */
464 static void WrappedCallPragma (StrBuf* B)
465 /* Handle the wrapped-call pragma */
467 StrBuf S = AUTO_STRBUF_INITIALIZER;
472 /* Check for the "push" or "pop" keywords */
473 switch (ParsePushPop (B)) {
476 Error ("Push or pop required");
493 Internal ("Invalid result from ParsePushPop");
497 /* A symbol argument must follow */
498 if (!SB_GetSym (B, &S, NULL)) {
502 /* Skip the following comma */
504 /* Error already flagged by GetComma */
505 Error ("Value required for wrapped-call identifier");
509 if (!GetNumber (B, &Val)) {
510 Error ("Value required for wrapped-call identifier");
514 if (Val < 0 || Val > 255) {
515 Error ("Identifier must be between 0-255");
520 Name = SB_GetConstBuf (&S);
521 Entry = FindSym(Name);
523 /* Check if the name is valid */
524 if (Entry && Entry->Flags & SC_FUNC) {
526 PushWrappedCall(Entry, Val);
527 Entry->Flags |= SC_REF;
528 Entry->V.F.Func->Flags |= FD_CALL_WRAPPER;
532 /* Segment name is invalid */
533 Error ("Wrapped-call target does not exist or is not a function");
538 /* Call the string buf destructor */
544 static void CharMapPragma (StrBuf* B)
545 /* Change the character map */
549 /* Read the character index */
550 if (!GetNumber (B, &Index)) {
553 if (Index < 0 || Index > 255) {
554 Error ("Character index out of range");
563 /* Read the character code */
564 if (!GetNumber (B, &C)) {
567 if (C < 0 || C > 255) {
568 Error ("Character code out of range");
572 /* Warn about remapping character code 0x00
573 ** (except when remapping it back to itself).
575 if (Index + C != 0 && IS_Get (&WarnRemapZero)) {
577 Warning ("Remapping from 0 is dangerous with string functions");
580 Warning ("Remapping to 0 can make string functions stop unexpectedly");
584 /* Remap the character */
585 TgtTranslateSet ((unsigned) Index, (unsigned char) C);
590 static void WarnPragma (StrBuf* B)
591 /* Enable/disable warnings */
596 /* A warning name must follow */
597 IntStack* S = GetWarning (B);
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");
631 /* Boolean argument follows */
632 if (HasStr (B, "true") || HasStr (B, "on")) {
634 } else if (HasStr (B, "false") || HasStr (B, "off")) {
636 } else if (!SB_GetNumber (B, &Val)) {
637 Error ("Invalid pragma argument");
641 /* Set/push the new value */
651 static void FlagPragma (StrBuf* B, IntStack* Stack)
652 /* Handle a pragma that expects a boolean paramater */
654 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
659 /* Try to read an identifier */
660 int IsIdent = SB_GetSym (B, &Ident, 0);
662 /* Check if we have a first argument named "pop" */
663 if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
665 /* No other arguments allowed */
669 /* Check if we have a first argument named "push" */
670 if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
675 IsIdent = SB_GetSym (B, &Ident, 0);
680 /* Boolean argument follows */
682 Val = BoolKeyword (&Ident);
683 } else if (!GetNumber (B, &Val)) {
687 /* Set/push the new value */
689 PushInt (Stack, Val);
695 /* Free the identifier */
701 static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
702 /* Handle a pragma that expects an int paramater */
707 /* Check for the "push" or "pop" keywords */
708 switch (ParsePushPop (B)) {
719 /* Pop the old value and bail out */
728 Internal ("Invalid result from ParsePushPop");
732 /* Integer argument follows */
733 if (!GetNumber (B, &Val)) {
737 /* Check the argument */
738 if (Val < Low || Val > High) {
739 Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
743 /* Set/push the new value */
745 PushInt (Stack, Val);
753 static void ParsePragma (void)
754 /* Parse the contents of the _Pragma statement */
757 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
759 /* Create a string buffer from the string literal */
760 StrBuf B = AUTO_STRBUF_INITIALIZER;
761 SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
763 /* Skip the string token */
766 /* Get the pragma name from the string */
768 if (!SB_GetSym (&B, &Ident, "-")) {
769 Error ("Invalid pragma");
773 /* Search for the name */
774 Pragma = FindPragma (&Ident);
776 /* Do we know this pragma? */
777 if (Pragma == PRAGMA_ILLEGAL) {
778 /* According to the ANSI standard, we're not allowed to generate errors
779 ** for unknown pragmas, but warn about them if enabled (the default).
781 if (IS_Get (&WarnUnknownPragma)) {
782 Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
787 /* Check for an open paren */
789 if (SB_Get (&B) != '(') {
790 Error ("'(' expected");
794 /* Skip white space before the argument */
797 /* Switch for the different pragmas */
801 IntPragma (&B, &DataAlignment, 1, 4096);
804 case PRAGMA_ALLOW_EAGER_INLINE:
805 FlagPragma (&B, &EagerlyInlineFuncs);
809 Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
811 case PRAGMA_BSS_NAME:
812 SegNamePragma (&B, SEG_BSS);
819 case PRAGMA_CHECKSTACK:
820 Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
822 case PRAGMA_CHECK_STACK:
823 FlagPragma (&B, &CheckStack);
827 Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
829 case PRAGMA_CODE_NAME:
830 SegNamePragma (&B, SEG_CODE);
833 case PRAGMA_CODESIZE:
834 IntPragma (&B, &CodeSizeFactor, 10, 1000);
838 Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
840 case PRAGMA_DATA_NAME:
841 SegNamePragma (&B, SEG_DATA);
844 case PRAGMA_INLINE_STDFUNCS:
845 FlagPragma (&B, &InlineStdFuncs);
848 case PRAGMA_LOCAL_STRINGS:
849 FlagPragma (&B, &LocalStrings);
852 case PRAGMA_OPTIMIZE:
853 FlagPragma (&B, &Optimize);
856 case PRAGMA_REGVARADDR:
857 FlagPragma (&B, &AllowRegVarAddr);
861 Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
863 case PRAGMA_REGISTER_VARS:
864 FlagPragma (&B, &EnableRegVars);
867 case PRAGMA_RODATASEG:
868 Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
870 case PRAGMA_RODATA_NAME:
871 SegNamePragma (&B, SEG_RODATA);
874 case PRAGMA_SIGNEDCHARS:
875 Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
877 case PRAGMA_SIGNED_CHARS:
878 FlagPragma (&B, &SignedChars);
881 case PRAGMA_STATICLOCALS:
882 Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
884 case PRAGMA_STATIC_LOCALS:
885 FlagPragma (&B, &StaticLocals);
888 case PRAGMA_WRAPPED_CALL:
889 WrappedCallPragma(&B);
896 case PRAGMA_WRITABLE_STRINGS:
897 FlagPragma (&B, &WritableStrings);
901 StringPragma (&B, MakeZPSym);
905 Internal ("Invalid pragma");
908 /* Closing paren expected */
910 if (SB_Get (&B) != ')') {
911 Error ("')' expected");
916 /* Allow an optional semicolon to be compatible with the old syntax */
917 if (SB_Peek (&B) == ';') {
922 /* Make sure nothing follows */
923 if (SB_Peek (&B) != '\0') {
924 Error ("Unexpected input following pragma directive");
928 /* Release the string buffers */
936 /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
938 /* Skip the token itself */
941 /* We expect an opening paren */
942 if (!ConsumeLParen ()) {
947 if (CurTok.Tok != TOK_SCONST) {
949 /* Print a diagnostic */
950 Error ("String literal expected");
952 /* Try some smart error recovery: Skip tokens until we reach the
953 ** enclosing paren, or a semicolon.
959 /* Parse the _Pragma statement */
963 /* Closing paren needed */