from tests._helpers import ( unittest, parameterized, Fraction, time, run_test, run_compiler_error_test, ArrayIndex, ArraySlice, Bool, BinOp, BuiltIn, DataArray, DataDict, DataRangeArray, Defini, Continva, Designa, DesignaDestructure, DesignaIndex, DumStatement, Erumpe, ExpressionStatement, Fvnctio, ID, InterpolatedString, Invoca, ModuleCall, Nullus, Numeral, PerStatement, Program, Redi, SiStatement, String, TemptaStatement, UnaryMinus, UnaryNot, Fractio, frac_to_fraction, fraction_to_frac, num_to_int, int_to_num, make_string, ValInt, ValStr, ValBool, ValList, ValDict, ValNul, ValFunc, ValFrac, CentvrionError, _RUNTIME_C, _cent_rng, Lexer, Parser, compile_program, os, subprocess, tempfile, StringIO, patch, ) # --- Comments --- comment_tests = [ # trailing line comment ('DIC("hello") // this is ignored', Program([], [ExpressionStatement(BuiltIn("DIC", [String("hello")]))]), ValStr("hello"), "hello\n"), # line comment on its own line before code ('// ignored\nDIC("hi")', Program([], [ExpressionStatement(BuiltIn("DIC", [String("hi")]))]), ValStr("hi"), "hi\n"), # inline block comment ('DIC(/* ignored */ "hi")', Program([], [ExpressionStatement(BuiltIn("DIC", [String("hi")]))]), ValStr("hi"), "hi\n"), # block comment spanning multiple lines ('/* line one\nline two */\nDIC("hi")', Program([], [ExpressionStatement(BuiltIn("DIC", [String("hi")]))]), ValStr("hi"), "hi\n"), # block comment mid-expression ("II /* ignored */ + III", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"))]), ValInt(5)), # line comment after expression (no output) ("II + III // ignored", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"))]), ValInt(5)), # division still works (/ token not confused with //) ("X / II", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))]), ValInt(5)), # multiple line comments ('// first\n// second\nDIC("ok")', Program([], [ExpressionStatement(BuiltIn("DIC", [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', 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', 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', 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): @parameterized.expand(comment_tests) def test_comments(self, source, nodes, value, output=""): run_test(self, source, nodes, value, output) # --- Scope --- scope_tests = [ # SI: variable assigned in true branch persists in outer scope ("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 ALIVD branch persists in outer scope ("SI FALSITAS TVNC { DESIGNA r VT X } ALIVD { 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 FAC { 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] FAC { 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] FAC { 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] FAC { 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,4]; last value assigned by loop is IV=4 ("DONICVM i VT I VSQVE IV FAC { DESIGNA nop VT I }\ni", Program([], [ PerStatement(DataRangeArray(Numeral("I"), Numeral("IV")), ID("i"), [Designa(ID("nop"), Numeral("I"))]), ExpressionStatement(ID("i")), ]), ValInt(4)), # 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 4 times → 5 ("DESIGNA cnt VT I\nDONICVM i VT I VSQVE IV FAC { 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(5)), # DONICVM: ERVMPE exits loop early; counter persists at break value ("DONICVM i VT I VSQVE X FAC {\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", 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", 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)", 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", 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): @parameterized.expand(scope_tests) def test_scope(self, source, nodes, value): run_test(self, source, nodes, value) # --- First-class functions / FVNCTIO --- fvnctio_tests = [ # Lambda assigned to variable, then called ( "DESIGNA f VT FVNCTIO (x) VT { REDI (x + I) }\nINVOCA f (V)", Program([], [ Designa(ID("f"), Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")])])), ExpressionStatement(Invoca(ID("f"), [Numeral("V")])), ]), ValInt(6), ), # IIFE: immediately invoked lambda ( "INVOCA FVNCTIO (x) VT { REDI (x * II) } (III)", Program([], [ ExpressionStatement(Invoca( Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("II"), "SYMBOL_TIMES")])]), [Numeral("III")], )), ]), ValInt(6), ), # Zero-arg lambda ( "INVOCA FVNCTIO () VT { REDI (XLII) } ()", Program([], [ ExpressionStatement(Invoca( Fvnctio([], [Redi([Numeral("XLII")])]), [], )), ]), ValInt(42), ), # Function passed as argument ( "DEFINI apply (f, x) VT { REDI (INVOCA f (x)) }\n" "DESIGNA dbl VT FVNCTIO (n) VT { REDI (n * II) }\n" "INVOCA apply (dbl, V)", Program([], [ Defini(ID("apply"), [ID("f"), ID("x")], [ Redi([Invoca(ID("f"), [ID("x")])]) ]), Designa(ID("dbl"), Fvnctio([ID("n")], [ Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")]) ])), ExpressionStatement(Invoca(ID("apply"), [ID("dbl"), Numeral("V")])), ]), ValInt(10), ), # Lambda uses caller-scope variable (copy-caller semantics) ( "DESIGNA n VT III\n" "DESIGNA f VT FVNCTIO (x) VT { REDI (x + n) }\n" "INVOCA f (V)", Program([], [ Designa(ID("n"), Numeral("III")), Designa(ID("f"), Fvnctio([ID("x")], [ Redi([BinOp(ID("x"), ID("n"), "SYMBOL_PLUS")]) ])), ExpressionStatement(Invoca(ID("f"), [Numeral("V")])), ]), ValInt(8), ), # Named function passed as value ( "DEFINI sqr (x) VT { REDI (x * x) }\n" "DESIGNA f VT sqr\n" "INVOCA f (IV)", Program([], [ Defini(ID("sqr"), [ID("x")], [Redi([BinOp(ID("x"), ID("x"), "SYMBOL_TIMES")])]), Designa(ID("f"), ID("sqr")), ExpressionStatement(Invoca(ID("f"), [Numeral("IV")])), ]), ValInt(16), ), # Nested lambdas ( "INVOCA FVNCTIO (x) VT { REDI (INVOCA FVNCTIO (y) VT { REDI (y + I) } (x)) } (V)", Program([], [ ExpressionStatement(Invoca( Fvnctio([ID("x")], [ Redi([Invoca( Fvnctio([ID("y")], [Redi([BinOp(ID("y"), Numeral("I"), "SYMBOL_PLUS")])]), [ID("x")], )]) ]), [Numeral("V")], )), ]), ValInt(6), ), # DIC on a function value ( "DESIGNA f VT FVNCTIO (x) VT { REDI (x) }\nDIC(f)", Program([], [ Designa(ID("f"), Fvnctio([ID("x")], [Redi([ID("x")])])), ExpressionStatement(BuiltIn("DIC", [ID("f")])), ]), ValStr("FVNCTIO"), "FVNCTIO\n", ), # Lambda stored in array, called via index ( "DESIGNA fns VT [FVNCTIO (x) VT { REDI (x + I) }, FVNCTIO (x) VT { REDI (x * II) }]\n" "INVOCA fns[I] (V)", Program([], [ Designa(ID("fns"), DataArray([ Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")])]), Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("II"), "SYMBOL_TIMES")])]), ])), ExpressionStatement(Invoca( ArrayIndex(ID("fns"), Numeral("I")), [Numeral("V")], )), ]), ValInt(6), ), # Lambda stored in dict, called via key ( 'DESIGNA d VT TABVLA {"add" VT FVNCTIO (x) VT { REDI (x + I) }}\n' 'INVOCA d["add"] (V)', Program([], [ Designa(ID("d"), DataDict([ (String("add"), Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")])])), ])), ExpressionStatement(Invoca( ArrayIndex(ID("d"), String("add")), [Numeral("V")], )), ]), ValInt(6), ), # Multi-param lambda ( "DESIGNA add VT FVNCTIO (a, b) VT { REDI (a + b) }\nINVOCA add (III, IV)", Program([], [ Designa(ID("add"), Fvnctio([ID("a"), ID("b")], [ Redi([BinOp(ID("a"), ID("b"), "SYMBOL_PLUS")]) ])), ExpressionStatement(Invoca(ID("add"), [Numeral("III"), Numeral("IV")])), ]), ValInt(7), ), ] class TestFvnctio(unittest.TestCase): @parameterized.expand(fvnctio_tests) def test_fvnctio(self, source, nodes, value, output=""): run_test(self, source, nodes, value, output) # --- Tempta/Cape (try/catch) --- tempta_tests = [ # Try block succeeds — catch not entered ( "TEMPTA {\nDESIGNA r VT I\n} CAPE e {\nDESIGNA r VT II\n}\nr", Program([], [ TemptaStatement( [Designa(ID("r"), Numeral("I"))], ID("e"), [Designa(ID("r"), Numeral("II"))], ), ExpressionStatement(ID("r")), ]), ValInt(1), ), # Try block errors — caught by catch ( "TEMPTA {\nDESIGNA r VT I / NVLLVS\n} CAPE e {\nDESIGNA r VT II\n}\nr", Program([], [ TemptaStatement( [Designa(ID("r"), BinOp(Numeral("I"), Nullus(), "SYMBOL_DIVIDE"))], ID("e"), [Designa(ID("r"), Numeral("II"))], ), ExpressionStatement(ID("r")), ]), ValInt(2), ), # Error variable contains the error message ( 'DESIGNA e VT NVLLVS\nTEMPTA {\nDESIGNA r VT I / NVLLVS\n} CAPE e {\nNVLLVS\n}\ne', Program([], [ Designa(ID("e"), Nullus()), TemptaStatement( [Designa(ID("r"), BinOp(Numeral("I"), Nullus(), "SYMBOL_DIVIDE"))], ID("e"), [ExpressionStatement(Nullus())], ), ExpressionStatement(ID("e")), ]), ValStr("Division by zero"), ), # Nested tempta — inner catches, outer unaffected ( "DESIGNA r VT NVLLVS\nTEMPTA {\nTEMPTA {\nDESIGNA r VT I / NVLLVS\n} CAPE e {\nDESIGNA r VT I\n}\n} CAPE e {\nDESIGNA r VT II\n}\nr", Program([], [ Designa(ID("r"), Nullus()), TemptaStatement( [TemptaStatement( [Designa(ID("r"), BinOp(Numeral("I"), Nullus(), "SYMBOL_DIVIDE"))], ID("e"), [Designa(ID("r"), Numeral("I"))], )], ID("e"), [Designa(ID("r"), Numeral("II"))], ), ExpressionStatement(ID("r")), ]), ValInt(1), ), # REDI inside catch block ( "DEFINI f () VT {\nTEMPTA {\nDESIGNA x VT I / NVLLVS\n} CAPE e {\nREDI (III)\n}\nREDI (IV)\n}\nINVOCA f ()", Program([], [ Defini(ID("f"), [], [ TemptaStatement( [Designa(ID("x"), BinOp(Numeral("I"), Nullus(), "SYMBOL_DIVIDE"))], ID("e"), [Redi([Numeral("III")])], ), Redi([Numeral("IV")]), ]), ExpressionStatement(Invoca(ID("f"), [])), ]), ValInt(3), ), # ERVMPE inside catch block (inside a loop) ( "DESIGNA r VT NVLLVS\nDVM r EST I FAC {\nTEMPTA {\nDESIGNA x VT I / NVLLVS\n} CAPE e {\nDESIGNA r VT I\nERVMPE\n}\n}\nr", Program([], [ Designa(ID("r"), Nullus()), DumStatement( BinOp(ID("r"), Numeral("I"), "KEYWORD_EST"), [TemptaStatement( [Designa(ID("x"), BinOp(Numeral("I"), Nullus(), "SYMBOL_DIVIDE"))], ID("e"), [Designa(ID("r"), Numeral("I")), Erumpe()], )], ), ExpressionStatement(ID("r")), ]), ValInt(1), ), # Statement after error in try block is not executed ( "DESIGNA r VT NVLLVS\nTEMPTA {\nDESIGNA x VT I / NVLLVS\nDESIGNA r VT III\n} CAPE e {\nDESIGNA r VT II\n}\nr", Program([], [ Designa(ID("r"), Nullus()), TemptaStatement( [Designa(ID("x"), BinOp(Numeral("I"), Nullus(), "SYMBOL_DIVIDE")), Designa(ID("r"), Numeral("III"))], ID("e"), [Designa(ID("r"), Numeral("II"))], ), ExpressionStatement(ID("r")), ]), ValInt(2), ), ] class TestTempta(unittest.TestCase): @parameterized.expand(tempta_tests) def test_tempta(self, source, nodes, value, output=""): run_test(self, source, nodes, value, output)