🐐 Better arrays
This commit is contained in:
10
README.md
10
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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
|
||||
40
tests.py
40
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")),
|
||||
|
||||
Reference in New Issue
Block a user