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 a506fa5a1..84f46504c 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);
}
@@ -1275,6 +1232,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 ();
@@ -3476,48 +3450,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.
@@ -3867,11 +3799,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 a607e3217..96bb0bf1c 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.
@@ -288,7 +293,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 +341,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 +351,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 +490,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 +840,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 +866,55 @@ static void DefineMacro (void)
+static 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 +962,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 +1018,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.
@@ -1028,9 +1065,12 @@ 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));
+ 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 */
@@ -1042,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);
@@ -1094,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);
}
@@ -1174,9 +1220,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);
@@ -1212,9 +1255,12 @@ 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));
+ 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 */
@@ -1250,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)) {
diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h
index 78a91a590..34f62c114 100644
--- a/src/cc65/preproc.h
+++ b/src/cc65/preproc.h
@@ -38,17 +38,6 @@
-/*****************************************************************************/
-/* data */
-/*****************************************************************************/
-
-
-
-/* Set when the preprocessor calls NoCodeConstExpr() recursively */
-extern unsigned char Preprocessing;
-
-
-
/*****************************************************************************/
/* code */
/*****************************************************************************/