diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index 98138ae..c806803 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -130,6 +130,9 @@ class ExpressionStatement(Node): def __init__(self, expression) -> None: self.expression = expression + def __eq__(self, other): + return type(self) == type(other) and self.expression == other.expression + def __repr__(self) -> str: return self.expression.__repr__() @@ -141,6 +144,9 @@ class DataArray(Node): def __init__(self, content) -> None: self.content = content + def __eq__(self, other): + return type(self) == type(other) and self.content == other.content + def __repr__(self) -> str: content_string = rep_join(self.content) return f"Array([{content_string}])" @@ -158,6 +164,9 @@ class DataRangeArray(Node): self.from_value = from_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: content_string = rep_join([self.from_value, self.to_value]) return f"RangeArray([{content_string}])" @@ -172,6 +181,9 @@ class String(Node): def __init__(self, value) -> None: self.value = value + def __eq__(self, other): + return type(self) == type(other) and self.value == other.value + def __repr__(self): return f"String({self.value})" @@ -183,6 +195,9 @@ class Numeral(Node): def __init__(self, value) -> None: self.value = value + def __eq__(self, other): + return type(self) == type(other) and self.value == other.value + def __repr__(self): return f"Numeral({self.value})" @@ -194,6 +209,9 @@ class Bool(Node): def __init__(self, value) -> None: self.value = value + def __eq__(self, other): + return type(self) == type(other) and self.value == other.value + def __repr__(self): return f"Bool({self.value})" @@ -205,6 +223,9 @@ class ModuleCall(BaseBox): def __init__(self, module_name) -> None: 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: return f"{self.module_name}" @@ -213,6 +234,9 @@ class ID(Node): def __init__(self, name: str) -> None: self.name = name + def __eq__(self, other): + return type(self) == type(other) and self.name == other.name + def __repr__(self) -> str: return f"ID({self.name})" @@ -225,6 +249,9 @@ class Designa(Node): self.id = variable 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: id_string = repr(self.id).replace('\n', '\n ') value_string = repr(self.value).replace('\n', '\n ') @@ -242,6 +269,9 @@ class Defini(Node): self.parameters = parameters 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: parameter_string = f"parameters([{rep_join(self.parameters)}])" statements_string = f"statements([{rep_join(self.statements)}])" @@ -259,6 +289,9 @@ class Redi(Node): def __init__(self, values) -> None: self.values = values + def __eq__(self, other): + return type(self) == type(other) and self.values == other.values + def __repr__(self) -> str: values_string = f"[{rep_join(self.values)}]" return f"Redi({values_string})" @@ -276,6 +309,9 @@ class Redi(Node): class Erumpe(Node): + def __eq__(self, other): + return type(self) == type(other) + def __repr__(self) -> str: return "Erumpe()" @@ -285,6 +321,9 @@ class Erumpe(Node): class Nullus(Node): + def __eq__(self, other): + return type(self) == type(other) + def __repr__(self) -> str: return "Nullus()" @@ -298,6 +337,9 @@ class BinOp(Node): self.right = right 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: binop_string = rep_join([self.left, self.right, self.op]) return f"BinOp({binop_string})" @@ -334,6 +376,9 @@ class UnaryMinus(Node): def __init__(self, expr): self.expr = expr + def __eq__(self, other): + return type(self) == type(other) and self.expr == other.expr + def __repr__(self): return f"UnaryMinus({self.expr!r})" @@ -347,6 +392,9 @@ class ArrayIndex(Node): self.array = array 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: return f"ArrayIndex({self.array!r}, {self.index!r})" @@ -366,6 +414,9 @@ class SiStatement(Node): self.statements = statements 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: test = repr(self.test) statements = f"statements([{rep_join(self.statements)}])" @@ -390,6 +441,9 @@ class DumStatement(Node): self.test = test 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: test = repr(self.test) statements = f"statements([{rep_join(self.statements)}])" @@ -418,6 +472,9 @@ class PerStatement(Node): self.variable_name = variable_name 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: test = repr(self.data_list) variable_name = repr(self.variable_name) @@ -447,6 +504,9 @@ class Invoca(Node): self.name = name 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: parameters_string = f"parameters([{rep_join(self.parameters)}])" invoca_string = rep_join([self.name, parameters_string]) @@ -475,6 +535,9 @@ class BuiltIn(Node): self.builtin = builtin 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: parameter_string = f"parameters([{rep_join(self.parameters)}])" builtin_string = rep_join([self.builtin, parameter_string]) @@ -521,6 +584,9 @@ class Program(BaseBox): self.modules = module_calls 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: modules_string = f"modules([{rep_join(self.modules)}])" statements_string = f"statements([{rep_join(self.statements)}])" diff --git a/tests.py b/tests.py index 4a386f9..387d562 100644 --- a/tests.py +++ b/tests.py @@ -23,13 +23,14 @@ def run_test(self, source, target_nodes, target_value, target_output="", input_l program = Parser().parse(tokens) ########################## - ####### Parser Test ###### (commented out — no __eq__ on AST nodes yet) + ####### Parser Test ###### ########################## - # self.assertEqual( - # program, - # target_nodes, - # f"Parser test:\n{program}\n{target_nodes}" - # ) + if target_nodes is not None: + self.assertEqual( + program, + target_nodes, + f"Parser test:\n{program}\n{target_nodes}" + ) ########################## #### Interpreter Test #### @@ -77,16 +78,16 @@ def run_test(self, source, target_nodes, target_value, target_output="", input_l # --- Output --- output_tests = [ - ("DICE(\"hello\")", None, ValStr("hello"), "hello\n"), - ("DICE(\"world\")", None, ValStr("world"), "world\n"), - ("DICE(III)", None, ValStr("III"), "III\n"), - ("DICE(X)", None, ValStr("X"), "X\n"), - ("DICE(MMXXV)", None, ValStr("MMXXV"), "MMXXV\n"), - ("DICE('hello')", None, ValStr("hello"), "hello\n"), - ("DICE('world')", None, ValStr("world"), "world\n"), - ("DICE(\"a\", \"b\")", None, ValStr("a b"), "a b\n"), - ("DICE(\"line one\")\nDICE(\"line two\")", None, ValStr("line two"), "line one\nline two\n"), - ("DICE(DICE(II))", None, ValStr("II"), "II\nII\n"), + ("DICE(\"hello\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("hello")]))]), ValStr("hello"), "hello\n"), + ("DICE(\"world\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("world")]))]), ValStr("world"), "world\n"), + ("DICE(III)", Program([], [ExpressionStatement(BuiltIn("DICE", [Numeral("III")]))]), ValStr("III"), "III\n"), + ("DICE(X)", Program([], [ExpressionStatement(BuiltIn("DICE", [Numeral("X")]))]), ValStr("X"), "X\n"), + ("DICE(MMXXV)", Program([], [ExpressionStatement(BuiltIn("DICE", [Numeral("MMXXV")]))]), ValStr("MMXXV"), "MMXXV\n"), + ("DICE('hello')", Program([], [ExpressionStatement(BuiltIn("DICE", [String("hello")]))]), ValStr("hello"), "hello\n"), + ("DICE('world')", Program([], [ExpressionStatement(BuiltIn("DICE", [String("world")]))]), ValStr("world"), "world\n"), + ("DICE(\"a\", \"b\")", Program([], [ExpressionStatement(BuiltIn("DICE", [String("a"), String("b")]))]), ValStr("a b"), "a b\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))", Program([], [ExpressionStatement(BuiltIn("DICE", [BuiltIn("DICE", [Numeral("II")])]))]), ValStr("II"), "II\nII\n"), ] class TestOutput(unittest.TestCase): @@ -98,17 +99,17 @@ class TestOutput(unittest.TestCase): # --- Arithmetic --- arithmetic_tests = [ - ("I + I", None, ValInt(2)), - ("X - III", None, ValInt(7)), - ("III * IV", None, ValInt(12)), - ("X / II", None, ValInt(5)), - ("X / III", None, ValInt(3)), # integer division: 10 // 3 = 3 - ("II + III * IV", None, ValInt(14)), # precedence: 2 + (3*4) = 14 - ("(II + III) * IV", None, ValInt(20)), # parens: (2+3)*4 = 20 - ("- III", None, ValInt(-3)), # unary negation - ("- (II + III)", None, ValInt(-5)), # unary negation of expression - ("- - II", None, ValInt(2)), # double negation - ("III + - II", None, ValInt(1)), # unary in binary context + ("I + I", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("I"), "SYMBOL_PLUS"))]), ValInt(2)), + ("X - III", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "SYMBOL_MINUS"))]), ValInt(7)), + ("III * IV", Program([], [ExpressionStatement(BinOp(Numeral("III"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(12)), + ("X / II", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))]), ValInt(5)), + ("X / III", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "SYMBOL_DIVIDE"))]), ValInt(3)), # integer division: 10 // 3 = 3 + ("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", Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(20)), # parens: (2+3)*4 = 20 + ("- III", Program([], [ExpressionStatement(UnaryMinus(Numeral("III")))]), ValInt(-3)), # unary negation + ("- (II + III)", Program([], [ExpressionStatement(UnaryMinus(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS")))]), ValInt(-5)), # unary negation of expression + ("- - II", Program([], [ExpressionStatement(UnaryMinus(UnaryMinus(Numeral("II"))))]), ValInt(2)), # double negation + ("III + - II", Program([], [ExpressionStatement(BinOp(Numeral("III"), UnaryMinus(Numeral("II")), "SYMBOL_PLUS"))]), ValInt(1)), # unary in binary context ] class TestArithmetic(unittest.TestCase): @@ -117,14 +118,93 @@ class TestArithmetic(unittest.TestCase): 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_tests = [ - ("DESIGNA x VT III\nx", None, ValInt(3)), - ("DESIGNA msg VT \"hello\"\nmsg", None, ValStr("hello")), - ("DESIGNA msg VT 'hello'\nmsg", None, ValStr("hello")), - ("DESIGNA a VT V\nDESIGNA b VT X\na + b", None, ValInt(15)), - ("DESIGNA x VT II\nDESIGNA x VT x + I\nx", None, ValInt(3)), + ("DESIGNA x VT III\nx", + Program([], [Designa(ID("x"), Numeral("III")), ExpressionStatement(ID("x"))]), + ValInt(3)), + ("DESIGNA msg VT \"hello\"\nmsg", + 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): @@ -137,37 +217,79 @@ class TestAssignment(unittest.TestCase): control_tests = [ # 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 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 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 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 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 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 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 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 ( "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 ( "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) - ("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 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 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): @@ -181,16 +303,38 @@ class TestControl(unittest.TestCase): function_tests = [ ( "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)", - 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) ( "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 --- builtin_tests = [ - ("AVDI_NVMERVS()", None, ValInt(3), "", ["III"]), - ("AVDI_NVMERVS()", None, ValInt(10), "", ["X"]), - ("CVM FORS\nFORTIS_NVMERVS(I, X)", None, ValInt(3)), - ("AVDI()", None, ValStr("hello"), "", ["hello"]), - ("LONGITVDO([(I, II, III)])", None, ValInt(3)), - ("CVM FORS\nFORTIS_ELECTIONIS([(I, II, III)])", None, ValInt(1)), + ("AVDI_NVMERVS()", Program([], [ExpressionStatement(BuiltIn("AVDI_NVMERVS", []))]), ValInt(3), "", ["III"]), + ("AVDI_NVMERVS()", Program([], [ExpressionStatement(BuiltIn("AVDI_NVMERVS", []))]), ValInt(10), "", ["X"]), + ("CVM FORS\nFORTIS_NVMERVS(I, X)", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("FORTIS_NVMERVS", [Numeral("I"), Numeral("X")]))]), ValInt(3)), + ("AVDI()", Program([], [ExpressionStatement(BuiltIn("AVDI", []))]), ValStr("hello"), "", ["hello"]), + ("LONGITVDO([(I, II, III)])", Program([], [ExpressionStatement(BuiltIn("LONGITVDO", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")])]))]), ValInt(3)), + ("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): @@ -364,15 +508,15 @@ class TestMakeString(unittest.TestCase): # --- DICE with non-integer types --- dice_type_tests = [ - ("DICE(VERITAS)", None, ValStr("VERVS"), "VERVS\n"), - ("DICE(FALSITAS)", None, ValStr("FALSVS"), "FALSVS\n"), - ("DICE(NVLLVS)", None, ValStr("NVLLVS"), "NVLLVS\n"), - ('DICE([(I, II)])', None, ValStr("[I II]"), "[I II]\n"), - ('DICE("")', None, ValStr(""), "\n"), + ("DICE(VERITAS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Bool(True)]))]), ValStr("VERVS"), "VERVS\n"), + ("DICE(FALSITAS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Bool(False)]))]), ValStr("FALSVS"), "FALSVS\n"), + ("DICE(NVLLVS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Nullus()]))]), ValStr("NVLLVS"), "NVLLVS\n"), + ('DICE([(I, II)])', Program([], [ExpressionStatement(BuiltIn("DICE", [DataArray([Numeral("I"), Numeral("II")])]))]), ValStr("[I II]"), "[I II]\n"), + ('DICE("")', Program([], [ExpressionStatement(BuiltIn("DICE", [String("")]))]), ValStr(""), "\n"), # 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 - ('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): @@ -385,17 +529,34 @@ class TestDiceTypes(unittest.TestCase): truthiness_tests = [ # 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 - ("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 - ("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 - ("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 ( "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_tests = [ - ("I - I", None, ValInt(0)), # result zero - ("I - V", None, ValInt(-4)), # negative result - ("I / V", None, ValInt(0)), # integer division → 0 - ("M * M", None, ValInt(1000000)), # large intermediate (not displayed) - ("(I + II) * (IV - I)", None, ValInt(9)), # nested parens + ("I - I", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("I"), "SYMBOL_MINUS"))]), ValInt(0)), # result zero + ("I - V", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("V"), "SYMBOL_MINUS"))]), ValInt(-4)), # negative result + ("I / V", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("V"), "SYMBOL_DIVIDE"))]), ValInt(0)), # integer division → 0 + ("M * M", Program([], [ExpressionStatement(BinOp(Numeral("M"), Numeral("M"), "SYMBOL_TIMES"))]), ValInt(1000000)), # large intermediate (not displayed) + ("(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): @@ -425,18 +586,22 @@ class TestArithmeticEdge(unittest.TestCase): comparison_tests = [ # EST on strings - ('\"hello\" EST \"hello\"', None, ValBool(True)), - ('\"hello\" EST \"world\"', None, ValBool(False)), + ('\"hello\" EST \"hello\"', Program([], [ExpressionStatement(BinOp(String("hello"), String("hello"), "KEYWORD_EST"))]), ValBool(True)), + ('\"hello\" EST \"world\"', Program([], [ExpressionStatement(BinOp(String("hello"), String("world"), "KEYWORD_EST"))]), ValBool(False)), # chain comparisons as conditions - ("SI III PLVS II TVNC { DESIGNA r VT I }\nr", None, ValInt(1)), - ("SI II PLVS III TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(2)), + ("SI III PLVS II TVNC { DESIGNA r VT I }\nr", + 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 - ("I EST I", None, ValBool(True)), - ("I EST II", None, ValBool(False)), - ("I MINVS II", None, ValBool(True)), - ("II MINVS I", None, ValBool(False)), - ("II PLVS I", None, ValBool(True)), - ("I PLVS II", None, ValBool(False)), + ("I EST I", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("I"), "KEYWORD_EST"))]), ValBool(True)), + ("I EST II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"))]), ValBool(False)), + ("I MINVS II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_MINVS"))]), ValBool(True)), + ("II MINVS I", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("I"), "KEYWORD_MINVS"))]), ValBool(False)), + ("II PLVS I", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("I"), "KEYWORD_PLVS"))]), ValBool(True)), + ("I PLVS II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_PLVS"))]), ValBool(False)), ] class TestComparisons(unittest.TestCase): @@ -449,38 +614,72 @@ class TestComparisons(unittest.TestCase): function_edge_tests = [ # 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 ( "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 ( "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) ( "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) # (skipped — ftable is populated at eval time, so definition order matters) # parameter shadows outer variable inside function ( "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 ( "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 ( "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 = [ # 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 - ("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 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 - ("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 # 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 - ("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] - ("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): @@ -567,11 +807,17 @@ class TestValues(unittest.TestCase): magnvm_tests = [ # 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) - ("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 - ("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): @@ -583,20 +829,28 @@ class TestMAGNVM(unittest.TestCase): # --- ET and AVT (boolean and/or) --- et_avt_tests = [ - ("VERITAS ET VERITAS", None, ValBool(True)), - ("VERITAS ET FALSITAS", None, ValBool(False)), - ("FALSITAS ET VERITAS", None, ValBool(False)), - ("FALSITAS ET FALSITAS", None, ValBool(False)), - ("VERITAS AVT VERITAS", None, ValBool(True)), - ("VERITAS AVT FALSITAS", None, ValBool(True)), - ("FALSITAS AVT VERITAS", None, ValBool(True)), - ("FALSITAS AVT FALSITAS", None, ValBool(False)), + ("VERITAS ET VERITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(True), "KEYWORD_ET"))]), ValBool(True)), + ("VERITAS ET FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(False), "KEYWORD_ET"))]), ValBool(False)), + ("FALSITAS ET VERITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(True), "KEYWORD_ET"))]), ValBool(False)), + ("FALSITAS ET FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(False), "KEYWORD_ET"))]), ValBool(False)), + ("VERITAS AVT VERITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(True), "KEYWORD_AVT"))]), ValBool(True)), + ("VERITAS AVT FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(False), "KEYWORD_AVT"))]), ValBool(True)), + ("FALSITAS AVT VERITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(True), "KEYWORD_AVT"))]), ValBool(True)), + ("FALSITAS AVT FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(False), Bool(False), "KEYWORD_AVT"))]), ValBool(False)), # short-circuit behaviour: combined with comparisons - ("(I EST I) ET (II EST II)", None, ValBool(True)), - ("(I EST II) AVT (II EST II)", None, ValBool(True)), + ("(I EST I) ET (II EST II)", + 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 - ("SI VERITAS ET VERITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(1)), - ("SI FALSITAS AVT FALSITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", None, ValInt(2)), + ("SI VERITAS ET VERITAS TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", + 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): @@ -610,13 +864,15 @@ class TestEtAvt(unittest.TestCase): array_index_tests = [ # basic indexing - ("[(I, II, III)][I]", None, ValInt(1)), # first element - ("[(I, II, III)][II]", None, ValInt(2)), # second element - ("[(I, II, III)][III]", None, ValInt(3)), # third 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]", Program([], [ExpressionStatement(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("II")))]), ValInt(2)), # second 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 - ("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 - ("[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): @@ -629,27 +885,33 @@ class TestArrayIndex(unittest.TestCase): comment_tests = [ # 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 - ('// ignored\nDICE("hi")', None, ValStr("hi"), "hi\n"), + ('// ignored\nDICE("hi")', Program([], [ExpressionStatement(BuiltIn("DICE", [String("hi")]))]), ValStr("hi"), "hi\n"), # 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 - ('/* 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 - ("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) - ("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 //) - ("X / II", None, ValInt(5)), + ("X / II", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))]), ValInt(5)), # 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 - ('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) - ('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 - ('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): @@ -662,39 +924,118 @@ class TestComments(unittest.TestCase): scope_tests = [ # 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 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 # 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 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 # 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 # 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 # [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 # 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 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 # 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 # 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 - ("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 # 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):