🐐 __eq__ and precendence rules

This commit is contained in:
2026-04-01 11:12:11 +02:00
parent 58149b5f65
commit 83657f17f5
2 changed files with 546 additions and 139 deletions

View File

@@ -130,6 +130,9 @@ class ExpressionStatement(Node):
def __init__(self, expression) -> None: def __init__(self, expression) -> None:
self.expression = expression self.expression = expression
def __eq__(self, other):
return type(self) == type(other) and self.expression == other.expression
def __repr__(self) -> str: def __repr__(self) -> str:
return self.expression.__repr__() return self.expression.__repr__()
@@ -141,6 +144,9 @@ class DataArray(Node):
def __init__(self, content) -> None: def __init__(self, content) -> None:
self.content = content self.content = content
def __eq__(self, other):
return type(self) == type(other) and self.content == other.content
def __repr__(self) -> str: def __repr__(self) -> str:
content_string = rep_join(self.content) content_string = rep_join(self.content)
return f"Array([{content_string}])" return f"Array([{content_string}])"
@@ -158,6 +164,9 @@ class DataRangeArray(Node):
self.from_value = from_value self.from_value = from_value
self.to_value = to_value self.to_value = to_value
def __eq__(self, other):
return type(self) == type(other) and self.from_value == other.from_value and self.to_value == other.to_value
def __repr__(self) -> str: def __repr__(self) -> str:
content_string = rep_join([self.from_value, self.to_value]) content_string = rep_join([self.from_value, self.to_value])
return f"RangeArray([{content_string}])" return f"RangeArray([{content_string}])"
@@ -172,6 +181,9 @@ class String(Node):
def __init__(self, value) -> None: def __init__(self, value) -> None:
self.value = value self.value = value
def __eq__(self, other):
return type(self) == type(other) and self.value == other.value
def __repr__(self): def __repr__(self):
return f"String({self.value})" return f"String({self.value})"
@@ -183,6 +195,9 @@ class Numeral(Node):
def __init__(self, value) -> None: def __init__(self, value) -> None:
self.value = value self.value = value
def __eq__(self, other):
return type(self) == type(other) and self.value == other.value
def __repr__(self): def __repr__(self):
return f"Numeral({self.value})" return f"Numeral({self.value})"
@@ -194,6 +209,9 @@ class Bool(Node):
def __init__(self, value) -> None: def __init__(self, value) -> None:
self.value = value self.value = value
def __eq__(self, other):
return type(self) == type(other) and self.value == other.value
def __repr__(self): def __repr__(self):
return f"Bool({self.value})" return f"Bool({self.value})"
@@ -205,6 +223,9 @@ class ModuleCall(BaseBox):
def __init__(self, module_name) -> None: def __init__(self, module_name) -> None:
self.module_name = module_name self.module_name = module_name
def __eq__(self, other):
return type(self) == type(other) and self.module_name == other.module_name
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self.module_name}" return f"{self.module_name}"
@@ -213,6 +234,9 @@ class ID(Node):
def __init__(self, name: str) -> None: def __init__(self, name: str) -> None:
self.name = name self.name = name
def __eq__(self, other):
return type(self) == type(other) and self.name == other.name
def __repr__(self) -> str: def __repr__(self) -> str:
return f"ID({self.name})" return f"ID({self.name})"
@@ -225,6 +249,9 @@ class Designa(Node):
self.id = variable self.id = variable
self.value = value self.value = value
def __eq__(self, other):
return type(self) == type(other) and self.id == other.id and self.value == other.value
def __repr__(self) -> str: def __repr__(self) -> str:
id_string = repr(self.id).replace('\n', '\n ') id_string = repr(self.id).replace('\n', '\n ')
value_string = repr(self.value).replace('\n', '\n ') value_string = repr(self.value).replace('\n', '\n ')
@@ -242,6 +269,9 @@ class Defini(Node):
self.parameters = parameters self.parameters = parameters
self.statements = statements self.statements = statements
def __eq__(self, other):
return type(self) == type(other) and self.name == other.name and self.parameters == other.parameters and self.statements == other.statements
def __repr__(self) -> str: def __repr__(self) -> str:
parameter_string = f"parameters([{rep_join(self.parameters)}])" parameter_string = f"parameters([{rep_join(self.parameters)}])"
statements_string = f"statements([{rep_join(self.statements)}])" statements_string = f"statements([{rep_join(self.statements)}])"
@@ -259,6 +289,9 @@ class Redi(Node):
def __init__(self, values) -> None: def __init__(self, values) -> None:
self.values = values self.values = values
def __eq__(self, other):
return type(self) == type(other) and self.values == other.values
def __repr__(self) -> str: def __repr__(self) -> str:
values_string = f"[{rep_join(self.values)}]" values_string = f"[{rep_join(self.values)}]"
return f"Redi({values_string})" return f"Redi({values_string})"
@@ -276,6 +309,9 @@ class Redi(Node):
class Erumpe(Node): class Erumpe(Node):
def __eq__(self, other):
return type(self) == type(other)
def __repr__(self) -> str: def __repr__(self) -> str:
return "Erumpe()" return "Erumpe()"
@@ -285,6 +321,9 @@ class Erumpe(Node):
class Nullus(Node): class Nullus(Node):
def __eq__(self, other):
return type(self) == type(other)
def __repr__(self) -> str: def __repr__(self) -> str:
return "Nullus()" return "Nullus()"
@@ -298,6 +337,9 @@ class BinOp(Node):
self.right = right self.right = right
self.op = op self.op = op
def __eq__(self, other):
return type(self) == type(other) and self.left == other.left and self.right == other.right and self.op == other.op
def __repr__(self) -> str: def __repr__(self) -> str:
binop_string = rep_join([self.left, self.right, self.op]) binop_string = rep_join([self.left, self.right, self.op])
return f"BinOp({binop_string})" return f"BinOp({binop_string})"
@@ -334,6 +376,9 @@ class UnaryMinus(Node):
def __init__(self, expr): def __init__(self, expr):
self.expr = expr self.expr = expr
def __eq__(self, other):
return type(self) == type(other) and self.expr == other.expr
def __repr__(self): def __repr__(self):
return f"UnaryMinus({self.expr!r})" return f"UnaryMinus({self.expr!r})"
@@ -347,6 +392,9 @@ class ArrayIndex(Node):
self.array = array self.array = array
self.index = index self.index = index
def __eq__(self, other):
return type(self) == type(other) and self.array == other.array and self.index == other.index
def __repr__(self) -> str: def __repr__(self) -> str:
return f"ArrayIndex({self.array!r}, {self.index!r})" return f"ArrayIndex({self.array!r}, {self.index!r})"
@@ -366,6 +414,9 @@ class SiStatement(Node):
self.statements = statements self.statements = statements
self.else_part = else_part self.else_part = else_part
def __eq__(self, other):
return type(self) == type(other) and self.test == other.test and self.statements == other.statements and self.else_part == other.else_part
def __repr__(self) -> str: def __repr__(self) -> str:
test = repr(self.test) test = repr(self.test)
statements = f"statements([{rep_join(self.statements)}])" statements = f"statements([{rep_join(self.statements)}])"
@@ -390,6 +441,9 @@ class DumStatement(Node):
self.test = test self.test = test
self.statements = statements self.statements = statements
def __eq__(self, other):
return type(self) == type(other) and self.test == other.test and self.statements == other.statements
def __repr__(self) -> str: def __repr__(self) -> str:
test = repr(self.test) test = repr(self.test)
statements = f"statements([{rep_join(self.statements)}])" statements = f"statements([{rep_join(self.statements)}])"
@@ -418,6 +472,9 @@ class PerStatement(Node):
self.variable_name = variable_name self.variable_name = variable_name
self.statements = statements self.statements = statements
def __eq__(self, other):
return type(self) == type(other) and self.data_list == other.data_list and self.variable_name == other.variable_name and self.statements == other.statements
def __repr__(self) -> str: def __repr__(self) -> str:
test = repr(self.data_list) test = repr(self.data_list)
variable_name = repr(self.variable_name) variable_name = repr(self.variable_name)
@@ -447,6 +504,9 @@ class Invoca(Node):
self.name = name self.name = name
self.parameters = parameters self.parameters = parameters
def __eq__(self, other):
return type(self) == type(other) and self.name == other.name and self.parameters == other.parameters
def __repr__(self) -> str: def __repr__(self) -> str:
parameters_string = f"parameters([{rep_join(self.parameters)}])" parameters_string = f"parameters([{rep_join(self.parameters)}])"
invoca_string = rep_join([self.name, parameters_string]) invoca_string = rep_join([self.name, parameters_string])
@@ -475,6 +535,9 @@ class BuiltIn(Node):
self.builtin = builtin self.builtin = builtin
self.parameters = parameters self.parameters = parameters
def __eq__(self, other):
return type(self) == type(other) and self.builtin == other.builtin and self.parameters == other.parameters
def __repr__(self) -> str: def __repr__(self) -> str:
parameter_string = f"parameters([{rep_join(self.parameters)}])" parameter_string = f"parameters([{rep_join(self.parameters)}])"
builtin_string = rep_join([self.builtin, parameter_string]) builtin_string = rep_join([self.builtin, parameter_string])
@@ -521,6 +584,9 @@ class Program(BaseBox):
self.modules = module_calls self.modules = module_calls
self.statements = statements self.statements = statements
def __eq__(self, other):
return type(self) == type(other) and self.modules == other.modules and self.statements == other.statements
def __repr__(self) -> str: def __repr__(self) -> str:
modules_string = f"modules([{rep_join(self.modules)}])" modules_string = f"modules([{rep_join(self.modules)}])"
statements_string = f"statements([{rep_join(self.statements)}])" statements_string = f"statements([{rep_join(self.statements)}])"

619
tests.py
View File

@@ -23,13 +23,14 @@ def run_test(self, source, target_nodes, target_value, target_output="", input_l
program = Parser().parse(tokens) program = Parser().parse(tokens)
########################## ##########################
####### Parser Test ###### (commented out — no __eq__ on AST nodes yet) ####### Parser Test ######
########################## ##########################
# self.assertEqual( if target_nodes is not None:
# program, self.assertEqual(
# target_nodes, program,
# f"Parser test:\n{program}\n{target_nodes}" target_nodes,
# ) f"Parser test:\n{program}\n{target_nodes}"
)
########################## ##########################
#### Interpreter Test #### #### Interpreter Test ####
@@ -77,16 +78,16 @@ def run_test(self, source, target_nodes, target_value, target_output="", input_l
# --- Output --- # --- Output ---
output_tests = [ output_tests = [
("DICE(\"hello\")", None, ValStr("hello"), "hello\n"), ("DICE(\"hello\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("hello")]))]), ValStr("hello"), "hello\n"),
("DICE(\"world\")", None, ValStr("world"), "world\n"), ("DICE(\"world\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("world")]))]), ValStr("world"), "world\n"),
("DICE(III)", None, ValStr("III"), "III\n"), ("DICE(III)", Program([], [ExpressionStatement(BuiltIn("DICE", [Numeral("III")]))]), ValStr("III"), "III\n"),
("DICE(X)", None, ValStr("X"), "X\n"), ("DICE(X)", Program([], [ExpressionStatement(BuiltIn("DICE", [Numeral("X")]))]), ValStr("X"), "X\n"),
("DICE(MMXXV)", None, ValStr("MMXXV"), "MMXXV\n"), ("DICE(MMXXV)", Program([], [ExpressionStatement(BuiltIn("DICE", [Numeral("MMXXV")]))]), ValStr("MMXXV"), "MMXXV\n"),
("DICE('hello')", None, ValStr("hello"), "hello\n"), ("DICE('hello')", Program([], [ExpressionStatement(BuiltIn("DICE", [String("hello")]))]), ValStr("hello"), "hello\n"),
("DICE('world')", None, ValStr("world"), "world\n"), ("DICE('world')", Program([], [ExpressionStatement(BuiltIn("DICE", [String("world")]))]), ValStr("world"), "world\n"),
("DICE(\"a\", \"b\")", None, ValStr("a b"), "a b\n"), ("DICE(\"a\", \"b\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("a"), String("b")]))]), ValStr("a b"), "a b\n"),
("DICE(\"line one\")\nDICE(\"line two\")", None, ValStr("line two"), "line one\nline two\n"), ("DICE(\"line one\")\nDICE(\"line two\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("line one")])), ExpressionStatement(BuiltIn("DICE", [String("line two")]))]), ValStr("line two"), "line one\nline two\n"),
("DICE(DICE(II))", None, ValStr("II"), "II\nII\n"), ("DICE(DICE(II))", Program([], [ExpressionStatement(BuiltIn("DICE", [BuiltIn("DICE", [Numeral("II")])]))]), ValStr("II"), "II\nII\n"),
] ]
class TestOutput(unittest.TestCase): class TestOutput(unittest.TestCase):
@@ -98,17 +99,17 @@ class TestOutput(unittest.TestCase):
# --- Arithmetic --- # --- Arithmetic ---
arithmetic_tests = [ arithmetic_tests = [
("I + I", None, ValInt(2)), ("I + I", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("I"), "SYMBOL_PLUS"))]), ValInt(2)),
("X - III", None, ValInt(7)), ("X - III", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "SYMBOL_MINUS"))]), ValInt(7)),
("III * IV", None, ValInt(12)), ("III * IV", Program([], [ExpressionStatement(BinOp(Numeral("III"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(12)),
("X / II", None, ValInt(5)), ("X / II", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))]), ValInt(5)),
("X / III", None, ValInt(3)), # integer division: 10 // 3 = 3 ("X / III", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "SYMBOL_DIVIDE"))]), ValInt(3)), # integer division: 10 // 3 = 3
("II + III * IV", None, ValInt(14)), # precedence: 2 + (3*4) = 14 ("II + III * IV", Program([], [ExpressionStatement(BinOp(Numeral("II"), BinOp(Numeral("III"), Numeral("IV"), "SYMBOL_TIMES"), "SYMBOL_PLUS"))]), ValInt(14)), # precedence: 2 + (3*4) = 14
("(II + III) * IV", None, ValInt(20)), # parens: (2+3)*4 = 20 ("(II + III) * IV", Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(20)), # parens: (2+3)*4 = 20
("- III", None, ValInt(-3)), # unary negation ("- III", Program([], [ExpressionStatement(UnaryMinus(Numeral("III")))]), ValInt(-3)), # unary negation
("- (II + III)", None, ValInt(-5)), # unary negation of expression ("- (II + III)", Program([], [ExpressionStatement(UnaryMinus(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS")))]), ValInt(-5)), # unary negation of expression
("- - II", None, ValInt(2)), # double negation ("- - II", Program([], [ExpressionStatement(UnaryMinus(UnaryMinus(Numeral("II"))))]), ValInt(2)), # double negation
("III + - II", None, ValInt(1)), # unary in binary context ("III + - II", Program([], [ExpressionStatement(BinOp(Numeral("III"), UnaryMinus(Numeral("II")), "SYMBOL_PLUS"))]), ValInt(1)), # unary in binary context
] ]
class TestArithmetic(unittest.TestCase): class TestArithmetic(unittest.TestCase):
@@ -117,14 +118,93 @@ class TestArithmetic(unittest.TestCase):
run_test(self, source, nodes, value) run_test(self, source, nodes, value)
# --- Precedence and associativity ---
#
# Precedence (lowest → highest):
# AVT < ET < (EST, PLVS, MINVS) < (+ -) < (* /) < UMINUS < INDEX
precedence_tests = [
# * binds tighter than -: 10 - (2*3) = 4, not (10-2)*3 = 24
("X - II * III",
Program([], [ExpressionStatement(BinOp(Numeral("X"), BinOp(Numeral("II"), Numeral("III"), "SYMBOL_TIMES"), "SYMBOL_MINUS"))]),
ValInt(4)),
# / binds tighter than +: (10/2) + 3 = 8, not 10/(2+3) = 2
("X / II + III",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"), Numeral("III"), "SYMBOL_PLUS"))]),
ValInt(8)),
# + binds tighter than EST: (2+3)==5 = True, not 2+(3==5) = type error
("II + III EST V",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), Numeral("V"), "KEYWORD_EST"))]),
ValBool(True)),
# * binds tighter than PLVS: (2*3)>4 = True
("II * III PLVS IV",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_TIMES"), Numeral("IV"), "KEYWORD_PLVS"))]),
ValBool(True)),
# comparison binds tighter than ET: (1==2) AND (2==2) = False AND True = False
("I EST II ET II EST II",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"), BinOp(Numeral("II"), Numeral("II"), "KEYWORD_EST"), "KEYWORD_ET"))]),
ValBool(False)),
# ET binds tighter than AVT: True OR (False AND False) = True
("VERITAS AVT FALSITAS ET FALSITAS",
Program([], [ExpressionStatement(BinOp(Bool(True), BinOp(Bool(False), Bool(False), "KEYWORD_ET"), "KEYWORD_AVT"))]),
ValBool(True)),
# UMINUS binds tighter than *: (-2)*3 = -6, not -(2*3) = -6 (same value, different tree)
("- II * III",
Program([], [ExpressionStatement(BinOp(UnaryMinus(Numeral("II")), Numeral("III"), "SYMBOL_TIMES"))]),
ValInt(-6)),
# UMINUS binds tighter than +: (-2)+3 = 1, not -(2+3) = -5
("- II + III",
Program([], [ExpressionStatement(BinOp(UnaryMinus(Numeral("II")), Numeral("III"), "SYMBOL_PLUS"))]),
ValInt(1)),
# INDEX binds tighter than UMINUS: -(arr[I]) = -1
("- [(I, II, III)][I]",
Program([], [ExpressionStatement(UnaryMinus(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("I"))))]),
ValInt(-1)),
# INDEX binds tighter than +: (arr[II]) + X = 2 + 10 = 12
("[(I, II, III)][II] + X",
Program([], [ExpressionStatement(BinOp(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("II")), Numeral("X"), "SYMBOL_PLUS"))]),
ValInt(12)),
# left-associativity of -: (10-3)-2 = 5, not 10-(3-2) = 9
("X - III - II",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("X"), Numeral("III"), "SYMBOL_MINUS"), Numeral("II"), "SYMBOL_MINUS"))]),
ValInt(5)),
# left-associativity of /: (12/2)/3 = 2, not 12/(2/3) = 18
("XII / II / III",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("XII"), Numeral("II"), "SYMBOL_DIVIDE"), Numeral("III"), "SYMBOL_DIVIDE"))]),
ValInt(2)),
# left-associativity of AVT: (False OR True) OR False = True
("FALSITAS AVT VERITAS AVT FALSITAS",
Program([], [ExpressionStatement(BinOp(BinOp(Bool(False), Bool(True), "KEYWORD_AVT"), Bool(False), "KEYWORD_AVT"))]),
ValBool(True)),
]
class TestPrecedence(unittest.TestCase):
@parameterized.expand(precedence_tests)
def test_precedence(self, source, nodes, value):
run_test(self, source, nodes, value)
# --- Assignment --- # --- Assignment ---
assignment_tests = [ assignment_tests = [
("DESIGNA x VT III\nx", None, ValInt(3)), ("DESIGNA x VT III\nx",
("DESIGNA msg VT \"hello\"\nmsg", None, ValStr("hello")), Program([], [Designa(ID("x"), Numeral("III")), ExpressionStatement(ID("x"))]),
("DESIGNA msg VT 'hello'\nmsg", None, ValStr("hello")), ValInt(3)),
("DESIGNA a VT V\nDESIGNA b VT X\na + b", None, ValInt(15)), ("DESIGNA msg VT \"hello\"\nmsg",
("DESIGNA x VT II\nDESIGNA x VT x + I\nx", None, ValInt(3)), Program([], [Designa(ID("msg"), String("hello")), ExpressionStatement(ID("msg"))]),
ValStr("hello")),
("DESIGNA msg VT 'hello'\nmsg",
Program([], [Designa(ID("msg"), String("hello")), ExpressionStatement(ID("msg"))]),
ValStr("hello")),
("DESIGNA a VT V\nDESIGNA b VT X\na + b",
Program([], [Designa(ID("a"), Numeral("V")), Designa(ID("b"), Numeral("X")),
ExpressionStatement(BinOp(ID("a"), ID("b"), "SYMBOL_PLUS"))]),
ValInt(15)),
("DESIGNA x VT II\nDESIGNA x VT x + I\nx",
Program([], [Designa(ID("x"), Numeral("II")),
Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")),
ExpressionStatement(ID("x"))]),
ValInt(3)),
] ]
class TestAssignment(unittest.TestCase): class TestAssignment(unittest.TestCase):
@@ -137,37 +217,79 @@ class TestAssignment(unittest.TestCase):
control_tests = [ control_tests = [
# SI without ALVID — true branch # SI without ALVID — true branch
("SI VERITAS TVNC { DESIGNA r VT I }\nr", None, ValInt(1)), ("SI VERITAS TVNC { DESIGNA r VT I }\nr",
Program([], [SiStatement(Bool(True), [Designa(ID("r"), Numeral("I"))], None), ExpressionStatement(ID("r"))]),
ValInt(1)),
# SI without ALVID — false branch # SI without ALVID — false branch
("SI FALSITAS TVNC { DESIGNA r VT I }", None, ValNul()), ("SI FALSITAS TVNC { DESIGNA r VT I }",
Program([], [SiStatement(Bool(False), [Designa(ID("r"), Numeral("I"))], None)]),
ValNul()),
# SI with ALVID — true branch # SI with ALVID — true branch
("SI VERITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), ("SI VERITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(Bool(True), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
# SI with ALVID — false branch # SI with ALVID — false branch
("SI FALSITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(2)), ("SI FALSITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(Bool(False), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(2)),
# SI with comparison — equal # SI with comparison — equal
("SI I EST I TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), ("SI I EST I TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(BinOp(Numeral("I"), Numeral("I"), "KEYWORD_EST"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
# SI with comparison — unequal # SI with comparison — unequal
("SI I EST II TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(2)), ("SI I EST II TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(2)),
# SI MINVS # SI MINVS
("SI I MINVS II TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), ("SI I MINVS II TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_MINVS"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
# SI PLVS # SI PLVS
("SI II PLVS I TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), ("SI II PLVS I TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(BinOp(Numeral("II"), Numeral("I"), "KEYWORD_PLVS"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
# ALVID SI chain # ALVID SI chain
( (
"SI I EST II TVNC { DESIGNA r VT I } ALVID SI I EST I TVNC { DESIGNA r VT II } ALVID { DESIGNA r VT III }\nr", "SI I EST II TVNC { DESIGNA r VT I } ALVID SI I EST I TVNC { DESIGNA r VT II } ALVID { DESIGNA r VT III }\nr",
None, ValInt(2), Program([], [
SiStatement(
BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"),
[Designa(ID("r"), Numeral("I"))],
[SiStatement(
BinOp(Numeral("I"), Numeral("I"), "KEYWORD_EST"),
[Designa(ID("r"), Numeral("II"))],
[Designa(ID("r"), Numeral("III"))],
)],
),
ExpressionStatement(ID("r")),
]),
ValInt(2),
), ),
# DVM (while not): loops until condition is true # DVM (while not): loops until condition is true
( (
"DESIGNA x VT I\nDVM x EST III FACE {\nDESIGNA x VT x + I\n}\nx", "DESIGNA x VT I\nDVM x EST III FACE {\nDESIGNA x VT x + I\n}\nx",
None, ValInt(3), Program([], [
Designa(ID("x"), Numeral("I")),
DumStatement(BinOp(ID("x"), Numeral("III"), "KEYWORD_EST"), [Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS"))]),
ExpressionStatement(ID("x")),
]),
ValInt(3),
), ),
# DVM with ERVMPE — loop body prints (testing DICE + ERVMPE together) # DVM with ERVMPE — loop body prints (testing DICE + ERVMPE together)
("DESIGNA x VT I\nDVM FALSITAS FACE {\nDICE(x)\nERVMPE\n}", None, ValStr("I"), "I\n"), ("DESIGNA x VT I\nDVM FALSITAS FACE {\nDICE(x)\nERVMPE\n}",
Program([], [
Designa(ID("x"), Numeral("I")),
DumStatement(Bool(False), [ExpressionStatement(BuiltIn("DICE", [ID("x")])), Erumpe()]),
]),
ValStr("I"), "I\n"),
# PER foreach # PER foreach
("PER i IN [(I, II, III)] FACE { DICE(i) }", None, ValStr("III"), "I\nII\nIII\n"), ("PER i IN [(I, II, III)] FACE { DICE(i) }",
Program([], [PerStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), ID("i"), [ExpressionStatement(BuiltIn("DICE", [ID("i")]))])]),
ValStr("III"), "I\nII\nIII\n"),
# DONICVM range loop # DONICVM range loop
("DONICVM i VT I VSQVE V FACE { DICE(i) }", None, ValStr("IV"), "I\nII\nIII\nIV\n"), ("DONICVM i VT I VSQVE V FACE { DICE(i) }",
Program([], [PerStatement(DataRangeArray(Numeral("I"), Numeral("V")), ID("i"), [ExpressionStatement(BuiltIn("DICE", [ID("i")]))])]),
ValStr("IV"), "I\nII\nIII\nIV\n"),
] ]
class TestControl(unittest.TestCase): class TestControl(unittest.TestCase):
@@ -181,16 +303,38 @@ class TestControl(unittest.TestCase):
function_tests = [ function_tests = [
( (
"DEFINI bis (n) VT { REDI (n * II) }\nINVOCA bis (III)", "DEFINI bis (n) VT { REDI (n * II) }\nINVOCA bis (III)",
None, ValInt(6), Program([], [
Defini(ID("bis"), [ID("n")], [Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")])]),
ExpressionStatement(Invoca(ID("bis"), [Numeral("III")])),
]),
ValInt(6),
), ),
( (
"DEFINI add (a, b) VT { REDI (a + b) }\nINVOCA add (III, IV)", "DEFINI add (a, b) VT { REDI (a + b) }\nINVOCA add (III, IV)",
None, ValInt(7), Program([], [
Defini(ID("add"), [ID("a"), ID("b")], [Redi([BinOp(ID("a"), ID("b"), "SYMBOL_PLUS")])]),
ExpressionStatement(Invoca(ID("add"), [Numeral("III"), Numeral("IV")])),
]),
ValInt(7),
), ),
# Fibonacci: fib(n<3)=1, fib(n)=fib(n-1)+fib(n-2) # Fibonacci: fib(n<3)=1, fib(n)=fib(n-1)+fib(n-2)
( (
"DEFINI fib (n) VT {\nSI n MINVS III TVNC { REDI (I) } ALVID { REDI (INVOCA fib (n - I) + INVOCA fib (n - II)) }\n}\nINVOCA fib (VII)", "DEFINI fib (n) VT {\nSI n MINVS III TVNC { REDI (I) } ALVID { REDI (INVOCA fib (n - I) + INVOCA fib (n - II)) }\n}\nINVOCA fib (VII)",
None, ValInt(13), Program([], [
Defini(ID("fib"), [ID("n")], [
SiStatement(
BinOp(ID("n"), Numeral("III"), "KEYWORD_MINVS"),
[Redi([Numeral("I")])],
[Redi([BinOp(
Invoca(ID("fib"), [BinOp(ID("n"), Numeral("I"), "SYMBOL_MINUS")]),
Invoca(ID("fib"), [BinOp(ID("n"), Numeral("II"), "SYMBOL_MINUS")]),
"SYMBOL_PLUS",
)])],
),
]),
ExpressionStatement(Invoca(ID("fib"), [Numeral("VII")])),
]),
ValInt(13),
), ),
] ]
@@ -203,12 +347,12 @@ class TestFunctions(unittest.TestCase):
# --- Builtins --- # --- Builtins ---
builtin_tests = [ builtin_tests = [
("AVDI_NVMERVS()", None, ValInt(3), "", ["III"]), ("AVDI_NVMERVS()", Program([], [ExpressionStatement(BuiltIn("AVDI_NVMERVS", []))]), ValInt(3), "", ["III"]),
("AVDI_NVMERVS()", None, ValInt(10), "", ["X"]), ("AVDI_NVMERVS()", Program([], [ExpressionStatement(BuiltIn("AVDI_NVMERVS", []))]), ValInt(10), "", ["X"]),
("CVM FORS\nFORTIS_NVMERVS(I, X)", None, ValInt(3)), ("CVM FORS\nFORTIS_NVMERVS(I, X)", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("FORTIS_NVMERVS", [Numeral("I"), Numeral("X")]))]), ValInt(3)),
("AVDI()", None, ValStr("hello"), "", ["hello"]), ("AVDI()", Program([], [ExpressionStatement(BuiltIn("AVDI", []))]), ValStr("hello"), "", ["hello"]),
("LONGITVDO([(I, II, III)])", None, ValInt(3)), ("LONGITVDO([(I, II, III)])", Program([], [ExpressionStatement(BuiltIn("LONGITVDO", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")])]))]), ValInt(3)),
("CVM FORS\nFORTIS_ELECTIONIS([(I, II, III)])", None, ValInt(1)), ("CVM FORS\nFORTIS_ELECTIONIS([(I, II, III)])", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("FORTIS_ELECTIONIS", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")])]))]), ValInt(1)),
] ]
class TestBuiltins(unittest.TestCase): class TestBuiltins(unittest.TestCase):
@@ -364,15 +508,15 @@ class TestMakeString(unittest.TestCase):
# --- DICE with non-integer types --- # --- DICE with non-integer types ---
dice_type_tests = [ dice_type_tests = [
("DICE(VERITAS)", None, ValStr("VERVS"), "VERVS\n"), ("DICE(VERITAS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Bool(True)]))]), ValStr("VERVS"), "VERVS\n"),
("DICE(FALSITAS)", None, ValStr("FALSVS"), "FALSVS\n"), ("DICE(FALSITAS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Bool(False)]))]), ValStr("FALSVS"), "FALSVS\n"),
("DICE(NVLLVS)", None, ValStr("NVLLVS"), "NVLLVS\n"), ("DICE(NVLLVS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Nullus()]))]), ValStr("NVLLVS"), "NVLLVS\n"),
('DICE([(I, II)])', None, ValStr("[I II]"), "[I II]\n"), ('DICE([(I, II)])', Program([], [ExpressionStatement(BuiltIn("DICE", [DataArray([Numeral("I"), Numeral("II")])]))]), ValStr("[I II]"), "[I II]\n"),
('DICE("")', None, ValStr(""), "\n"), ('DICE("")', Program([], [ExpressionStatement(BuiltIn("DICE", [String("")]))]), ValStr(""), "\n"),
# arithmetic result printed as numeral # arithmetic result printed as numeral
("DICE(II + III)", None, ValStr("V"), "V\n"), ("DICE(II + III)", Program([], [ExpressionStatement(BuiltIn("DICE", [BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS")]))]), ValStr("V"), "V\n"),
# multiple args of mixed types # multiple args of mixed types
('DICE("x", VERITAS)', None, ValStr("x VERVS"), "x VERVS\n"), ('DICE("x", VERITAS)', Program([], [ExpressionStatement(BuiltIn("DICE", [String("x"), Bool(True)]))]), ValStr("x VERVS"), "x VERVS\n"),
] ]
class TestDiceTypes(unittest.TestCase): class TestDiceTypes(unittest.TestCase):
@@ -385,17 +529,34 @@ class TestDiceTypes(unittest.TestCase):
truthiness_tests = [ truthiness_tests = [
# nonzero int is truthy # nonzero int is truthy
("SI I TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), ("SI I TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(Numeral("I"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
# zero int is falsy # zero int is falsy
("DESIGNA z VT I - I\nSI z TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(2)), ("DESIGNA z VT I - I\nSI z TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [
Designa(ID("z"), BinOp(Numeral("I"), Numeral("I"), "SYMBOL_MINUS")),
SiStatement(ID("z"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]),
ExpressionStatement(ID("r")),
]),
ValInt(2)),
# non-empty list is truthy # non-empty list is truthy
("SI [(I)] TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), ("SI [(I)] TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(DataArray([Numeral("I")]), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
# empty list is falsy # empty list is falsy
("SI [()] TVNC { DESIGNA r VT II } ALVID { DESIGNA r VT I }\nr", None, ValInt(1)), ("SI [()] TVNC { DESIGNA r VT II } ALVID { DESIGNA r VT I }\nr",
Program([], [SiStatement(DataArray([]), [Designa(ID("r"), Numeral("II"))], [Designa(ID("r"), Numeral("I"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
# DVM exits when condition becomes truthy # DVM exits when condition becomes truthy
( (
"DESIGNA x VT I\nDVM x PLVS III FACE {\nDESIGNA x VT x + I\n}\nx", "DESIGNA x VT I\nDVM x PLVS III FACE {\nDESIGNA x VT x + I\n}\nx",
None, ValInt(4), Program([], [
Designa(ID("x"), Numeral("I")),
DumStatement(BinOp(ID("x"), Numeral("III"), "KEYWORD_PLVS"), [Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS"))]),
ExpressionStatement(ID("x")),
]),
ValInt(4),
), ),
] ]
@@ -408,11 +569,11 @@ class TestTruthiness(unittest.TestCase):
# --- Arithmetic: edge cases --- # --- Arithmetic: edge cases ---
arithmetic_edge_tests = [ arithmetic_edge_tests = [
("I - I", None, ValInt(0)), # result zero ("I - I", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("I"), "SYMBOL_MINUS"))]), ValInt(0)), # result zero
("I - V", None, ValInt(-4)), # negative result ("I - V", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("V"), "SYMBOL_MINUS"))]), ValInt(-4)), # negative result
("I / V", None, ValInt(0)), # integer division → 0 ("I / V", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("V"), "SYMBOL_DIVIDE"))]), ValInt(0)), # integer division → 0
("M * M", None, ValInt(1000000)), # large intermediate (not displayed) ("M * M", Program([], [ExpressionStatement(BinOp(Numeral("M"), Numeral("M"), "SYMBOL_TIMES"))]), ValInt(1000000)), # large intermediate (not displayed)
("(I + II) * (IV - I)", None, ValInt(9)), # nested parens ("(I + II) * (IV - I)", Program([], [ExpressionStatement(BinOp(BinOp(Numeral("I"), Numeral("II"), "SYMBOL_PLUS"), BinOp(Numeral("IV"), Numeral("I"), "SYMBOL_MINUS"), "SYMBOL_TIMES"))]), ValInt(9)), # nested parens
] ]
class TestArithmeticEdge(unittest.TestCase): class TestArithmeticEdge(unittest.TestCase):
@@ -425,18 +586,22 @@ class TestArithmeticEdge(unittest.TestCase):
comparison_tests = [ comparison_tests = [
# EST on strings # EST on strings
('\"hello\" EST \"hello\"', None, ValBool(True)), ('\"hello\" EST \"hello\"', Program([], [ExpressionStatement(BinOp(String("hello"), String("hello"), "KEYWORD_EST"))]), ValBool(True)),
('\"hello\" EST \"world\"', None, ValBool(False)), ('\"hello\" EST \"world\"', Program([], [ExpressionStatement(BinOp(String("hello"), String("world"), "KEYWORD_EST"))]), ValBool(False)),
# chain comparisons as conditions # chain comparisons as conditions
("SI III PLVS II TVNC { DESIGNA r VT I }\nr", None, ValInt(1)), ("SI III PLVS II TVNC { DESIGNA r VT I }\nr",
("SI II PLVS III TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(2)), Program([], [SiStatement(BinOp(Numeral("III"), Numeral("II"), "KEYWORD_PLVS"), [Designa(ID("r"), Numeral("I"))], None), ExpressionStatement(ID("r"))]),
ValInt(1)),
("SI II PLVS III TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
Program([], [SiStatement(BinOp(Numeral("II"), Numeral("III"), "KEYWORD_PLVS"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(2)),
# result of comparison is ValBool # result of comparison is ValBool
("I EST I", None, ValBool(True)), ("I EST I", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("I"), "KEYWORD_EST"))]), ValBool(True)),
("I EST II", None, ValBool(False)), ("I EST II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"))]), ValBool(False)),
("I MINVS II", None, ValBool(True)), ("I MINVS II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_MINVS"))]), ValBool(True)),
("II MINVS I", None, ValBool(False)), ("II MINVS I", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("I"), "KEYWORD_MINVS"))]), ValBool(False)),
("II PLVS I", None, ValBool(True)), ("II PLVS I", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("I"), "KEYWORD_PLVS"))]), ValBool(True)),
("I PLVS II", None, ValBool(False)), ("I PLVS II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_PLVS"))]), ValBool(False)),
] ]
class TestComparisons(unittest.TestCase): class TestComparisons(unittest.TestCase):
@@ -449,38 +614,72 @@ class TestComparisons(unittest.TestCase):
function_edge_tests = [ function_edge_tests = [
# no explicit REDI → returns ValNul # no explicit REDI → returns ValNul
("DEFINI f () VT { I }\nINVOCA f ()", None, ValNul()), ("DEFINI f () VT { I }\nINVOCA f ()",
Program([], [Defini(ID("f"), [], [ExpressionStatement(Numeral("I"))]), ExpressionStatement(Invoca(ID("f"), []))]),
ValNul()),
# REDI multiple values → ValList # REDI multiple values → ValList
( (
"DEFINI pair (a, b) VT { REDI (a, b) }\nINVOCA pair (I, II)", "DEFINI pair (a, b) VT { REDI (a, b) }\nINVOCA pair (I, II)",
None, ValList([ValInt(1), ValInt(2)]), Program([], [
Defini(ID("pair"), [ID("a"), ID("b")], [Redi([ID("a"), ID("b")])]),
ExpressionStatement(Invoca(ID("pair"), [Numeral("I"), Numeral("II")])),
]),
ValList([ValInt(1), ValInt(2)]),
), ),
# function doesn't mutate outer vtable # function doesn't mutate outer vtable
( (
"DESIGNA x VT I\nDEFINI f () VT { DESIGNA x VT V\nREDI (x) }\nINVOCA f ()\nx", "DESIGNA x VT I\nDEFINI f () VT { DESIGNA x VT V\nREDI (x) }\nINVOCA f ()\nx",
None, ValInt(1), Program([], [
Designa(ID("x"), Numeral("I")),
Defini(ID("f"), [], [Designa(ID("x"), Numeral("V")), Redi([ID("x")])]),
ExpressionStatement(Invoca(ID("f"), [])),
ExpressionStatement(ID("x")),
]),
ValInt(1),
), ),
# function can read outer vtable (closure-like) # function can read outer vtable (closure-like)
( (
"DESIGNA x VT VII\nDEFINI f () VT { REDI (x) }\nINVOCA f ()", "DESIGNA x VT VII\nDEFINI f () VT { REDI (x) }\nINVOCA f ()",
None, ValInt(7), Program([], [
Designa(ID("x"), Numeral("VII")),
Defini(ID("f"), [], [Redi([ID("x")])]),
ExpressionStatement(Invoca(ID("f"), [])),
]),
ValInt(7),
), ),
# function defined after use is still a parse error (definition must precede call at runtime) # function defined after use is still a parse error (definition must precede call at runtime)
# (skipped — ftable is populated at eval time, so definition order matters) # (skipped — ftable is populated at eval time, so definition order matters)
# parameter shadows outer variable inside function # parameter shadows outer variable inside function
( (
"DESIGNA n VT I\nDEFINI f (n) VT { REDI (n * II) }\nINVOCA f (X)\nn", "DESIGNA n VT I\nDEFINI f (n) VT { REDI (n * II) }\nINVOCA f (X)\nn",
None, ValInt(1), Program([], [
Designa(ID("n"), Numeral("I")),
Defini(ID("f"), [ID("n")], [Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")])]),
ExpressionStatement(Invoca(ID("f"), [Numeral("X")])),
ExpressionStatement(ID("n")),
]),
ValInt(1),
), ),
# function aliasing: assign f to g, invoke via g # function aliasing: assign f to g, invoke via g
( (
"DEFINI f (n) VT { REDI (n * II) }\nDESIGNA g VT f\nINVOCA g (V)", "DEFINI f (n) VT { REDI (n * II) }\nDESIGNA g VT f\nINVOCA g (V)",
None, ValInt(10), Program([], [
Defini(ID("f"), [ID("n")], [Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")])]),
Designa(ID("g"), ID("f")),
ExpressionStatement(Invoca(ID("g"), [Numeral("V")])),
]),
ValInt(10),
), ),
# alias is independent: redefining f doesn't affect g # alias is independent: redefining f doesn't affect g
( (
"DEFINI f (n) VT { REDI (n * II) }\nDESIGNA g VT f\nDEFINI f (n) VT { REDI (n * III) }\nINVOCA g (V)", "DEFINI f (n) VT { REDI (n * II) }\nDESIGNA g VT f\nDEFINI f (n) VT { REDI (n * III) }\nINVOCA g (V)",
None, ValInt(10), Program([], [
Defini(ID("f"), [ID("n")], [Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")])]),
Designa(ID("g"), ID("f")),
Defini(ID("f"), [ID("n")], [Redi([BinOp(ID("n"), Numeral("III"), "SYMBOL_TIMES")])]),
ExpressionStatement(Invoca(ID("g"), [Numeral("V")])),
]),
ValInt(10),
), ),
] ]
@@ -494,20 +693,61 @@ class TestFunctionEdge(unittest.TestCase):
loop_edge_tests = [ loop_edge_tests = [
# range(3, 3) is empty — body never runs, program returns ValNul # range(3, 3) is empty — body never runs, program returns ValNul
("DONICVM i VT III VSQVE III FACE { DICE(i) }", None, ValNul(), ""), ("DONICVM i VT III VSQVE III FACE { DICE(i) }",
Program([], [PerStatement(DataRangeArray(Numeral("III"), Numeral("III")), ID("i"), [ExpressionStatement(BuiltIn("DICE", [ID("i")]))])]),
ValNul(), ""),
# empty array — body never runs # empty array — body never runs
("PER i IN [()] FACE { DICE(i) }", None, ValNul(), ""), ("PER i IN [()] FACE { DICE(i) }",
Program([], [PerStatement(DataArray([]), ID("i"), [ExpressionStatement(BuiltIn("DICE", [ID("i")]))])]),
ValNul(), ""),
# PER breaks on element 2 — last assigned i is 2 # PER breaks on element 2 — last assigned i is 2
("PER i IN [(I, II, III)] FACE { SI i EST II TVNC { ERVMPE } }\ni", None, ValInt(2), ""), ("PER i IN [(I, II, III)] FACE { SI i EST II TVNC { ERVMPE } }\ni",
Program([], [
PerStatement(
DataArray([Numeral("I"), Numeral("II"), Numeral("III")]),
ID("i"),
[SiStatement(BinOp(ID("i"), Numeral("II"), "KEYWORD_EST"), [Erumpe()], None)],
),
ExpressionStatement(ID("i")),
]),
ValInt(2), ""),
# nested DVM: inner always breaks; outer runs until btr==3 # nested DVM: inner always breaks; outer runs until btr==3
("DESIGNA btr VT I\nDVM btr EST III FACE {\nDVM FALSITAS FACE {\nERVMPE\n}\nDESIGNA btr VT btr + I\n}\nbtr", None, ValInt(3), ""), ("DESIGNA btr VT I\nDVM btr EST III FACE {\nDVM FALSITAS FACE {\nERVMPE\n}\nDESIGNA btr VT btr + I\n}\nbtr",
Program([], [
Designa(ID("btr"), Numeral("I")),
DumStatement(
BinOp(ID("btr"), Numeral("III"), "KEYWORD_EST"),
[DumStatement(Bool(False), [Erumpe()]), Designa(ID("btr"), BinOp(ID("btr"), Numeral("I"), "SYMBOL_PLUS"))],
),
ExpressionStatement(ID("btr")),
]),
ValInt(3), ""),
# nested PER: inner always breaks on first element; outer completes both iterations # nested PER: inner always breaks on first element; outer completes both iterations
# cnt starts at 1, increments twice → 3 # cnt starts at 1, increments twice → 3
("DESIGNA cnt VT I\nPER i IN [(I, II)] FACE {\nPER k IN [(I, II)] FACE {\nERVMPE\n}\nDESIGNA cnt VT cnt + I\n}\ncnt", None, ValInt(3), ""), ("DESIGNA cnt VT I\nPER i IN [(I, II)] FACE {\nPER k IN [(I, II)] FACE {\nERVMPE\n}\nDESIGNA cnt VT cnt + I\n}\ncnt",
Program([], [
Designa(ID("cnt"), Numeral("I")),
PerStatement(
DataArray([Numeral("I"), Numeral("II")]),
ID("i"),
[PerStatement(DataArray([Numeral("I"), Numeral("II")]), ID("k"), [Erumpe()]),
Designa(ID("cnt"), BinOp(ID("cnt"), Numeral("I"), "SYMBOL_PLUS"))],
),
ExpressionStatement(ID("cnt")),
]),
ValInt(3), ""),
# DVM condition true from start — body never runs # DVM condition true from start — body never runs
("DESIGNA x VT I\nDVM VERITAS FACE {\nDESIGNA x VT x + I\n}\nx", None, ValInt(1), ""), ("DESIGNA x VT I\nDVM VERITAS FACE {\nDESIGNA x VT x + I\n}\nx",
Program([], [
Designa(ID("x"), Numeral("I")),
DumStatement(Bool(True), [Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS"))]),
ExpressionStatement(ID("x")),
]),
ValInt(1), ""),
# single iteration: [I VSQVE II] = [1] # single iteration: [I VSQVE II] = [1]
("DONICVM i VT I VSQVE II FACE { DICE(i) }", None, ValStr("I"), "I\n"), ("DONICVM i VT I VSQVE II FACE { DICE(i) }",
Program([], [PerStatement(DataRangeArray(Numeral("I"), Numeral("II")), ID("i"), [ExpressionStatement(BuiltIn("DICE", [ID("i")]))])]),
ValStr("I"), "I\n"),
] ]
class TestLoopEdge(unittest.TestCase): class TestLoopEdge(unittest.TestCase):
@@ -567,11 +807,17 @@ class TestValues(unittest.TestCase):
magnvm_tests = [ magnvm_tests = [
# M+M+M+M = 4000; MAGNVM allows display as "MV_" # M+M+M+M = 4000; MAGNVM allows display as "MV_"
("CVM MAGNVM\nDICE(M + M + M + M)", None, ValStr("MV_"), "MV_\n"), ("CVM MAGNVM\nDICE(M + M + M + M)",
Program([ModuleCall("MAGNVM")], [ExpressionStatement(BuiltIn("DICE", [BinOp(BinOp(BinOp(Numeral("M"), Numeral("M"), "SYMBOL_PLUS"), Numeral("M"), "SYMBOL_PLUS"), Numeral("M"), "SYMBOL_PLUS")]))]),
ValStr("MV_"), "MV_\n"),
# I_ = 1000 with MAGNVM (same value as M, but written with thousands operator) # I_ = 1000 with MAGNVM (same value as M, but written with thousands operator)
("CVM MAGNVM\nI_", None, ValInt(1000), ""), ("CVM MAGNVM\nI_",
Program([ModuleCall("MAGNVM")], [ExpressionStatement(Numeral("I_"))]),
ValInt(1000), ""),
# I_ + I_ = 2000; displayed as MM with MAGNVM # I_ + I_ = 2000; displayed as MM with MAGNVM
("CVM MAGNVM\nDICE(I_ + I_)", None, ValStr("MM"), "MM\n"), ("CVM MAGNVM\nDICE(I_ + I_)",
Program([ModuleCall("MAGNVM")], [ExpressionStatement(BuiltIn("DICE", [BinOp(Numeral("I_"), Numeral("I_"), "SYMBOL_PLUS")]))]),
ValStr("MM"), "MM\n"),
] ]
class TestMAGNVM(unittest.TestCase): class TestMAGNVM(unittest.TestCase):
@@ -583,20 +829,28 @@ class TestMAGNVM(unittest.TestCase):
# --- ET and AVT (boolean and/or) --- # --- ET and AVT (boolean and/or) ---
et_avt_tests = [ et_avt_tests = [
("VERITAS ET VERITAS", None, ValBool(True)), ("VERITAS ET VERITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(True), "KEYWORD_ET"))]), ValBool(True)),
("VERITAS ET FALSITAS", None, ValBool(False)), ("VERITAS ET FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(False), "KEYWORD_ET"))]), ValBool(False)),
("FALSITAS ET VERITAS", None, ValBool(False)), ("FALSITAS ET VERITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(True), "KEYWORD_ET"))]), ValBool(False)),
("FALSITAS ET FALSITAS", None, ValBool(False)), ("FALSITAS ET FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(False), "KEYWORD_ET"))]), ValBool(False)),
("VERITAS AVT VERITAS", None, ValBool(True)), ("VERITAS AVT VERITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(True), "KEYWORD_AVT"))]), ValBool(True)),
("VERITAS AVT FALSITAS", None, ValBool(True)), ("VERITAS AVT FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(False), "KEYWORD_AVT"))]), ValBool(True)),
("FALSITAS AVT VERITAS", None, ValBool(True)), ("FALSITAS AVT VERITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(True), "KEYWORD_AVT"))]), ValBool(True)),
("FALSITAS AVT FALSITAS", None, ValBool(False)), ("FALSITAS AVT FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(False), "KEYWORD_AVT"))]), ValBool(False)),
# short-circuit behaviour: combined with comparisons # short-circuit behaviour: combined with comparisons
("(I EST I) ET (II EST II)", None, ValBool(True)), ("(I EST I) ET (II EST II)",
("(I EST II) AVT (II EST II)", None, ValBool(True)), Program([], [ExpressionStatement(BinOp(BinOp(Numeral("I"), Numeral("I"), "KEYWORD_EST"), BinOp(Numeral("II"), Numeral("II"), "KEYWORD_EST"), "KEYWORD_ET"))]),
ValBool(True)),
("(I EST II) AVT (II EST II)",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"), BinOp(Numeral("II"), Numeral("II"), "KEYWORD_EST"), "KEYWORD_AVT"))]),
ValBool(True)),
# used as SI condition # used as SI condition
("SI VERITAS ET VERITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), ("SI VERITAS ET VERITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr",
("SI FALSITAS AVT FALSITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(2)), Program([], [SiStatement(BinOp(Bool(True), Bool(True), "KEYWORD_ET"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
("SI FALSITAS AVT FALSITAS TVNC { DESIGNA r VT I } ALVID { 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"))]),
ValInt(2)),
] ]
class TestEtAvt(unittest.TestCase): class TestEtAvt(unittest.TestCase):
@@ -610,13 +864,15 @@ class TestEtAvt(unittest.TestCase):
array_index_tests = [ array_index_tests = [
# basic indexing # basic indexing
("[(I, II, III)][I]", None, ValInt(1)), # first element ("[(I, II, III)][I]", Program([], [ExpressionStatement(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("I")))]), ValInt(1)), # first element
("[(I, II, III)][II]", None, ValInt(2)), # second element ("[(I, II, III)][II]", Program([], [ExpressionStatement(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("II")))]), ValInt(2)), # second element
("[(I, II, III)][III]", None, ValInt(3)), # third element ("[(I, II, III)][III]", Program([], [ExpressionStatement(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("III")))]), ValInt(3)), # third element
# index into a variable # index into a variable
("DESIGNA a VT [(X, XX, XXX)]\na[II]", None, ValInt(20)), # second element ("DESIGNA a VT [(X, XX, XXX)]\na[II]",
Program([], [Designa(ID("a"), DataArray([Numeral("X"), Numeral("XX"), Numeral("XXX")])), ExpressionStatement(ArrayIndex(ID("a"), Numeral("II")))]),
ValInt(20)), # second element
# index into range array # index into range array
("[I VSQVE V][II]", None, ValInt(2)), # second element of [1,2,3,4] ("[I VSQVE V][II]", Program([], [ExpressionStatement(ArrayIndex(DataRangeArray(Numeral("I"), Numeral("V")), Numeral("II")))]), ValInt(2)), # second element of [1,2,3,4]
] ]
class TestArrayIndex(unittest.TestCase): class TestArrayIndex(unittest.TestCase):
@@ -629,27 +885,33 @@ class TestArrayIndex(unittest.TestCase):
comment_tests = [ comment_tests = [
# trailing line comment # trailing line comment
('DICE("hello") // this is ignored', None, ValStr("hello"), "hello\n"), ('DICE("hello") // this is ignored', Program([], [ExpressionStatement(BuiltIn("DICE", [String("hello")]))]), ValStr("hello"), "hello\n"),
# line comment on its own line before code # line comment on its own line before code
('// ignored\nDICE("hi")', None, ValStr("hi"), "hi\n"), ('// ignored\nDICE("hi")', Program([], [ExpressionStatement(BuiltIn("DICE", [String("hi")]))]), ValStr("hi"), "hi\n"),
# inline block comment # inline block comment
('DICE(/* ignored */ "hi")', None, ValStr("hi"), "hi\n"), ('DICE(/* ignored */ "hi")', Program([], [ExpressionStatement(BuiltIn("DICE", [String("hi")]))]), ValStr("hi"), "hi\n"),
# block comment spanning multiple lines # block comment spanning multiple lines
('/* line one\nline two */\nDICE("hi")', None, ValStr("hi"), "hi\n"), ('/* line one\nline two */\nDICE("hi")', Program([], [ExpressionStatement(BuiltIn("DICE", [String("hi")]))]), ValStr("hi"), "hi\n"),
# block comment mid-expression # block comment mid-expression
("II /* ignored */ + III", None, ValInt(5)), ("II /* ignored */ + III", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"))]), ValInt(5)),
# line comment after expression (no output) # line comment after expression (no output)
("II + III // ignored", None, ValInt(5)), ("II + III // ignored", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"))]), ValInt(5)),
# division still works (/ token not confused with //) # division still works (/ token not confused with //)
("X / II", None, ValInt(5)), ("X / II", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))]), ValInt(5)),
# multiple line comments # multiple line comments
('// first\n// second\nDICE("ok")', None, ValStr("ok"), "ok\n"), ('// first\n// second\nDICE("ok")', Program([], [ExpressionStatement(BuiltIn("DICE", [String("ok")]))]), ValStr("ok"), "ok\n"),
# comment-only line between two statements # comment-only line between two statements
('DESIGNA x VT I\n// set y\nDESIGNA y VT II\nx + y', None, ValInt(3)), ('DESIGNA x VT I\n// set y\nDESIGNA y VT II\nx + y',
Program([], [Designa(ID("x"), Numeral("I")), Designa(ID("y"), Numeral("II")), ExpressionStatement(BinOp(ID("x"), ID("y"), "SYMBOL_PLUS"))]),
ValInt(3)),
# blank line between two statements (double newline) # blank line between two statements (double newline)
('DESIGNA x VT I\n\nDESIGNA y VT II\nx + y', None, ValInt(3)), ('DESIGNA x VT I\n\nDESIGNA y VT II\nx + y',
Program([], [Designa(ID("x"), Numeral("I")), Designa(ID("y"), Numeral("II")), ExpressionStatement(BinOp(ID("x"), ID("y"), "SYMBOL_PLUS"))]),
ValInt(3)),
# multiple comment-only lines between statements # multiple comment-only lines between statements
('DESIGNA x VT I\n// one\n// two\nDESIGNA y VT III\nx + y', None, ValInt(4)), ('DESIGNA x VT I\n// one\n// two\nDESIGNA y VT III\nx + y',
Program([], [Designa(ID("x"), Numeral("I")), Designa(ID("y"), Numeral("III")), ExpressionStatement(BinOp(ID("x"), ID("y"), "SYMBOL_PLUS"))]),
ValInt(4)),
] ]
class TestComments(unittest.TestCase): class TestComments(unittest.TestCase):
@@ -662,39 +924,118 @@ class TestComments(unittest.TestCase):
scope_tests = [ scope_tests = [
# SI: variable assigned in true branch persists in outer scope # SI: variable assigned in true branch persists in outer scope
("SI VERITAS TVNC { DESIGNA r VT X }\nr", None, ValInt(10)), ("SI VERITAS TVNC { DESIGNA r VT X }\nr",
Program([], [SiStatement(Bool(True), [Designa(ID("r"), Numeral("X"))], None), ExpressionStatement(ID("r"))]),
ValInt(10)),
# SI: variable assigned in ALVID branch persists in outer scope # SI: variable assigned in ALVID branch persists in outer scope
("SI FALSITAS TVNC { DESIGNA r VT X } ALVID { DESIGNA r VT V }\nr", None, ValInt(5)), ("SI FALSITAS TVNC { DESIGNA r VT X } ALVID { DESIGNA r VT V }\nr",
Program([], [SiStatement(Bool(False), [Designa(ID("r"), Numeral("X"))], [Designa(ID("r"), Numeral("V"))]), ExpressionStatement(ID("r"))]),
ValInt(5)),
# DVM: variable assigned in body persists after loop exits # DVM: variable assigned in body persists after loop exits
# x goes 1→2→3→4→5; r tracks x each iteration; loop exits when x==5 # x goes 1→2→3→4→5; r tracks x each iteration; loop exits when x==5
("DESIGNA x VT I\nDVM x EST V FACE { DESIGNA x VT x + I\nDESIGNA r VT x }\nr", None, ValInt(5)), ("DESIGNA x VT I\nDVM x EST V FACE { DESIGNA x VT x + I\nDESIGNA r VT x }\nr",
Program([], [
Designa(ID("x"), Numeral("I")),
DumStatement(BinOp(ID("x"), Numeral("V"), "KEYWORD_EST"), [
Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")),
Designa(ID("r"), ID("x")),
]),
ExpressionStatement(ID("r")),
]),
ValInt(5)),
# PER: loop variable holds last array element after loop (no ERVMPE) # PER: loop variable holds last array element after loop (no ERVMPE)
("PER i IN [(I, II, III)] FACE { DESIGNA nop VT I }\ni", None, ValInt(3)), ("PER i IN [(I, II, III)] FACE { DESIGNA nop VT I }\ni",
Program([], [
PerStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), ID("i"), [Designa(ID("nop"), Numeral("I"))]),
ExpressionStatement(ID("i")),
]),
ValInt(3)),
# PER: reassigning loop var in body doesn't prevent remaining iterations from running # PER: reassigning loop var in body doesn't prevent remaining iterations from running
# cnt increments once per iteration (all 3); C=100 doesn't replace next element assignment # cnt increments once per iteration (all 3); C=100 doesn't replace next element assignment
("DESIGNA cnt VT I\nPER i IN [(I, II, III)] FACE { DESIGNA i VT C\nDESIGNA cnt VT cnt + I }\ncnt", None, ValInt(4)), ("DESIGNA cnt VT I\nPER i IN [(I, II, III)] FACE { DESIGNA i VT C\nDESIGNA cnt VT cnt + I }\ncnt",
Program([], [
Designa(ID("cnt"), Numeral("I")),
PerStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), ID("i"), [
Designa(ID("i"), Numeral("C")),
Designa(ID("cnt"), BinOp(ID("cnt"), Numeral("I"), "SYMBOL_PLUS")),
]),
ExpressionStatement(ID("cnt")),
]),
ValInt(4)),
# PER: loop var after loop reflects the last body assignment, not the last array element # PER: loop var after loop reflects the last body assignment, not the last array element
# body sets i=C=100 on every iteration; after loop ends, i stays at 100 # body sets i=C=100 on every iteration; after loop ends, i stays at 100
("PER i IN [(I, II, III)] FACE { DESIGNA i VT C }\ni", None, ValInt(100)), ("PER i IN [(I, II, III)] FACE { DESIGNA i VT C }\ni",
Program([], [
PerStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), ID("i"), [Designa(ID("i"), Numeral("C"))]),
ExpressionStatement(ID("i")),
]),
ValInt(100)),
# DONICVM: counter holds last range value after loop ends # DONICVM: counter holds last range value after loop ends
# [I VSQVE IV] = [1,2,3]; last value assigned by loop is III=3 # [I VSQVE IV] = [1,2,3]; last value assigned by loop is III=3
("DONICVM i VT I VSQVE IV FACE { DESIGNA nop VT I }\ni", None, ValInt(3)), ("DONICVM i VT I VSQVE IV FACE { DESIGNA nop VT I }\ni",
Program([], [
PerStatement(DataRangeArray(Numeral("I"), Numeral("IV")), ID("i"), [Designa(ID("nop"), Numeral("I"))]),
ExpressionStatement(ID("i")),
]),
ValInt(3)),
# DONICVM: reassigning counter inside body doesn't reduce the number of iterations # DONICVM: reassigning counter inside body doesn't reduce the number of iterations
# range [I VSQVE IV] evaluated once; i reset each time; cnt still increments 3 times → 4 # range [I VSQVE IV] evaluated once; i reset each time; cnt still increments 3 times → 4
("DESIGNA cnt VT I\nDONICVM i VT I VSQVE IV FACE { DESIGNA cnt VT cnt + I\nDESIGNA i VT C }\ncnt", None, ValInt(4)), ("DESIGNA cnt VT I\nDONICVM i VT I VSQVE IV FACE { DESIGNA cnt VT cnt + I\nDESIGNA i VT C }\ncnt",
Program([], [
Designa(ID("cnt"), Numeral("I")),
PerStatement(DataRangeArray(Numeral("I"), Numeral("IV")), ID("i"), [
Designa(ID("cnt"), BinOp(ID("cnt"), Numeral("I"), "SYMBOL_PLUS")),
Designa(ID("i"), Numeral("C")),
]),
ExpressionStatement(ID("cnt")),
]),
ValInt(4)),
# DONICVM: ERVMPE exits loop early; counter persists at break value # DONICVM: ERVMPE exits loop early; counter persists at break value
("DONICVM i VT I VSQVE X FACE {\nSI i EST III TVNC { ERVMPE }\n}\ni", None, ValInt(3)), ("DONICVM i VT I VSQVE X FACE {\nSI i EST III TVNC { ERVMPE }\n}\ni",
Program([], [
PerStatement(DataRangeArray(Numeral("I"), Numeral("X")), ID("i"), [
SiStatement(BinOp(ID("i"), Numeral("III"), "KEYWORD_EST"), [Erumpe()], None),
]),
ExpressionStatement(ID("i")),
]),
ValInt(3)),
# Function: modifying parameter inside function does not affect outer variable of same name # Function: modifying parameter inside function does not affect outer variable of same name
# outer n=1; f receives n=5 and modifies its local copy; outer n unchanged # outer n=1; f receives n=5 and modifies its local copy; outer n unchanged
("DESIGNA n VT I\nDEFINI f (n) VT { DESIGNA n VT n + X\nREDI (n) }\nINVOCA f (V)\nn", None, ValInt(1)), ("DESIGNA n VT I\nDEFINI f (n) VT { DESIGNA n VT n + X\nREDI (n) }\nINVOCA f (V)\nn",
Program([], [
Designa(ID("n"), Numeral("I")),
Defini(ID("f"), [ID("n")], [Designa(ID("n"), BinOp(ID("n"), Numeral("X"), "SYMBOL_PLUS")), Redi([ID("n")])]),
ExpressionStatement(Invoca(ID("f"), [Numeral("V")])),
ExpressionStatement(ID("n")),
]),
ValInt(1)),
# Function: mutating outer variable inside function (via DESIGNA) is not visible outside # Function: mutating outer variable inside function (via DESIGNA) is not visible outside
# Invoca creates func_vtable = vtable.copy(); mutations to func_vtable don't propagate back # Invoca creates func_vtable = vtable.copy(); mutations to func_vtable don't propagate back
("DESIGNA x VT I\nDEFINI f () VT { DESIGNA x VT C\nREDI (x) }\nINVOCA f ()\nx", None, ValInt(1)), ("DESIGNA x VT I\nDEFINI f () VT { DESIGNA x VT C\nREDI (x) }\nINVOCA f ()\nx",
Program([], [
Designa(ID("x"), Numeral("I")),
Defini(ID("f"), [], [Designa(ID("x"), Numeral("C")), Redi([ID("x")])]),
ExpressionStatement(Invoca(ID("f"), [])),
ExpressionStatement(ID("x")),
]),
ValInt(1)),
# Function: two successive calls with same parameter name don't share state # Function: two successive calls with same parameter name don't share state
("DEFINI f (n) VT { REDI (n * II) }\nINVOCA f (III) + INVOCA f (IV)", None, ValInt(14)), ("DEFINI f (n) VT { REDI (n * II) }\nINVOCA f (III) + INVOCA f (IV)",
Program([], [
Defini(ID("f"), [ID("n")], [Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")])]),
ExpressionStatement(BinOp(Invoca(ID("f"), [Numeral("III")]), Invoca(ID("f"), [Numeral("IV")]), "SYMBOL_PLUS")),
]),
ValInt(14)),
# Function: calling f(I) with param named n does not overwrite outer n=II # Function: calling f(I) with param named n does not overwrite outer n=II
# f is defined before n is designated; INVOCA creates a local copy, outer vtable unchanged # f is defined before n is designated; INVOCA creates a local copy, outer vtable unchanged
("DEFINI f (n) VT { REDI (n * II) }\nDESIGNA n VT II\nINVOCA f (I)\nn", None, ValInt(2)), ("DEFINI f (n) VT { REDI (n * II) }\nDESIGNA n VT II\nINVOCA f (I)\nn",
Program([], [
Defini(ID("f"), [ID("n")], [Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")])]),
Designa(ID("n"), Numeral("II")),
ExpressionStatement(Invoca(ID("f"), [Numeral("I")])),
ExpressionStatement(ID("n")),
]),
ValInt(2)),
] ]
class TestScope(unittest.TestCase): class TestScope(unittest.TestCase):