🐐 __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

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)
##########################
####### 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):