From 3a80c4e941258548da0c6271fa7c0e3073ad9de5 Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Sat, 25 Apr 2026 18:18:13 +0200 Subject: [PATCH] :goat: Array functions --- README.md | 15 ++++++ centvrion/ast_nodes.py | 26 ++++++++++ centvrion/compiler/emit_expr.py | 9 ++++ centvrion/compiler/runtime/cent_runtime.c | 50 +++++++++++++++++++ centvrion/compiler/runtime/cent_runtime.h | 3 ++ centvrion/lexer.py | 3 ++ snippets/syntaxes/centvrion.sublime-syntax | 2 +- tests/03_test_builtins.py | 24 +++++++++ .../syntaxes/cent.tmLanguage.json | 2 +- 9 files changed, 132 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a52a816..7c6b710 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,21 @@ Returns the keys of `dict` as an array. Sorts an array in ascending order. Returns a new sorted array. All elements must be the same type — integers, fractions, or strings. Integers and fractions sort numerically; strings sort lexicographically. +### ADDE +`ADDE(array, value)` + +Returns a new array with `value` appended at the end. The original array is unchanged. + +### TOLLE +`TOLLE(array, idx)` + +Returns a new array with the element at 1-based position `idx` removed. The index must be an integer in the range `[I, LONGITVDO(array)]`; out-of-range indices raise an error. + +### INSERE +`INSERE(array, idx, value)` + +Returns a new array with `value` inserted at 1-based position `idx`, shifting later elements one position to the right. The index must be an integer in the range `[I, LONGITVDO(array) + I]`; passing `LONGITVDO(array) + I` is equivalent to `ADDE`. + ### SENATVS `SENATVS(bool, ...)` or `SENATVS([bool])` diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index b7229c4..a98403d 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -1496,6 +1496,32 @@ class BuiltIn(Node): case "EVERRE": print("\033[2J\033[H", end="", flush=True) return vtable, ValNul() + case "ADDE": + if len(params) != 2: + raise CentvrionError("ADDE takes exactly II arguments") + if not isinstance(params[0], ValList): + raise CentvrionError("ADDE requires an array") + return vtable, ValList(list(params[0].value()) + [params[1]]) + case "TOLLE": + if len(params) != 2: + raise CentvrionError("TOLLE takes exactly II arguments") + if not isinstance(params[0], ValList): + raise CentvrionError("TOLLE requires an array") + items = list(params[0].value()) + idx = _to_index_int(params[1]) + if idx < 1 or idx > len(items): + raise CentvrionError(f"Index {idx} out of range for array of length {len(items)}") + return vtable, ValList(items[:idx - 1] + items[idx:]) + case "INSERE": + if len(params) != 3: + raise CentvrionError("INSERE takes exactly III arguments") + if not isinstance(params[0], ValList): + raise CentvrionError("INSERE requires an array") + items = list(params[0].value()) + idx = _to_index_int(params[1]) + if idx < 1 or idx > len(items) + 1: + raise CentvrionError(f"Index {idx} out of range for array of length {len(items)}") + return vtable, ValList(items[:idx - 1] + [params[2]] + items[idx - 1:]) case "ORDINA": if not isinstance(params[0], ValList): raise CentvrionError("ORDINA requires an array") diff --git a/centvrion/compiler/emit_expr.py b/centvrion/compiler/emit_expr.py index c319727..b53d3ed 100644 --- a/centvrion/compiler/emit_expr.py +++ b/centvrion/compiler/emit_expr.py @@ -313,6 +313,15 @@ def _emit_builtin(node, ctx): case "ORDINA": lines.append(f"CentValue {tmp} = cent_ordina({param_vars[0]});") + case "ADDE": + lines.append(f"CentValue {tmp} = cent_adde({param_vars[0]}, {param_vars[1]});") + + case "TOLLE": + lines.append(f"CentValue {tmp} = cent_tolle({param_vars[0]}, {param_vars[1]});") + + case "INSERE": + lines.append(f"CentValue {tmp} = cent_insere({param_vars[0]}, {param_vars[1]}, {param_vars[2]});") + case "EVERRE": lines.append("cent_everre();") lines.append(f"CentValue {tmp} = cent_null();") diff --git a/centvrion/compiler/runtime/cent_runtime.c b/centvrion/compiler/runtime/cent_runtime.c index 254da4b..ff1eb9b 100644 --- a/centvrion/compiler/runtime/cent_runtime.c +++ b/centvrion/compiler/runtime/cent_runtime.c @@ -848,6 +848,56 @@ CentValue cent_ordina(CentValue lst) { return result; } +static long _index_arg(CentValue idx, const char *name) { + if (idx.type == CENT_INT) + return idx.ival; + if (idx.type == CENT_FRAC && idx.fval.den == 1) + return idx.fval.num; + cent_type_error(name); + return 0; +} + +CentValue cent_adde(CentValue lst, CentValue v) { + if (lst.type != CENT_LIST) + cent_type_error("'ADDE' requires a list"); + int len = lst.lval.len; + CentValue result = cent_list_new(len + 1); + for (int i = 0; i < len; i++) + cent_list_push(&result, lst.lval.items[i]); + cent_list_push(&result, v); + return result; +} + +CentValue cent_tolle(CentValue lst, CentValue idx) { + if (lst.type != CENT_LIST) + cent_type_error("'TOLLE' requires a list"); + long i = _index_arg(idx, "'TOLLE' index must be an integer"); + int len = lst.lval.len; + if (i < 1 || i > len) + cent_runtime_error("'TOLLE' index out of range"); + CentValue result = cent_list_new(len - 1); + for (int j = 0; j < len; j++) + if (j != i - 1) + cent_list_push(&result, lst.lval.items[j]); + return result; +} + +CentValue cent_insere(CentValue lst, CentValue idx, CentValue v) { + if (lst.type != CENT_LIST) + cent_type_error("'INSERE' requires a list"); + long i = _index_arg(idx, "'INSERE' index must be an integer"); + int len = lst.lval.len; + if (i < 1 || i > len + 1) + cent_runtime_error("'INSERE' index out of range"); + CentValue result = cent_list_new(len + 1); + for (int j = 0; j < i - 1; j++) + cent_list_push(&result, lst.lval.items[j]); + cent_list_push(&result, v); + for (int j = i - 1; j < len; j++) + cent_list_push(&result, lst.lval.items[j]); + return result; +} + /* ------------------------------------------------------------------ */ /* Array helpers */ /* ------------------------------------------------------------------ */ diff --git a/centvrion/compiler/runtime/cent_runtime.h b/centvrion/compiler/runtime/cent_runtime.h index d97cc27..58ed0b8 100644 --- a/centvrion/compiler/runtime/cent_runtime.h +++ b/centvrion/compiler/runtime/cent_runtime.h @@ -239,6 +239,9 @@ CentValue cent_senatus(CentValue *args, int n); /* SENATVS */ CentValue cent_typvs(CentValue v); /* TYPVS */ void cent_dormi(CentValue n); /* DORMI */ CentValue cent_ordina(CentValue lst); /* ORDINA */ +CentValue cent_adde(CentValue lst, CentValue v); /* ADDE */ +CentValue cent_tolle(CentValue lst, CentValue idx); /* TOLLE */ +CentValue cent_insere(CentValue lst, CentValue idx, CentValue v); /* INSERE */ CentValue cent_lege(CentValue path); /* LEGE */ void cent_scribe(CentValue path, CentValue content); /* SCRIBE */ void cent_adivnge(CentValue path, CentValue content); /* ADIVNGE */ diff --git a/centvrion/lexer.py b/centvrion/lexer.py index 207847e..d87ade1 100644 --- a/centvrion/lexer.py +++ b/centvrion/lexer.py @@ -46,6 +46,7 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [ ]] builtin_tokens = [("BUILTIN", i) for i in [ + "ADDE", "AVDI_NVMERVS", "AVDI", "CLAVES", @@ -55,6 +56,7 @@ builtin_tokens = [("BUILTIN", i) for i in [ "EVERRE", "FORTVITVS_NVMERVS", "FORTVITA_ELECTIO", + "INSERE", "LITTERA", "LONGITVDO", "MAIVSCVLA", @@ -63,6 +65,7 @@ builtin_tokens = [("BUILTIN", i) for i in [ "ORDINA", "SEMEN", "SENATVS", + "TOLLE", "TYPVS", "LEGE", "SCRIBE", diff --git a/snippets/syntaxes/centvrion.sublime-syntax b/snippets/syntaxes/centvrion.sublime-syntax index 52002a3..fd8b6fc 100644 --- a/snippets/syntaxes/centvrion.sublime-syntax +++ b/snippets/syntaxes/centvrion.sublime-syntax @@ -70,7 +70,7 @@ contexts: scope: constant.language.centvrion builtins: - - match: '\b(ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TYPVS)\b' + - match: '\b(ADDE|ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|INSERE|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TOLLE|TYPVS)\b' scope: support.function.builtin.centvrion modules: diff --git a/tests/03_test_builtins.py b/tests/03_test_builtins.py index cf327ff..1ed272c 100644 --- a/tests/03_test_builtins.py +++ b/tests/03_test_builtins.py @@ -76,6 +76,30 @@ builtin_tests = [ ("CVM FRACTIO\nORDINA([III, S, II])", Program([ModuleCall("FRACTIO")], [ExpressionStatement(BuiltIn("ORDINA", [DataArray([Numeral("III"), Fractio("S"), Numeral("II")])]))]), ValList([ValFrac(Fraction(1, 2)), ValInt(2), ValInt(3)])), # ORDINA: array passed via variable ("DESIGNA x VT [III, I, II]\nORDINA(x)", Program([], [Designa(ID("x"), DataArray([Numeral("III"), Numeral("I"), Numeral("II")])), ExpressionStatement(BuiltIn("ORDINA", [ID("x")]))]), ValList([ValInt(1), ValInt(2), ValInt(3)])), + # ADDE: append to non-empty + ("ADDE([I, II], III)", Program([], [ExpressionStatement(BuiltIn("ADDE", [DataArray([Numeral("I"), Numeral("II")]), Numeral("III")]))]), ValList([ValInt(1), ValInt(2), ValInt(3)])), + # ADDE: append to empty + ("ADDE([], V)", Program([], [ExpressionStatement(BuiltIn("ADDE", [DataArray([]), Numeral("V")]))]), ValList([ValInt(5)])), + # ADDE: heterogeneous types are allowed + ('ADDE([I, II], "x")', Program([], [ExpressionStatement(BuiltIn("ADDE", [DataArray([Numeral("I"), Numeral("II")]), String("x")]))]), ValList([ValInt(1), ValInt(2), ValStr("x")])), + # ADDE: print form to confirm output rendering + ("DIC(ADDE([I, II], III))", Program([], [ExpressionStatement(BuiltIn("DIC", [BuiltIn("ADDE", [DataArray([Numeral("I"), Numeral("II")]), Numeral("III")])]))]), ValStr("[I II III]"), "[I II III]\n"), + # TOLLE: remove middle element + ("TOLLE([I, II, III], II)", Program([], [ExpressionStatement(BuiltIn("TOLLE", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("II")]))]), ValList([ValInt(1), ValInt(3)])), + # TOLLE: remove first element + ("TOLLE([I, II, III], I)", Program([], [ExpressionStatement(BuiltIn("TOLLE", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("I")]))]), ValList([ValInt(2), ValInt(3)])), + # TOLLE: remove last element + ("TOLLE([I, II, III], III)", Program([], [ExpressionStatement(BuiltIn("TOLLE", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("III")]))]), ValList([ValInt(1), ValInt(2)])), + # TOLLE: single-element array → empty + ("TOLLE([V], I)", Program([], [ExpressionStatement(BuiltIn("TOLLE", [DataArray([Numeral("V")]), Numeral("I")]))]), ValList([])), + # INSERE: insert into middle + ("INSERE([I, III], II, II)", Program([], [ExpressionStatement(BuiltIn("INSERE", [DataArray([Numeral("I"), Numeral("III")]), Numeral("II"), Numeral("II")]))]), ValList([ValInt(1), ValInt(2), ValInt(3)])), + # INSERE: insert at front + ("INSERE([II, III], I, I)", Program([], [ExpressionStatement(BuiltIn("INSERE", [DataArray([Numeral("II"), Numeral("III")]), Numeral("I"), Numeral("I")]))]), ValList([ValInt(1), ValInt(2), ValInt(3)])), + # INSERE: insert at len+1 (== append) + ("INSERE([I, II], III, III)", Program([], [ExpressionStatement(BuiltIn("INSERE", [DataArray([Numeral("I"), Numeral("II")]), Numeral("III"), Numeral("III")]))]), ValList([ValInt(1), ValInt(2), ValInt(3)])), + # INSERE: into empty array at idx 1 + ("INSERE([], I, V)", Program([], [ExpressionStatement(BuiltIn("INSERE", [DataArray([]), Numeral("I"), Numeral("V")]))]), ValList([ValInt(5)])), # TYPVS: integer ("TYPVS(V)", Program([], [ExpressionStatement(BuiltIn("TYPVS", [Numeral("V")]))]), ValStr("NVMERVS")), # TYPVS: string diff --git a/vscode-extension/syntaxes/cent.tmLanguage.json b/vscode-extension/syntaxes/cent.tmLanguage.json index eeea8c8..3806be8 100644 --- a/vscode-extension/syntaxes/cent.tmLanguage.json +++ b/vscode-extension/syntaxes/cent.tmLanguage.json @@ -65,7 +65,7 @@ "patterns": [ { "name": "support.function.builtin.cent", - "match": "\\b(ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TYPVS)\\b" + "match": "\\b(ADDE|ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|INSERE|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TOLLE|TYPVS)\\b" } ] },