X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fpragma.c;h=608ef7e6d03db81ab588ea64c6453f8c07faa9b4;hb=05f72963695f843cca3fd3dac1be0175b470b179;hp=b26ef7bc81d7ea989fea8cee011f419f50beb097;hpb=2df5db1f04e462b4ecb40e18cc9258a13e312de3;p=cc65 diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index b26ef7bc8..608ef7e6d 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 1998-2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@cc65.org */ +/* (C) 1998-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -37,6 +37,8 @@ #include /* common */ +#include "chartype.h" +#include "segnames.h" #include "tgttrans.h" /* cc65 */ @@ -47,7 +49,6 @@ #include "litpool.h" #include "scanner.h" #include "scanstrbuf.h" -#include "segments.h" #include "symtab.h" #include "pragma.h" @@ -61,41 +62,77 @@ /* Tokens for the #pragmas */ typedef enum { - PR_ILLEGAL = -1, - PR_BSSSEG, - PR_CHARMAP, - PR_CHECKSTACK, - PR_CODESEG, - PR_DATASEG, - PR_REGVARADDR, - PR_RODATASEG, - PR_SIGNEDCHARS, - PR_STATICLOCALS, - PR_ZPSYM, - PR_COUNT + PRAGMA_ILLEGAL = -1, + PRAGMA_BSS_NAME, + PRAGMA_BSSSEG, /* obsolete */ + PRAGMA_CHARMAP, + PRAGMA_CHECK_STACK, + PRAGMA_CHECKSTACK, /* obsolete */ + PRAGMA_CODE_NAME, + PRAGMA_CODESEG, /* obsolete */ + PRAGMA_CODESIZE, + PRAGMA_DATA_NAME, + PRAGMA_DATASEG, /* obsolete */ + PRAGMA_LOCAL_STRINGS, + PRAGMA_OPTIMIZE, + PRAGMA_REGVARADDR, + PRAGMA_REGISTER_VARS, + PRAGMA_REGVARS, /* obsolete */ + PRAGMA_RODATA_NAME, + PRAGMA_RODATASEG, /* obsolete */ + PRAGMA_SIGNED_CHARS, + PRAGMA_SIGNEDCHARS, /* obsolete */ + PRAGMA_STATIC_LOCALS, + PRAGMA_STATICLOCALS, /* obsolete */ + PRAGMA_WARN, + PRAGMA_WRITABLE_STRINGS, + PRAGMA_ZPSYM, + PRAGMA_COUNT } pragma_t; /* Pragma table */ static const struct Pragma { const char* Key; /* Keyword */ pragma_t Tok; /* Token */ -} Pragmas[PR_COUNT] = { - { "bssseg", PR_BSSSEG }, - { "charmap", PR_CHARMAP }, - { "checkstack", PR_CHECKSTACK }, - { "codeseg", PR_CODESEG }, - { "dataseg", PR_DATASEG }, - { "regvaraddr", PR_REGVARADDR }, - { "rodataseg", PR_RODATASEG }, - { "signedchars", PR_SIGNEDCHARS }, - { "staticlocals", PR_STATICLOCALS }, - { "zpsym", PR_ZPSYM }, +} Pragmas[PRAGMA_COUNT] = { + { "bss-name", PRAGMA_BSS_NAME }, + { "bssseg", PRAGMA_BSSSEG }, /* obsolete */ + { "charmap", PRAGMA_CHARMAP }, + { "check-stack", PRAGMA_CHECK_STACK }, + { "checkstack", PRAGMA_CHECKSTACK }, /* obsolete */ + { "code-name", PRAGMA_CODE_NAME }, + { "codeseg", PRAGMA_CODESEG }, /* obsolete */ + { "codesize", PRAGMA_CODESIZE }, + { "data-name", PRAGMA_DATA_NAME }, + { "dataseg", PRAGMA_DATASEG }, /* obsolete */ + { "local-strings", PRAGMA_LOCAL_STRINGS }, + { "optimize", PRAGMA_OPTIMIZE }, + { "register-vars", PRAGMA_REGISTER_VARS }, + { "regvaraddr", PRAGMA_REGVARADDR }, + { "regvars", PRAGMA_REGVARS }, /* obsolete */ + { "rodata-name", PRAGMA_RODATA_NAME }, + { "rodataseg", PRAGMA_RODATASEG }, /* obsolete */ + { "signed-chars", PRAGMA_SIGNED_CHARS }, + { "signedchars", PRAGMA_SIGNEDCHARS }, /* obsolete */ + { "static-locals", PRAGMA_STATIC_LOCALS }, + { "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */ + { "warn", PRAGMA_WARN }, + { "writable-strings", PRAGMA_WRITABLE_STRINGS }, + { "zpsym", PRAGMA_ZPSYM }, }; +/* Result of ParsePushPop */ +typedef enum { + PP_NONE, + PP_POP, + PP_PUSH, + PP_ERROR, +} PushPopResult; + /*****************************************************************************/ -/* Code */ +/* Helper functions */ /*****************************************************************************/ @@ -119,33 +156,218 @@ static int CmpKey (const void* Key, const void* Elem) -static pragma_t FindPragma (const char* Key) -/* Find a pragma and return the token. Return PR_ILLEGAL if the keyword is +static pragma_t FindPragma (const StrBuf* Key) +/* Find a pragma and return the token. Return PRAGMA_ILLEGAL if the keyword is * not a valid pragma. */ { struct Pragma* P; - P = bsearch (Key, Pragmas, PR_COUNT, sizeof (Pragmas[0]), CmpKey); - return P? P->Tok : PR_ILLEGAL; + P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey); + return P? P->Tok : PRAGMA_ILLEGAL; +} + + + +static int GetComma (StrBuf* B) +/* Expects and skips a comma in B. Prints an error and returns zero if no + * comma is found. Return a value <> 0 otherwise. + */ +{ + SB_SkipWhite (B); + if (SB_Get (B) != ',') { + Error ("Comma expected"); + return 0; + } + SB_SkipWhite (B); + return 1; +} + + + +static int GetString (StrBuf* B, StrBuf* S) +/* Expects and skips a string in B. Prints an error and returns zero if no + * string is found. Returns a value <> 0 otherwise. + */ +{ + if (!SB_GetString (B, S)) { + Error ("String literal expected"); + return 0; + } + return 1; +} + + + +static int GetNumber (StrBuf* B, long* Val) +/* Expects and skips a number in B. Prints an eror and returns zero if no + * number is found. Returns a value <> 0 otherwise. + */ +{ + if (!SB_GetNumber (B, Val)) { + Error ("Constant integer expected"); + return 0; + } + return 1; +} + + + +static IntStack* GetWarning (StrBuf* B) +/* Get a warning name from the string buffer. Returns a pointer to the intstack + * that holds the state of the warning, and NULL in case of errors. The + * function will output error messages in case of problems. + */ +{ + IntStack* S = 0; + StrBuf W = AUTO_STRBUF_INITIALIZER; + + /* The warning name is a symbol but the '-' char is allowed within */ + if (SB_GetSym (B, &W, "-")) { + + /* Map the warning name to an IntStack that contains its state */ + S = FindWarning (SB_GetConstBuf (&W)); + + /* Handle errors */ + if (S == 0) { + Error ("Pragma expects a warning name as first argument"); + } + } + + /* Deallocate the string */ + SB_Done (&W); + + /* Done */ + return S; +} + + + +static int HasStr (StrBuf* B, const char* E) +/* Checks if E follows in B. If so, skips it and returns true */ +{ + unsigned Len = strlen (E); + if (SB_GetLen (B) - SB_GetIndex (B) >= Len) { + if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) { + /* Found */ + SB_SkipMultiple (B, Len); + return 1; + } + } + return 0; +} + + + +static PushPopResult ParsePushPop (StrBuf* B) +/* Check for and parse the "push" and "pop" keywords. In case of "push", a + * following comma is expected and skipped. + */ +{ + StrBuf Ident = AUTO_STRBUF_INITIALIZER; + PushPopResult Res = PP_NONE; + + + /* Try to read an identifier */ + if (SB_GetSym (B, &Ident, 0)) { + + /* Check if we have a first argument named "pop" */ + if (SB_CompareStr (&Ident, "pop") == 0) { + + Res = PP_POP; + + /* Check if we have a first argument named "push" */ + } else if (SB_CompareStr (&Ident, "push") == 0) { + + Res = PP_PUSH; + + /* Skip the following comma */ + if (!GetComma (B)) { + Res = PP_ERROR; + } + + } else { + + /* Unknown keyword */ + Error ("Invalid pragma arguments"); + Res = PP_ERROR; + } + } + + /* Free the string buffer and return the result */ + SB_Done (&Ident); + return Res; +} + + + +static void PopInt (IntStack* S) +/* Pops an integer from an IntStack. Prints an error if the stack is empty */ +{ + if (IS_GetCount (S) < 2) { + Error ("Cannot pop, stack is empty"); + } else { + IS_Drop (S); + } +} + + + +static void PushInt (IntStack* S, long Val) +/* Pushes an integer onto an IntStack. Prints an error if the stack is full */ +{ + if (IS_IsFull (S)) { + Error ("Cannot push: stack overflow"); + } else { + IS_Push (S, Val); + } +} + + + +static int BoolKeyword (StrBuf* Ident) +/* Check if the identifier in Ident is a keyword for a boolean value. Currently + * accepted are true/false/on/off. + */ +{ + if (SB_CompareStr (Ident, "true") == 0) { + return 1; + } + if (SB_CompareStr (Ident, "on") == 0) { + return 1; + } + if (SB_CompareStr (Ident, "false") == 0) { + return 0; + } + if (SB_CompareStr (Ident, "off") == 0) { + return 0; + } + + /* Error */ + Error ("Pragma argument must be one of `on', `off', `true' or `false'"); + return 0; } +/*****************************************************************************/ +/* Pragma handling functions */ +/*****************************************************************************/ + + + static void StringPragma (StrBuf* B, void (*Func) (const char*)) /* Handle a pragma that expects a string parameter */ { - StrBuf S; + StrBuf S = AUTO_STRBUF_INITIALIZER; /* We expect a string here */ - if (SB_GetString (B, &S)) { + if (GetString (B, &S)) { /* Call the given function with the string argument */ Func (SB_GetConstBuf (&S)); - } else { - Error ("String literal expected"); } /* Call the string buf destructor */ - DoneStrBuf (&S); + SB_Done (&S); } @@ -153,32 +375,66 @@ static void StringPragma (StrBuf* B, void (*Func) (const char*)) static void SegNamePragma (StrBuf* B, segment_t Seg) /* Handle a pragma that expects a segment name parameter */ { - StrBuf S; + StrBuf S = AUTO_STRBUF_INITIALIZER; + const char* Name; + + /* Check for the "push" or "pop" keywords */ + int Push = 0; + switch (ParsePushPop (B)) { + + case PP_NONE: + break; + + case PP_PUSH: + Push = 1; + break; + + case PP_POP: + /* Pop the old value and output it */ + PopSegName (Seg); + g_segname (Seg); + + /* Done */ + goto ExitPoint; - if (SB_GetString (B, &S)) { + case PP_ERROR: + /* Bail out */ + goto ExitPoint; - /* Get the string */ - const char* Name = SB_GetConstBuf (&S); + default: + Internal ("Invalid result from ParsePushPop"); - /* Check if the name is valid */ - if (ValidSegName (Name)) { + } - /* Set the new name */ - g_segname (Seg, Name); + /* A string argument must follow */ + if (!GetString (B, &S)) { + goto ExitPoint; + } - } else { + /* Get the string */ + Name = SB_GetConstBuf (&S); - /* Segment name is invalid */ - Error ("Illegal segment name: `%s'", Name); + /* Check if the name is valid */ + if (ValidSegName (Name)) { - } + /* Set the new name */ + if (Push) { + PushSegName (Seg, Name); + } else { + SetSegName (Seg, Name); + } + g_segname (Seg); } else { - Error ("String literal expected"); - } + /* Segment name is invalid */ + Error ("Illegal segment name: `%s'", Name); + + } + +ExitPoint: /* Call the string buf destructor */ - DoneStrBuf (&S); + SB_Done (&S); } @@ -186,58 +442,205 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) static void CharMapPragma (StrBuf* B) /* Change the character map */ { - unsigned Index, C; - - ExprDesc Val; + long Index, C; /* Read the character index */ - ConstIntExpr (&Val); - if (Val.ConstVal < 1 || Val.ConstVal > 255) { - Error ("Character index out of range"); - Index = 'A'; - } else { - Index = Val.ConstVal; + if (!GetNumber (B, &Index)) { + return; + } + if (Index < 1 || Index > 255) { + if (Index == 0) { + /* For groepaz */ + Error ("Remapping 0 is not allowed"); + } else { + Error ("Character index out of range"); + } + return; } /* Comma follows */ - ConsumeComma (); + if (!GetComma (B)) { + return; + } /* Read the character code */ - ConstIntExpr (&Val); - if (Val.ConstVal < 1 || Val.ConstVal > 255) { - Error ("Character code out of range"); - C = 'A'; - } else { - C = Val.ConstVal; + if (!GetNumber (B, &C)) { + return; + } + if (C < 1 || C > 255) { + if (C == 0) { + /* For groepaz */ + Error ("Remapping 0 is not allowed"); + } else { + Error ("Character code out of range"); + } + return; } /* Remap the character */ - TgtTranslateSet (Index, C); + TgtTranslateSet ((unsigned) Index, (unsigned char) C); +} + + + +static void WarnPragma (StrBuf* B) +/* Enable/disable warnings */ +{ + long Val; + int Push; + + /* A warning name must follow */ + IntStack* S =GetWarning (B); + if (S == 0) { + return; + } + + /* Comma follows */ + if (!GetComma (B)) { + return; + } + + /* Check for the "push" or "pop" keywords */ + switch (ParsePushPop (B)) { + + case PP_NONE: + Push = 0; + break; + + case PP_PUSH: + Push = 1; + break; + + case PP_POP: + /* Pop the old value and bail out */ + PopInt (S); + return; + + case PP_ERROR: + /* Bail out */ + return; + + default: + Internal ("Invalid result from ParsePushPop"); + } + + /* Boolean argument follows */ + if (HasStr (B, "true") || HasStr (B, "on")) { + Val = 1; + } else if (HasStr (B, "false") || HasStr (B, "off")) { + Val = 0; + } else if (!SB_GetNumber (B, &Val)) { + Error ("Invalid pragma argument"); + return; + } + + /* Set/push the new value */ + if (Push) { + PushInt (S, Val); + } else { + IS_Set (S, Val); + } } -static void FlagPragma (StrBuf* B, unsigned char* Flag) +static void FlagPragma (StrBuf* B, IntStack* Stack) /* Handle a pragma that expects a boolean paramater */ { - ident Ident; - - if (SB_Peek (B) == '0') { - SB_Skip (B); - *Flag = 0; - } else if (SB_Peek (B) == '1') { - SB_Skip (B); - *Flag = 1; - } else if (SB_GetSym (B, Ident)) { - if (strcmp (Ident, "true") == 0 || strcmp (Ident, "on") == 0) { - *Flag = 1; - } else if (strcmp (Ident, "false") == 0 || strcmp (Ident, "off") == 0) { - *Flag = 0; - } else { - Error ("Pragma argument must be one of `on', `off', `true' or `false'"); + StrBuf Ident = AUTO_STRBUF_INITIALIZER; + long Val; + int Push; + + + /* Try to read an identifier */ + int IsIdent = SB_GetSym (B, &Ident, 0); + + /* Check if we have a first argument named "pop" */ + if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) { + PopInt (Stack); + /* No other arguments allowed */ + return; + } + + /* Check if we have a first argument named "push" */ + if (IsIdent && SB_CompareStr (&Ident, "push") == 0) { + Push = 1; + if (!GetComma (B)) { + goto ExitPoint; } + IsIdent = SB_GetSym (B, &Ident, 0); } else { - Error ("Invalid pragma argument"); + Push = 0; + } + + /* Boolean argument follows */ + if (IsIdent) { + Val = BoolKeyword (&Ident); + } else if (!GetNumber (B, &Val)) { + goto ExitPoint; + } + + /* Set/push the new value */ + if (Push) { + PushInt (Stack, Val); + } else { + IS_Set (Stack, Val); + } + +ExitPoint: + /* Free the identifier */ + SB_Done (&Ident); +} + + + +static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) +/* Handle a pragma that expects an int paramater */ +{ + long Val; + int Push; + + /* Check for the "push" or "pop" keywords */ + switch (ParsePushPop (B)) { + + case PP_NONE: + Push = 0; + break; + + case PP_PUSH: + Push = 1; + break; + + case PP_POP: + /* Pop the old value and bail out */ + PopInt (Stack); + return; + + case PP_ERROR: + /* Bail out */ + return; + + default: + Internal ("Invalid result from ParsePushPop"); + + } + + /* Integer argument follows */ + if (!GetNumber (B, &Val)) { + return; + } + + /* Check the argument */ + if (Val < Low || Val > High) { + Error ("Pragma argument out of bounds (%ld-%ld)", Low, High); + return; + } + + /* Set/push the new value */ + if (Push) { + PushInt (Stack, Val); + } else { + IS_Set (Stack, Val); } } @@ -247,47 +650,41 @@ static void ParsePragma (void) /* Parse the contents of the _Pragma statement */ { pragma_t Pragma; - ident Ident; + StrBuf Ident = AUTO_STRBUF_INITIALIZER; /* Create a string buffer from the string literal */ StrBuf B = AUTO_STRBUF_INITIALIZER; - GetLiteralStrBuf (&B, CurTok.IVal); - - /* Reset the string pointer, effectivly clearing the string from the - * string table. Since we're working with one token lookahead, this - * will fail if the next token is also a string token, but that's a - * syntax error anyway, because we expect a right paren. - */ - ResetLiteralPoolOffs (CurTok.IVal); + SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); /* Skip the string token */ NextToken (); /* Get the pragma name from the string */ SB_SkipWhite (&B); - if (!SB_GetSym (&B, Ident)) { + if (!SB_GetSym (&B, &Ident, "-")) { Error ("Invalid pragma"); - return; + goto ExitPoint; } /* Search for the name */ - Pragma = FindPragma (Ident); + Pragma = FindPragma (&Ident); /* Do we know this pragma? */ - if (Pragma == PR_ILLEGAL) { + if (Pragma == PRAGMA_ILLEGAL) { /* According to the ANSI standard, we're not allowed to generate errors - * for unknown pragmas, however, we're allowed to warn - and we will - * do so. Otherwise one typo may give you hours of bug hunting... + * for unknown pragmas, but warn about them if enabled (the default). */ - Warning ("Unknown pragma `%s'", Ident); - return; + if (IS_Get (&WarnUnknownPragma)) { + Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident)); + } + goto ExitPoint; } /* Check for an open paren */ SB_SkipWhite (&B); if (SB_Get (&B) != '(') { Error ("'(' expected"); - return; + goto ExitPoint; } /* Skip white space before the argument */ @@ -296,43 +693,91 @@ static void ParsePragma (void) /* Switch for the different pragmas */ switch (Pragma) { - case PR_BSSSEG: + case PRAGMA_BSSSEG: + Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead"); + /* FALLTHROUGH */ + case PRAGMA_BSS_NAME: SegNamePragma (&B, SEG_BSS); break; - case PR_CHARMAP: + case PRAGMA_CHARMAP: CharMapPragma (&B); break; - case PR_CHECKSTACK: + case PRAGMA_CHECKSTACK: + Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead"); + /* FALLTHROUGH */ + case PRAGMA_CHECK_STACK: FlagPragma (&B, &CheckStack); break; - case PR_CODESEG: + case PRAGMA_CODESEG: + Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead"); + /* FALLTHROUGH */ + case PRAGMA_CODE_NAME: SegNamePragma (&B, SEG_CODE); break; - case PR_DATASEG: + case PRAGMA_CODESIZE: + IntPragma (&B, &CodeSizeFactor, 10, 1000); + break; + + case PRAGMA_DATASEG: + Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead"); + /* FALLTHROUGH */ + case PRAGMA_DATA_NAME: SegNamePragma (&B, SEG_DATA); break; - case PR_REGVARADDR: - FlagPragma (&B, &AllowRegVarAddr); + case PRAGMA_LOCAL_STRINGS: + FlagPragma (&B, &LocalStrings); + break; + + case PRAGMA_OPTIMIZE: + FlagPragma (&B, &Optimize); + break; + + case PRAGMA_REGVARADDR: + FlagPragma (&B, &AllowRegVarAddr); + break; + + case PRAGMA_REGVARS: + Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead"); + /* FALLTHROUGH */ + case PRAGMA_REGISTER_VARS: + FlagPragma (&B, &EnableRegVars); break; - case PR_RODATASEG: + case PRAGMA_RODATASEG: + Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead"); + /* FALLTHROUGH */ + case PRAGMA_RODATA_NAME: SegNamePragma (&B, SEG_RODATA); break; - case PR_SIGNEDCHARS: - FlagPragma (&B, &SignedChars); + case PRAGMA_SIGNEDCHARS: + Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead"); + /* FALLTHROUGH */ + case PRAGMA_SIGNED_CHARS: + FlagPragma (&B, &SignedChars); break; - case PR_STATICLOCALS: + case PRAGMA_STATICLOCALS: + Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead"); + /* FALLTHROUGH */ + case PRAGMA_STATIC_LOCALS: FlagPragma (&B, &StaticLocals); break; - case PR_ZPSYM: + case PRAGMA_WARN: + WarnPragma (&B); + break; + + case PRAGMA_WRITABLE_STRINGS: + FlagPragma (&B, &WritableStrings); + break; + + case PRAGMA_ZPSYM: StringPragma (&B, MakeZPSym); break; @@ -344,17 +789,25 @@ static void ParsePragma (void) SB_SkipWhite (&B); if (SB_Get (&B) != ')') { Error ("')' expected"); - return; + goto ExitPoint; + } + SB_SkipWhite (&B); + + /* Allow an optional semicolon to be compatible with the old syntax */ + if (SB_Peek (&B) == ';') { + SB_Skip (&B); + SB_SkipWhite (&B); } /* Make sure nothing follows */ - SB_SkipWhite (&B); if (SB_Peek (&B) != '\0') { Error ("Unexpected input following pragma directive"); } - /* Release the StrBuf */ - DoneStrBuf (&B); +ExitPoint: + /* Release the string buffers */ + SB_Done (&B); + SB_Done (&Ident); }