🐐 Array slicing

This commit is contained in:
2026-04-21 22:53:40 +02:00
parent 559b1b100e
commit 378c28102c
13 changed files with 423 additions and 2 deletions

View File

@@ -847,6 +847,49 @@ class ArrayIndex(Node):
return vtable, lst[i - 1]
def _to_index_int(val):
if isinstance(val, ValInt):
return val.value()
if isinstance(val, ValFrac) and val.value().denominator == 1:
return val.value().numerator
raise CentvrionError("Array index must be a number")
class ArraySlice(Node):
def __init__(self, array, from_index, to_index) -> None:
self.array = array
self.from_index = from_index
self.to_index = to_index
def __eq__(self, other):
return (type(self) == type(other)
and self.array == other.array
and self.from_index == other.from_index
and self.to_index == other.to_index)
def __repr__(self) -> str:
return f"ArraySlice({self.array!r}, {self.from_index!r}, {self.to_index!r})"
def print(self):
return f"{self.array.print()}[{self.from_index.print()} VSQVE {self.to_index.print()}]"
def _eval(self, vtable):
vtable, array = self.array.eval(vtable)
vtable, from_val = self.from_index.eval(vtable)
vtable, to_val = self.to_index.eval(vtable)
if not isinstance(array, ValList):
raise CentvrionError("Cannot slice a non-array value")
from_int = _to_index_int(from_val)
to_int = _to_index_int(to_val)
lst = array.value()
if from_int < 1 or to_int > len(lst) or from_int > to_int:
raise CentvrionError(
f"Slice [{from_int} VSQVE {to_int}] out of range"
f" for array of length {len(lst)}"
)
return vtable, ValList(lst[from_int - 1 : to_int])
class SiStatement(Node):
def __init__(self, test, statements, else_part) -> None:
self.test = test

View File

@@ -2,7 +2,7 @@ from centvrion.errors import CentvrionError
from centvrion.ast_nodes import (
String, InterpolatedString, Numeral, Fractio, Bool, Nullus, ID,
BinOp, UnaryMinus, UnaryNot,
ArrayIndex, DataArray, DataRangeArray, DataDict,
ArrayIndex, ArraySlice, DataArray, DataRangeArray, DataDict,
BuiltIn, Invoca, Fvnctio,
num_to_int, frac_to_fraction,
)
@@ -120,6 +120,15 @@ def emit_expr(node, ctx):
tmp = ctx.fresh_tmp()
return arr_lines + idx_lines + [f"CentValue {tmp} = cent_list_index({arr_var}, {idx_var});"], tmp
if isinstance(node, ArraySlice):
arr_lines, arr_var = emit_expr(node.array, ctx)
lo_lines, lo_var = emit_expr(node.from_index, ctx)
hi_lines, hi_var = emit_expr(node.to_index, ctx)
tmp = ctx.fresh_tmp()
return arr_lines + lo_lines + hi_lines + [
f"CentValue {tmp} = cent_list_slice({arr_var}, {lo_var}, {hi_var});"
], tmp
if isinstance(node, DataArray):
lines = []
tmp = ctx.fresh_tmp()

View File

@@ -735,6 +735,22 @@ CentValue cent_list_index(CentValue lst, CentValue idx) {
return lst.lval.items[i - 1];
}
CentValue cent_list_slice(CentValue lst, CentValue lo, CentValue hi) {
if (lst.type != CENT_LIST)
cent_type_error("slice requires a list");
if (lo.type != CENT_INT || hi.type != CENT_INT)
cent_type_error("slice indices must be integers");
long from = lo.ival;
long to = hi.ival;
if (from < 1 || to > lst.lval.len || from > to)
cent_runtime_error("slice out of range");
int len = (int)(to - from + 1);
CentValue result = cent_list_new(len);
for (long j = from; j <= to; j++)
cent_list_push(&result, lst.lval.items[j - 1]);
return result;
}
void cent_list_index_set(CentValue *lst, CentValue idx, CentValue v) {
if (lst->type == CENT_DICT) {
cent_dict_set(lst, idx, v);

View File

@@ -234,6 +234,7 @@ void cent_adivnge(CentValue path, CentValue content); /* ADIVNGE */
CentValue cent_list_new(int cap);
void cent_list_push(CentValue *lst, CentValue v);
CentValue cent_list_index(CentValue lst, CentValue idx); /* 1-based */
CentValue cent_list_slice(CentValue lst, CentValue lo, CentValue hi); /* 1-based, inclusive */
void cent_list_index_set(CentValue *lst, CentValue idx, CentValue v);
/* ------------------------------------------------------------------ */

View File

@@ -326,6 +326,10 @@ class Parser():
def array_index(tokens):
return ast_nodes.ArrayIndex(tokens[0], tokens[2])
@self.pg.production('expression : expression SYMBOL_LBRACKET expression KEYWORD_VSQVE expression SYMBOL_RBRACKET', precedence='INDEX')
def array_slice(tokens):
return ast_nodes.ArraySlice(tokens[0], tokens[2], tokens[4])
# ids
@self.pg.production('ids : SYMBOL_LPARENS id_list')
def ids(tokens):