Merge pull request #1800 from acqn/PPFix

[cc65] Preprocessor fixes
This commit is contained in:
Bob Andrews
2022-07-28 01:48:41 +02:00
committed by GitHub
6 changed files with 1091 additions and 203 deletions

View File

@@ -103,6 +103,7 @@
<ClInclude Include="cc65\macrotab.h" /> <ClInclude Include="cc65\macrotab.h" />
<ClInclude Include="cc65\opcodes.h" /> <ClInclude Include="cc65\opcodes.h" />
<ClInclude Include="cc65\output.h" /> <ClInclude Include="cc65\output.h" />
<ClInclude Include="cc65\ppexpr.h" />
<ClInclude Include="cc65\pragma.h" /> <ClInclude Include="cc65\pragma.h" />
<ClInclude Include="cc65\preproc.h" /> <ClInclude Include="cc65\preproc.h" />
<ClInclude Include="cc65\reginfo.h" /> <ClInclude Include="cc65\reginfo.h" />
@@ -182,6 +183,7 @@
<ClCompile Include="cc65\main.c" /> <ClCompile Include="cc65\main.c" />
<ClCompile Include="cc65\opcodes.c" /> <ClCompile Include="cc65\opcodes.c" />
<ClCompile Include="cc65\output.c" /> <ClCompile Include="cc65\output.c" />
<ClCompile Include="cc65\ppexpr.c" />
<ClCompile Include="cc65\pragma.c" /> <ClCompile Include="cc65\pragma.c" />
<ClCompile Include="cc65\preproc.c" /> <ClCompile Include="cc65\preproc.c" />
<ClCompile Include="cc65\reginfo.c" /> <ClCompile Include="cc65\reginfo.c" />

View File

@@ -304,13 +304,9 @@ void PushAddr (const ExprDesc* Expr)
static void WarnConstCompareResult (const ExprDesc* Expr) static void WarnConstCompareResult (const ExprDesc* Expr)
/* If the result of a comparison is constant, this is suspicious when not in /* If the result of a comparison is constant, this is suspicious */
** preprocessor mode.
*/
{ {
if (!Preprocessing && if (!ED_NeedsConst (Expr) && IS_Get (&WarnConstComparison) != 0) {
!ED_NeedsConst (Expr) &&
IS_Get (&WarnConstComparison) != 0) {
Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false"); 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. */ /* This is the lowest level of the expression parser. */
{ {
SymEntry* Sym; 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; unsigned Flags = E->Flags & E_MASK_KEEP_MAKE;
switch (CurTok.Tok) { switch (CurTok.Tok) {
case TOK_LPAREN:
/* Process parenthesized subexpression by calling the whole parser
** recursively.
*/
NextToken ();
hie0 (E);
ConsumeRParen ();
break;
case TOK_BOOL_AND: case TOK_BOOL_AND:
/* A computed goto label address */ /* A computed goto label address */
if (IS_Get (&Standard) >= STD_CC65) { if (IS_Get (&Standard) >= STD_CC65) {
@@ -1160,9 +1117,9 @@ static void Primary (ExprDesc* E)
/* Cannot use type symbols */ /* Cannot use type symbols */
Error ("Variable identifier expected"); Error ("Variable identifier expected");
/* Assume an int type to make E valid */ /* Assume an int type to make E valid */
E->Flags |= E_LOC_STACK | E_RTYPE_LVAL; E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int; E->Type = type_int;
return; break;
} }
/* Mark the symbol as referenced */ /* Mark the symbol as referenced */
@@ -1275,6 +1232,23 @@ static void Primary (ExprDesc* E)
NextToken (); NextToken ();
break; 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: case TOK_ASM:
/* ASM statement */ /* ASM statement */
AsmStatement (); 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) static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated)
/* Process "exp && exp". This should only be called within hieOr. /* Process "exp && exp". This should only be called within hieOr.
** Return true if logic AND does occur. ** Return true if logic AND does occur.
@@ -3867,11 +3799,7 @@ static void hieQuest (ExprDesc* Expr)
Type* ResultType; /* Type of result */ Type* ResultType; /* Type of result */
/* Call the lower level eval routine */ /* 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 */ /* Check if it's a ternary expression */
if (CurTok.Tok == TOK_QUEST) { if (CurTok.Tok == TOK_QUEST) {

845
src/cc65/ppexpr.c Normal file
View File

@@ -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;
}
}

76
src/cc65/ppexpr.h Normal file
View File

@@ -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

View File

@@ -48,13 +48,13 @@
/* cc65 */ /* cc65 */
#include "codegen.h" #include "codegen.h"
#include "error.h" #include "error.h"
#include "expr.h"
#include "global.h" #include "global.h"
#include "ident.h" #include "ident.h"
#include "incpath.h" #include "incpath.h"
#include "input.h" #include "input.h"
#include "lineinfo.h" #include "lineinfo.h"
#include "macrotab.h" #include "macrotab.h"
#include "ppexpr.h"
#include "preproc.h" #include "preproc.h"
#include "scanner.h" #include "scanner.h"
#include "standard.h" #include "standard.h"
@@ -68,7 +68,7 @@
/* Set when the preprocessor calls expr() recursively */ /* Set when the preprocessor calls expr() recursively */
unsigned char Preprocessing = 0; static unsigned char Preprocessing = 0;
/* Management data for #if */ /* Management data for #if */
#define MAX_IFS 256 #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); static unsigned Pass1 (StrBuf* Source, StrBuf* Target);
/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
** and the "defined" operator. ** and the "defined" operator.
@@ -288,7 +293,7 @@ static void OldStyleComment (void)
/* Remember the current line number, so we can output better error /* Remember the current line number, so we can output better error
** messages if the comment is not terminated in the current file. ** messages if the comment is not terminated in the current file.
*/ */
unsigned StartingLine = GetCurrentLine(); unsigned StartingLine = GetCurrentLine ();
/* Skip the start of comment chars */ /* Skip the start of comment chars */
NextChar (); NextChar ();
@@ -336,8 +341,8 @@ static void NewStyleComment (void)
static int SkipWhitespace (int SkipLines) static int SkipWhitespace (int SkipLines)
/* Skip white space in the input stream. Do also skip newlines if SkipLines /* Skip white space and comments in the input stream. Do also skip newlines if
** is true. Return zero if nothing was skipped, otherwise return a ** SkipLines is true. Return zero if nothing was skipped, otherwise return a
** value != zero. ** value != zero.
*/ */
{ {
@@ -346,6 +351,12 @@ static int SkipWhitespace (int SkipLines)
if (IsSpace (CurC)) { if (IsSpace (CurC)) {
NextChar (); NextChar ();
Skipped = 1; 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) { } else if (CurC == '\0' && SkipLines) {
/* End of line, read next */ /* End of line, read next */
if (NextLine () != 0) { if (NextLine () != 0) {
@@ -479,16 +490,6 @@ static void ReadMacroArgs (MacroExp* E)
if (SB_NotEmpty (&Arg)) { if (SB_NotEmpty (&Arg)) {
SB_AppendChar (&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') { } else if (CurC == '\0') {
/* End of input inside macro argument list */ /* End of input inside macro argument list */
PPError ("Unterminated argument list invoking macro '%s'", E->M->Name); 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 /* Remove whitespace and comments from the line, store the preprocessed
** line into the macro replacement buffer. ** line into the macro replacement buffer.
*/ */
Pass1 (Line, &M->Replacement); TranslationPhase3 (Line, &M->Replacement);
/* Remove whitespace from the end of the line */ /* Remove whitespace from the end of the line */
while (IsSpace (SB_LookAtLast (&M->Replacement))) { 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) static unsigned Pass1 (StrBuf* Source, StrBuf* Target)
/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments /* Preprocessor pass 1. Remove whitespace, old and new style comments. Handle
** and the "defined" operator. ** the "defined" operator.
*/ */
{ {
unsigned IdentCount; unsigned IdentCount;
@@ -915,16 +962,6 @@ static unsigned Pass1 (StrBuf* Source, StrBuf* Target)
} }
} else if (IsQuote (CurC)) { } else if (IsQuote (CurC)) {
CopyQuotedString (Target); 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 { } else {
SB_AppendChar (Target, CurC); SB_AppendChar (Target, CurC);
NextChar (); NextChar ();
@@ -981,7 +1018,7 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target)
static void PreprocessLine (void) 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 /* Trim whitespace and remove comments. The function returns the number of
** identifiers found. If there were any, we will have to check for macros. ** identifiers found. If there were any, we will have to check for macros.
@@ -1028,9 +1065,12 @@ static void DoError (void)
{ {
SkipWhitespace (0); SkipWhitespace (0);
if (CurC == '\0') { if (CurC == '\0') {
PPError ("Invalid #error directive"); PPError ("#error");
} else { } 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 */ /* Clear the rest of line */
@@ -1042,10 +1082,11 @@ static void DoError (void)
static int DoIf (int Skip) static int DoIf (int Skip)
/* Process #if directive */ /* Process #if directive */
{ {
/* We're about to abuse the compiler expression parser to evaluate the PPExpr Expr = AUTO_PPEXPR_INITIALIZER;
** #if expression. Save the current tokens to come back here later.
** NOTE: Yes, this is a hack, but it saves a complete separate expression if (!Skip) {
** evaluation for the preprocessor. /* 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 SavedCurTok = CurTok;
Token SavedNextTok = NextTok; Token SavedNextTok = NextTok;
@@ -1076,7 +1117,7 @@ static int DoIf (int Skip)
NextToken (); NextToken ();
/* Call the expression parser */ /* Call the expression parser */
ExprDesc Expr = NoCodeConstExpr (hie1); ParsePPExpr (&Expr);
/* End preprocessing mode */ /* End preprocessing mode */
Preprocessing = 0; Preprocessing = 0;
@@ -1084,6 +1125,7 @@ static int DoIf (int Skip)
/* Reset the old tokens */ /* Reset the old tokens */
CurTok = SavedCurTok; CurTok = SavedCurTok;
NextTok = SavedNextTok; NextTok = SavedNextTok;
}
/* Set the #if condition according to the expression result */ /* Set the #if condition according to the expression result */
return PushIf (Skip, 1, Expr.IVal != 0); return PushIf (Skip, 1, Expr.IVal != 0);
@@ -1094,14 +1136,18 @@ static int DoIf (int Skip)
static int DoIfDef (int skip, int flag) static int DoIfDef (int skip, int flag)
/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */ /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
{ {
int Value = 0;
if (!skip) {
ident Ident; ident Ident;
SkipWhitespace (0); SkipWhitespace (0);
if (MacName (Ident) == 0) { if (MacName (Ident)) {
return 0; Value = IsMacro (Ident);
} else {
return PushIf (skip, flag, IsMacro(Ident));
} }
}
return PushIf (skip, flag, Value);
} }
@@ -1174,9 +1220,6 @@ static void DoPragma (void)
** the _Pragma() compiler operator. ** the _Pragma() compiler operator.
*/ */
{ {
/* Skip blanks following the #pragma directive */
SkipWhitespace (0);
/* Copy the remainder of the line into MLine removing comments and ws */ /* Copy the remainder of the line into MLine removing comments and ws */
SB_Clear (MLine); SB_Clear (MLine);
Pass1 (Line, MLine); Pass1 (Line, MLine);
@@ -1212,9 +1255,12 @@ static void DoWarning (void)
{ {
SkipWhitespace (0); SkipWhitespace (0);
if (CurC == '\0') { if (CurC == '\0') {
PPError ("Invalid #warning directive"); PPWarning ("#warning");
} else { } 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 */ /* Clear the rest of line */
@@ -1250,7 +1296,9 @@ void Preprocess (void)
continue; continue;
} }
if (!IsSym (Directive)) { if (!IsSym (Directive)) {
if (!Skip) {
PPError ("Preprocessor directive expected"); PPError ("Preprocessor directive expected");
}
ClearLine (); ClearLine ();
} else { } else {
switch (FindPPToken (Directive)) { switch (FindPPToken (Directive)) {

View File

@@ -38,17 +38,6 @@
/*****************************************************************************/
/* data */
/*****************************************************************************/
/* Set when the preprocessor calls NoCodeConstExpr() recursively */
extern unsigned char Preprocessing;
/*****************************************************************************/ /*****************************************************************************/
/* code */ /* code */
/*****************************************************************************/ /*****************************************************************************/