diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index 90490db..38c8f5d 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -92,7 +92,7 @@ def num_to_int(n, m, s=False): return sum(nums) -def int_to_num(n, m, s=False): +def int_to_num(n, m, s=False) -> str: if n < 0: if not s: raise CentvrionError("Cannot display negative numbers without 'SVBNVLLA' module") @@ -120,7 +120,7 @@ def int_to_num(n, m, s=False): return ''.join(nums) -def make_string(val, magnvm=False, svbnvlla=False): +def make_string(val, magnvm=False, svbnvlla=False) -> str: if isinstance(val, ValStr): return val.value() elif isinstance(val, ValInt): @@ -128,7 +128,7 @@ def make_string(val, magnvm=False, svbnvlla=False): elif isinstance(val, ValFrac): return fraction_to_frac(val.value(), magnvm, svbnvlla) elif isinstance(val, ValBool): - return "VERVS" if val.value() else "FALSVS" + return "VERITAS" if val.value() else "FALSITAS" elif isinstance(val, ValNul): return "NVLLVS" elif isinstance(val, ValList): @@ -166,7 +166,7 @@ def frac_to_fraction(s, magnvm=False, svbnvlla=False): return total -def fraction_to_frac(f, magnvm=False, svbnvlla=False): +def fraction_to_frac(f, magnvm=False, svbnvlla=False) -> str: if f < 0: if not svbnvlla: raise CentvrionError("Cannot display negative numbers without 'SVBNVLLA' module") @@ -200,16 +200,20 @@ class Node(BaseBox): def _eval(self, vtable): raise NotImplementedError + def print(self): + return "Node()" + class ExpressionStatement(Node): - def __init__(self, expression) -> None: + def __init__(self, expression: Node) -> 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__() + expr = repr(self.expression).replace('\n', '\n ') + return f"ExpressionStatement(\n {expr}\n)" def print(self): return self.expression.print() @@ -284,7 +288,7 @@ class String(Node): class Numeral(Node): - def __init__(self, value) -> None: + def __init__(self, value: str) -> None: self.value = value def __eq__(self, other): @@ -301,7 +305,7 @@ class Numeral(Node): class Fractio(Node): - def __init__(self, value) -> None: + def __init__(self, value: str) -> None: self.value = value def __eq__(self, other): @@ -372,7 +376,7 @@ class ID(Node): class Designa(Node): - def __init__(self, variable: ID, value) -> None: + def __init__(self, variable: ID, value: Node) -> None: self.id = variable self.value = value @@ -426,7 +430,7 @@ class DesignaIndex(Node): class Defini(Node): - def __init__(self, name, parameters, statements) -> None: + def __init__(self, name: ID, parameters: list[ID], statements: list[Node]) -> None: self.name = name self.parameters = parameters self.statements = statements @@ -453,7 +457,7 @@ class Defini(Node): class Redi(Node): - def __init__(self, values) -> None: + def __init__(self, values: list[Node]) -> None: self.values = values def __eq__(self, other): @@ -524,7 +528,7 @@ class Nullus(Node): class BinOp(Node): - def __init__(self, left, right, op) -> None: + def __init__(self, left: Node, right: Node, op: str) -> None: self.left = left self.right = right self.op = op @@ -601,7 +605,7 @@ class BinOp(Node): class UnaryMinus(Node): - def __init__(self, expr): + def __init__(self, expr: Node): self.expr = expr def __eq__(self, other): @@ -617,9 +621,12 @@ class UnaryMinus(Node): if "SVBNVLLA" not in vtable["#modules"]: raise CentvrionError("Cannot use unary minus without 'SVBNVLLA' module") vtable, val = self.expr.eval(vtable) - if not isinstance(val, ValInt): + if isinstance(val, ValInt): + return vtable, ValInt(-val.value()) + elif isinstance(val, ValFrac): + return vtable, ValFrac(-val.value()) + else: raise CentvrionError("Unary minus requires a number") - return vtable, ValInt(-val.value()) class UnaryNot(Node): @@ -661,9 +668,12 @@ class ArrayIndex(Node): vtable, index = self.index.eval(vtable) if not isinstance(array, ValList): raise CentvrionError("Cannot index a non-array value") - if not isinstance(index, ValInt): + if isinstance(index, ValInt): + i = index.value() + elif isinstance(index, ValFrac) and index.value().denominator == 1: + i = index.value().numerator + else: raise CentvrionError("Array index must be a number") - i = index.value() lst = array.value() if i < 1 or i > len(lst): raise CentvrionError(f"Index {i} out of range for array of length {len(lst)}") @@ -682,7 +692,10 @@ class SiStatement(Node): def __repr__(self) -> str: test = repr(self.test) statements = f"statements([{rep_join(self.statements)}])" - else_part = f"statements([{rep_join(self.else_part) if self.else_part else ''}])" + if self.else_part is None: + else_part = "None" + else: + else_part = f"statements([{rep_join(self.else_part)}])" si_string = rep_join([test, statements, else_part]) return f"Si({si_string})" @@ -713,7 +726,7 @@ class SiStatement(Node): class DumStatement(Node): - def __init__(self, test, statements) -> None: + def __init__(self, test: Node, statements: list[Node]) -> None: self.test = test self.statements = statements @@ -838,7 +851,7 @@ class Invoca(Node): class BuiltIn(Node): - def __init__(self, builtin, parameters) -> None: + def __init__(self, builtin: str, parameters: list[Node]) -> None: self.builtin = builtin self.parameters = parameters @@ -907,7 +920,7 @@ class BuiltIn(Node): class Program(BaseBox): - def __init__(self, module_calls: list[ModuleCall], statements) -> None: + def __init__(self, module_calls: list[ModuleCall], statements: list[Node]) -> None: self.modules = module_calls self.statements = statements diff --git a/centvrion/compiler/runtime/cent_runtime.c b/centvrion/compiler/runtime/cent_runtime.c index 904bcfe..1dad2f7 100644 --- a/centvrion/compiler/runtime/cent_runtime.c +++ b/centvrion/compiler/runtime/cent_runtime.c @@ -221,11 +221,11 @@ static int write_val(CentValue v, char *buf, int bufsz) { case CENT_BOOL: if (v.bval) { - if (buf && bufsz > 5) { memcpy(buf, "VERVS", 5); buf[5] = '\0'; } - return 5; + if (buf && bufsz > 7) { memcpy(buf, "VERITAS", 7); buf[7] = '\0'; } + return 7; } else { - if (buf && bufsz > 6) { memcpy(buf, "FALSVS", 6); buf[6] = '\0'; } - return 6; + if (buf && bufsz > 8) { memcpy(buf, "FALSITAS", 8); buf[8] = '\0'; } + return 8; } case CENT_NULL: @@ -527,9 +527,13 @@ void cent_list_push(CentValue *lst, CentValue v) { CentValue cent_list_index(CentValue lst, CentValue idx) { if (lst.type != CENT_LIST) cent_type_error("index requires a list"); - if (idx.type != CENT_INT) + long i; + if (idx.type == CENT_INT) + i = idx.ival; + else if (idx.type == CENT_FRAC && idx.fval.den == 1) + i = idx.fval.num; + else cent_type_error("list index must be an integer"); - long i = idx.ival; if (i < 1 || i > lst.lval.len) cent_runtime_error("list index out of range"); return lst.lval.items[i - 1]; diff --git a/tests.py b/tests.py index 6f31204..95adad7 100644 --- a/tests.py +++ b/tests.py @@ -103,6 +103,8 @@ def run_test(self, source, target_nodes, target_value, target_output="", input_l os.unlink(tmp_c_path) os.unlink(tmp_bin_path) + assert target_nodes is not None, "All tests must have target nodes" + # --- Output --- @@ -429,6 +431,8 @@ error_tests = [ ("DESIGNA x VT I\nINVOCA x ()", CentvrionError), # invoking a non-function ("SI I TVNC { DESIGNA r VT I }", CentvrionError), # non-bool SI condition: int ("IIIS", CentvrionError), # fraction without FRACTIO module + ("CVM FRACTIO\n[I, II, III][IIIS]", CentvrionError), # fractional index (IIIS = 7/2) + ("CVM FRACTIO\n[I, II, III][I / II]", CentvrionError), # fractional index from division (1/2) ("DESIGNA z VT I - I\nSI z TVNC { DESIGNA r VT I }", CentvrionError), # non-bool SI condition: zero int ("SI [I] TVNC { DESIGNA r VT I }", CentvrionError), # non-bool SI condition: non-empty list ("SI [] TVNC { DESIGNA r VT I }", CentvrionError), # non-bool SI condition: empty list @@ -489,21 +493,22 @@ repr_tests = [ ("erumpe", Erumpe(), "Erumpe()"), ("module_call", ModuleCall("FORS"), "FORS"), ("id", ID("x"), "ID(x)"), - ("expression_stmt", ExpressionStatement(String("hi")), "String(hi)"), + ("expression_stmt", ExpressionStatement(String("hi")), "ExpressionStatement(\n String(hi)\n)"), ("data_array", DataArray([Numeral("I"), Numeral("II")]), "Array([\n Numeral(I),\n Numeral(II)\n])"), ("data_range_array", DataRangeArray(Numeral("I"), Numeral("X")), "RangeArray([\n Numeral(I),\n Numeral(X)\n])"), ("designa", Designa(ID("x"), Numeral("III")), "Designa(\n ID(x),\n Numeral(III)\n)"), ("binop", BinOp(Numeral("I"), Numeral("II"), "SYMBOL_PLUS"), "BinOp(\n Numeral(I),\n Numeral(II),\n SYMBOL_PLUS\n)"), ("redi", Redi([Numeral("I")]), "Redi([\n Numeral(I)\n])"), - ("si_no_else", SiStatement(Bool(True), [ExpressionStatement(Erumpe())], None), "Si(\n Bool(True),\n statements([\n Erumpe()\n ]),\n statements([])\n)"), - ("si_with_else", SiStatement(Bool(True), [ExpressionStatement(Erumpe())], [ExpressionStatement(Erumpe())]), "Si(\n Bool(True),\n statements([\n Erumpe()\n ]),\n statements([\n Erumpe()\n ])\n)"), - ("dum", DumStatement(Bool(False), [ExpressionStatement(Erumpe())]), "Dum(\n Bool(False),\n statements([\n Erumpe()\n ])\n)"), - ("per", PerStatement(DataArray([Numeral("I")]), ID("i"), [ExpressionStatement(Erumpe())]), "Per(\n Array([\n Numeral(I)\n ]),\n ID(i),\n statements([\n Erumpe()\n ])\n)"), + ("si_no_else", SiStatement(Bool(True), [ExpressionStatement(Erumpe())], None), "Si(\n Bool(True),\n statements([\n ExpressionStatement(\n Erumpe()\n )\n ]),\n None\n)"), + ("si_with_else", SiStatement(Bool(True), [ExpressionStatement(Erumpe())], [ExpressionStatement(Erumpe())]), "Si(\n Bool(True),\n statements([\n ExpressionStatement(\n Erumpe()\n )\n ]),\n statements([\n ExpressionStatement(\n Erumpe()\n )\n ])\n)"), + ("si_empty_else", SiStatement(Bool(True), [ExpressionStatement(Erumpe())], []), "Si(\n Bool(True),\n statements([\n ExpressionStatement(\n Erumpe()\n )\n ]),\n statements([])\n)"), + ("dum", DumStatement(Bool(False), [ExpressionStatement(Erumpe())]), "Dum(\n Bool(False),\n statements([\n ExpressionStatement(\n Erumpe()\n )\n ])\n)"), + ("per", PerStatement(DataArray([Numeral("I")]), ID("i"), [ExpressionStatement(Erumpe())]), "Per(\n Array([\n Numeral(I)\n ]),\n ID(i),\n statements([\n ExpressionStatement(\n Erumpe()\n )\n ])\n)"), ("invoca", Invoca(ID("f"), [Numeral("I")]), "Invoca(\n ID(f),\n parameters([\n Numeral(I)\n ])\n)"), ("builtin", BuiltIn("DICE", [String("hi")]), "Builtin(\n DICE,\n parameters([\n String(hi)\n ])\n)"), - ("defini", Defini(ID("f"), [ID("n")], [ExpressionStatement(Redi([Numeral("I")]))]), "Defini(\n ID(f),\n parameters([\n ID(n)\n ]),\n statements([\n Redi([\n Numeral(I)\n ])\n ])\n)"), - ("program_no_modules", Program([], [ExpressionStatement(Numeral("I"))]), "modules([]),\nstatements([\n Numeral(I)\n])"), - ("program_with_module", Program([ModuleCall("FORS")], [ExpressionStatement(Numeral("I"))]), "modules([\n FORS\n]),\nstatements([\n Numeral(I)\n])"), + ("defini", Defini(ID("f"), [ID("n")], [ExpressionStatement(Redi([Numeral("I")]))]), "Defini(\n ID(f),\n parameters([\n ID(n)\n ]),\n statements([\n ExpressionStatement(\n Redi([\n Numeral(I)\n ])\n )\n ])\n)"), + ("program_no_modules", Program([], [ExpressionStatement(Numeral("I"))]), "modules([]),\nstatements([\n ExpressionStatement(\n Numeral(I)\n )\n])"), + ("program_with_module", Program([ModuleCall("FORS")], [ExpressionStatement(Numeral("I"))]), "modules([\n FORS\n]),\nstatements([\n ExpressionStatement(\n Numeral(I)\n )\n])"), ] class TestRepr(unittest.TestCase): @@ -584,10 +589,10 @@ class TestMakeString(unittest.TestCase): self.assertEqual(make_string(ValInt(3)), "III") def test_bool_true(self): - self.assertEqual(make_string(ValBool(True)), "VERVS") + self.assertEqual(make_string(ValBool(True)), "VERITAS") def test_bool_false(self): - self.assertEqual(make_string(ValBool(False)), "FALSVS") + self.assertEqual(make_string(ValBool(False)), "FALSITAS") def test_nul(self): self.assertEqual(make_string(ValNul()), "NVLLVS") @@ -601,22 +606,22 @@ class TestMakeString(unittest.TestCase): def test_nested_list(self): self.assertEqual( make_string(ValList([ValStr("a"), ValBool(True)])), - "[a VERVS]" + "[a VERITAS]" ) # --- DICE with non-integer types --- dice_type_tests = [ - ("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(VERITAS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Bool(True)]))]), ValStr("VERITAS"), "VERITAS\n"), + ("DICE(FALSITAS)", Program([], [ExpressionStatement(BuiltIn("DICE", [Bool(False)]))]), ValStr("FALSITAS"), "FALSITAS\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)", Program([], [ExpressionStatement(BuiltIn("DICE", [BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS")]))]), ValStr("V"), "V\n"), # multiple args of mixed types - ('DICE("x", VERITAS)', Program([], [ExpressionStatement(BuiltIn("DICE", [String("x"), Bool(True)]))]), ValStr("x VERVS"), "x VERVS\n"), + ('DICE("x", VERITAS)', Program([], [ExpressionStatement(BuiltIn("DICE", [String("x"), Bool(True)]))]), ValStr("x VERITAS"), "x VERITAS\n"), ] class TestDiceTypes(unittest.TestCase): @@ -756,8 +761,6 @@ function_edge_tests = [ ]), 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", @@ -793,25 +796,53 @@ function_edge_tests = [ # REDI inside SI exits function, skips remaining statements in block ( "DEFINI f () VT {\nSI VERITAS TVNC {\nREDI (I)\nREDI (II)\n}\n}\nINVOCA f ()", - None, + Program([],[ + Defini(ID("f"), [], [SiStatement(Bool(True),[Redi([Numeral("I")]),Redi([Numeral("II")])],None)]), + ExpressionStatement(Invoca(ID("f"),[])) + ]), ValInt(1), ), # REDI inside DVM exits loop and function ( "DEFINI f () VT {\nDESIGNA x VT I\nDVM FALSITAS FACE {\nREDI (x)\n}\n}\nINVOCA f ()", - None, + Program([],[ + Defini(ID("f"), [], [ + Designa(ID("x"), Numeral("I")), + DumStatement(Bool(False), [Redi([ID("x")])]) + ]), + ExpressionStatement(Invoca(ID("f"),[])) + ]), ValInt(1), ), # REDI inside PER exits loop and function ( "DEFINI f () VT {\nPER x IN [I, II, III] FACE {\nSI x EST II TVNC {\nREDI (x)\n}\n}\n}\nINVOCA f ()", - None, + Program([],[ + Defini(ID("f"), [], [ + PerStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), ID("x"), [ + SiStatement(BinOp(ID("x"), Numeral("II"), "KEYWORD_EST"), [ + Redi([ID("x")]) + ], None) + ]) + ]), + ExpressionStatement(Invoca(ID("f"),[])) + ]), ValInt(2), ), # REDI inside nested loops exits all loops and function ( "DEFINI f () VT {\nDESIGNA x VT I\nDVM FALSITAS FACE {\nDVM FALSITAS FACE {\nREDI (x)\n}\n}\n}\nINVOCA f ()", - None, + Program([],[ + Defini(ID("f"), [], [ + Designa(ID("x"), Numeral("I")), + DumStatement(Bool(False), [ + DumStatement(Bool(False), [ + Redi([ID("x")]) + ]) + ]) + ]), + ExpressionStatement(Invoca(ID("f"),[])) + ]), ValInt(1), ), ] @@ -1026,7 +1057,7 @@ et_avt_tests = [ ("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 + # short-circuit behavior: combined with comparisons ("(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)), @@ -1062,6 +1093,24 @@ array_index_tests = [ ValInt(20)), # second element # index into range array ("[I VSQVE V][II]", Program([], [ExpressionStatement(ArrayIndex(DataRangeArray(Numeral("I"), Numeral("V")), Numeral("II")))]), ValInt(2)), # second element of [1,2,3,4] + # expression as index + ("[I, II, III][I + I]", + Program([], [ExpressionStatement(ArrayIndex( + DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), + BinOp(Numeral("I"), Numeral("I"), "SYMBOL_PLUS")))]), + ValInt(2)), + # division result as index (no FRACTIO): IV / II = 2 + ("[X, XX, XXX][IV / II]", + Program([], [ExpressionStatement(ArrayIndex( + DataArray([Numeral("X"), Numeral("XX"), Numeral("XXX")]), + BinOp(Numeral("IV"), Numeral("II"), "SYMBOL_DIVIDE")))]), + ValInt(20)), + # whole-number fraction (from division) as index, with FRACTIO imported + ("CVM FRACTIO\n[X, XX, XXX][IV / II]", + Program([ModuleCall("FRACTIO")], [ExpressionStatement(ArrayIndex( + DataArray([Numeral("X"), Numeral("XX"), Numeral("XXX")]), + BinOp(Numeral("IV"), Numeral("II"), "SYMBOL_DIVIDE")))]), + ValInt(20)), ] class TestArrayIndex(unittest.TestCase): @@ -1342,22 +1391,57 @@ fractio_tests = [ ValFrac(Fraction(7) + Fraction(100, 144))), # Arithmetic ("CVM FRACTIO\nIIIS + S", - None, ValFrac(Fraction(4))), + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_PLUS")) + ]), + ValFrac(Fraction(4)) + ), ("CVM FRACTIO\nIIIS - S", - None, ValFrac(Fraction(3))), + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_MINUS")) + ]), + ValFrac(Fraction(3)) + ), ("CVM FRACTIO\nS * IV", - None, ValFrac(Fraction(2))), + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("S"), Numeral("IV"), "SYMBOL_TIMES")) + ]), + ValFrac(Fraction(2)) + ), # Division returns fraction ("CVM FRACTIO\nI / IV", - None, ValFrac(Fraction(1, 4))), + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Numeral("I"), Numeral("IV"), "SYMBOL_DIVIDE")) + ]), + ValFrac(Fraction(1, 4)) + ), ("CVM FRACTIO\nI / III", - None, ValFrac(Fraction(1, 3))), + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Numeral("I"), Numeral("III"), "SYMBOL_DIVIDE")) + ]), + ValFrac(Fraction(1, 3)) + ), # Integer division still works without fractions in operands... but with FRACTIO returns ValFrac ("CVM FRACTIO\nX / II", - None, ValFrac(Fraction(5))), + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE")) + ]), + ValFrac(Fraction(5)) + ), # String concatenation with fraction ("CVM FRACTIO\nDICE(IIIS & \"!\")", - None, ValStr("IIIS!"), "IIIS!\n"), + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BuiltIn("DICE", [BinOp(Fractio("IIIS"), String("!"), "SYMBOL_AMPERSAND")])) + ]), + ValStr("IIIS!"), "IIIS!\n" + ), + # Negative fractions + ("CVM FRACTIO\nCVM SVBNVLLA\n-IIS", + Program([ModuleCall("FRACTIO"),ModuleCall("SVBNVLLA")],[ + ExpressionStatement(UnaryMinus(Fractio("IIS"))) + ]), + ValFrac(Fraction(-5,2)) + ) ] class TestFractio(unittest.TestCase): @@ -1368,18 +1452,72 @@ class TestFractio(unittest.TestCase): fractio_comparison_tests = [ # fraction vs fraction - ("CVM FRACTIO\nIIIS PLVS III", None, ValBool(True)), # 3.5 > 3 - ("CVM FRACTIO\nIII MINVS IIIS", None, ValBool(True)), # 3 < 3.5 - ("CVM FRACTIO\nIIIS MINVS IV", None, ValBool(True)), # 3.5 < 4 - ("CVM FRACTIO\nIV PLVS IIIS", None, ValBool(True)), # 4 > 3.5 - ("CVM FRACTIO\nIIIS PLVS IIIS", None, ValBool(False)), # 3.5 > 3.5 is False - ("CVM FRACTIO\nIIIS MINVS IIIS", None, ValBool(False)), # 3.5 < 3.5 is False + ("CVM FRACTIO\nIIIS PLVS III", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("III"), "KEYWORD_PLVS")) + ]), + ValBool(True) + ), + ("CVM FRACTIO\nIII MINVS IIIS", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Numeral("III"), Fractio("IIIS"), "KEYWORD_MINVS")) + ]), + ValBool(True) + ), + ("CVM FRACTIO\nIIIS MINVS IV", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("IV"), "KEYWORD_MINVS")) + ]), + ValBool(True) + ), + ("CVM FRACTIO\nIV PLVS IIIS", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Numeral("IV"), Fractio("IIIS"), "KEYWORD_PLVS")) + ]), + ValBool(True) + ), + ("CVM FRACTIO\nIIIS PLVS IIIS", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_PLVS")) + ]), + ValBool(False) + ), + ("CVM FRACTIO\nIIIS MINVS IIIS", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_MINVS")) + ]), + ValBool(False) + ), # equality: fraction == fraction - ("CVM FRACTIO\nIIIS EST IIIS", None, ValBool(True)), - ("CVM FRACTIO\nIIIS EST IV", None, ValBool(False)), + ("CVM FRACTIO\nIIIS EST IIIS", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_EST")) + ]), + ValBool(True) + ), + ("CVM FRACTIO\nIIIS EST IV", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("IV"), "KEYWORD_EST")) + ]), + ValBool(False) + ), # equality: fraction == whole number (ValFrac(4) vs ValInt(4)) - ("CVM FRACTIO\nIIIS + S EST IV", None, ValBool(True)), # 3.5+0.5 == 4 - ("CVM FRACTIO\nS + S EST I", None, ValBool(True)), # 0.5+0.5 == 1 + ("CVM FRACTIO\nIIIS + S EST IV", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp( + BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_PLUS"), + Numeral("IV"), "KEYWORD_EST")) + ]), + ValBool(True) + ), + ("CVM FRACTIO\nS + S EST I", + Program([ModuleCall("FRACTIO")], [ + ExpressionStatement(BinOp( + BinOp(Fractio("S"), Fractio("S"), "SYMBOL_PLUS"), + Numeral("I"), "KEYWORD_EST")) + ]), + ValBool(True) + ), ] class TestFractioComparisons(unittest.TestCase):