1 /*****************************************************************************/
5 /* Pragma handling for the cc65 C compiler */
9 /* (C) 1998-2009, 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 */
84 static const struct Pragma {
85 const char* Key; /* Keyword */
86 pragma_t Tok; /* Token */
87 } Pragmas[PR_COUNT] = {
88 { "bssseg", PR_BSSSEG },
89 { "charmap", PR_CHARMAP },
90 { "checkstack", PR_CHECKSTACK },
91 { "codeseg", PR_CODESEG },
92 { "codesize", PR_CODESIZE },
93 { "dataseg", PR_DATASEG },
94 { "optimize", PR_OPTIMIZE },
95 { "regvaraddr", PR_REGVARADDR },
96 { "regvars", PR_REGVARS },
97 { "rodataseg", PR_RODATASEG },
98 { "signedchars", PR_SIGNEDCHARS },
99 { "staticlocals", PR_STATICLOCALS },
101 { "zpsym", PR_ZPSYM },
104 /* Result of ParsePushPop */
114 /*****************************************************************************/
115 /* Helper functions */
116 /*****************************************************************************/
120 static void PragmaErrorSkip (void)
121 /* Called in case of an error, skips tokens until the closing paren or a
122 * semicolon is reached.
125 static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
126 SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
131 static int CmpKey (const void* Key, const void* Elem)
132 /* Compare function for bsearch */
134 return strcmp ((const char*) Key, ((const struct Pragma*) Elem)->Key);
139 static pragma_t FindPragma (const StrBuf* Key)
140 /* Find a pragma and return the token. Return PR_ILLEGAL if the keyword is
141 * not a valid pragma.
145 P = bsearch (SB_GetConstBuf (Key), Pragmas, PR_COUNT, sizeof (Pragmas[0]), CmpKey);
146 return P? P->Tok : PR_ILLEGAL;
151 static int GetComma (StrBuf* B)
152 /* Expects and skips a comma in B. Prints an error and returns zero if no
153 * comma is found. Return a value <> 0 otherwise.
157 if (SB_Get (B) != ',') {
158 Error ("Comma expected");
167 static int GetString (StrBuf* B, StrBuf* S)
168 /* Expects and skips a string in B. Prints an error and returns zero if no
169 * string is found. Returns a value <> 0 otherwise.
172 if (!SB_GetString (B, S)) {
173 Error ("String literal expected");
181 static int GetNumber (StrBuf* B, long* Val)
182 /* Expects and skips a number in B. Prints an eror and returns zero if no
183 * number is found. Returns a value <> 0 otherwise.
186 if (!SB_GetNumber (B, Val)) {
187 Error ("Constant integer expected");
195 static IntStack* GetWarning (StrBuf* B)
196 /* Get a warning name from the string buffer. Returns a pointer to the intstack
197 * that holds the state of the warning, and NULL in case of errors. The
198 * function will output error messages in case of problems.
202 StrBuf W = AUTO_STRBUF_INITIALIZER;
204 /* The warning name is a symbol but the '-' char is allowed within */
205 if (SB_GetSym (B, &W, "-")) {
207 /* Map the warning name to an IntStack that contains its state */
208 S = FindWarning (SB_GetConstBuf (&W));
212 Error ("Pragma expects a warning name as first argument");
216 /* Deallocate the string */
225 static int HasStr (StrBuf* B, const char* E)
226 /* Checks if E follows in B. If so, skips it and returns true */
228 unsigned Len = strlen (E);
229 if (SB_GetLen (B) - SB_GetIndex (B) >= Len) {
230 if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) {
232 SB_SkipMultiple (B, Len);
241 static PushPopResult ParsePushPop (StrBuf* B)
242 /* Check for and parse the "push" and "pop" keywords. In case of "push", a
243 * following comma is expected and skipped.
246 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
247 PushPopResult Res = PP_NONE;
250 /* Try to read an identifier */
251 if (SB_GetSym (B, &Ident, 0)) {
253 /* Check if we have a first argument named "pop" */
254 if (SB_CompareStr (&Ident, "pop") == 0) {
258 /* Check if we have a first argument named "push" */
259 } else if (SB_CompareStr (&Ident, "push") == 0) {
263 /* Skip the following comma */
270 /* Unknown keyword */
271 Error ("Invalid pragma arguments");
276 /* Free the string buffer and return the result */
283 static void PopInt (IntStack* S)
284 /* Pops an integer from an IntStack. Prints an error if the stack is empty */
286 if (IS_GetCount (S) < 2) {
287 Error ("Cannot pop, stack is empty");
295 static void PushInt (IntStack* S, long Val)
296 /* Pushes an integer onto an IntStack. Prints an error if the stack is full */
299 Error ("Cannot push: stack overflow");
307 static int BoolKeyword (StrBuf* Ident)
308 /* Check if the identifier in Ident is a keyword for a boolean value. Currently
309 * accepted are true/false/on/off.
312 if (SB_CompareStr (Ident, "true") == 0) {
315 if (SB_CompareStr (Ident, "on") == 0) {
318 if (SB_CompareStr (Ident, "false") == 0) {
321 if (SB_CompareStr (Ident, "off") == 0) {
326 Error ("Pragma argument must be one of `on', `off', `true' or `false'");
332 /*****************************************************************************/
333 /* Pragma handling functions */
334 /*****************************************************************************/
338 static void StringPragma (StrBuf* B, void (*Func) (const char*))
339 /* Handle a pragma that expects a string parameter */
341 StrBuf S = AUTO_STRBUF_INITIALIZER;
343 /* We expect a string here */
344 if (GetString (B, &S)) {
345 /* Call the given function with the string argument */
346 Func (SB_GetConstBuf (&S));
349 /* Call the string buf destructor */
355 static void SegNamePragma (StrBuf* B, segment_t Seg)
356 /* Handle a pragma that expects a segment name parameter */
358 StrBuf S = AUTO_STRBUF_INITIALIZER;
361 /* Check for the "push" or "pop" keywords */
363 switch (ParsePushPop (B)) {
373 /* Pop the old value and output it */
385 Internal ("Invalid result from ParsePushPop");
389 /* A string argument must follow */
390 if (!GetString (B, &S)) {
395 Name = SB_GetConstBuf (&S);
397 /* Check if the name is valid */
398 if (ValidSegName (Name)) {
400 /* Set the new name */
402 PushSegName (Seg, Name);
404 SetSegName (Seg, Name);
410 /* Segment name is invalid */
411 Error ("Illegal segment name: `%s'", Name);
416 /* Call the string buf destructor */
422 static void CharMapPragma (StrBuf* B)
423 /* Change the character map */
427 /* Read the character index */
428 if (!GetNumber (B, &Index)) {
431 if (Index < 1 || Index > 255) {
434 Error ("Remapping 0 is not allowed");
436 Error ("Character index out of range");
446 /* Read the character code */
447 if (!GetNumber (B, &C)) {
450 if (C < 1 || C > 255) {
453 Error ("Remapping 0 is not allowed");
455 Error ("Character code out of range");
460 /* Remap the character */
461 TgtTranslateSet ((unsigned) Index, (unsigned char) C);
466 static void WarnPragma (StrBuf* B)
467 /* Enable/disable warnings */
472 /* A warning name must follow */
473 IntStack* S =GetWarning (B);
483 /* Check for the "push" or "pop" keywords */
484 switch (ParsePushPop (B)) {
495 /* Pop the old value and bail out */
504 Internal ("Invalid result from ParsePushPop");
507 /* Boolean argument follows */
508 if (HasStr (B, "true") || HasStr (B, "on")) {
510 } else if (HasStr (B, "false") || HasStr (B, "off")) {
512 } else if (!SB_GetNumber (B, &Val)) {
513 Error ("Invalid pragma argument");
517 /* Set/push the new value */
527 static void FlagPragma (StrBuf* B, IntStack* Stack)
528 /* Handle a pragma that expects a boolean paramater */
530 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
535 /* Try to read an identifier */
536 int IsIdent = SB_GetSym (B, &Ident, 0);
538 /* Check if we have a first argument named "pop" */
539 if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
541 /* No other arguments allowed */
545 /* Check if we have a first argument named "push" */
546 if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
551 IsIdent = SB_GetSym (B, &Ident, 0);
556 /* Boolean argument follows */
558 Val = BoolKeyword (&Ident);
559 } else if (!GetNumber (B, &Val)) {
563 /* Set/push the new value */
565 PushInt (Stack, Val);
571 /* Free the identifier */
577 static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
578 /* Handle a pragma that expects an int paramater */
583 /* Check for the "push" or "pop" keywords */
584 switch (ParsePushPop (B)) {
595 /* Pop the old value and bail out */
604 Internal ("Invalid result from ParsePushPop");
608 /* Integer argument follows */
609 if (!GetNumber (B, &Val)) {
613 /* Check the argument */
614 if (Val < Low || Val > High) {
615 Error ("Pragma argument out of bounds (%ld-%ld)", Low, High);
619 /* Set/push the new value */
621 PushInt (Stack, Val);
629 static void ParsePragma (void)
630 /* Parse the contents of the _Pragma statement */
633 StrBuf Ident = AUTO_STRBUF_INITIALIZER;
635 /* Create a string buffer from the string literal */
636 StrBuf B = AUTO_STRBUF_INITIALIZER;
637 GetLiteralStrBuf (&B, CurTok.IVal);
639 /* Reset the string pointer, effectivly clearing the string from the
640 * string table. Since we're working with one token lookahead, this
641 * will fail if the next token is also a string token, but that's a
642 * syntax error anyway, because we expect a right paren.
644 ResetLiteralPoolOffs (CurTok.IVal);
646 /* Skip the string token */
649 /* Get the pragma name from the string */
651 if (!SB_GetSym (&B, &Ident, "-")) {
652 Error ("Invalid pragma");
656 /* Search for the name */
657 Pragma = FindPragma (&Ident);
659 /* Do we know this pragma? */
660 if (Pragma == PR_ILLEGAL) {
661 /* According to the ANSI standard, we're not allowed to generate errors
662 * for unknown pragmas, however, we're allowed to warn - and we will
663 * do so. Otherwise one typo may give you hours of bug hunting...
665 Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
669 /* Check for an open paren */
671 if (SB_Get (&B) != '(') {
672 Error ("'(' expected");
676 /* Skip white space before the argument */
679 /* Switch for the different pragmas */
683 SegNamePragma (&B, SEG_BSS);
691 FlagPragma (&B, &CheckStack);
695 SegNamePragma (&B, SEG_CODE);
699 IntPragma (&B, &CodeSizeFactor, 10, 1000);
703 SegNamePragma (&B, SEG_DATA);
707 FlagPragma (&B, &Optimize);
711 FlagPragma (&B, &AllowRegVarAddr);
715 FlagPragma (&B, &EnableRegVars);
719 SegNamePragma (&B, SEG_RODATA);
723 FlagPragma (&B, &SignedChars);
726 case PR_STATICLOCALS:
727 FlagPragma (&B, &StaticLocals);
735 StringPragma (&B, MakeZPSym);
739 Internal ("Invalid pragma");
742 /* Closing paren expected */
744 if (SB_Get (&B) != ')') {
745 Error ("')' expected");
750 /* Allow an optional semicolon to be compatible with the old syntax */
751 if (SB_Peek (&B) == ';') {
756 /* Make sure nothing follows */
757 if (SB_Peek (&B) != '\0') {
758 Error ("Unexpected input following pragma directive");
762 /* Release the string buffers */
770 /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
772 /* Skip the token itself */
775 /* We expect an opening paren */
776 if (!ConsumeLParen ()) {
781 if (CurTok.Tok != TOK_SCONST) {
783 /* Print a diagnostic */
784 Error ("String literal expected");
786 /* Try some smart error recovery: Skip tokens until we reach the
787 * enclosing paren, or a semicolon.
793 /* Parse the _Pragma statement */
797 /* Closing paren needed */