🐐 short circuit evaluation
This commit is contained in:
@@ -806,6 +806,20 @@ class BinOp(Node):
|
|||||||
return f"({self.left.print()} {OP_STR[self.op]} {self.right.print()})"
|
return f"({self.left.print()} {OP_STR[self.op]} {self.right.print()})"
|
||||||
|
|
||||||
def _eval(self, vtable):
|
def _eval(self, vtable):
|
||||||
|
# Short-circuit for logical operators
|
||||||
|
if self.op == "KEYWORD_AVT":
|
||||||
|
vtable, left = self.left.eval(vtable)
|
||||||
|
if bool(left):
|
||||||
|
return vtable, ValBool(True)
|
||||||
|
vtable, right = self.right.eval(vtable)
|
||||||
|
return vtable, ValBool(bool(right))
|
||||||
|
if self.op == "KEYWORD_ET":
|
||||||
|
vtable, left = self.left.eval(vtable)
|
||||||
|
if not bool(left):
|
||||||
|
return vtable, ValBool(False)
|
||||||
|
vtable, right = self.right.eval(vtable)
|
||||||
|
return vtable, ValBool(bool(right))
|
||||||
|
|
||||||
vtable, left = self.left.eval(vtable)
|
vtable, left = self.left.eval(vtable)
|
||||||
vtable, right = self.right.eval(vtable)
|
vtable, right = self.right.eval(vtable)
|
||||||
lv, rv = left.value(), right.value()
|
lv, rv = left.value(), right.value()
|
||||||
@@ -874,10 +888,6 @@ class BinOp(Node):
|
|||||||
(isinstance(left, ValNul) and isinstance(right, ValInt) and rv == 0)):
|
(isinstance(left, ValNul) and isinstance(right, ValInt) and rv == 0)):
|
||||||
return vtable, ValBool(False)
|
return vtable, ValBool(False)
|
||||||
return vtable, ValBool(lv != rv)
|
return vtable, ValBool(lv != rv)
|
||||||
case "KEYWORD_ET":
|
|
||||||
return vtable, ValBool(bool(left) and bool(right))
|
|
||||||
case "KEYWORD_AVT":
|
|
||||||
return vtable, ValBool(bool(left) or bool(right))
|
|
||||||
case _:
|
case _:
|
||||||
raise Exception(self.op)
|
raise Exception(self.op)
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,25 @@ def emit_expr(node, ctx):
|
|||||||
return [f'CentValue {tmp} = cent_scope_get(&_scope, "{node.name}");'], tmp
|
return [f'CentValue {tmp} = cent_scope_get(&_scope, "{node.name}");'], tmp
|
||||||
|
|
||||||
if isinstance(node, BinOp):
|
if isinstance(node, BinOp):
|
||||||
|
# Short-circuit for logical operators
|
||||||
|
if node.op in ("KEYWORD_AVT", "KEYWORD_ET"):
|
||||||
|
l_lines, l_var = emit_expr(node.left, ctx)
|
||||||
|
r_lines, r_var = emit_expr(node.right, ctx)
|
||||||
|
tmp = ctx.fresh_tmp()
|
||||||
|
if node.op == "KEYWORD_AVT":
|
||||||
|
lines = l_lines + [f"CentValue {tmp};"]
|
||||||
|
lines += [f"if (cent_truthy({l_var})) {{ {tmp} = cent_bool(1); }} else {{"]
|
||||||
|
lines += [f" {l}" for l in r_lines]
|
||||||
|
lines += [f" {tmp} = cent_bool(cent_truthy({r_var}));"]
|
||||||
|
lines += ["}"]
|
||||||
|
else:
|
||||||
|
lines = l_lines + [f"CentValue {tmp};"]
|
||||||
|
lines += [f"if (!cent_truthy({l_var})) {{ {tmp} = cent_bool(0); }} else {{"]
|
||||||
|
lines += [f" {l}" for l in r_lines]
|
||||||
|
lines += [f" {tmp} = cent_bool(cent_truthy({r_var}));"]
|
||||||
|
lines += ["}"]
|
||||||
|
return lines, tmp
|
||||||
|
|
||||||
l_lines, l_var = emit_expr(node.left, ctx)
|
l_lines, l_var = emit_expr(node.left, ctx)
|
||||||
r_lines, r_var = emit_expr(node.right, ctx)
|
r_lines, r_var = emit_expr(node.right, ctx)
|
||||||
tmp = ctx.fresh_tmp()
|
tmp = ctx.fresh_tmp()
|
||||||
|
|||||||
29
tests.py
29
tests.py
@@ -698,6 +698,9 @@ error_tests = [
|
|||||||
("DEFINI f () VT { REDI(I) }\nINVOCA f (I)", CentvrionError), # args to zero-param function
|
("DEFINI f () VT { REDI(I) }\nINVOCA f (I)", CentvrionError), # args to zero-param function
|
||||||
("SI NVLLVS TVNC { DESIGNA r VT I }", CentvrionError), # NVLLVS cannot be used as boolean
|
("SI NVLLVS TVNC { DESIGNA r VT I }", CentvrionError), # NVLLVS cannot be used as boolean
|
||||||
("NVLLVS AVT VERITAS", CentvrionError), # NVLLVS cannot be used as boolean in AVT
|
("NVLLVS AVT VERITAS", CentvrionError), # NVLLVS cannot be used as boolean in AVT
|
||||||
|
("FALSITAS AVT NVLLVS", CentvrionError), # no short-circuit: right side evaluated, NVLLVS not boolean
|
||||||
|
("VERITAS ET NVLLVS", CentvrionError), # no short-circuit: right side evaluated, NVLLVS not boolean
|
||||||
|
("NVLLVS ET VERITAS", CentvrionError), # NVLLVS cannot be used as boolean in ET
|
||||||
('"hello" + " world"', CentvrionError), # use & for string concatenation, not +
|
('"hello" + " world"', CentvrionError), # use & for string concatenation, not +
|
||||||
("[I, II][III]", CentvrionError), # index too high
|
("[I, II][III]", CentvrionError), # index too high
|
||||||
("CVM SVBNVLLA\n[I, II][-I]", CentvrionError), # negative index
|
("CVM SVBNVLLA\n[I, II][-I]", CentvrionError), # negative index
|
||||||
@@ -1612,6 +1615,32 @@ et_avt_tests = [
|
|||||||
("SI FALSITAS AVT FALSITAS TVNC { DESIGNA r VT I } ALIVD { DESIGNA r VT II }\nr",
|
("SI FALSITAS AVT FALSITAS TVNC { DESIGNA r VT I } ALIVD { DESIGNA r VT II }\nr",
|
||||||
Program([], [SiStatement(BinOp(Bool(False), Bool(False), "KEYWORD_AVT"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
|
Program([], [SiStatement(BinOp(Bool(False), Bool(False), "KEYWORD_AVT"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
|
||||||
ValInt(2)),
|
ValInt(2)),
|
||||||
|
# short-circuit: right side not evaluated when result is determined
|
||||||
|
("VERITAS AVT NVLLVS",
|
||||||
|
Program([], [ExpressionStatement(BinOp(Bool(True), Nullus(), "KEYWORD_AVT"))]),
|
||||||
|
ValBool(True)),
|
||||||
|
("FALSITAS ET NVLLVS",
|
||||||
|
Program([], [ExpressionStatement(BinOp(Bool(False), Nullus(), "KEYWORD_ET"))]),
|
||||||
|
ValBool(False)),
|
||||||
|
# short-circuit with side-effect-prone expressions
|
||||||
|
("DESIGNA x VT NVLLVS\nSI x EST NVLLVS AVT [I, II][x] EST I TVNC { DESIGNA r VT I } ALIVD { DESIGNA r VT II }\nr",
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("x"), Nullus()),
|
||||||
|
SiStatement(
|
||||||
|
BinOp(BinOp(ID("x"), Nullus(), "KEYWORD_EST"), BinOp(ArrayIndex(DataArray([Numeral("I"), Numeral("II")]), ID("x")), Numeral("I"), "KEYWORD_EST"), "KEYWORD_AVT"),
|
||||||
|
[Designa(ID("r"), Numeral("I"))],
|
||||||
|
[Designa(ID("r"), Numeral("II"))]),
|
||||||
|
ExpressionStatement(ID("r"))]),
|
||||||
|
ValInt(1)),
|
||||||
|
("DESIGNA x VT NVLLVS\nSI x DISPAR NVLLVS ET [I, II][x] EST I TVNC { DESIGNA r VT I } ALIVD { DESIGNA r VT II }\nr",
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("x"), Nullus()),
|
||||||
|
SiStatement(
|
||||||
|
BinOp(BinOp(ID("x"), Nullus(), "KEYWORD_DISPAR"), BinOp(ArrayIndex(DataArray([Numeral("I"), Numeral("II")]), ID("x")), Numeral("I"), "KEYWORD_EST"), "KEYWORD_ET"),
|
||||||
|
[Designa(ID("r"), Numeral("I"))],
|
||||||
|
[Designa(ID("r"), Numeral("II"))]),
|
||||||
|
ExpressionStatement(ID("r"))]),
|
||||||
|
ValInt(2)),
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestEtAvt(unittest.TestCase):
|
class TestEtAvt(unittest.TestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user