From 58149b5f65dc120050f3b3f8dce7dac3627290c9 Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Tue, 31 Mar 2026 22:10:55 +0200 Subject: [PATCH] :goat: Array indexing --- README.md | 9 +++++++++ centvrion/ast_nodes.py | 18 ++++++++++++++++++ centvrion/parser.py | 5 +++++ tests.py | 16 +++++++++------- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 621b331..4d23f5f 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,15 @@ Arrays are defined using square brackets (`[]`) and commas (`,`). An array of in 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 ### SI/TVNC If-then statements are denoted with the keywords `SI` (if) and `TVNC` (then). Thus, the code diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index ad0f461..98138ae 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -342,6 +342,24 @@ class UnaryMinus(Node): 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): def __init__(self, test, statements, else_part) -> None: self.test = test diff --git a/centvrion/parser.py b/centvrion/parser.py index 6d9a4ad..2d4acad 100644 --- a/centvrion/parser.py +++ b/centvrion/parser.py @@ -16,6 +16,7 @@ class Parser(): ('left', ["SYMBOL_PLUS", "SYMBOL_MINUS"]), ('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]), ('right', ["UMINUS"]), + ('left', ["INDEX"]), ] ) @@ -194,6 +195,10 @@ class Parser(): def range_array(tokens): 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 @self.pg.production('ids : SYMBOL_LPARENS id_list') def ids(tokens): diff --git a/tests.py b/tests.py index a8d2b66..4a386f9 100644 --- a/tests.py +++ b/tests.py @@ -5,7 +5,7 @@ from unittest.mock import patch from parameterized import parameterized from centvrion.ast_nodes import ( - Bool, BinOp, BuiltIn, DataArray, DataRangeArray, Defini, + ArrayIndex, Bool, BinOp, BuiltIn, DataArray, DataRangeArray, Defini, Designa, DumStatement, Erumpe, ExpressionStatement, ID, Invoca, ModuleCall, Nullus, Numeral, PerStatement, 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 () 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 ] class TestErrors(unittest.TestCase): @@ -604,17 +606,17 @@ class TestEtAvt(unittest.TestCase): # --- Array indexing --- -# Indexing is 0-based; NVLLVS serves as index 0 +# Indexing is 1-based; I is the first element array_index_tests = [ # basic indexing - ("[(I, II, III)][NVLLVS]", None, ValInt(1)), # index 0 → first element - ("[(I, II, III)][I]", None, ValInt(2)), # index 1 → second element - ("[(I, II, III)][II]", None, ValInt(3)), # index 2 → third element + ("[(I, II, III)][I]", None, ValInt(1)), # first element + ("[(I, II, III)][II]", None, ValInt(2)), # second element + ("[(I, II, III)][III]", None, ValInt(3)), # third element # 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 - ("[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):