cc65/src/cc65/pragma.c

1127 lines
31 KiB
C

/*****************************************************************************/
/* */
/* pragma.c */
/* */
/* Pragma handling for the cc65 C compiler */
/* */
/* */
/* */
/* (C) 1998-2011, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdlib.h>
#include <string.h>
/* common */
#include "addrsize.h"
#include "chartype.h"
#include "segnames.h"
#include "tgttrans.h"
#include "xmalloc.h"
/* cc65 */
#include "codegen.h"
#include "error.h"
#include "global.h"
#include "litpool.h"
#include "scanner.h"
#include "scanstrbuf.h"
#include "symtab.h"
#include "pragma.h"
#include "wrappedcall.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Tokens for the #pragmas */
typedef enum {
PRAGMA_ILLEGAL = -1,
PRAGMA_ALIGN,
PRAGMA_ALLOW_EAGER_INLINE,
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_INLINE_STDFUNCS,
PRAGMA_LOCAL_STRINGS,
PRAGMA_MESSAGE,
PRAGMA_OPTIMIZE,
PRAGMA_REGISTER_VARS,
PRAGMA_REGVARADDR,
PRAGMA_REGVARS, /* obsolete */
PRAGMA_RODATA_NAME,
PRAGMA_RODATASEG, /* obsolete */
PRAGMA_SIGNED_CHARS,
PRAGMA_SIGNEDCHARS, /* obsolete */
PRAGMA_STATIC_LOCALS,
PRAGMA_STATICLOCALS, /* obsolete */
PRAGMA_WARN,
PRAGMA_WRAPPED_CALL,
PRAGMA_WRITABLE_STRINGS,
PRAGMA_ZPSYM,
PRAGMA_COUNT
} pragma_t;
/* Pragma table */
static const struct Pragma {
const char* Key; /* Keyword */
pragma_t Tok; /* Token */
} Pragmas[PRAGMA_COUNT] = {
{ "align", PRAGMA_ALIGN },
{ "allow-eager-inline", PRAGMA_ALLOW_EAGER_INLINE },
{ "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 */
{ "inline-stdfuncs", PRAGMA_INLINE_STDFUNCS },
{ "local-strings", PRAGMA_LOCAL_STRINGS },
{ "message", PRAGMA_MESSAGE },
{ "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 },
{ "wrapped-call", PRAGMA_WRAPPED_CALL },
{ "writable-strings", PRAGMA_WRITABLE_STRINGS },
{ "zpsym", PRAGMA_ZPSYM },
};
/* Result of ParsePushPop */
typedef enum {
PP_NONE,
PP_POP,
PP_PUSH,
PP_ERROR,
} PushPopResult;
/* Effective scope of the pragma.
** This talks about how far the pragma has effects on whenever it shows up,
** even in the middle of an expression, statement or something.
*/
typedef enum {
PES_NONE,
PES_IMM, /* No way back */
PES_EXPR, /* Current expression/declarator */
PES_STMT, /* Current statement/declaration */
PES_SCOPE, /* Current scope */
PES_FUNC, /* Current function */
PES_FILE, /* Current file */
PES_ALL, /* All */
} pragma_scope_t;
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static void PragmaErrorSkip (void)
/* Called in case of an error, skips tokens until the closing paren or a
** semicolon is reached.
*/
{
static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
}
static int CmpKey (const void* Key, const void* Elem)
/* Compare function for bsearch */
{
return strcmp ((const char*) Key, ((const struct Pragma*) Elem)->Key);
}
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 (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;
/* Remember the current string index, so we can go back in case of errors */
unsigned Index = SB_GetIndex (B);
/* 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)) {
/* Error already flagged by GetComma */
Res = PP_ERROR;
}
} else {
/* Unknown keyword, roll back */
SB_SetIndex (B, Index);
}
}
/* 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 IsBoolKeyword (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;
}
static void ApplyPragma (int PushPop, IntStack* Stack, long Val)
/* Apply a pragma immediately */
{
if (PushPop > 0) {
/* Push the new value */
PushInt (Stack, Val);
} else if (PushPop < 0) {
/* Pop the old value */
PopInt (Stack);
} else {
/* Set the new value */
IS_Set (Stack, Val);
}
}
static void ApplySegNamePragma (pragma_t Token, int PushPop, const char* Name, unsigned char AddrSize)
/* Process a segname pragma */
{
segment_t Seg = SEG_CODE;
switch (Token) {
case PRAGMA_CODE_NAME:
case PRAGMA_CODESEG:
Seg = SEG_CODE;
break;
case PRAGMA_RODATA_NAME:
case PRAGMA_RODATASEG:
Seg = SEG_RODATA;
break;
case PRAGMA_DATA_NAME:
case PRAGMA_DATASEG:
Seg = SEG_DATA;
break;
case PRAGMA_BSS_NAME:
case PRAGMA_BSSSEG:
Seg = SEG_BSS;
break;
default:
Internal ("Unknown segment name pragma: %02X", Token);
break;
}
/* Set the new name */
if (PushPop > 0) {
PushSegName (Seg, Name);
} else if (PushPop < 0) {
PopSegName (Seg);
} else {
SetSegName (Seg, Name);
}
/* Set the optional address size for the segment if valid */
if (PushPop >= 0 && AddrSize != ADDR_SIZE_INVALID) {
SetSegAddrSize (Name, AddrSize);
}
/* BSS variables are output at the end of the compilation. Don't
** bother to change their segment, now.
*/
if (Seg != SEG_BSS) {
g_segname (Seg);
}
}
/*****************************************************************************/
/* Pragma handling functions */
/*****************************************************************************/
static void StringPragma (pragma_scope_t Scope, StrBuf* B, void (*Func) (const char*))
/* Handle a pragma that expects a string parameter */
{
StrBuf S = AUTO_STRBUF_INITIALIZER;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* We expect a string here */
if (GetString (B, &S)) {
/* Call the given function with the string argument */
Func (SB_GetConstBuf (&S));
}
/* Call the string buf destructor */
SB_Done (&S);
}
static void SegNamePragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B)
/* Handle a pragma that expects a segment name parameter */
{
const char* Name;
unsigned char AddrSize = ADDR_SIZE_INVALID;
StrBuf S = AUTO_STRBUF_INITIALIZER;
StrBuf A = AUTO_STRBUF_INITIALIZER;
int PushPop = 0;
/* Unused at the moment */
(void)Scope;
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
case PP_NONE:
break;
case PP_PUSH:
PushPop = 1;
break;
case PP_POP:
/* Pop the old value and output it */
ApplySegNamePragma (Token, -1, 0, 0);
/* Done */
goto ExitPoint;
case PP_ERROR:
/* Bail out */
goto ExitPoint;
default:
Internal ("Invalid result from ParsePushPop");
}
/* A string argument must follow */
if (!GetString (B, &S)) {
goto ExitPoint;
}
/* Get the name string of the segment */
Name = SB_GetConstBuf (&S);
/* Check if the name is valid */
if (ValidSegName (Name)) {
/* Skip the following comma */
SB_SkipWhite (B);
if (SB_Peek (B) == ',') {
SB_Skip (B);
SB_SkipWhite (B);
/* A string argument must follow */
if (!GetString (B, &A)) {
goto ExitPoint;
}
/* Get the address size for the segment */
AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A));
/* Check the address size for the segment */
if (AddrSize == ADDR_SIZE_INVALID) {
Warning ("Invalid address size for segment");
}
}
/* Set the new name and optionally address size */
ApplySegNamePragma (Token, PushPop, Name, AddrSize);
} else {
/* Segment name is invalid */
Error ("Illegal segment name: '%s'", Name);
}
ExitPoint:
/* Call the string buf destructor */
SB_Done (&S);
SB_Done (&A);
}
static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B)
/* Handle the wrapped-call pragma */
{
StrBuf S = AUTO_STRBUF_INITIALIZER;
const char *Name;
long Val;
SymEntry *Entry;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
case PP_NONE:
Error ("Push or pop required");
break;
case PP_PUSH:
break;
case PP_POP:
PopWrappedCall();
/* Done */
goto ExitPoint;
case PP_ERROR:
/* Bail out */
goto ExitPoint;
default:
Internal ("Invalid result from ParsePushPop");
}
/* A symbol argument must follow */
if (!SB_GetSym (B, &S, NULL)) {
goto ExitPoint;
}
/* Skip the following comma */
if (!GetComma (B)) {
/* Error already flagged by GetComma */
Error ("Value or the word 'bank' required for wrapped-call identifier");
goto ExitPoint;
}
/* Next must be either a numeric value, or "bank" */
if (HasStr (B, "bank")) {
Val = WRAPPED_CALL_USE_BANK;
} else if (!GetNumber (B, &Val)) {
Error ("Value required for wrapped-call identifier");
goto ExitPoint;
}
if (!(Val == WRAPPED_CALL_USE_BANK) && (Val < 0 || Val > 255)) {
Error ("Identifier must be between 0-255");
goto ExitPoint;
}
/* Get the string */
Name = SB_GetConstBuf (&S);
Entry = FindSym(Name);
/* Check if the name is valid */
if (Entry && (Entry->Flags & SC_TYPEMASK) == SC_FUNC) {
PushWrappedCall(Entry, (unsigned int) Val);
Entry->Flags |= SC_REF;
GetFuncDesc (Entry->Type)->Flags |= FD_CALL_WRAPPER;
} else {
/* Segment name is invalid */
Error ("Wrapped-call target does not exist or is not a function");
}
ExitPoint:
/* Call the string buf destructor */
SB_Done (&S);
}
static void CharMapPragma (pragma_scope_t Scope, StrBuf* B)
/* Change the character map */
{
long Index, C;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* Read the character index */
if (!GetNumber (B, &Index)) {
return;
}
if (Index < 0 || Index > 255) {
Error ("Character index out of range");
return;
}
/* Comma follows */
if (!GetComma (B)) {
return;
}
/* Read the character code */
if (!GetNumber (B, &C)) {
return;
}
if (C < 0 || C > 255) {
Error ("Character code out of range");
return;
}
/* Warn about remapping character code 0x00
** (except when remapping it back to itself).
*/
if (Index + C != 0 && IS_Get (&WarnRemapZero)) {
if (Index == 0) {
Warning ("Remapping from 0 is dangerous with string functions");
}
else if (C == 0) {
Warning ("Remapping to 0 can make string functions stop unexpectedly");
}
}
/* Remap the character */
TgtTranslateSet ((unsigned) Index, (unsigned char) C);
}
static void WarnPragma (pragma_scope_t Scope, StrBuf* B)
/* Enable/disable warnings */
{
long Val;
int Push;
/* A warning name must follow */
IntStack* S = GetWarning (B);
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
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 (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack)
/* Handle a pragma that expects a boolean parameter */
{
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
long Val;
int PushPop = 0;
/* Unused at the moment */
(void)Scope;
(void)Token;
/* 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) {
/* Pop the old value and bail out */
ApplyPragma (-1, Stack, 0);
/* No other arguments allowed */
return;
}
/* Check if we have a first argument named "push" */
if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
PushPop = 1;
if (!GetComma (B)) {
goto ExitPoint;
}
IsIdent = SB_GetSym (B, &Ident, 0);
}
/* Boolean argument follows */
if (IsIdent) {
Val = IsBoolKeyword (&Ident);
} else if (!GetNumber (B, &Val)) {
goto ExitPoint;
}
/* Add this pragma and apply it whenever appropriately */
ApplyPragma (PushPop, Stack, Val);
ExitPoint:
/* Free the identifier */
SB_Done (&Ident);
}
static void IntPragma (pragma_scope_t Scope, pragma_t Token, StrBuf* B, IntStack* Stack, long Low, long High)
/* Handle a pragma that expects an int parameter */
{
long Val;
int Push;
/* Unused at the moment */
(void)Scope;
(void)Token;
/* 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 */
ApplyPragma (-1, Stack, 0);
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;
}
/* Add this pragma and apply it whenever appropriately */
ApplyPragma (Push, Stack, Val);
}
static void NoteMessagePragma (const char* Message)
/* Wrapper for printf-like Note() function protected from user-provided format
** specifiers.
*/
{
Note ("%s", Message);
}
static void ParsePragmaString (void)
/* Parse the contents of _Pragma */
{
pragma_t Pragma;
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
/* Create a string buffer from the string literal */
StrBuf B = AUTO_STRBUF_INITIALIZER;
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, "-")) {
Error ("Invalid pragma");
goto ExitPoint;
}
/* Search for the name */
Pragma = FindPragma (&Ident);
/* Do we know this pragma? */
if (Pragma == PRAGMA_ILLEGAL) {
/* According to the ANSI standard, we're not allowed to generate errors
** for unknown pragmas, but warn about them if enabled (the default).
*/
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");
goto ExitPoint;
}
/* Skip white space before the argument */
SB_SkipWhite (&B);
/* Switch for the different pragmas */
switch (Pragma) {
case PRAGMA_ALIGN:
/* TODO: PES_EXPR (PES_DECL) */
IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096);
break;
case PRAGMA_ALLOW_EAGER_INLINE:
FlagPragma (PES_STMT, Pragma, &B, &EagerlyInlineFuncs);
break;
case PRAGMA_BSSSEG:
Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
/* FALLTHROUGH */
case PRAGMA_BSS_NAME:
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B);
break;
case PRAGMA_CHARMAP:
CharMapPragma (PES_IMM, &B);
break;
case PRAGMA_CHECKSTACK:
Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
/* FALLTHROUGH */
case PRAGMA_CHECK_STACK:
/* TODO: PES_SCOPE maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &CheckStack);
break;
case PRAGMA_CODESEG:
Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
/* FALLTHROUGH */
case PRAGMA_CODE_NAME:
/* PES_FUNC is the only sensible option so far */
SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B);
break;
case PRAGMA_CODESIZE:
/* PES_EXPR would be optimization nightmare */
IntPragma (PES_STMT, Pragma, &B, &CodeSizeFactor, 10, 1000);
break;
case PRAGMA_DATASEG:
Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
/* FALLTHROUGH */
case PRAGMA_DATA_NAME:
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B);
break;
case PRAGMA_INLINE_STDFUNCS:
/* TODO: PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs);
break;
case PRAGMA_LOCAL_STRINGS:
/* TODO: PES_STMT or even PES_EXPR */
FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings);
break;
case PRAGMA_MESSAGE:
/* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, NoteMessagePragma);
break;
case PRAGMA_OPTIMIZE:
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &Optimize);
break;
case PRAGMA_REGVARADDR:
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &AllowRegVarAddr);
break;
case PRAGMA_REGVARS:
Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
/* FALLTHROUGH */
case PRAGMA_REGISTER_VARS:
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &EnableRegVars);
break;
case PRAGMA_RODATASEG:
Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
/* FALLTHROUGH */
case PRAGMA_RODATA_NAME:
/* TODO: PES_STMT or even PES_EXPR maybe? */
SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, &B);
break;
case PRAGMA_SIGNEDCHARS:
Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
/* FALLTHROUGH */
case PRAGMA_SIGNED_CHARS:
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &SignedChars);
break;
case PRAGMA_STATICLOCALS:
Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
/* FALLTHROUGH */
case PRAGMA_STATIC_LOCALS:
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals);
break;
case PRAGMA_WRAPPED_CALL:
/* PES_IMM is the only sensible option */
WrappedCallPragma (PES_IMM, &B);
break;
case PRAGMA_WARN:
/* PES_IMM is the only sensible option */
WarnPragma (PES_IMM, &B);
break;
case PRAGMA_WRITABLE_STRINGS:
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings);
break;
case PRAGMA_ZPSYM:
/* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, MakeZPSym);
break;
default:
Internal ("Invalid pragma");
}
/* Closing paren expected */
SB_SkipWhite (&B);
if (SB_Get (&B) != ')') {
Error ("')' expected");
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 */
if (SB_Peek (&B) != '\0') {
Error ("Unexpected input following pragma directive");
}
ExitPoint:
/* Release the string buffers */
SB_Done (&B);
SB_Done (&Ident);
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void ConsumePragma (void)
/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma()
** operator.
*/
{
/* Skip the _Pragma token */
NextToken ();
/* Prevent from translating string literals in _Pragma */
++InPragmaParser;
/* We expect an opening paren */
if (!ConsumeLParen ()) {
--InPragmaParser;
return;
}
/* String literal */
if (CurTok.Tok != TOK_SCONST) {
/* Print a diagnostic */
Error ("String literal expected");
/* Try some smart error recovery: Skip tokens until we reach the
** enclosing paren, or a semicolon.
*/
PragmaErrorSkip ();
} else {
/* Parse the pragma */
ParsePragmaString ();
}
--InPragmaParser;
/* Closing paren needed */
ConsumeRParen ();
}