1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-26 05:29:30 +00:00

Merge pull request #2226 from acqn/PragmaFix

[cc65] Pragma fixes
This commit is contained in:
Bob Andrews 2023-10-16 16:57:09 +02:00 committed by GitHub
commit 77c6a6a693
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 506 additions and 186 deletions

View File

@ -837,21 +837,21 @@ This cc65 version has some extensions to the ISO C standard.
<itemize>
<item> The compiler allows to insert assembler expressions into the output
file. The syntax is
<item> The compiler allows to insert inline assembler code in the form of the
<tt/asm/ expression into the output file. The syntax is
<tscreen><verb>
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
asm [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
or
<tscreen><verb>
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
The first form is in the user namespace; and, is disabled if the <tt/-A/
switch is given.
There is a whole section covering inline assembler expressions,
There is a whole section covering the inline assembler,
<ref id="inline-asm" name="see there">.
<p>
@ -1008,6 +1008,13 @@ This cc65 version has some extensions to the ISO C standard.
<tt/_Static_assert/ is also available as the macro <tt/static_assert/ in
<tt/assert.h/.
Note: The string literal as the message in the <tt/_Static_assert/
declaration is not subject to string literal translation (see
<tt/<ref id="pragma-charmap" name="#pragma&nbsp;charmap()">/) and will
always be in the host encoding. On the other hand, any character or
string literals present in the condition expression of the
<tt/_Static_assert/ declaration will be translated as usual.
<item> cc65 supports bit-fields of any integral type that is int-sized or
smaller, and enumerated types with those types as their underlying
type. (Only <tt/int/, <tt/signed int/, and <tt/unsigned int/ are
@ -1317,7 +1324,9 @@ parameter with the <tt/#pragma/.
<sect1><tt>#pragma charmap (&lt;index&gt;, &lt;code&gt;)</tt><label id="pragma-charmap"><p>
Each literal string and each literal character in the source is translated
Each literal string and each literal character in the preprocessed source,
except when used in an <tt/asm/ expression as the inline assembler code or
in a <tt/_Static_assert/ declaration as the failure message, is translated
by use of a translation table. That translation table is preset when the
compiler is started, depending on the target system; for example, to map
ISO-8859-1 characters into PETSCII if the target is a Commodore machine.
@ -1714,23 +1723,23 @@ bloated code and a slowdown.
<sect>Inline assembler<label id="inline-asm"><p>
The compiler allows to insert assembler expressions into the output file. The
syntax is
The compiler allows to insert inline assembler code in the form of the <tt/asm/
expression into the output file. The syntax is
<tscreen><verb>
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
asm [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
or
<tscreen><verb>
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ;
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen>
<p>
The first form is in the user namespace; and, is disabled by <tt><ref
id="option--standard" name="--standard"></tt> if the argument is not <tt/cc65/.
The <tt/asm/ expression can be used only inside a function. Please note that
the result of an inline assembler expression is always of type <tt/void/.
The <tt/asm/ expression can be used only inside a function. The result of an
<tt/asm/ expression is always of type <tt/void/.
The contents of the string literal are preparsed by the compiler; and, inserted
into the generated assembly output, so that it can be processed further by
@ -1757,6 +1766,13 @@ The string literal may contain format specifiers from the following list. For
each format specifier, an argument is expected which is inserted instead of
the format specifier, before passing the assembly code line to the backend.
Note: The string literal as the inline assembler code itself in the <tt/asm/
expression is not subject to string literal translation (see
<tt/<ref id="pragma-charmap" name="#pragma&nbsp;charmap()">/) and will always
be in the host encoding. On the other hand, all character and string literals
as the arguments for replacing the format specifiers will be translated as
usual.
<itemize>
<item><tt/%b/ - Numerical 8-bit value
<item><tt/%w/ - Numerical 16-bit value

View File

@ -417,6 +417,9 @@ void AsmStatement (void)
** a string literal in parenthesis.
*/
{
/* Prevent from translating the inline code string literal in asm */
NoCharMap = 1;
/* Skip the ASM */
NextToken ();
@ -431,9 +434,15 @@ void AsmStatement (void)
/* Need left parenthesis */
if (!ConsumeLParen ()) {
NoCharMap = 0;
return;
}
/* We have got the inline code string untranslated, now reenable string
** literal translation for string arguments (if any).
*/
NoCharMap = 0;
/* String literal */
if (CurTok.Tok != TOK_SCONST) {

View File

@ -111,12 +111,6 @@ static void Parse (void)
continue;
}
/* Check for a #pragma */
if (CurTok.Tok == TOK_PRAGMA) {
DoPragma ();
continue;
}
/* Check for a _Static_assert */
if (CurTok.Tok == TOK_STATIC_ASSERT) {
ParseStaticAssert ();

View File

@ -1336,8 +1336,6 @@ static void Primary (ExprDesc* E)
/* String literal */
if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
E->V.LVal = UseLiteral (CurTok.SVal);
/* Translate into target charset */
TranslateLiteral (E->V.LVal);
} else {
E->V.LVal = CurTok.SVal;
}

View File

@ -245,9 +245,6 @@ static void DefineBitFieldData (StructInitData* SI)
static void DefineStrData (Literal* Lit, unsigned Count)
{
/* Translate into target charset */
TranslateLiteral (Lit);
/* Output the data */
g_defbytes (GetLiteralStr (Lit), Count);
}

View File

@ -160,13 +160,24 @@ void ReleaseLiteral (Literal* L)
void TranslateLiteral (Literal* L)
/* Translate a literal into the target charset. */
/* Translate a literal into the target charset */
{
TgtTranslateBuf (SB_GetBuf (&L->Data), SB_GetLen (&L->Data));
}
void ConcatLiteral (Literal* L, const Literal* Appended)
/* Concatenate string literals */
{
if (SB_GetLen (&L->Data) > 0 && SB_LookAtLast (&L->Data) == '\0') {
SB_Drop (&L->Data, 1);
}
SB_Append (&L->Data, &Appended->Data);
}
unsigned GetLiteralLabel (const Literal* L)
/* Return the asm label for a literal */
{

View File

@ -75,7 +75,10 @@ void ReleaseLiteral (Literal* L);
/* Decrement the reference counter for the literal */
void TranslateLiteral (Literal* L);
/* Translate a literal into the target charset. */
/* Translate a literal into the target charset */
void ConcatLiteral (Literal* L, const Literal* Appended);
/* Concatenate string literals */
unsigned GetLiteralLabel (const Literal* L);
/* Return the asm label for a literal */

View File

@ -41,12 +41,11 @@
#include "chartype.h"
#include "segnames.h"
#include "tgttrans.h"
#include "xmalloc.h"
/* cc65 */
#include "codegen.h"
#include "error.h"
#include "expr.h"
#include "funcdesc.h"
#include "global.h"
#include "litpool.h"
#include "scanner.h"
@ -58,7 +57,7 @@
/*****************************************************************************/
/* data */
/* Data */
/*****************************************************************************/
@ -142,6 +141,21 @@ typedef enum {
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;
/*****************************************************************************/
@ -339,7 +353,7 @@ static void PushInt (IntStack* S, long Val)
static int BoolKeyword (StrBuf* Ident)
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.
*/
@ -364,17 +378,92 @@ static int BoolKeyword (StrBuf* Ident)
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 (StrBuf* B, void (*Func) (const char*))
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 */
@ -387,14 +476,17 @@ static void StringPragma (StrBuf* B, void (*Func) (const char*))
static void SegNamePragma (StrBuf* B, segment_t Seg)
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 Push = 0;
int PushPop = 0;
/* Unused at the moment */
(void)Scope;
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
@ -403,19 +495,12 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
break;
case PP_PUSH:
Push = 1;
PushPop = 1;
break;
case PP_POP:
/* Pop the old value and output it */
PopSegName (Seg);
/* 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);
}
ApplySegNamePragma (Token, -1, 0, 0);
/* Done */
goto ExitPoint;
@ -454,27 +539,14 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
/* Get the address size for the segment */
AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A));
/* Set the address size for the segment if valid */
if (AddrSize != ADDR_SIZE_INVALID) {
SetSegAddrSize (Name, AddrSize);
} else {
Warning ("Invalid address size for segment!");
/* 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 */
if (Push) {
PushSegName (Seg, Name);
} else {
SetSegName (Seg, Name);
}
/* 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);
}
ApplySegNamePragma (Token, PushPop, Name, AddrSize);
} else {
@ -484,13 +556,15 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
}
ExitPoint:
/* Call the string buf destructor */
SB_Done (&S);
SB_Done (&A);
}
static void WrappedCallPragma (StrBuf* B)
static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B)
/* Handle the wrapped-call pragma */
{
StrBuf S = AUTO_STRBUF_INITIALIZER;
@ -498,6 +572,9 @@ static void WrappedCallPragma (StrBuf* B)
long Val;
SymEntry *Entry;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) {
@ -573,11 +650,14 @@ ExitPoint:
static void CharMapPragma (StrBuf* B)
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;
@ -619,7 +699,7 @@ static void CharMapPragma (StrBuf* B)
static void WarnPragma (StrBuf* B)
static void WarnPragma (pragma_scope_t Scope, StrBuf* B)
/* Enable/disable warnings */
{
long Val;
@ -627,6 +707,10 @@ static void WarnPragma (StrBuf* B)
/* A warning name must follow */
IntStack* S = GetWarning (B);
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
if (S == 0) {
return;
}
@ -680,48 +764,47 @@ static void WarnPragma (StrBuf* B)
static void FlagPragma (StrBuf* B, IntStack* Stack)
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 Push;
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) {
PopInt (Stack);
/* 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) {
Push = 1;
PushPop = 1;
if (!GetComma (B)) {
goto ExitPoint;
}
IsIdent = SB_GetSym (B, &Ident, 0);
} else {
Push = 0;
}
/* Boolean argument follows */
if (IsIdent) {
Val = BoolKeyword (&Ident);
Val = IsBoolKeyword (&Ident);
} else if (!GetNumber (B, &Val)) {
goto ExitPoint;
}
/* Set/push the new value */
if (Push) {
PushInt (Stack, Val);
} else {
IS_Set (Stack, Val);
}
/* Add this pragma and apply it whenever appropriately */
ApplyPragma (PushPop, Stack, Val);
ExitPoint:
/* Free the identifier */
@ -730,12 +813,16 @@ ExitPoint:
static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
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)) {
@ -749,7 +836,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
case PP_POP:
/* Pop the old value and bail out */
PopInt (Stack);
ApplyPragma (-1, Stack, 0);
return;
case PP_ERROR:
@ -772,31 +859,32 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
return;
}
/* Set/push the new value */
if (Push) {
PushInt (Stack, Val);
} else {
IS_Set (Stack, Val);
}
/* Add this pragma and apply it whenever appropriately */
ApplyPragma (Push, Stack, Val);
}
static void MakeMessage (const char* Message)
static void NoteMessagePragma (const char* Message)
/* Wrapper for printf-like Note() function protected from user-provided format
** specifiers.
*/
{
Note ("%s", Message);
}
static void ParsePragma (void)
/* Parse the contents of the _Pragma statement */
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 */
@ -837,111 +925,130 @@ static void ParsePragma (void)
switch (Pragma) {
case PRAGMA_ALIGN:
IntPragma (&B, &DataAlignment, 1, 4096);
/* TODO: PES_EXPR (PES_DECL) */
IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096);
break;
case PRAGMA_ALLOW_EAGER_INLINE:
FlagPragma (&B, &EagerlyInlineFuncs);
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:
SegNamePragma (&B, SEG_BSS);
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B);
break;
case PRAGMA_CHARMAP:
CharMapPragma (&B);
CharMapPragma (PES_IMM, &B);
break;
case PRAGMA_CHECKSTACK:
Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
/* FALLTHROUGH */
case PRAGMA_CHECK_STACK:
FlagPragma (&B, &CheckStack);
/* 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:
SegNamePragma (&B, SEG_CODE);
/* PES_FUNC is the only sensible option so far */
SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B);
break;
case PRAGMA_CODESIZE:
IntPragma (&B, &CodeSizeFactor, 10, 1000);
/* 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:
SegNamePragma (&B, SEG_DATA);
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B);
break;
case PRAGMA_INLINE_STDFUNCS:
FlagPragma (&B, &InlineStdFuncs);
/* TODO: PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs);
break;
case PRAGMA_LOCAL_STRINGS:
FlagPragma (&B, &LocalStrings);
/* TODO: PES_STMT or even PES_EXPR */
FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings);
break;
case PRAGMA_MESSAGE:
StringPragma (&B, MakeMessage);
/* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, NoteMessagePragma);
break;
case PRAGMA_OPTIMIZE:
FlagPragma (&B, &Optimize);
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &Optimize);
break;
case PRAGMA_REGVARADDR:
FlagPragma (&B, &AllowRegVarAddr);
/* 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:
FlagPragma (&B, &EnableRegVars);
/* 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:
SegNamePragma (&B, SEG_RODATA);
/* 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:
FlagPragma (&B, &SignedChars);
/* 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:
FlagPragma (&B, &StaticLocals);
/* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals);
break;
case PRAGMA_WRAPPED_CALL:
WrappedCallPragma(&B);
/* PES_IMM is the only sensible option */
WrappedCallPragma (PES_IMM, &B);
break;
case PRAGMA_WARN:
WarnPragma (&B);
/* PES_IMM is the only sensible option */
WarnPragma (PES_IMM, &B);
break;
case PRAGMA_WRITABLE_STRINGS:
FlagPragma (&B, &WritableStrings);
/* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings);
break;
case PRAGMA_ZPSYM:
StringPragma (&B, MakeZPSym);
/* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, MakeZPSym);
break;
default:
@ -975,20 +1082,31 @@ ExitPoint:
void DoPragma (void)
/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void ConsumePragma (void)
/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma()
** operator.
*/
{
/* Skip the token itself */
/* 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");
@ -996,13 +1114,13 @@ void DoPragma (void)
** enclosing paren, or a semicolon.
*/
PragmaErrorSkip ();
} else {
/* Parse the _Pragma statement */
ParsePragma ();
/* Parse the pragma */
ParsePragmaString ();
}
--InPragmaParser;
/* Closing paren needed */
ConsumeRParen ();
}

View File

@ -44,8 +44,10 @@
void DoPragma (void);
/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */
void ConsumePragma (void);
/* Parse a pragma. The pragma comes always in the form of the new C99 _Pragma()
** operator.
*/

View File

@ -56,6 +56,7 @@
#include "ident.h"
#include "input.h"
#include "litpool.h"
#include "pragma.h"
#include "preproc.h"
#include "scanner.h"
#include "standard.h"
@ -69,9 +70,12 @@
Token CurTok; /* The current token */
Token NextTok; /* The next token */
int PPParserRunning; /* Is tokenizer used by the preprocessor */
static Token SavedTok; /* Saved token */
Token CurTok; /* The current token */
Token NextTok; /* The next token */
int PPParserRunning; /* Is tokenizer used by the preprocessor */
int NoCharMap; /* Disable literal translation */
unsigned InPragmaParser; /* Depth of pragma parser calling */
@ -323,7 +327,7 @@ static void SetTok (int tok)
static int ParseChar (void)
/* Parse a character. Converts escape chars into character codes. */
/* Parse a character token. Converts escape chars into character codes. */
{
int C;
int HadError;
@ -425,7 +429,7 @@ static int ParseChar (void)
static void CharConst (void)
/* Parse a character constant. */
/* Parse a character constant token */
{
int C;
@ -453,7 +457,7 @@ static void CharConst (void)
}
/* Translate into target charset */
NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
NextTok.IVal = SignExtendChar (C);
/* Character constants have type int */
NextTok.Type = type_int;
@ -462,7 +466,7 @@ static void CharConst (void)
static void StringConst (void)
/* Parse a quoted string */
/* Parse a quoted string token */
{
/* String buffer */
StrBuf S = AUTO_STRBUF_INITIALIZER;
@ -470,43 +474,34 @@ static void StringConst (void)
/* Assume next token is a string constant */
NextTok.Tok = TOK_SCONST;
/* Concatenate strings. If at least one of the concenated strings is a wide
** character literal, the whole string is a wide char literal, otherwise
** it's a normal string literal.
*/
while (1) {
/* Check if this is a normal or a wide char string */
if (CurC == 'L' && NextC == '\"') {
/* Wide character literal */
NextTok.Tok = TOK_WCSCONST;
NextChar ();
NextChar ();
} else if (CurC == '\"') {
/* Skip the quote char */
NextChar ();
} else {
/* No string */
goto ExitPoint;
}
/* Check if this is a normal or a wide char string */
if (CurC == 'L' && NextC == '\"') {
/* Wide character literal */
NextTok.Tok = TOK_WCSCONST;
NextChar ();
NextChar ();
} else if (CurC == '\"') {
/* Skip the quote char */
NextChar ();
} else {
/* No string */
/* Read until end of string */
while (CurC != '\"') {
if (CurC == '\0') {
Error ("Unexpected newline");
break;
}
/* Read until end of string */
while (CurC != '\"') {
if (CurC == '\0') {
Error ("Unexpected newline");
break;
}
SB_AppendChar (&S, ParseChar ());
}
/* Skip closing quote char if there was one */
NextChar ();
/* Skip white space, read new input */
SkipWhite ();
SB_AppendChar (&S, ParseChar ());
}
/* Skip closing quote char if there was one */
NextChar ();
ExitPoint:
/* Terminate the string */
SB_AppendChar (&S, '\0');
@ -520,7 +515,7 @@ static void StringConst (void)
static void NumericConst (void)
/* Parse a numeric constant */
/* Parse a numeric constant token */
{
unsigned Base; /* Temporary number base according to prefix */
unsigned Index;
@ -800,39 +795,49 @@ static void NumericConst (void)
void NextToken (void)
static void GetNextInputToken (void)
/* Get next token from input stream */
{
ident token;
/* We have to skip white space here before shifting tokens, since the
** tokens and the current line info is invalid at startup and will get
** initialized by reading the first time from the file. Remember if
** we were at end of input and handle that later.
*/
int GotEOF = (SkipWhite() == 0);
if (!NoCharMap && !InPragmaParser) {
/* Translate string and character literals into target charset */
if (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST) {
TranslateLiteral (NextTok.SVal);
} else if (NextTok.Tok == TOK_CCONST || NextTok.Tok == TOK_WCCONST) {
NextTok.IVal = SignExtendChar (TgtTranslateChar (NextTok.IVal));
}
}
/* Current token is the lookahead token */
if (CurTok.LI) {
ReleaseLineInfo (CurTok.LI);
}
/* Get the current token */
CurTok = NextTok;
/* When reading the first time from the file, the line info in NextTok,
** which was copied to CurTok is invalid. Since the information from
** the token is used for error messages, we must make it valid.
*/
if (CurTok.LI == 0) {
CurTok.LI = UseLineInfo (GetCurLineInfo ());
}
if (SavedTok.Tok == TOK_INVALID) {
/* We have to skip white space here before shifting tokens, since the
** tokens and the current line info is invalid at startup and will get
** initialized by reading the first time from the file. Remember if we
** were at end of input and handle that later.
*/
int GotEOF = (SkipWhite () == 0);
/* Remember the starting position of the next token */
NextTok.LI = UseLineInfo (GetCurLineInfo ());
/* Remember the starting position of the next token */
NextTok.LI = UseLineInfo (GetCurLineInfo ());
/* Now handle end of input. */
if (GotEOF) {
/* End of file reached */
NextTok.Tok = TOK_CEOF;
/* Now handle end of input */
if (GotEOF) {
/* End of file reached */
NextTok.Tok = TOK_CEOF;
return;
}
} else {
/* Just use the saved token */
NextTok = SavedTok;
SavedTok.Tok = TOK_INVALID;
return;
}
@ -859,7 +864,8 @@ void NextToken (void)
if (!PPParserRunning) {
/* Check for a keyword */
if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
NextTok.Tok = FindKey (token);
if (NextTok.Tok != TOK_IDENT) {
/* Reserved word found */
return;
}
@ -1117,7 +1123,90 @@ void NextToken (void)
UnknownChar (CurC);
}
}
void NextToken (void)
/* Get next non-pragma token from input stream consuming any pragmas
** encountered. Adjacent string literal tokens will be concatenated.
*/
{
/* Used for string literal concatenation */
Token PrevTok;
/* When reading the first time from the file, the line info in NextTok,
** which will be copied to CurTok is invalid. Since the information from
** the token is used for error messages, we must make it valid.
*/
if (NextTok.LI == 0) {
NextTok.LI = UseLineInfo (GetCurLineInfo ());
}
PrevTok.Tok = TOK_INVALID;
while (1) {
/* Read the next token from the file */
GetNextInputToken ();
/* Consume all pragmas at hand, including those nested in a _Pragma() */
if (CurTok.Tok == TOK_PRAGMA) {
/* Repeated and/or nested _Pragma()'s will be handled recursively */
ConsumePragma ();
}
/* Check for string concatenation */
if (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST) {
if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) {
/* Warn on non-ISO behavior */
if (InPragmaParser) {
Warning ("Concatenated string literals in _Pragma operation");
}
/* Concatenate strings */
ConcatLiteral (PrevTok.SVal, CurTok.SVal);
/* If at least one of the concatenated strings is a wide
** character literal, the whole string is a wide char
** literal, otherwise it is a normal string literal.
*/
if (CurTok.Tok == TOK_WCSCONST) {
PrevTok.Tok = TOK_WCSCONST;
PrevTok.Type = CurTok.Type;
}
}
if (NextTok.Tok == TOK_SCONST ||
NextTok.Tok == TOK_WCSCONST ||
NextTok.Tok == TOK_PRAGMA) {
/* Remember current string literal token */
if (PrevTok.Tok == TOK_INVALID) {
PrevTok = CurTok;
PrevTok.LI = UseLineInfo (PrevTok.LI);
}
/* Keep looping */
continue;
}
}
break;
}
/* Use the concatenated string literal token if there is one */
if (PrevTok.Tok == TOK_SCONST || PrevTok.Tok == TOK_WCSCONST) {
if (CurTok.Tok != TOK_SCONST && CurTok.Tok != TOK_WCSCONST) {
/* Push back the incoming tokens */
SavedTok = NextTok;
NextTok = CurTok;
} else {
/* The last string literal token can be just replaced */
if (CurTok.LI) {
ReleaseLineInfo (CurTok.LI);
}
}
/* Replace the current token with the concatenated string literal */
CurTok = PrevTok;
}
}

View File

@ -219,9 +219,11 @@ struct Token {
const Type* Type; /* Type if integer or float constant */
};
extern Token CurTok; /* The current token */
extern Token NextTok; /* The next token */
extern int PPParserRunning; /* Is tokenizer used by the preprocessor */
extern Token CurTok; /* The current token */
extern Token NextTok; /* The next token */
extern int PPParserRunning; /* Is tokenizer used by the preprocessor */
extern int NoCharMap; /* Disable literal translation */
extern unsigned InPragmaParser; /* Depth of pragma parser calling */
@ -299,7 +301,9 @@ void CopyPPNumber (StrBuf* Target);
/* Copy a pp-number from the input to Target */
void NextToken (void);
/* Get next token from input stream */
/* Get next non-pragma token from input stream consuming any pragmas
** encountered. Adjacent string literal tokens will be concatenated.
*/
void SkipTokens (const token_t* TokenList, unsigned TokenCount);
/* Skip tokens until we reach TOK_CEOF or a token in the given token list.

View File

@ -72,9 +72,15 @@ void ParseStaticAssert ()
** support the C2X syntax with only an expression.
*/
if (CurTok.Tok == TOK_COMMA) {
/* Skip the comma. */
/* Prevent from translating the message string literal */
NoCharMap = 1;
/* Skip the comma and get the next token */
NextToken ();
/* Reenable string literal translation */
NoCharMap = 0;
/* String literal */
if (CurTok.Tok != TOK_SCONST) {
Error ("String literal expected for static_assert message");

View File

@ -735,10 +735,6 @@ int AnyStatement (int* PendingToken)
GotBreak = 1;
break;
case TOK_PRAGMA:
DoPragma ();
break;
case TOK_SEMI:
/* Empty statement. Ignore it */
CheckSemi (PendingToken);

77
test/val/bug2151.c Normal file
View File

@ -0,0 +1,77 @@
/* Bug #2151 - #pragma causes errors when used within functions */
#include <stdio.h>
#include <string.h>
#pragma charmap(0x61, 0x61)
_Static_assert('A'==
#pragma charmap(0x61, 0x41)
'a'
#pragma charmap(0x61, 0x42)
,
#pragma charmap(0x61, 0x61)
"charmap failed");
char str[] =
"a"
#pragma charmap(0x61, 0x42)
"a"
#pragma charmap(0x61, 0x43)
"a"
#pragma charmap(0x61, 0x61)
;
unsigned failures;
#pragma bss-name("BSS1")
int
#pragma code-name("CODE_WUT")
main _Pragma
#pragma charmap(0x61, 0x32)
(
"message(\"_Pragma string"
/* Concatenated string literals in _Pragma is a cc65 extension */
" unaffected by charmap\")"
)
#pragma charmap(0x61, 0x61)
(
void
_Pragma _Pragma (
#pragma message("nested message 1")
"message(\"nested message 2\")"
)
(
"message(\"_Pragma in function parentheses\")")
#pragma code-name("CODE")
)
#pragma bss-name("BSS")
{
extern int y;
#pragma bss-name("BSS2")
static
#pragma zpsym ("y")
int x; // TODO: currently in "BSS", but supposed to be in "BSS2"?
x = 0;
if (memcmp(str, "aBC", 3))
{
++failures;
printf("%3s\n", str);
}
if (x + y != 0)
{
++failures;
printf("%d\n", x + y);
}
if (failures != 0)
{
printf("faiures: %d\n", failures);
}
return failures;
#pragma bss-name("BSS")
}
int y;