🐐 Array indexing
This commit is contained in:
@@ -102,6 +102,15 @@ Arrays are defined using square brackets (`[]`) and commas (`,`). An array of in
|
|||||||
DESIGNA x VT [I VSQVE X]
|
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)]
|
||||||
|
DICE(x[I])
|
||||||
|
|
||||||
|
> I
|
||||||
|
```
|
||||||
|
|
||||||
## Conditionals
|
## Conditionals
|
||||||
### SI/TVNC
|
### SI/TVNC
|
||||||
If-then statements are denoted with the keywords `SI` (if) and `TVNC` (then). Thus, the code
|
If-then statements are denoted with the keywords `SI` (if) and `TVNC` (then). Thus, the code
|
||||||
|
|||||||
@@ -342,6 +342,24 @@ class UnaryMinus(Node):
|
|||||||
return vtable, ValInt(-val.value())
|
return vtable, ValInt(-val.value())
|
||||||
|
|
||||||
|
|
||||||
|
class ArrayIndex(Node):
|
||||||
|
def __init__(self, array, index) -> None:
|
||||||
|
self.array = array
|
||||||
|
self.index = index
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"ArrayIndex({self.array!r}, {self.index!r})"
|
||||||
|
|
||||||
|
def _eval(self, vtable):
|
||||||
|
vtable, array = self.array.eval(vtable)
|
||||||
|
vtable, index = self.index.eval(vtable)
|
||||||
|
i = index.value()
|
||||||
|
lst = array.value()
|
||||||
|
if i < 1 or i > len(lst):
|
||||||
|
raise IndexError(f"Index {i} out of range for array of length {len(lst)}")
|
||||||
|
return vtable, lst[i - 1]
|
||||||
|
|
||||||
|
|
||||||
class SiStatement(Node):
|
class SiStatement(Node):
|
||||||
def __init__(self, test, statements, else_part) -> None:
|
def __init__(self, test, statements, else_part) -> None:
|
||||||
self.test = test
|
self.test = test
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class Parser():
|
|||||||
('left', ["SYMBOL_PLUS", "SYMBOL_MINUS"]),
|
('left', ["SYMBOL_PLUS", "SYMBOL_MINUS"]),
|
||||||
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]),
|
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]),
|
||||||
('right', ["UMINUS"]),
|
('right', ["UMINUS"]),
|
||||||
|
('left', ["INDEX"]),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -194,6 +195,10 @@ class Parser():
|
|||||||
def range_array(tokens):
|
def range_array(tokens):
|
||||||
return ast_nodes.DataRangeArray(tokens[1], tokens[3])
|
return ast_nodes.DataRangeArray(tokens[1], tokens[3])
|
||||||
|
|
||||||
|
@self.pg.production('expression : expression SYMBOL_LBRACKET expression SYMBOL_RBRACKET', precedence='INDEX')
|
||||||
|
def array_index(tokens):
|
||||||
|
return ast_nodes.ArrayIndex(tokens[0], tokens[2])
|
||||||
|
|
||||||
# ids
|
# ids
|
||||||
@self.pg.production('ids : SYMBOL_LPARENS id_list')
|
@self.pg.production('ids : SYMBOL_LPARENS id_list')
|
||||||
def ids(tokens):
|
def ids(tokens):
|
||||||
|
|||||||
16
tests.py
16
tests.py
@@ -5,7 +5,7 @@ from unittest.mock import patch
|
|||||||
from parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
|
||||||
from centvrion.ast_nodes import (
|
from centvrion.ast_nodes import (
|
||||||
Bool, BinOp, BuiltIn, DataArray, DataRangeArray, Defini,
|
ArrayIndex, Bool, BinOp, BuiltIn, DataArray, DataRangeArray, Defini,
|
||||||
Designa, DumStatement, Erumpe, ExpressionStatement, ID,
|
Designa, DumStatement, Erumpe, ExpressionStatement, ID,
|
||||||
Invoca, ModuleCall, Nullus, Numeral, PerStatement,
|
Invoca, ModuleCall, Nullus, Numeral, PerStatement,
|
||||||
Program, Redi, SiStatement, String, UnaryMinus,
|
Program, Redi, SiStatement, String, UnaryMinus,
|
||||||
@@ -231,6 +231,8 @@ error_tests = [
|
|||||||
("DEFINI f (x, y) VT { REDI(x) }\nINVOCA f (I)", TypeError), # too few args
|
("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
|
("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
|
("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
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestErrors(unittest.TestCase):
|
class TestErrors(unittest.TestCase):
|
||||||
@@ -604,17 +606,17 @@ class TestEtAvt(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
# --- Array indexing ---
|
# --- Array indexing ---
|
||||||
# Indexing is 0-based; NVLLVS serves as index 0
|
# Indexing is 1-based; I is the first element
|
||||||
|
|
||||||
array_index_tests = [
|
array_index_tests = [
|
||||||
# basic indexing
|
# basic indexing
|
||||||
("[(I, II, III)][NVLLVS]", None, ValInt(1)), # index 0 → first element
|
("[(I, II, III)][I]", None, ValInt(1)), # first element
|
||||||
("[(I, II, III)][I]", None, ValInt(2)), # index 1 → second element
|
("[(I, II, III)][II]", None, ValInt(2)), # second element
|
||||||
("[(I, II, III)][II]", None, ValInt(3)), # index 2 → third element
|
("[(I, II, III)][III]", None, ValInt(3)), # third element
|
||||||
# index into a variable
|
# index into a variable
|
||||||
("DESIGNA a VT [(X, XX, XXX)]\na[I]", None, ValInt(20)),
|
("DESIGNA a VT [(X, XX, XXX)]\na[II]", None, ValInt(20)), # second element
|
||||||
# index into range array
|
# index into range array
|
||||||
("[I VSQVE V][II]", None, ValInt(3)),
|
("[I VSQVE V][II]", None, ValInt(2)), # second element of [1,2,3,4]
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestArrayIndex(unittest.TestCase):
|
class TestArrayIndex(unittest.TestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user