From d3d6de2704fe9d7b177f68a1a38f6612705ae05d Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 21 Jul 2022 11:09:56 +0800 Subject: [PATCH 1/5] Allowed the pp-tokens following #error and #warning directives to be optional. --- src/cc65/preproc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index a607e3217..5057b5d71 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1028,7 +1028,7 @@ static void DoError (void) { SkipWhitespace (0); if (CurC == '\0') { - PPError ("Invalid #error directive"); + PPError ("#error"); } else { PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); } @@ -1212,7 +1212,7 @@ static void DoWarning (void) { SkipWhitespace (0); if (CurC == '\0') { - PPError ("Invalid #warning directive"); + PPWarning ("#warning"); } else { PPWarning ("#warning: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); } From f91c8ad247397f66d567cd4a5c84531725f088d5 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 21 Jul 2022 11:09:59 +0800 Subject: [PATCH 2/5] Allowed comments right after "#" in preporcessor directives. --- src/cc65/preproc.c | 99 +++++++++++++++++++++++++++++++--------------- src/cc65/preproc.h | 5 +++ 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 5057b5d71..58b924f6a 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -288,7 +288,7 @@ static void OldStyleComment (void) /* Remember the current line number, so we can output better error ** messages if the comment is not terminated in the current file. */ - unsigned StartingLine = GetCurrentLine(); + unsigned StartingLine = GetCurrentLine (); /* Skip the start of comment chars */ NextChar (); @@ -336,8 +336,8 @@ static void NewStyleComment (void) static int SkipWhitespace (int SkipLines) -/* Skip white space in the input stream. Do also skip newlines if SkipLines -** is true. Return zero if nothing was skipped, otherwise return a +/* Skip white space and comments in the input stream. Do also skip newlines if +** SkipLines is true. Return zero if nothing was skipped, otherwise return a ** value != zero. */ { @@ -346,6 +346,12 @@ static int SkipWhitespace (int SkipLines) if (IsSpace (CurC)) { NextChar (); Skipped = 1; + } else if (CurC == '/' && NextC == '*') { + OldStyleComment (); + Skipped = 1; + } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { + NewStyleComment (); + Skipped = 1; } else if (CurC == '\0' && SkipLines) { /* End of line, read next */ if (NextLine () != 0) { @@ -479,16 +485,6 @@ static void ReadMacroArgs (MacroExp* E) if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } - } else if (CurC == '/' && NextC == '*') { - if (SB_NotEmpty (&Arg)) { - SB_AppendChar (&Arg, ' '); - } - OldStyleComment (); - } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { - if (SB_NotEmpty (&Arg)) { - SB_AppendChar (&Arg, ' '); - } - NewStyleComment (); } else if (CurC == '\0') { /* End of input inside macro argument list */ PPError ("Unterminated argument list invoking macro '%s'", E->M->Name); @@ -839,7 +835,7 @@ static void DefineMacro (void) /* Remove whitespace and comments from the line, store the preprocessed ** line into the macro replacement buffer. */ - Pass1 (Line, &M->Replacement); + TranslationPhase3 (Line, &M->Replacement); /* Remove whitespace from the end of the line */ while (IsSpace (SB_LookAtLast (&M->Replacement))) { @@ -865,9 +861,55 @@ static void DefineMacro (void) +void TranslationPhase3 (StrBuf* Source, StrBuf* Target) +/* Mimic Translation Phase 3. Handle old and new style comments. Collapse +** non-newline whitespace sequences. +*/ +{ + /* Switch to the new input source */ + StrBuf* OldSource = InitLine (Source); + + /* Loop removing ws and comments */ + while (CurC != '\0') { + int HasWhiteSpace = 0; + while (1) { + /* Squeeze runs of blanks */ + if (IsSpace (CurC)) { + NextChar (); + HasWhiteSpace = 1; + } else if (CurC == '/' && NextC == '*') { + OldStyleComment (); + HasWhiteSpace = 1; + } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { + NewStyleComment (); + HasWhiteSpace = 1; + } else { + /* No more white space */ + break; + } + } + if (HasWhiteSpace) { + SB_AppendChar (Target, ' '); + } else if (IsQuote (CurC)) { + CopyQuotedString (Target); + } else { + SB_AppendChar (Target, CurC); + NextChar (); + } + } + + /* Terminate the new input line */ + SB_Terminate (Target); + + /* Switch back to the old source */ + InitLine (OldSource); +} + + + static unsigned Pass1 (StrBuf* Source, StrBuf* Target) -/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments -** and the "defined" operator. +/* Preprocessor pass 1. Remove whitespace, old and new style comments. Handle +** the "defined" operator. */ { unsigned IdentCount; @@ -915,16 +957,6 @@ static unsigned Pass1 (StrBuf* Source, StrBuf* Target) } } else if (IsQuote (CurC)) { CopyQuotedString (Target); - } else if (CurC == '/' && NextC == '*') { - if (!IsSpace (SB_LookAtLast (Target))) { - SB_AppendChar (Target, ' '); - } - OldStyleComment (); - } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { - if (!IsSpace (SB_LookAtLast (Target))) { - SB_AppendChar (Target, ' '); - } - NewStyleComment (); } else { SB_AppendChar (Target, CurC); NextChar (); @@ -981,7 +1013,7 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target) static void PreprocessLine (void) -/* Translate one line. */ +/* Translate one line with defined macros replaced */ { /* Trim whitespace and remove comments. The function returns the number of ** identifiers found. If there were any, we will have to check for macros. @@ -1030,7 +1062,10 @@ static void DoError (void) if (CurC == '\0') { PPError ("#error"); } else { - PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); + StrBuf MsgLine = AUTO_STRBUF_INITIALIZER; + TranslationPhase3 (Line, &MsgLine); + PPError ("#error: %s", SB_GetConstBuf (&MsgLine) + SB_GetIndex (&MsgLine)); + SB_Done (&MsgLine); } /* Clear the rest of line */ @@ -1174,9 +1209,6 @@ static void DoPragma (void) ** the _Pragma() compiler operator. */ { - /* Skip blanks following the #pragma directive */ - SkipWhitespace (0); - /* Copy the remainder of the line into MLine removing comments and ws */ SB_Clear (MLine); Pass1 (Line, MLine); @@ -1214,7 +1246,10 @@ static void DoWarning (void) if (CurC == '\0') { PPWarning ("#warning"); } else { - PPWarning ("#warning: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); + StrBuf MsgLine = AUTO_STRBUF_INITIALIZER; + TranslationPhase3 (Line, &MsgLine); + PPWarning ("#warning: %s", SB_GetConstBuf (&MsgLine) + SB_GetIndex (&MsgLine)); + SB_Done (&MsgLine); } /* Clear the rest of line */ diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 78a91a590..c65636ef0 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -55,6 +55,11 @@ extern unsigned char Preprocessing; +void TranslationPhase3 (StrBuf* Source, StrBuf* Target); +/* Mimic Translation Phase 3. Handle old and new style comments. Collapse +** non-newline whitespace sequences. +*/ + void Preprocess (void); /* Preprocess a line */ From 16a8a647469337a5e3bcd7b0c6f0a9a341f6a724 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 21 Jul 2022 11:10:02 +0800 Subject: [PATCH 3/5] Use a dedicated expression parser for the preprocessor. --- src/cc65.vcxproj | 2 + src/cc65/expr.c | 146 ++------ src/cc65/ppexpr.c | 845 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/ppexpr.h | 76 ++++ src/cc65/preproc.c | 93 ++--- src/cc65/preproc.h | 16 - 6 files changed, 1010 insertions(+), 168 deletions(-) create mode 100644 src/cc65/ppexpr.c create mode 100644 src/cc65/ppexpr.h diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 5cddc1862..7e2e8ea8c 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -103,6 +103,7 @@ + @@ -182,6 +183,7 @@ + diff --git a/src/cc65/expr.c b/src/cc65/expr.c index eb0afb38b..a666db470 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -304,13 +304,9 @@ void PushAddr (const ExprDesc* Expr) static void WarnConstCompareResult (const ExprDesc* Expr) -/* If the result of a comparison is constant, this is suspicious when not in -** preprocessor mode. -*/ +/* If the result of a comparison is constant, this is suspicious */ { - if (!Preprocessing && - !ED_NeedsConst (Expr) && - IS_Get (&WarnConstComparison) != 0) { + if (!ED_NeedsConst (Expr) && IS_Get (&WarnConstComparison) != 0) { Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false"); } } @@ -1075,58 +1071,19 @@ static void Primary (ExprDesc* E) /* This is the lowest level of the expression parser. */ { SymEntry* Sym; - - /* Character and integer constants. */ - if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) { - E->IVal = CurTok.IVal; - E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; - NextToken (); - return; - } - - /* Floating point constant */ - if (CurTok.Tok == TOK_FCONST) { - E->V.FVal = CurTok.FVal; - E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; - NextToken (); - return; - } - - /* Process parenthesized subexpression by calling the whole parser - ** recursively. - */ - if (CurTok.Tok == TOK_LPAREN) { - NextToken (); - hie0 (E); - ConsumeRParen (); - return; - } - - /* If we run into an identifier in preprocessing mode, we assume that this - ** is an undefined macro and replace it by a constant value of zero. - */ - if (Preprocessing && CurTok.Tok == TOK_IDENT) { - NextToken (); - ED_MakeConstAbsInt (E, 0); - return; - } - - /* All others may only be used if the expression evaluation is not called - ** recursively by the preprocessor. - */ - if (Preprocessing) { - /* Illegal expression in PP mode */ - Error ("Preprocessor expression expected"); - ED_MakeConstAbsInt (E, 1); - return; - } - unsigned Flags = E->Flags & E_MASK_KEEP_MAKE; switch (CurTok.Tok) { + case TOK_LPAREN: + /* Process parenthesized subexpression by calling the whole parser + ** recursively. + */ + NextToken (); + hie0 (E); + ConsumeRParen (); + break; + case TOK_BOOL_AND: /* A computed goto label address */ if (IS_Get (&Standard) >= STD_CC65) { @@ -1160,9 +1117,9 @@ static void Primary (ExprDesc* E) /* Cannot use type symbols */ Error ("Variable identifier expected"); /* Assume an int type to make E valid */ - E->Flags |= E_LOC_STACK | E_RTYPE_LVAL; - E->Type = type_int; - return; + E->Flags = E_LOC_STACK | E_RTYPE_LVAL; + E->Type = type_int; + break; } /* Mark the symbol as referenced */ @@ -1175,11 +1132,11 @@ static void Primary (ExprDesc* E) if ((Sym->Flags & SC_CONST) == SC_CONST) { /* Enum or some other numeric constant */ E->Flags = E_LOC_NONE | E_RTYPE_RVAL; - E->IVal = Sym->V.ConstVal; + E->IVal = Sym->V.ConstVal; } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { /* Function */ E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; - E->Name = (uintptr_t) Sym->Name; + E->Name = (uintptr_t) Sym->Name; } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) { /* Local variable. If this is a parameter for a variadic ** function, we have to add some address calculations, and the @@ -1202,10 +1159,10 @@ static void Primary (ExprDesc* E) /* Static variable */ if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) { E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; - E->Name = (uintptr_t) Sym->Name; + E->Name = (uintptr_t) Sym->Name; } else { E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; - E->Name = Sym->V.L.Label; + E->Name = Sym->V.L.Label; } } else { /* Local static variable */ @@ -1251,7 +1208,7 @@ static void Primary (ExprDesc* E) /* Undeclared Variable */ Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0); E->Flags = E_LOC_STACK | E_RTYPE_LVAL; - E->Type = type_int; + E->Type = type_int; Error ("Undefined symbol: '%s'", Ident); } @@ -1273,6 +1230,23 @@ static void Primary (ExprDesc* E) NextToken (); break; + case TOK_ICONST: + case TOK_CCONST: + /* Character and integer constants */ + E->IVal = CurTok.IVal; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; + NextToken (); + break; + + case TOK_FCONST: + /* Floating point constant */ + E->V.FVal = CurTok.FVal; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; + NextToken (); + break; + case TOK_ASM: /* ASM statement */ AsmStatement (); @@ -3474,48 +3448,6 @@ static void hie2 (ExprDesc* Expr) -static void hieAndPP (ExprDesc* Expr) -/* Process "exp && exp" in preprocessor mode (that is, when the parser is -** called recursively from the preprocessor. -*/ -{ - *Expr = NoCodeConstAbsIntExpr (hie2); - while (CurTok.Tok == TOK_BOOL_AND) { - - /* Skip the && */ - NextToken (); - - /* Get rhs */ - ExprDesc Expr2 = NoCodeConstAbsIntExpr (hie2); - - /* Combine the two */ - Expr->IVal = (Expr->IVal && Expr2.IVal); - } -} - - - -static void hieOrPP (ExprDesc *Expr) -/* Process "exp || exp" in preprocessor mode (that is, when the parser is -** called recursively from the preprocessor. -*/ -{ - *Expr = NoCodeConstAbsIntExpr (hieAndPP); - while (CurTok.Tok == TOK_BOOL_OR) { - - /* Skip the && */ - NextToken (); - - /* Get rhs */ - ExprDesc Expr2 = NoCodeConstAbsIntExpr (hieAndPP); - - /* Combine the two */ - Expr->IVal = (Expr->IVal || Expr2.IVal); - } -} - - - static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Process "exp && exp". This should only be called within hieOr. ** Return true if logic AND does occur. @@ -3865,11 +3797,7 @@ static void hieQuest (ExprDesc* Expr) Type* ResultType; /* Type of result */ /* Call the lower level eval routine */ - if (Preprocessing) { - ExprWithCheck (hieOrPP, Expr); - } else { - ExprWithCheck (hieOr, Expr); - } + ExprWithCheck (hieOr, Expr); /* Check if it's a ternary expression */ if (CurTok.Tok == TOK_QUEST) { diff --git a/src/cc65/ppexpr.c b/src/cc65/ppexpr.c new file mode 100644 index 000000000..da25e12f1 --- /dev/null +++ b/src/cc65/ppexpr.c @@ -0,0 +1,845 @@ +/*****************************************************************************/ +/* */ +/* ppexpr.h */ +/* */ +/* Expressions for C preprocessor */ +/* */ +/* */ +/* */ +/* (C) 2022 The cc65 Authors */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "error.h" +#include "scanner.h" +#include "ppexpr.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* PP expression parser status */ +static int PPEvaluationEnabled = 0; +static int PPEvaluationFailed = 0; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void PPhie0 (PPExpr* Expr); +static void PPhie1 (PPExpr* Expr); + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static token_t PPFindTok (token_t Tok, const token_t* Table) +/* Find a token in a generator table */ +{ + while (*Table != TOK_INVALID) { + if (*Table == Tok) { + return Tok; + } + ++Table; + } + return TOK_INVALID; +} + + + +static void PPExprInit (PPExpr* Expr) +/* Initialize the expression */ +{ + Expr->IVal = 0; + Expr->Flags = PPEXPR_NONE; +} + + + +static void PPErrorSkipLine (void) +/* Skip the remain tokens till the end of the line and set the expression +** parser error flag. +*/ +{ + SkipTokens (0, 0); + PPEvaluationFailed = 1; +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void PPhiePrimary (PPExpr* Expr) +/* This is the lowest level of the PP expression parser */ +{ + switch (CurTok.Tok) { + case TOK_ICONST: + case TOK_CCONST: + /* Character and integer constants */ + Expr->IVal = CurTok.IVal; + /* According to the C standard, all signed types act as intmax_t + ** and all unsigned types act as uintmax_t. + */ + if (IsSignUnsigned (CurTok.Type)) { + Expr->Flags |= PPEXPR_UNSIGNED; + } + NextToken (); + break; + + case TOK_FCONST: + /* Floating point constant */ + PPError ("Floating constant in preprocessor expression"); + Expr->IVal = 0; + NextToken (); + break; + + case TOK_LPAREN: + /* Parse parenthesized subexpression by calling the whole parser + ** recursively. + */ + NextToken (); + PPhie0 (Expr); + ConsumeRParen (); + break; + + case TOK_IDENT: + /* Assume that this identifier is an undefined macro and replace + ** it by a constant value of zero. + */ + NextToken (); + Expr->Flags |= PPEXPR_UNDEFINED; + Expr->IVal = 0; + break; + + default: + /* Illegal expression in PP mode */ + PPError ("Preprocessor expression expected"); + PPErrorSkipLine (); + break; + } +} + + + +static void PPhie11 (PPExpr* Expr) +/* Handle compound types (structs and arrays) etc which are invalid in PP */ +{ + /* Evaluate the lhs */ + PPhiePrimary (Expr); + + /* Check for a rhs */ + while (CurTok.Tok == TOK_INC || CurTok.Tok == TOK_DEC || + CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN || + CurTok.Tok == TOK_DOT || CurTok.Tok == TOK_PTR_REF) { + + switch (CurTok.Tok) { + + case TOK_LBRACK: + PPError ("Token \".\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_LPAREN: + /* Function call syntax is not recognized in preprocessor + ** expressions. + */ + PPError ("Missing binary operator before token \"(\""); + PPErrorSkipLine (); + break; + + case TOK_DOT: + PPError ("Token \".\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_PTR_REF: + PPError ("Token \"->\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_INC: + PPError ("Token \"++\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_DEC: + PPError ("Token \"--\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + default: + Internal ("Invalid token in PPhie11: %d", CurTok.Tok); + + } + } +} + + + +void PPhie10 (PPExpr* Expr) +/* Handle prefixing unary operators */ +{ + switch (CurTok.Tok) { + + case TOK_INC: + PPError ("Token \"++\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_DEC: + PPError ("Token \"--\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_PLUS: + NextToken (); + PPhie10 (Expr); + Expr->IVal = +Expr->IVal; + break; + + case TOK_MINUS: + NextToken (); + PPhie10 (Expr); + Expr->IVal = -Expr->IVal; + break; + + case TOK_COMP: + NextToken (); + PPhie10 (Expr); + Expr->IVal = ~Expr->IVal; + break; + + case TOK_BOOL_NOT: + NextToken (); + PPhie10 (Expr); + Expr->IVal = !Expr->IVal; + break; + + case TOK_STAR: + case TOK_AND: + case TOK_SIZEOF: + default: + /* Type cast, sizeof, *, &, are not recognized in preprocessor + ** expressions. So everything is treated as as expression here. + */ + PPhie11 (Expr); + break; + } +} + + + +static void PPhie_internal (const token_t* Ops, /* List of generators */ + PPExpr* Expr, + void (*hienext) (PPExpr*)) +/* Helper function */ +{ + token_t Tok; + + hienext (Expr); + + while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) { + + PPExpr Rhs; + PPExprInit (&Rhs); + + /* Remember the operator token, then skip it */ + NextToken (); + + /* Get the right hand side */ + hienext (&Rhs); + + if (PPEvaluationEnabled) { + + /* If either side is unsigned, the result is unsigned */ + Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED; + + /* Handle the op differently for signed and unsigned integers */ + if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) { + + /* Evaluate the result for signed operands */ + signed long Val1 = Expr->IVal; + signed long Val2 = Rhs.IVal; + switch (Tok) { + case TOK_OR: + Expr->IVal = (Val1 | Val2); + break; + case TOK_XOR: + Expr->IVal = (Val1 ^ Val2); + break; + case TOK_AND: + Expr->IVal = (Val1 & Val2); + break; + case TOK_PLUS: + Expr->IVal = (Val1 + Val2); + break; + case TOK_MINUS: + Expr->IVal = (Val1 - Val2); + break; + case TOK_MUL: + Expr->IVal = (Val1 * Val2); + break; + case TOK_DIV: + if (Val2 == 0) { + PPError ("Division by zero"); + Expr->IVal = 0; + } else { + Expr->IVal = (Val1 / Val2); + } + break; + case TOK_MOD: + if (Val2 == 0) { + PPError ("Modulo operation with zero"); + Expr->IVal = 0; + } else { + Expr->IVal = (Val1 % Val2); + } + break; + default: + Internal ("PPhie_internal: got token 0x%X\n", Tok); + } + + } else { + + /* Evaluate the result for unsigned operands */ + unsigned long Val1 = Expr->IVal; + unsigned long Val2 = Rhs.IVal; + switch (Tok) { + case TOK_OR: + Expr->IVal = (Val1 | Val2); + break; + case TOK_XOR: + Expr->IVal = (Val1 ^ Val2); + break; + case TOK_AND: + Expr->IVal = (Val1 & Val2); + break; + case TOK_PLUS: + Expr->IVal = (Val1 + Val2); + break; + case TOK_MINUS: + Expr->IVal = (Val1 - Val2); + break; + case TOK_MUL: + Expr->IVal = (Val1 * Val2); + break; + case TOK_DIV: + if (Val2 == 0) { + PPError ("Division by zero"); + Expr->IVal = 0; + } else { + Expr->IVal = (Val1 / Val2); + } + break; + case TOK_MOD: + if (Val2 == 0) { + PPError ("Modulo operation with zero"); + Expr->IVal = 0; + } else { + Expr->IVal = (Val1 % Val2); + } + break; + default: + Internal ("PPhie_internal: got token 0x%X\n", Tok); + } + } + } + } +} + + + +static void PPhie_compare (const token_t* Ops, /* List of generators */ + PPExpr* Expr, + void (*hienext) (PPExpr*)) +/* Helper function for the compare operators */ +{ + token_t Tok; + + hienext (Expr); + + while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) { + + PPExpr Rhs; + + PPExprInit (&Rhs); + + /* Skip the operator token */ + NextToken (); + + /* Get the right hand side */ + hienext (&Rhs); + + if (PPEvaluationEnabled) { + + /* If either side is unsigned, the comparison is unsigned */ + Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED; + + /* Determine if this is a signed or unsigned compare */ + if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) { + + /* Evaluate the result for signed operands */ + signed long Val1 = Expr->IVal; + signed long Val2 = Rhs.IVal; + switch (Tok) { + case TOK_EQ: Expr->IVal = (Val1 == Val2); break; + case TOK_NE: Expr->IVal = (Val1 != Val2); break; + case TOK_LT: Expr->IVal = (Val1 < Val2); break; + case TOK_LE: Expr->IVal = (Val1 <= Val2); break; + case TOK_GE: Expr->IVal = (Val1 >= Val2); break; + case TOK_GT: Expr->IVal = (Val1 > Val2); break; + default: Internal ("PPhie_compare: got token 0x%X\n", Tok); + } + + } else { + + /* Evaluate the result for unsigned operands */ + unsigned long Val1 = Expr->IVal; + unsigned long Val2 = Rhs.IVal; + switch (Tok) { + case TOK_EQ: Expr->IVal = (Val1 == Val2); break; + case TOK_NE: Expr->IVal = (Val1 != Val2); break; + case TOK_LT: Expr->IVal = (Val1 < Val2); break; + case TOK_LE: Expr->IVal = (Val1 <= Val2); break; + case TOK_GE: Expr->IVal = (Val1 >= Val2); break; + case TOK_GT: Expr->IVal = (Val1 > Val2); break; + default: Internal ("PPhie_compare: got token 0x%X\n", Tok); + } + } + } + } + + /* The result is signed */ + Expr->Flags &= ~PPEXPR_UNSIGNED; +} + + + +static void PPhie9 (PPExpr* Expr) +/* Handle "*", "/" and "%" operators */ +{ + static const token_t PPhie9_ops[] = { + TOK_STAR, + TOK_DIV, + TOK_MOD, + TOK_INVALID + }; + + PPhie_internal (PPhie9_ops, Expr, PPhie10); +} + + + +static void PPhie8 (PPExpr* Expr) +/* Handle "+" and "-" binary operators */ +{ + static const token_t PPhie8_ops[] = { + TOK_PLUS, + TOK_MINUS, + TOK_INVALID + }; + + PPhie_internal (PPhie8_ops, Expr, PPhie9); +} + + + +static void PPhie7 (PPExpr* Expr) +/* Handle the "<<" and ">>" shift operators */ +{ + /* Evaluate the lhs */ + PPhie8 (Expr); + + while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) { + + token_t Op; /* The operator token */ + PPExpr Rhs; + PPExprInit (&Rhs); + + /* Remember the operator, then skip its token */ + Op = CurTok.Tok; + NextToken (); + + /* Get the right hand side */ + PPhie8 (&Rhs); + + /* Evaluate */ + if (PPEvaluationEnabled) { + /* To shift by a negative value is equivalent to shift to the + ** opposite direction. + */ + if ((Rhs.Flags & PPEXPR_UNSIGNED) != 0 && Rhs.IVal > (long)LONG_BITS) { + Rhs.IVal = (long)LONG_BITS; + } + if (Op == TOK_SHR) { + Rhs.IVal = -Rhs.IVal; + } + + /* Evaluate the result */ + if ((Expr->Flags & PPEXPR_UNSIGNED) != 0) { + if (Rhs.IVal >= (long)LONG_BITS) { + /* For now we use (unsigned) long types for integer constants */ + PPWarning ("Integer overflow in preprocessor expression"); + Expr->IVal = 0; + } else if (Rhs.IVal > 0) { + Expr->IVal <<= Rhs.IVal; + } else if (Rhs.IVal < -(long)LONG_BITS) { + Expr->IVal = 0; + } else if (Rhs.IVal < 0) { + Expr->IVal = (unsigned long)Expr->IVal >> -Rhs.IVal; + } + } else { + if (Rhs.IVal >= (long)(LONG_BITS - 1)) { + /* For now we use (unsigned) long types for integer constants */ + PPWarning ("Integer overflow in preprocessor expression"); + Expr->IVal = 0; + } else if (Rhs.IVal > 0) { + Expr->IVal <<= Rhs.IVal; + } else if (Rhs.IVal < -(long)LONG_BITS) { + Expr->IVal = -1; + } else if (Rhs.IVal < 0) { + Expr->IVal >>= Expr->IVal >> -Rhs.IVal; + } + } + } + } +} + + + +static void PPhie6 (PPExpr* Expr) +/* Handle greater-than type relational operators */ +{ + static const token_t PPhie6_ops [] = { + TOK_LT, + TOK_LE, + TOK_GE, + TOK_GT, + TOK_INVALID + }; + + PPhie_compare (PPhie6_ops, Expr, PPhie7); +} + + + +static void PPhie5 (PPExpr* Expr) +/* Handle "==" and "!=" relational operators */ +{ + static const token_t PPhie5_ops[] = { + TOK_EQ, + TOK_NE, + TOK_INVALID + }; + + PPhie_compare (PPhie5_ops, Expr, PPhie6); +} + + + +static void PPhie4 (PPExpr* Expr) +/* Handle the bitwise AND "&" operator */ +{ + static const token_t PPhie4_ops[] = { + TOK_AND, + TOK_INVALID + }; + + PPhie_internal (PPhie4_ops, Expr, PPhie5); +} + + + +static void PPhie3 (PPExpr* Expr) +/* Handle the bitwise exclusive OR "^" operator */ +{ + static const token_t PPhie3_ops[] = { + TOK_XOR, + TOK_INVALID + }; + + PPhie_internal (PPhie3_ops, Expr, PPhie4); +} + + + +static void PPhie2 (PPExpr* Expr) +/* Handle the bitwise OR "|" operator */ +{ + static const token_t PPhie2_ops[] = { + TOK_OR, + TOK_INVALID + }; + + PPhie_internal (PPhie2_ops, Expr, PPhie3); +} + + + +static void PPhieAnd (PPExpr* Expr) +/* Handle the logical AND "expr1 && expr2" operator */ +{ + /* Get one operand */ + PPhie2 (Expr); + + if (CurTok.Tok == TOK_BOOL_AND) { + + int PPEvaluationEnabledPrev = PPEvaluationEnabled; + PPExpr One; + + /* Do logical and */ + Expr->IVal = (Expr->IVal != 0); + if (Expr->IVal == 0) { + PPEvaluationEnabled = 0; + } + + /* While there are more expressions */ + while (CurTok.Tok == TOK_BOOL_AND) { + /* Skip the && */ + NextToken (); + + /* Get one operand */ + PPExprInit (&One); + PPhie2 (&One); + + /* Evaluate */ + if (PPEvaluationEnabled) { + if (One.IVal == 0) { + /* Skip evaluating remaining */ + PPEvaluationEnabled = 0; + /* The value of the result will be false */ + Expr->IVal = 0; + } + } + } + + /* Restore evaluation as before */ + PPEvaluationEnabled = PPEvaluationEnabledPrev; + } +} + + + +static void PPhieOr (PPExpr* Expr) +/* Handle the logical OR "||" operator */ +{ + /* Call the next level parser */ + PPhieAnd (Expr); + + if (CurTok.Tok == TOK_BOOL_OR) { + + int PPEvaluationEnabledPrev = PPEvaluationEnabled; + PPExpr One; + + /* Do logical or */ + Expr->IVal = (Expr->IVal != 0); + if (Expr->IVal != 0) { + PPEvaluationEnabled = 0; + } + + /* While there are more expressions */ + while (CurTok.Tok == TOK_BOOL_OR) { + /* Skip the || */ + NextToken (); + + /* Get rhs subexpression */ + PPExprInit (&One); + PPhieAnd (&One); + + /* Evaluate */ + if (PPEvaluationEnabled) { + if (One.IVal != 0) { + /* Skip evaluating remaining */ + PPEvaluationEnabled = 0; + /* The value of the result will be true */ + Expr->IVal = 1; + } + } + } + + /* Restore evaluation as before */ + PPEvaluationEnabled = PPEvaluationEnabledPrev; + } +} + + + +static void PPhieQuest (PPExpr* Expr) +/* Handle the ternary "expr1 ? expr2 : expr3 " operator */ +{ + /* Call the lower level eval routine */ + PPhieOr (Expr); + + /* Check if it's a ternary expression */ + if (CurTok.Tok == TOK_QUEST) { + int PPEvaluationEnabledPrev = PPEvaluationEnabled; + PPExpr Expr2; /* Expression 2 */ + PPExpr Expr3; /* Expression 3 */ + + /* Skip the question mark */ + NextToken (); + + /* Disable evaluation for Expr2 if the condition is false */ + if (Expr->IVal == 0) { + PPEvaluationEnabled = 0; + } + + /* Parse second expression */ + PPExprInit (&Expr2); + PPhie0 (&Expr2); + + /* Skip the colon */ + ConsumeColon (); + + /* Disable evaluation for Expr3 if the condition is true */ + if (Expr->IVal != 0) { + PPEvaluationEnabled = 0; + } + + /* Parse third expression */ + PPExprInit (&Expr3); + PPhie1 (&Expr3); + + /* Set the result */ + Expr->IVal = Expr->IVal ? Expr2.IVal != 0 : Expr3.IVal != 0; + + /* Restore evaluation as before */ + PPEvaluationEnabled = PPEvaluationEnabledPrev; + } +} + + + +static void PPhie1 (PPExpr* Expr) +/* Handle first level of expression hierarchy */ +{ + PPhieQuest (Expr); + + if (!PPEvaluationEnabled) { + /* Skip evaluation */ + return; + } + + switch (CurTok.Tok) { + + case TOK_ASSIGN: + PPError ("Token \"=\" is not valid in preprocessor expressions"); + break; + + case TOK_PLUS_ASSIGN: + PPError ("Token \"+=\" is not valid in preprocessor expressions"); + break; + + case TOK_MINUS_ASSIGN: + PPError ("Token \"-=\" is not valid in preprocessor expressions"); + break; + + case TOK_MUL_ASSIGN: + PPError ("Token \"*=\" is not valid in preprocessor expressions"); + break; + + case TOK_DIV_ASSIGN: + PPError ("Token \"/=\" is not valid in preprocessor expressions"); + break; + + case TOK_MOD_ASSIGN: + PPError ("Token \"%%=\" is not valid in preprocessor expressions"); + break; + + case TOK_SHL_ASSIGN: + PPError ("Token \"<<=\" is not valid in preprocessor expressions"); + break; + + case TOK_SHR_ASSIGN: + PPError ("Token \">>=\" is not valid in preprocessor expressions"); + break; + + case TOK_AND_ASSIGN: + PPError ("Token \"&=\" is not valid in preprocessor expressions"); + break; + + case TOK_OR_ASSIGN: + PPError ("Token \"|=\" is not valid in preprocessor expressions"); + break; + + case TOK_XOR_ASSIGN: + PPError ("Token \"^=\" is not valid in preprocessor expressions"); + break; + + default: + break; + } +} + + + +static void PPhie0 (PPExpr* Expr) +/* Handle the comma "," operator */ +{ + PPhie1 (Expr); + + while (CurTok.Tok == TOK_COMMA) { + /* Skip the comma */ + NextToken (); + /* Reset the expression */ + PPExprInit (Expr); + /* Use the next operand as the value instead */ + PPhie1 (Expr); + } +} + + + +void ParsePPExpr (PPExpr* Expr) +/* Parse a line for PP expression */ +{ + /* Initialize the parser status */ + PPEvaluationFailed = 0; + PPEvaluationEnabled = 1; + + /* Parse */ + PPExprInit (Expr); + PPhie0 (Expr); + + /* If the evaluation fails, the result is always zero */ + if (PPEvaluationFailed) { + Expr->IVal = 0; + } +} diff --git a/src/cc65/ppexpr.h b/src/cc65/ppexpr.h new file mode 100644 index 000000000..683c6c49d --- /dev/null +++ b/src/cc65/ppexpr.h @@ -0,0 +1,76 @@ +/*****************************************************************************/ +/* */ +/* ppexpr.h */ +/* */ +/* Expressions for C preprocessor */ +/* */ +/* */ +/* */ +/* (C) 2022 The cc65 Authors */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef PPEXPR_H +#define PPEXPR_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* PPExpr data struct */ +typedef struct PPExpr PPExpr; +struct PPExpr +{ + long IVal; + unsigned Flags; +}; + +/* PPExpr initializers */ +#define AUTO_PPEXPR_INITIALIZER { 0, 0 } +#define STATIC_PPEXPR_INITIALIZER { 0, 0 } + +/* PPExpr flags */ +#define PPEXPR_NONE 0U +#define PPEXPR_UNSIGNED 1U +#define PPEXPR_UNDEFINED 2U + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ParsePPExpr (PPExpr* Expr); +/* Parse a line for PP expression */ + + + +/* End of ppexpr.h */ + +#endif diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 58b924f6a..7e690874a 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -48,13 +48,13 @@ /* cc65 */ #include "codegen.h" #include "error.h" -#include "expr.h" #include "global.h" #include "ident.h" #include "incpath.h" #include "input.h" #include "lineinfo.h" #include "macrotab.h" +#include "ppexpr.h" #include "preproc.h" #include "scanner.h" #include "standard.h" @@ -68,7 +68,7 @@ /* Set when the preprocessor calls expr() recursively */ -unsigned char Preprocessing = 0; +static unsigned char Preprocessing = 0; /* Management data for #if */ #define MAX_IFS 256 @@ -98,6 +98,11 @@ struct MacroExp { +static void TranslationPhase3 (StrBuf* Source, StrBuf* Target); +/* Mimic Translation Phase 3. Handle old and new style comments. Collapse +** non-newline whitespace sequences. +*/ + static unsigned Pass1 (StrBuf* Source, StrBuf* Target); /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments ** and the "defined" operator. @@ -861,7 +866,7 @@ static void DefineMacro (void) -void TranslationPhase3 (StrBuf* Source, StrBuf* Target) +static void TranslationPhase3 (StrBuf* Source, StrBuf* Target) /* Mimic Translation Phase 3. Handle old and new style comments. Collapse ** non-newline whitespace sequences. */ @@ -1077,48 +1082,50 @@ static void DoError (void) static int DoIf (int Skip) /* Process #if directive */ { - /* We're about to abuse the compiler expression parser to evaluate the - ** #if expression. Save the current tokens to come back here later. - ** NOTE: Yes, this is a hack, but it saves a complete separate expression - ** evaluation for the preprocessor. - */ - Token SavedCurTok = CurTok; - Token SavedNextTok = NextTok; + PPExpr Expr = AUTO_PPEXPR_INITIALIZER; - /* Make sure the line infos for the tokens won't get removed */ - if (SavedCurTok.LI) { - UseLineInfo (SavedCurTok.LI); + if (!Skip) { + /* We're about to use a dedicated expression parser to evaluate the #if + ** expression. Save the current tokens to come back here later. + */ + Token SavedCurTok = CurTok; + Token SavedNextTok = NextTok; + + /* Make sure the line infos for the tokens won't get removed */ + if (SavedCurTok.LI) { + UseLineInfo (SavedCurTok.LI); + } + if (SavedNextTok.LI) { + UseLineInfo (SavedNextTok.LI); + } + + /* Switch into special preprocessing mode */ + Preprocessing = 1; + + /* Expand macros in this line */ + PreprocessLine (); + + /* Add two semicolons as sentinels to the line, so the following + ** expression evaluation will eat these two tokens but nothing from + ** the following line. + */ + SB_AppendStr (Line, ";;"); + SB_Terminate (Line); + + /* Load CurTok and NextTok with tokens from the new input */ + NextToken (); + NextToken (); + + /* Call the expression parser */ + ParsePPExpr (&Expr); + + /* End preprocessing mode */ + Preprocessing = 0; + + /* Reset the old tokens */ + CurTok = SavedCurTok; + NextTok = SavedNextTok; } - if (SavedNextTok.LI) { - UseLineInfo (SavedNextTok.LI); - } - - /* Switch into special preprocessing mode */ - Preprocessing = 1; - - /* Expand macros in this line */ - PreprocessLine (); - - /* Add two semicolons as sentinels to the line, so the following - ** expression evaluation will eat these two tokens but nothing from - ** the following line. - */ - SB_AppendStr (Line, ";;"); - SB_Terminate (Line); - - /* Load CurTok and NextTok with tokens from the new input */ - NextToken (); - NextToken (); - - /* Call the expression parser */ - ExprDesc Expr = NoCodeConstExpr (hie1); - - /* End preprocessing mode */ - Preprocessing = 0; - - /* Reset the old tokens */ - CurTok = SavedCurTok; - NextTok = SavedNextTok; /* Set the #if condition according to the expression result */ return PushIf (Skip, 1, Expr.IVal != 0); diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index c65636ef0..34f62c114 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -38,28 +38,12 @@ -/*****************************************************************************/ -/* data */ -/*****************************************************************************/ - - - -/* Set when the preprocessor calls NoCodeConstExpr() recursively */ -extern unsigned char Preprocessing; - - - /*****************************************************************************/ /* code */ /*****************************************************************************/ -void TranslationPhase3 (StrBuf* Source, StrBuf* Target); -/* Mimic Translation Phase 3. Handle old and new style comments. Collapse -** non-newline whitespace sequences. -*/ - void Preprocess (void); /* Preprocess a line */ From be26b0631b271e6690f0c2d8a0ceb2cd4c9df855 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 24 Jul 2022 19:07:02 +0800 Subject: [PATCH 4/5] Fixed #ifdef and #ifndef. --- src/cc65/preproc.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 7e690874a..107541994 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1136,14 +1136,18 @@ static int DoIf (int Skip) static int DoIfDef (int skip, int flag) /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */ { - ident Ident; + int Value = 0; - SkipWhitespace (0); - if (MacName (Ident) == 0) { - return 0; - } else { - return PushIf (skip, flag, IsMacro(Ident)); + if (!skip) { + ident Ident; + + SkipWhitespace (0); + if (MacName (Ident)) { + Value = IsMacro (Ident); + } } + + return PushIf (skip, flag, Value); } From 8289c94e69682c33296978280889764afb35f00e Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 26 Jul 2022 14:28:27 +0800 Subject: [PATCH 5/5] Fixed skipped lines consist of a leading # followed by any non-directive PP-token sequence. --- src/cc65/preproc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 107541994..96bb0bf1c 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1296,7 +1296,9 @@ void Preprocess (void) continue; } if (!IsSym (Directive)) { - PPError ("Preprocessor directive expected"); + if (!Skip) { + PPError ("Preprocessor directive expected"); + } ClearLine (); } else { switch (FindPPToken (Directive)) {