Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logical operators || and && are short-circuiting #665

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/asm/rpn.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
return expr->isSymbol;
}

void rpn_EnterShortCircuitOp(bool startShortCircuit);
void rpn_LeaveShortCircuitOp(void);
bool rpn_IsShortCircuited(void);
void rpn_Symbol(struct Expression *expr, char const *tzSym);
void rpn_Number(struct Expression *expr, uint32_t i);
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
Expand Down
3 changes: 3 additions & 0 deletions src/asm/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,9 @@ static char const *readInterpolation(void)
}
}

if (rpn_IsShortCircuited())
return NULL;

if (i == sizeof(symName)) {
warning(WARNING_LONG_STR, "Symbol name too long\n");
i--;
Expand Down
99 changes: 70 additions & 29 deletions src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -1185,11 +1185,27 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
| T_OP_LOGICNOT relocexpr %prec NEG {
rpn_LOGNOT(&$$, &$2);
}
| relocexpr T_OP_LOGICOR relocexpr {
rpn_BinaryOp(RPN_LOGOR, &$$, &$1, &$3);
}
| relocexpr T_OP_LOGICAND relocexpr {
rpn_BinaryOp(RPN_LOGAND, &$$, &$1, &$3);
| relocexpr T_OP_LOGICOR {
// If the first operand is nonzero...
rpn_EnterShortCircuitOp(rpn_isKnown(&$1) && $1.nVal);
} relocexpr {
// ...then short-circuit to 1 without evaluating the second operand.
if (rpn_IsShortCircuited())
rpn_Number(&$$, 1);
else
rpn_BinaryOp(RPN_LOGOR, &$$, &$1, &$4);
rpn_LeaveShortCircuitOp();
}
| relocexpr T_OP_LOGICAND {
// If the first operand is zero...
rpn_EnterShortCircuitOp(rpn_isKnown(&$1) && !$1.nVal);
} relocexpr {
// ...then short-circuit to 0 without evaluating the second operand.
if (rpn_IsShortCircuited())
rpn_Number(&$$, 0);
else
rpn_BinaryOp(RPN_LOGAND, &$$, &$1, &$4);
rpn_LeaveShortCircuitOp();
}
| relocexpr T_OP_LOGICEQU relocexpr {
rpn_BinaryOp(RPN_LOGEQ, &$$, &$1, &$3);
Expand Down Expand Up @@ -1355,32 +1371,52 @@ const_no_str : relocexpr_no_str {

string : T_STRING
| T_OP_STRSUB T_LPAREN string T_COMMA uconst T_COMMA uconst T_RPAREN {
strsubUTF8($$, $3, $5, $7);
if (rpn_IsShortCircuited())
$$[0] = '\0';
else
strsubUTF8($$, $3, $5, $7);
}
| T_OP_STRCAT T_LPAREN T_RPAREN {
$$[0] = '\0';
}
| T_OP_STRCAT T_LPAREN strcat_args T_RPAREN {
strcpy($$, $3);
if (rpn_IsShortCircuited())
$$[0] = '\0';
else
strcpy($$, $3);
}
| T_OP_STRUPR T_LPAREN string T_RPAREN {
upperstring($$, $3);
if (rpn_IsShortCircuited())
$$[0] = '\0';
else
upperstring($$, $3);
}
| T_OP_STRLWR T_LPAREN string T_RPAREN {
lowerstring($$, $3);
if (rpn_IsShortCircuited())
$$[0] = '\0';
else
lowerstring($$, $3);
}
| T_OP_STRRPL T_LPAREN string T_COMMA string T_COMMA string T_RPAREN {
strrpl($$, sizeof($$), $3, $5, $7);
if (rpn_IsShortCircuited())
$$[0] = '\0';
else
strrpl($$, sizeof($$), $3, $5, $7);
}
| T_OP_STRFMT T_LPAREN strfmt_args T_RPAREN {
strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args);
if (rpn_IsShortCircuited())
$$[0] = '\0';
else
strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args);
freeStrFmtArgList(&$3);
}
;

strcat_args : string
| strcat_args T_COMMA string {
if (snprintf($$, sizeof($$), "%s%s", $1, $3) > MAXSTRLEN)
if (rpn_IsShortCircuited())
$$[0] = '\0';
else if (snprintf($$, sizeof($$), "%s%s", $1, $3) > MAXSTRLEN)
warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
$1, $3);
}
Expand All @@ -1395,31 +1431,36 @@ strfmt_args : string strfmt_va_args {
;

strfmt_va_args : /* empty */ {
initStrFmtArgList(&$$);
if (!rpn_IsShortCircuited())
initStrFmtArgList(&$$);
}
| strfmt_va_args T_COMMA relocexpr_no_str {
int32_t value;
if (!rpn_IsShortCircuited()) {
int32_t value;

if (!rpn_isKnown(&$3)) {
error("Expected constant expression: %s\n",
$3.reason);
value = 0;
} else {
value = $3.nVal;
}
if (!rpn_isKnown(&$3)) {
error("Expected constant expression: %s\n",
$3.reason);
value = 0;
} else {
value = $3.nVal;
}

size_t i = nextStrFmtArgListIndex(&$1);
size_t i = nextStrFmtArgListIndex(&$1);

$1.args[i].number = value;
$1.args[i].isNumeric = true;
$$ = $1;
$1.args[i].number = value;
$1.args[i].isNumeric = true;
$$ = $1;
}
}
| strfmt_va_args T_COMMA string {
size_t i = nextStrFmtArgListIndex(&$1);
if (!rpn_IsShortCircuited()) {
size_t i = nextStrFmtArgListIndex(&$1);

$1.args[i].string = strdup($3);
$1.args[i].isNumeric = false;
$$ = $1;
$1.args[i].string = strdup($3);
$1.args[i].isNumeric = false;
$$ = $1;
}
}
;

Expand Down
42 changes: 42 additions & 0 deletions src/asm/rpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include "asm/symbol.h"
#include "asm/warning.h"

uint32_t nestedShortCircuitOps; // How many nested short-circuitable operators there currently are
uint32_t shortCircuitAtDepth; // The nested depth at which short-circuiting occurred (0 if N/A)

/* Makes an expression "not known", also setting its error message */
#define makeUnknown(expr_, ...) do { \
struct Expression *_expr = expr_; \
Expand Down Expand Up @@ -95,6 +98,25 @@ void rpn_Free(struct Expression *expr)
rpn_Init(expr);
}

void rpn_EnterShortCircuitOp(bool startShortCircuit)
{
nestedShortCircuitOps++;
if (startShortCircuit && shortCircuitAtDepth == 0)
shortCircuitAtDepth = nestedShortCircuitOps;
}

void rpn_LeaveShortCircuitOp(void)
{
nestedShortCircuitOps--;
if (shortCircuitAtDepth > nestedShortCircuitOps)
shortCircuitAtDepth = 0;
}

bool rpn_IsShortCircuited(void)
{
return shortCircuitAtDepth > 0 && shortCircuitAtDepth >= nestedShortCircuitOps;
}

/*
* Add symbols, constants and operators to expression
*/
Expand All @@ -106,6 +128,11 @@ void rpn_Number(struct Expression *expr, uint32_t i)

void rpn_Symbol(struct Expression *expr, char const *tzSym)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

struct Symbol *sym = sym_FindScopedSymbol(tzSym);

if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
Expand Down Expand Up @@ -157,6 +184,11 @@ void rpn_BankSelf(struct Expression *expr)

void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

struct Symbol const *sym = sym_FindScopedSymbol(tzSym);

/* The @ symbol is treated differently. */
Expand Down Expand Up @@ -189,6 +221,11 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)

void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

rpn_Init(expr);

struct Section *pSection = out_FindSectionByName(tzSectionName);
Expand Down Expand Up @@ -335,6 +372,11 @@ static bool isDiffConstant(struct Expression const *src1,
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
const struct Expression *src1, const struct Expression *src2)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

expr->isSymbol = false;

/* First, check if the expression is known */
Expand Down
8 changes: 8 additions & 0 deletions test/asm/short-circuiting.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

println 1 == 1 || undef_n == 42

println 1 == 2 && 0 < STRLEN("{undef_s}")

println ((1 == 1 || (undef_a || undef_b)) || undef_c)

println 1 == 2 && 0 < STRLEN(STRFMT("%d%s%v", "{undef_s}"))
Empty file added test/asm/short-circuiting.err
Empty file.
4 changes: 4 additions & 0 deletions test/asm/short-circuiting.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$1
$0
$1
$0