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
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> <itemize>
<item> The compiler allows to insert assembler expressions into the output <item> The compiler allows to insert inline assembler code in the form of the
file. The syntax is <tt/asm/ expression into the output file. The syntax is
<tscreen><verb> <tscreen><verb>
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ; asm [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen> </verb></tscreen>
or or
<tscreen><verb> <tscreen><verb>
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ; __asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen> </verb></tscreen>
The first form is in the user namespace; and, is disabled if the <tt/-A/ The first form is in the user namespace; and, is disabled if the <tt/-A/
switch is given. 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">. <ref id="inline-asm" name="see there">.
<p> <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/_Static_assert/ is also available as the macro <tt/static_assert/ in
<tt/assert.h/. <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 <item> cc65 supports bit-fields of any integral type that is int-sized or
smaller, and enumerated types with those types as their underlying smaller, and enumerated types with those types as their underlying
type. (Only <tt/int/, <tt/signed int/, and <tt/unsigned int/ are 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> <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 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 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. 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> <sect>Inline assembler<label id="inline-asm"><p>
The compiler allows to insert assembler expressions into the output file. The The compiler allows to insert inline assembler code in the form of the <tt/asm/
syntax is expression into the output file. The syntax is
<tscreen><verb> <tscreen><verb>
asm [optional volatile] (&lt;string literal&gt;[, optional parameters]) ; asm [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen> </verb></tscreen>
or or
<tscreen><verb> <tscreen><verb>
__asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters]) ; __asm__ [optional volatile] (&lt;string literal&gt;[, optional parameters])
</verb></tscreen> </verb></tscreen>
<p> <p>
The first form is in the user namespace; and, is disabled by <tt><ref 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/. 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 <tt/asm/ expression can be used only inside a function. The result of an
the result of an inline assembler expression is always of type <tt/void/. <tt/asm/ expression is always of type <tt/void/.
The contents of the string literal are preparsed by the compiler; and, inserted 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 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 each format specifier, an argument is expected which is inserted instead of
the format specifier, before passing the assembly code line to the backend. 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> <itemize>
<item><tt/%b/ - Numerical 8-bit value <item><tt/%b/ - Numerical 8-bit value
<item><tt/%w/ - Numerical 16-bit value <item><tt/%w/ - Numerical 16-bit value

View File

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

View File

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

View File

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

View File

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

View File

@@ -160,13 +160,24 @@ void ReleaseLiteral (Literal* L)
void TranslateLiteral (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)); 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) unsigned GetLiteralLabel (const Literal* L)
/* Return the asm label for a literal */ /* Return the asm label for a literal */
{ {

View File

@@ -75,7 +75,10 @@ void ReleaseLiteral (Literal* L);
/* Decrement the reference counter for the literal */ /* Decrement the reference counter for the literal */
void TranslateLiteral (Literal* L); 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); unsigned GetLiteralLabel (const Literal* L);
/* Return the asm label for a literal */ /* Return the asm label for a literal */

View File

@@ -41,12 +41,11 @@
#include "chartype.h" #include "chartype.h"
#include "segnames.h" #include "segnames.h"
#include "tgttrans.h" #include "tgttrans.h"
#include "xmalloc.h"
/* cc65 */ /* cc65 */
#include "codegen.h" #include "codegen.h"
#include "error.h" #include "error.h"
#include "expr.h"
#include "funcdesc.h"
#include "global.h" #include "global.h"
#include "litpool.h" #include "litpool.h"
#include "scanner.h" #include "scanner.h"
@@ -58,7 +57,7 @@
/*****************************************************************************/ /*****************************************************************************/
/* data */ /* Data */
/*****************************************************************************/ /*****************************************************************************/
@@ -142,6 +141,21 @@ typedef enum {
PP_ERROR, PP_ERROR,
} PushPopResult; } 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 /* Check if the identifier in Ident is a keyword for a boolean value. Currently
** accepted are true/false/on/off. ** 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 */ /* 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 */ /* Handle a pragma that expects a string parameter */
{ {
StrBuf S = AUTO_STRBUF_INITIALIZER; StrBuf S = AUTO_STRBUF_INITIALIZER;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* We expect a string here */ /* We expect a string here */
if (GetString (B, &S)) { if (GetString (B, &S)) {
/* Call the given function with the string argument */ /* 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 */ /* Handle a pragma that expects a segment name parameter */
{ {
const char* Name; const char* Name;
unsigned char AddrSize = ADDR_SIZE_INVALID; unsigned char AddrSize = ADDR_SIZE_INVALID;
StrBuf S = AUTO_STRBUF_INITIALIZER; StrBuf S = AUTO_STRBUF_INITIALIZER;
StrBuf A = 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 */ /* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) { switch (ParsePushPop (B)) {
@@ -403,19 +495,12 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
break; break;
case PP_PUSH: case PP_PUSH:
Push = 1; PushPop = 1;
break; break;
case PP_POP: case PP_POP:
/* Pop the old value and output it */ /* Pop the old value and output it */
PopSegName (Seg); ApplySegNamePragma (Token, -1, 0, 0);
/* 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);
}
/* Done */ /* Done */
goto ExitPoint; goto ExitPoint;
@@ -454,27 +539,14 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
/* Get the address size for the segment */ /* Get the address size for the segment */
AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A)); AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A));
/* Set the address size for the segment if valid */ /* Check the address size for the segment */
if (AddrSize != ADDR_SIZE_INVALID) { if (AddrSize == ADDR_SIZE_INVALID) {
SetSegAddrSize (Name, AddrSize); Warning ("Invalid address size for segment");
} else {
Warning ("Invalid address size for segment!");
} }
} }
/* Set the new name and optionally address size */ /* Set the new name and optionally address size */
if (Push) { ApplySegNamePragma (Token, PushPop, Name, AddrSize);
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);
}
} else { } else {
@@ -484,13 +556,15 @@ static void SegNamePragma (StrBuf* B, segment_t Seg)
} }
ExitPoint: ExitPoint:
/* Call the string buf destructor */ /* Call the string buf destructor */
SB_Done (&S); SB_Done (&S);
SB_Done (&A); SB_Done (&A);
} }
static void WrappedCallPragma (StrBuf* B)
static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B)
/* Handle the wrapped-call pragma */ /* Handle the wrapped-call pragma */
{ {
StrBuf S = AUTO_STRBUF_INITIALIZER; StrBuf S = AUTO_STRBUF_INITIALIZER;
@@ -498,6 +572,9 @@ static void WrappedCallPragma (StrBuf* B)
long Val; long Val;
SymEntry *Entry; SymEntry *Entry;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* Check for the "push" or "pop" keywords */ /* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) { 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 */ /* Change the character map */
{ {
long Index, C; long Index, C;
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
/* Read the character index */ /* Read the character index */
if (!GetNumber (B, &Index)) { if (!GetNumber (B, &Index)) {
return; 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 */ /* Enable/disable warnings */
{ {
long Val; long Val;
@@ -627,6 +707,10 @@ static void WarnPragma (StrBuf* B)
/* A warning name must follow */ /* A warning name must follow */
IntStack* S = GetWarning (B); IntStack* S = GetWarning (B);
/* Only PES_IMM is supported */
CHECK (Scope == PES_IMM);
if (S == 0) { if (S == 0) {
return; 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 */ /* Handle a pragma that expects a boolean parameter */
{ {
StrBuf Ident = AUTO_STRBUF_INITIALIZER; StrBuf Ident = AUTO_STRBUF_INITIALIZER;
long Val; long Val;
int Push; int PushPop = 0;
/* Unused at the moment */
(void)Scope;
(void)Token;
/* Try to read an identifier */ /* Try to read an identifier */
int IsIdent = SB_GetSym (B, &Ident, 0); int IsIdent = SB_GetSym (B, &Ident, 0);
/* Check if we have a first argument named "pop" */ /* Check if we have a first argument named "pop" */
if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) { if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
PopInt (Stack); /* Pop the old value and bail out */
ApplyPragma (-1, Stack, 0);
/* No other arguments allowed */ /* No other arguments allowed */
return; return;
} }
/* Check if we have a first argument named "push" */ /* Check if we have a first argument named "push" */
if (IsIdent && SB_CompareStr (&Ident, "push") == 0) { if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
Push = 1; PushPop = 1;
if (!GetComma (B)) { if (!GetComma (B)) {
goto ExitPoint; goto ExitPoint;
} }
IsIdent = SB_GetSym (B, &Ident, 0); IsIdent = SB_GetSym (B, &Ident, 0);
} else {
Push = 0;
} }
/* Boolean argument follows */ /* Boolean argument follows */
if (IsIdent) { if (IsIdent) {
Val = BoolKeyword (&Ident); Val = IsBoolKeyword (&Ident);
} else if (!GetNumber (B, &Val)) { } else if (!GetNumber (B, &Val)) {
goto ExitPoint; goto ExitPoint;
} }
/* Set/push the new value */ /* Add this pragma and apply it whenever appropriately */
if (Push) { ApplyPragma (PushPop, Stack, Val);
PushInt (Stack, Val);
} else {
IS_Set (Stack, Val);
}
ExitPoint: ExitPoint:
/* Free the identifier */ /* 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 */ /* Handle a pragma that expects an int parameter */
{ {
long Val; long Val;
int Push; int Push;
/* Unused at the moment */
(void)Scope;
(void)Token;
/* Check for the "push" or "pop" keywords */ /* Check for the "push" or "pop" keywords */
switch (ParsePushPop (B)) { switch (ParsePushPop (B)) {
@@ -749,7 +836,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
case PP_POP: case PP_POP:
/* Pop the old value and bail out */ /* Pop the old value and bail out */
PopInt (Stack); ApplyPragma (-1, Stack, 0);
return; return;
case PP_ERROR: case PP_ERROR:
@@ -772,31 +859,32 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
return; return;
} }
/* Set/push the new value */ /* Add this pragma and apply it whenever appropriately */
if (Push) { ApplyPragma (Push, Stack, Val);
PushInt (Stack, Val);
} else {
IS_Set (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); Note ("%s", Message);
} }
static void ParsePragma (void) static void ParsePragmaString (void)
/* Parse the contents of the _Pragma statement */ /* Parse the contents of _Pragma */
{ {
pragma_t Pragma; pragma_t Pragma;
StrBuf Ident = AUTO_STRBUF_INITIALIZER; StrBuf Ident = AUTO_STRBUF_INITIALIZER;
/* Create a string buffer from the string literal */ /* Create a string buffer from the string literal */
StrBuf B = AUTO_STRBUF_INITIALIZER; StrBuf B = AUTO_STRBUF_INITIALIZER;
SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); SB_Append (&B, GetLiteralStrBuf (CurTok.SVal));
/* Skip the string token */ /* Skip the string token */
@@ -837,111 +925,130 @@ static void ParsePragma (void)
switch (Pragma) { switch (Pragma) {
case PRAGMA_ALIGN: case PRAGMA_ALIGN:
IntPragma (&B, &DataAlignment, 1, 4096); /* TODO: PES_EXPR (PES_DECL) */
IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096);
break; break;
case PRAGMA_ALLOW_EAGER_INLINE: case PRAGMA_ALLOW_EAGER_INLINE:
FlagPragma (&B, &EagerlyInlineFuncs); FlagPragma (PES_STMT, Pragma, &B, &EagerlyInlineFuncs);
break; break;
case PRAGMA_BSSSEG: case PRAGMA_BSSSEG:
Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead"); Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_BSS_NAME: 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; break;
case PRAGMA_CHARMAP: case PRAGMA_CHARMAP:
CharMapPragma (&B); CharMapPragma (PES_IMM, &B);
break; break;
case PRAGMA_CHECKSTACK: case PRAGMA_CHECKSTACK:
Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead"); Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_CHECK_STACK: case PRAGMA_CHECK_STACK:
FlagPragma (&B, &CheckStack); /* TODO: PES_SCOPE maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &CheckStack);
break; break;
case PRAGMA_CODESEG: case PRAGMA_CODESEG:
Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead"); Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_CODE_NAME: 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; break;
case PRAGMA_CODESIZE: case PRAGMA_CODESIZE:
IntPragma (&B, &CodeSizeFactor, 10, 1000); /* PES_EXPR would be optimization nightmare */
IntPragma (PES_STMT, Pragma, &B, &CodeSizeFactor, 10, 1000);
break; break;
case PRAGMA_DATASEG: case PRAGMA_DATASEG:
Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead"); Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_DATA_NAME: 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; break;
case PRAGMA_INLINE_STDFUNCS: case PRAGMA_INLINE_STDFUNCS:
FlagPragma (&B, &InlineStdFuncs); /* TODO: PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs);
break; break;
case PRAGMA_LOCAL_STRINGS: case PRAGMA_LOCAL_STRINGS:
FlagPragma (&B, &LocalStrings); /* TODO: PES_STMT or even PES_EXPR */
FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings);
break; break;
case PRAGMA_MESSAGE: case PRAGMA_MESSAGE:
StringPragma (&B, MakeMessage); /* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, NoteMessagePragma);
break; break;
case PRAGMA_OPTIMIZE: case PRAGMA_OPTIMIZE:
FlagPragma (&B, &Optimize); /* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_STMT, Pragma, &B, &Optimize);
break; break;
case PRAGMA_REGVARADDR: case PRAGMA_REGVARADDR:
FlagPragma (&B, &AllowRegVarAddr); /* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &AllowRegVarAddr);
break; break;
case PRAGMA_REGVARS: case PRAGMA_REGVARS:
Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead"); Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_REGISTER_VARS: case PRAGMA_REGISTER_VARS:
FlagPragma (&B, &EnableRegVars); /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &EnableRegVars);
break; break;
case PRAGMA_RODATASEG: case PRAGMA_RODATASEG:
Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead"); Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_RODATA_NAME: case PRAGMA_RODATA_NAME:
SegNamePragma (&B, SEG_RODATA); /* TODO: PES_STMT or even PES_EXPR maybe? */
SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, &B);
break; break;
case PRAGMA_SIGNEDCHARS: case PRAGMA_SIGNEDCHARS:
Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead"); Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_SIGNED_CHARS: case PRAGMA_SIGNED_CHARS:
FlagPragma (&B, &SignedChars); /* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &SignedChars);
break; break;
case PRAGMA_STATICLOCALS: case PRAGMA_STATICLOCALS:
Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead"); Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead");
/* FALLTHROUGH */ /* FALLTHROUGH */
case PRAGMA_STATIC_LOCALS: case PRAGMA_STATIC_LOCALS:
FlagPragma (&B, &StaticLocals); /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals);
break; break;
case PRAGMA_WRAPPED_CALL: case PRAGMA_WRAPPED_CALL:
WrappedCallPragma(&B); /* PES_IMM is the only sensible option */
WrappedCallPragma (PES_IMM, &B);
break; break;
case PRAGMA_WARN: case PRAGMA_WARN:
WarnPragma (&B); /* PES_IMM is the only sensible option */
WarnPragma (PES_IMM, &B);
break; break;
case PRAGMA_WRITABLE_STRINGS: case PRAGMA_WRITABLE_STRINGS:
FlagPragma (&B, &WritableStrings); /* TODO: PES_STMT or even PES_EXPR maybe? */
FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings);
break; break;
case PRAGMA_ZPSYM: case PRAGMA_ZPSYM:
StringPragma (&B, MakeZPSym); /* PES_IMM is the only sensible option */
StringPragma (PES_IMM, &B, MakeZPSym);
break; break;
default: 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 (); NextToken ();
/* Prevent from translating string literals in _Pragma */
++InPragmaParser;
/* We expect an opening paren */ /* We expect an opening paren */
if (!ConsumeLParen ()) { if (!ConsumeLParen ()) {
--InPragmaParser;
return; return;
} }
/* String literal */ /* String literal */
if (CurTok.Tok != TOK_SCONST) { if (CurTok.Tok != TOK_SCONST) {
/* Print a diagnostic */ /* Print a diagnostic */
Error ("String literal expected"); Error ("String literal expected");
@@ -996,13 +1114,13 @@ void DoPragma (void)
** enclosing paren, or a semicolon. ** enclosing paren, or a semicolon.
*/ */
PragmaErrorSkip (); PragmaErrorSkip ();
} else { } else {
/* Parse the pragma */
/* Parse the _Pragma statement */ ParsePragmaString ();
ParsePragma ();
} }
--InPragmaParser;
/* Closing paren needed */ /* Closing paren needed */
ConsumeRParen (); ConsumeRParen ();
} }

View File

@@ -44,8 +44,10 @@
void DoPragma (void); void ConsumePragma (void);
/* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ /* 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 "ident.h"
#include "input.h" #include "input.h"
#include "litpool.h" #include "litpool.h"
#include "pragma.h"
#include "preproc.h" #include "preproc.h"
#include "scanner.h" #include "scanner.h"
#include "standard.h" #include "standard.h"
@@ -69,9 +70,12 @@
static Token SavedTok; /* Saved token */
Token CurTok; /* The current token */ Token CurTok; /* The current token */
Token NextTok; /* The next token */ Token NextTok; /* The next token */
int PPParserRunning; /* Is tokenizer used by the preprocessor */ 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) 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 C;
int HadError; int HadError;
@@ -425,7 +429,7 @@ static int ParseChar (void)
static void CharConst (void) static void CharConst (void)
/* Parse a character constant. */ /* Parse a character constant token */
{ {
int C; int C;
@@ -453,7 +457,7 @@ static void CharConst (void)
} }
/* Translate into target charset */ /* Translate into target charset */
NextTok.IVal = SignExtendChar (TgtTranslateChar (C)); NextTok.IVal = SignExtendChar (C);
/* Character constants have type int */ /* Character constants have type int */
NextTok.Type = type_int; NextTok.Type = type_int;
@@ -462,7 +466,7 @@ static void CharConst (void)
static void StringConst (void) static void StringConst (void)
/* Parse a quoted string */ /* Parse a quoted string token */
{ {
/* String buffer */ /* String buffer */
StrBuf S = AUTO_STRBUF_INITIALIZER; StrBuf S = AUTO_STRBUF_INITIALIZER;
@@ -470,12 +474,6 @@ static void StringConst (void)
/* Assume next token is a string constant */ /* Assume next token is a string constant */
NextTok.Tok = TOK_SCONST; 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 */ /* Check if this is a normal or a wide char string */
if (CurC == 'L' && NextC == '\"') { if (CurC == 'L' && NextC == '\"') {
/* Wide character literal */ /* Wide character literal */
@@ -487,7 +485,7 @@ static void StringConst (void)
NextChar (); NextChar ();
} else { } else {
/* No string */ /* No string */
break; goto ExitPoint;
} }
/* Read until end of string */ /* Read until end of string */
@@ -502,10 +500,7 @@ static void StringConst (void)
/* Skip closing quote char if there was one */ /* Skip closing quote char if there was one */
NextChar (); NextChar ();
/* Skip white space, read new input */ ExitPoint:
SkipWhite ();
}
/* Terminate the string */ /* Terminate the string */
SB_AppendChar (&S, '\0'); SB_AppendChar (&S, '\0');
@@ -520,7 +515,7 @@ static void StringConst (void)
static void NumericConst (void) static void NumericConst (void)
/* Parse a numeric constant */ /* Parse a numeric constant token */
{ {
unsigned Base; /* Temporary number base according to prefix */ unsigned Base; /* Temporary number base according to prefix */
unsigned Index; unsigned Index;
@@ -800,41 +795,51 @@ static void NumericConst (void)
void NextToken (void) static void GetNextInputToken (void)
/* Get next token from input stream */ /* Get next token from input stream */
{ {
ident token; ident token;
/* We have to skip white space here before shifting tokens, since the if (!NoCharMap && !InPragmaParser) {
** tokens and the current line info is invalid at startup and will get /* Translate string and character literals into target charset */
** initialized by reading the first time from the file. Remember if if (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST) {
** we were at end of input and handle that later. TranslateLiteral (NextTok.SVal);
*/ } else if (NextTok.Tok == TOK_CCONST || NextTok.Tok == TOK_WCCONST) {
int GotEOF = (SkipWhite() == 0); NextTok.IVal = SignExtendChar (TgtTranslateChar (NextTok.IVal));
}
}
/* Current token is the lookahead token */ /* Current token is the lookahead token */
if (CurTok.LI) { if (CurTok.LI) {
ReleaseLineInfo (CurTok.LI); ReleaseLineInfo (CurTok.LI);
} }
/* Get the current token */
CurTok = NextTok; CurTok = NextTok;
/* When reading the first time from the file, the line info in NextTok, if (SavedTok.Tok == TOK_INVALID) {
** which was copied to CurTok is invalid. Since the information from /* We have to skip white space here before shifting tokens, since the
** the token is used for error messages, we must make it valid. ** 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.
*/ */
if (CurTok.LI == 0) { int GotEOF = (SkipWhite () == 0);
CurTok.LI = UseLineInfo (GetCurLineInfo ());
}
/* Remember the starting position of the next token */ /* Remember the starting position of the next token */
NextTok.LI = UseLineInfo (GetCurLineInfo ()); NextTok.LI = UseLineInfo (GetCurLineInfo ());
/* Now handle end of input. */ /* Now handle end of input */
if (GotEOF) { if (GotEOF) {
/* End of file reached */ /* End of file reached */
NextTok.Tok = TOK_CEOF; NextTok.Tok = TOK_CEOF;
return; return;
} }
} else {
/* Just use the saved token */
NextTok = SavedTok;
SavedTok.Tok = TOK_INVALID;
return;
}
/* Determine the next token from the lookahead */ /* Determine the next token from the lookahead */
if (IsPPNumber (CurC, NextC)) { if (IsPPNumber (CurC, NextC)) {
@@ -859,7 +864,8 @@ void NextToken (void)
if (!PPParserRunning) { if (!PPParserRunning) {
/* Check for a keyword */ /* Check for a keyword */
if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) { NextTok.Tok = FindKey (token);
if (NextTok.Tok != TOK_IDENT) {
/* Reserved word found */ /* Reserved word found */
return; return;
} }
@@ -1117,7 +1123,90 @@ void NextToken (void)
UnknownChar (CurC); 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

@@ -222,6 +222,8 @@ struct Token {
extern Token CurTok; /* The current token */ extern Token CurTok; /* The current token */
extern Token NextTok; /* The next token */ extern Token NextTok; /* The next token */
extern int PPParserRunning; /* Is tokenizer used by the preprocessor */ 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 */ /* Copy a pp-number from the input to Target */
void NextToken (void); 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); void SkipTokens (const token_t* TokenList, unsigned TokenCount);
/* Skip tokens until we reach TOK_CEOF or a token in the given token list. /* 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. ** support the C2X syntax with only an expression.
*/ */
if (CurTok.Tok == TOK_COMMA) { 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 (); NextToken ();
/* Reenable string literal translation */
NoCharMap = 0;
/* String literal */ /* String literal */
if (CurTok.Tok != TOK_SCONST) { if (CurTok.Tok != TOK_SCONST) {
Error ("String literal expected for static_assert message"); Error ("String literal expected for static_assert message");

View File

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