diff --git a/README.md b/README.md index 4d23f5f..5ea5a96 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,13 @@ The base `CENTVRION` syntax does not allow for floats. However, the `FRACTIO` mo Booleans are denoted with the keywords `VERITAS` for true and `FALSITAS` for false. ### Arrays -Arrays are defined using square brackets (`[]`) and commas (`,`). An array of integers can also be initialized with the `VSQVE` keyword: +Arrays are defined using square brackets (`[]`) and commas (`,`): + +``` +DESIGNA x VT [I, II, III] +``` + +An array of integers can also be initialized with the `VSQVE` keyword: ``` DESIGNA x VT [I VSQVE X] @@ -105,7 +111,7 @@ DESIGNA x VT [I VSQVE X] Individual elements can be accessed by index using square brackets. Indexing is 1-based, so `I` refers to the first element: ``` -DESIGNA x VT [(I, II, III)] +DESIGNA x VT [I, II, III] DICE(x[I]) > I diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index 464086a..f504a12 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -163,7 +163,7 @@ class DataArray(Node): def print(self): items = ", ".join(i.print() for i in self.content) - return f"[({items})]" + return f"[{items}]" def _eval(self, vtable): vals = [] diff --git a/centvrion/parser.py b/centvrion/parser.py index 2d4acad..6aa0622 100644 --- a/centvrion/parser.py +++ b/centvrion/parser.py @@ -138,6 +138,17 @@ class Parser(): else: return [calls[0]] + calls[2] + @self.pg.production('array_items : ') + @self.pg.production('array_items : expression') + @self.pg.production('array_items : expression SYMBOL_COMMA array_items') + def array_items(calls): + if len(calls) == 0: + return [] + elif len(calls) == 1: + return [calls[0]] + else: + return [calls[0]] + calls[2] + @self.pg.production('expression : id') def expression_id(tokens): return tokens[0] @@ -187,7 +198,7 @@ class Parser(): def parens(tokens): return tokens[1] - @self.pg.production('expression : SYMBOL_LBRACKET expressions SYMBOL_RBRACKET') + @self.pg.production('expression : SYMBOL_LBRACKET array_items SYMBOL_RBRACKET') def array(tokens): return ast_nodes.DataArray(tokens[1]) @@ -221,7 +232,7 @@ class Parser(): @self.pg.error def error_handle(token): - raise SyntaxError(token.name, token.value, token.source_pos) + raise SyntaxError(f"{token.name}, {token.value}, {token.source_pos}") parser = self.pg.build() return parser.parse(tokens_input) # type: ignore diff --git a/tests.py b/tests.py index de38f76..3f7c67a 100644 --- a/tests.py +++ b/tests.py @@ -157,11 +157,11 @@ precedence_tests = [ Program([], [ExpressionStatement(BinOp(UnaryMinus(Numeral("II")), Numeral("III"), "SYMBOL_PLUS"))]), ValInt(1)), # INDEX binds tighter than UMINUS: -(arr[I]) = -1 - ("- [(I, II, III)][I]", + ("- [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", + ("[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 @@ -283,7 +283,7 @@ control_tests = [ ]), ValStr("I"), "I\n"), # PER foreach - ("PER i IN [(I, II, III)] FACE { DICE(i) }", + ("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 @@ -351,8 +351,8 @@ builtin_tests = [ ("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)), + ("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): @@ -375,8 +375,8 @@ error_tests = [ ("DEFINI f (x, y) VT { REDI(x) }\nINVOCA f (I)", TypeError), # too few args ("DEFINI f () VT { REDI(I) }\nINVOCA f (I)", TypeError), # args to zero-param function ("SI NVLLVS TVNC { DESIGNA r VT I }", TypeError), # NVLLVS cannot be used as boolean - ("[(I, II)][III]", IndexError), # index too high - ("[(I, II)][-I]", IndexError), # negative index + ("[I, II][III]", IndexError), # index too high + ("[I, II][-I]", IndexError), # negative index ] class TestErrors(unittest.TestCase): @@ -511,7 +511,7 @@ 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(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([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"), @@ -541,11 +541,11 @@ truthiness_tests = [ ]), ValInt(2)), # non-empty list is truthy - ("SI [(I)] TVNC { DESIGNA r VT I } ALVID { DESIGNA r VT II }\nr", + ("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", + ("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 @@ -697,11 +697,11 @@ loop_edge_tests = [ 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) }", + ("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", + ("PER i IN [I, II, III] FACE { SI i EST II TVNC { ERVMPE } }\ni", Program([], [ PerStatement( DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), @@ -724,7 +724,7 @@ loop_edge_tests = [ 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", + ("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( @@ -864,11 +864,11 @@ class TestEtAvt(unittest.TestCase): array_index_tests = [ # basic indexing - ("[(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 + ("[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]", + ("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 @@ -944,7 +944,7 @@ scope_tests = [ ]), 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", + ("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")), @@ -952,7 +952,7 @@ scope_tests = [ 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", + ("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"), [ @@ -964,7 +964,7 @@ scope_tests = [ 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", + ("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")),