diff --git a/README.md b/README.md index 5e71828..20d4c2b 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,14 @@ Individual elements can be accessed by index using square brackets. Indexing is > I ``` +A sub-array can be extracted with `VSQVE` inside the index brackets. Both bounds are inclusive and 1-based: + +![Array slicing](snippets/array_slice.png) + +``` +> [XX, XXX, XL] +``` + ### Dicts (TABVLA) Dicts are key-value maps created with the `TABVLA` keyword and curly braces: diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index 0559386..1168909 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -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 diff --git a/centvrion/compiler/emit_expr.py b/centvrion/compiler/emit_expr.py index 3c29c52..d9002a1 100644 --- a/centvrion/compiler/emit_expr.py +++ b/centvrion/compiler/emit_expr.py @@ -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() diff --git a/centvrion/compiler/runtime/cent_runtime.c b/centvrion/compiler/runtime/cent_runtime.c index d48be47..c5d014e 100644 --- a/centvrion/compiler/runtime/cent_runtime.c +++ b/centvrion/compiler/runtime/cent_runtime.c @@ -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); diff --git a/centvrion/compiler/runtime/cent_runtime.h b/centvrion/compiler/runtime/cent_runtime.h index c40cfa2..21f2d27 100644 --- a/centvrion/compiler/runtime/cent_runtime.h +++ b/centvrion/compiler/runtime/cent_runtime.h @@ -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); /* ------------------------------------------------------------------ */ diff --git a/centvrion/parser.py b/centvrion/parser.py index 3c207c5..e787084 100644 --- a/centvrion/parser.py +++ b/centvrion/parser.py @@ -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): diff --git a/language/main.aux b/language/main.aux new file mode 100644 index 0000000..d490c35 --- /dev/null +++ b/language/main.aux @@ -0,0 +1,2 @@ +\relax +\gdef \@abspage@last{2} diff --git a/language/main.log b/language/main.log new file mode 100644 index 0000000..d08eed8 --- /dev/null +++ b/language/main.log @@ -0,0 +1,269 @@ +This is XeTeX, Version 3.141592653-2.6-0.999998 (TeX Live 2026/Arch Linux) (preloaded format=xelatex 2026.4.8) 21 APR 2026 22:51 +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**main.tex +(./main.tex +LaTeX2e <2025-11-01> +L3 programming layer <2026-01-19> +(/usr/share/texmf-dist/tex/latex/base/article.cls +Document Class: article 2025/01/22 v1.4n Standard LaTeX document class +(/usr/share/texmf-dist/tex/latex/base/size10.clo +File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option) +) +\c@part=\count271 +\c@section=\count272 +\c@subsection=\count273 +\c@subsubsection=\count274 +\c@paragraph=\count275 +\c@subparagraph=\count276 +\c@figure=\count277 +\c@table=\count278 +\abovecaptionskip=\skip49 +\belowcaptionskip=\skip50 +\bibindent=\dimen148 +) +(/usr/share/texmf-dist/tex/latex/geometry/geometry.sty +Package: geometry 2020/01/02 v5.9 Page Geometry + +(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty +Package: keyval 2022/05/29 v1.15 key=value parser (DPC) +\KV@toks@=\toks17 +) +(/usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty +Package: ifvtex 2019/10/25 v1.7 ifvtex legacy package. Use iftex instead. + +(/usr/share/texmf-dist/tex/generic/iftex/iftex.sty +Package: iftex 2024/12/12 v1.0g TeX engine tests +)) +\Gm@cnth=\count279 +\Gm@cntv=\count280 +\c@Gm@tempcnt=\count281 +\Gm@bindingoffset=\dimen149 +\Gm@wd@mp=\dimen150 +\Gm@odd@mp=\dimen151 +\Gm@even@mp=\dimen152 +\Gm@layoutwidth=\dimen153 +\Gm@layoutheight=\dimen154 +\Gm@layouthoffset=\dimen155 +\Gm@layoutvoffset=\dimen156 +\Gm@dimlist=\toks18 +) +(/usr/share/texmf-dist/tex/latex/fontspec/fontspec.sty +(/usr/share/texmf-dist/tex/latex/l3packages/xparse/xparse.sty +(/usr/share/texmf-dist/tex/latex/l3kernel/expl3.sty +Package: expl3 2026-01-19 L3 programming layer (loader) + +(/usr/share/texmf-dist/tex/latex/l3backend/l3backend-xetex.def +File: l3backend-xetex.def 2025-10-09 L3 backend support: XeTeX +\g__graphics_track_int=\count282 +\g__pdfannot_backend_int=\count283 +\g__pdfannot_backend_link_int=\count284 +)) +Package: xparse 2025-10-09 L3 Experimental document command parser +) +Package: fontspec 2025/09/29 v2.9g Font selection for XeLaTeX and LuaLaTeX + +(/usr/share/texmf-dist/tex/latex/fontspec/fontspec-xetex.sty +Package: fontspec-xetex 2025/09/29 v2.9g Font selection for XeLaTeX and LuaLaTe +X +\l__fontspec_script_int=\count285 +\l__fontspec_language_int=\count286 +\l__fontspec_strnum_int=\count287 +\l__fontspec_tmp_int=\count288 +\l__fontspec_tmpa_int=\count289 +\l__fontspec_tmpb_int=\count290 +\l__fontspec_tmpc_int=\count291 +\l__fontspec_em_int=\count292 +\l__fontspec_emdef_int=\count293 +\l__fontspec_strong_int=\count294 +\l__fontspec_strongdef_int=\count295 +\l__fontspec_tmpa_dim=\dimen157 +\l__fontspec_tmpb_dim=\dimen158 +\l__fontspec_tmpc_dim=\dimen159 + +(/usr/share/texmf-dist/tex/latex/base/fontenc.sty +Package: fontenc 2025/07/18 v2.1d Standard LaTeX package +) +(/usr/share/texmf-dist/tex/latex/fontspec/fontspec.cfg))) + +Package fontspec Info: +(fontspec) Hurmit Nerd Font Mono scale = 0.7. + + +Package fontspec Info: +(fontspec) Hurmit Nerd Font Mono scale = 0.7. + + +Package fontspec Info: +(fontspec) Hurmit Nerd Font Mono/B scale = 0.7. + + +Package fontspec Info: +(fontspec) Hurmit Nerd Font Mono/I scale = 0.7. + + +Package fontspec Info: +(fontspec) Hurmit Nerd Font Mono/BI scale = 0.7. + + +Package fontspec Info: +(fontspec) Font family 'HurmitNerdFontMono(0)' created for font +(fontspec) 'Hurmit Nerd Font Mono' with options +(fontspec) [WordSpace={1,0,0},HyphenChar=None,PunctuationSpace=Word +Space,Scale=0.7]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: <->s*[0.7]"Hurmit +(fontspec) Nerd Font Mono/OT:script=DFLT;language=dflt;" +(fontspec) - 'bold' (b/n) with NFSS spec.: <->s*[0.7]"Hurmit Nerd +(fontspec) Font Mono/B/OT:script=DFLT;language=dflt;" +(fontspec) - 'italic' (m/it) with NFSS spec.: <->s*[0.7]"Hurmit +(fontspec) Nerd Font Mono/I/OT:script=DFLT;language=dflt;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.7]"Hurmit Nerd Font +(fontspec) Mono/BI/OT:script=DFLT;language=dflt;" + + +No file main.aux. +\openout1 = `main.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 11. +LaTeX Font Info: ... okay on input line 11. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 11. +LaTeX Font Info: ... okay on input line 11. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 11. +LaTeX Font Info: ... okay on input line 11. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 11. +LaTeX Font Info: ... okay on input line 11. +LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 11. +LaTeX Font Info: ... okay on input line 11. +LaTeX Font Info: Checking defaults for TU/lmr/m/n on input line 11. +LaTeX Font Info: ... okay on input line 11. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 11. +LaTeX Font Info: ... okay on input line 11. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 11. +LaTeX Font Info: ... okay on input line 11. +*geometry* driver: auto-detecting +*geometry* detected driver: xetex +*geometry* verbose mode - [ preamble ] result: +* driver: xetex +* paper: a4paper +* layout: +* layoutoffset:(h,v)=(0.0pt,0.0pt) +* modes: +* h-part:(L,W,R)=(72.26999pt, 452.9679pt, 72.26999pt) +* v-part:(T,H,B)=(72.26999pt, 700.50687pt, 72.26999pt) +* \paperwidth=597.50787pt +* \paperheight=845.04684pt +* \textwidth=452.9679pt +* \textheight=700.50687pt +* \oddsidemargin=0.0pt +* \evensidemargin=0.0pt +* \topmargin=-37.0pt +* \headheight=12.0pt +* \headsep=25.0pt +* \topskip=10.0pt +* \footskip=30.0pt +* \marginparwidth=57.0pt +* \marginparsep=11.0pt +* \columnsep=10.0pt +* \skip\footins=9.0pt plus 4.0pt minus 2.0pt +* \hoffset=0.0pt +* \voffset=0.0pt +* \mag=1000 +* \@twocolumnfalse +* \@twosidefalse +* \@mparswitchfalse +* \@reversemarginfalse +* (1in=72.27pt=25.4mm, 1cm=28.453pt) + + +Package fontspec Info: +(fontspec) Adjusting the maths setup (use [no-math] to avoid +(fontspec) this). + +\symlegacymaths=\mathgroup4 +LaTeX Font Info: Overwriting symbol font `legacymaths' in version `bold' +(Font) OT1/cmr/m/n --> OT1/cmr/bx/n on input line 11. +LaTeX Font Info: Redeclaring math accent \acute on input line 11. +LaTeX Font Info: Redeclaring math accent \grave on input line 11. +LaTeX Font Info: Redeclaring math accent \ddot on input line 11. +LaTeX Font Info: Redeclaring math accent \tilde on input line 11. +LaTeX Font Info: Redeclaring math accent \bar on input line 11. +LaTeX Font Info: Redeclaring math accent \breve on input line 11. +LaTeX Font Info: Redeclaring math accent \check on input line 11. +LaTeX Font Info: Redeclaring math accent \hat on input line 11. +LaTeX Font Info: Redeclaring math accent \dot on input line 11. +LaTeX Font Info: Redeclaring math accent \mathring on input line 11. +LaTeX Font Info: Redeclaring math symbol \colon on input line 11. +LaTeX Font Info: Redeclaring math symbol \Gamma on input line 11. +LaTeX Font Info: Redeclaring math symbol \Delta on input line 11. +LaTeX Font Info: Redeclaring math symbol \Theta on input line 11. +LaTeX Font Info: Redeclaring math symbol \Lambda on input line 11. +LaTeX Font Info: Redeclaring math symbol \Xi on input line 11. +LaTeX Font Info: Redeclaring math symbol \Pi on input line 11. +LaTeX Font Info: Redeclaring math symbol \Sigma on input line 11. +LaTeX Font Info: Redeclaring math symbol \Upsilon on input line 11. +LaTeX Font Info: Redeclaring math symbol \Phi on input line 11. +LaTeX Font Info: Redeclaring math symbol \Psi on input line 11. +LaTeX Font Info: Redeclaring math symbol \Omega on input line 11. +LaTeX Font Info: Redeclaring math symbol \mathdollar on input line 11. +LaTeX Font Info: Redeclaring symbol font `operators' on input line 11. +LaTeX Font Info: Encoding `OT1' has changed to `TU' for symbol font +(Font) `operators' in the math version `normal' on input line 11. +LaTeX Font Info: Overwriting symbol font `operators' in version `normal' +(Font) OT1/cmr/m/n --> TU/lmr/m/n on input line 11. +LaTeX Font Info: Encoding `OT1' has changed to `TU' for symbol font +(Font) `operators' in the math version `bold' on input line 11. +LaTeX Font Info: Overwriting symbol font `operators' in version `bold' +(Font) OT1/cmr/bx/n --> TU/lmr/m/n on input line 11. +LaTeX Font Info: Overwriting symbol font `operators' in version `normal' +(Font) TU/lmr/m/n --> TU/lmr/m/n on input line 11. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `normal' +(Font) OT1/cmr/m/it --> TU/lmr/m/it on input line 11. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `normal' +(Font) OT1/cmr/bx/n --> TU/lmr/b/n on input line 11. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `normal' +(Font) OT1/cmss/m/n --> TU/lmss/m/n on input line 11. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `normal' +(Font) OT1/cmtt/m/n --> TU/HurmitNerdFontMono(0)/m/n on input +line 11. +LaTeX Font Info: Overwriting symbol font `operators' in version `bold' +(Font) TU/lmr/m/n --> TU/lmr/b/n on input line 11. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' +(Font) OT1/cmr/bx/it --> TU/lmr/b/it on input line 11. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' +(Font) OT1/cmss/bx/n --> TU/lmss/b/n on input line 11. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' +(Font) OT1/cmtt/m/n --> TU/HurmitNerdFontMono(0)/b/n on input +line 11. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <7> on input line 14. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <5> on input line 14. +LaTeX Font Info: Font shape `TU/HurmitNerdFontMono(0)/m/n' will be +(Font) scaled to size 6.99997pt on input line 22. + +LaTeX Warning: Float too large for page by 44.293pt on input line 93. + +[1 + +] [2] (./main.aux) + *********** +LaTeX2e <2025-11-01> +L3 programming layer <2026-01-19> + *********** + ) +Here is how much of TeX's memory you used: + 3526 strings out of 470191 + 106539 string characters out of 5479698 + 562689 words of memory out of 5000000 + 32135 multiletter control sequences out of 15000+600000 + 627857 words of font info for 57 fonts, out of 8000000 for 9000 + 14 hyphenation exceptions out of 8191 + 73i,9n,93p,432b,328s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on main.pdf (2 pages). diff --git a/language/main.pdf b/language/main.pdf index 6d031a8..28d0d6e 100644 Binary files a/language/main.pdf and b/language/main.pdf differ diff --git a/language/main.tex b/language/main.tex index 347f017..b64a1d2 100644 --- a/language/main.tex +++ b/language/main.tex @@ -59,6 +59,7 @@ \languageline{expression}{\texttt{FVNCTIO} \texttt{(} \textit{optional-ids} \texttt{)} \texttt{VT} \textit{scope}} \\ \languageline{expression}{\textit{literal}} \\ \languageline{expression}{\textit{expression} \texttt{[} \textit{expression} \texttt{]}} \\ + \languageline{expression}{\textit{expression} \texttt{[} \textit{expression} \texttt{VSQVE} \textit{expression} \texttt{]} \textnormal{\small\ (inclusive slice)}} \\ \languageline{expression}{\textit{expression} \textbf{binop} \textit{expression}} \\ \languageline{expression}{\textbf{unop} \textit{expression}} \\ \hline \languageline{literal}{\textbf{string}} \\ diff --git a/snippets/array_slice.cent b/snippets/array_slice.cent new file mode 100644 index 0000000..8838f03 --- /dev/null +++ b/snippets/array_slice.cent @@ -0,0 +1,2 @@ +DESIGNA x VT [X, XX, XXX, XL, L] +DICE(x[II VSQVE IV]) diff --git a/snippets/array_slice.png b/snippets/array_slice.png new file mode 100644 index 0000000..6f09fbc Binary files /dev/null and b/snippets/array_slice.png differ diff --git a/tests.py b/tests.py index f25c4b1..fbd20cc 100644 --- a/tests.py +++ b/tests.py @@ -11,7 +11,7 @@ from parameterized import parameterized from fractions import Fraction from centvrion.ast_nodes import ( - ArrayIndex, Bool, BinOp, BuiltIn, DataArray, DataDict, DataRangeArray, + ArrayIndex, ArraySlice, Bool, BinOp, BuiltIn, DataArray, DataDict, DataRangeArray, Defini, Continva, Designa, DesignaDestructure, DesignaIndex, DumStatement, Erumpe, ExpressionStatement, Fvnctio, ID, InterpolatedString, Invoca, ModuleCall, Nullus, Numeral, PerStatement, Program, Redi, SiStatement, @@ -673,6 +673,15 @@ error_tests = [ ("DESIGNA a, b VT III", CentvrionError), # destructure non-array ("DESIGNA a, b VT [I]", CentvrionError), # destructure length mismatch: too many targets ("DESIGNA a, b VT [I, II, III]", CentvrionError), # destructure length mismatch: too few targets + ("[I, II, III][II VSQVE IV]", CentvrionError), # slice upper bound out of range + ("[I, II, III][NVLLVS VSQVE II]", CentvrionError), # slice with non-integer bound + ("I[I VSQVE II]", CentvrionError), # slice on non-array + ("[I, II, III][III VSQVE I]", CentvrionError), # slice from > to + ("CVM SVBNVLLA\n[I, II, III][-I VSQVE II]", CentvrionError), # slice with negative lower bound + ("CVM SVBNVLLA\n[I, II, III][I VSQVE -I]", CentvrionError), # slice with negative upper bound + ("CVM FRACTIO\n[I, II, III][IIIS VSQVE III]", CentvrionError), # slice with fractional lower bound + ("CVM FRACTIO\n[I, II, III][I VSQVE IIIS]", CentvrionError), # slice with fractional upper bound + ("CVM FRACTIO\n[I, II, III][I / II VSQVE III]", CentvrionError), # slice with division-fraction lower bound ] class TestErrors(unittest.TestCase): @@ -1517,6 +1526,63 @@ class TestArrayIndexAssign(unittest.TestCase): run_test(self, source, nodes, value) +# --- Array slicing --- + +array_slice_tests = [ + # basic slice from middle + ("[X, XX, XXX, XL, L][II VSQVE IV]", + Program([], [ExpressionStatement(ArraySlice( + DataArray([Numeral("X"), Numeral("XX"), Numeral("XXX"), Numeral("XL"), Numeral("L")]), + Numeral("II"), Numeral("IV")))]), + ValList([ValInt(20), ValInt(30), ValInt(40)])), + # slice of length 1 + ("[I, II, III][II VSQVE II]", + Program([], [ExpressionStatement(ArraySlice( + DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), + Numeral("II"), Numeral("II")))]), + ValList([ValInt(2)])), + # full array slice + ("[I, II, III][I VSQVE III]", + Program([], [ExpressionStatement(ArraySlice( + DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), + Numeral("I"), Numeral("III")))]), + ValList([ValInt(1), ValInt(2), ValInt(3)])), + # slice on variable + ("DESIGNA a VT [I, II, III, IV, V]\na[II VSQVE IV]", + Program([], [ + Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III"), Numeral("IV"), Numeral("V")])), + ExpressionStatement(ArraySlice(ID("a"), Numeral("II"), Numeral("IV"))), + ]), + ValList([ValInt(2), ValInt(3), ValInt(4)])), + # slice then index (chained) + ("[I, II, III, IV][I VSQVE III][II]", + Program([], [ExpressionStatement(ArrayIndex( + ArraySlice( + DataArray([Numeral("I"), Numeral("II"), Numeral("III"), Numeral("IV")]), + Numeral("I"), Numeral("III")), + Numeral("II")))]), + ValInt(2)), + # slice on range array + ("[I VSQVE X][III VSQVE VII]", + Program([], [ExpressionStatement(ArraySlice( + DataRangeArray(Numeral("I"), Numeral("X")), + Numeral("III"), Numeral("VII")))]), + ValList([ValInt(3), ValInt(4), ValInt(5), ValInt(6), ValInt(7)])), + # expression as slice bounds + ("[I, II, III, IV, V][I + I VSQVE II + II]", + Program([], [ExpressionStatement(ArraySlice( + DataArray([Numeral("I"), Numeral("II"), Numeral("III"), Numeral("IV"), Numeral("V")]), + BinOp(Numeral("I"), Numeral("I"), "SYMBOL_PLUS"), + BinOp(Numeral("II"), Numeral("II"), "SYMBOL_PLUS")))]), + ValList([ValInt(2), ValInt(3), ValInt(4)])), +] + +class TestArraySlice(unittest.TestCase): + @parameterized.expand(array_slice_tests) + def test_array_slice(self, source, nodes, value): + run_test(self, source, nodes, value) + + # --- Comments --- comment_tests = [