diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 6d036c149..37e3e493d 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -837,21 +837,21 @@ This cc65 version has some extensions to the ISO C standard. - 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 + - asm [optional volatile] (<string literal>[, optional parameters]) ; + asm [optional volatile] (<string literal>[, optional parameters]) or - __asm__ [optional volatile] (<string literal>[, optional parameters]) ; + __asm__ [optional volatile] (<string literal>[, optional parameters]) The first form is in the user namespace; and, is disabled if the .

@@ -1008,6 +1008,13 @@ This cc65 version has some extensions to the ISO C standard. /) and will + always be in the host encoding. On the other hand, any character or + string literals present in the condition expression of the + 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 #pragma charmap (<index>, <code>)

- 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 Inline assembler

-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 - asm [optional volatile] (<string literal>[, optional parameters]) ; + asm [optional volatile] (<string literal>[, optional parameters]) or - __asm__ [optional volatile] (<string literal>[, optional parameters]) ; + __asm__ [optional volatile] (<string literal>[, optional parameters])

The first form is in the user namespace; and, is disabled by if the argument is not /) 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. + V.LVal = UseLiteral (CurTok.SVal); - /* Translate into target charset */ - TranslateLiteral (E->V.LVal); } else { E->V.LVal = CurTok.SVal; } diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index f576c0255..5702d57c1 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -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); } diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index d741f87d0..5433f6d95 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -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 */ { diff --git a/src/cc65/litpool.h b/src/cc65/litpool.h index 78f432138..5f444bfb8 100644 --- a/src/cc65/litpool.h +++ b/src/cc65/litpool.h @@ -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 */ diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index 83ed362c8..db306040d 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -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 (); } diff --git a/src/cc65/pragma.h b/src/cc65/pragma.h index d1b94fa23..55e907453 100644 --- a/src/cc65/pragma.h +++ b/src/cc65/pragma.h @@ -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. +*/ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index bf3d9365d..00dde9e83 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -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; + } } diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 338ad6a65..e6b788660 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -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. diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index 1bf8dd4c5..abb2c57ca 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -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"); diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 613129e1b..18df2b2b1 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -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); diff --git a/test/val/bug2151.c b/test/val/bug2151.c new file mode 100644 index 000000000..25f145506 --- /dev/null +++ b/test/val/bug2151.c @@ -0,0 +1,77 @@ +/* Bug #2151 - #pragma causes errors when used within functions */ + +#include +#include + +#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;