🐐 Zipping

This commit is contained in:
2026-04-25 18:34:47 +02:00
parent 3a80c4e941
commit 8d06407527
10 changed files with 109 additions and 2 deletions

View File

@@ -376,6 +376,16 @@ Returns a new array with the element at 1-based position `idx` removed. The inde
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`.
### NECTE
`NECTE(array1, array2)`
Weaves two arrays together into a new array of two-element pair arrays. The two inputs must have equal length; mismatched lengths raise an error.
### IVNGE
`IVNGE(keys, values)`
Builds a dict by yoking two parallel arrays — the i-th element of `keys` becomes the key for the i-th element of `values`. The two arrays must have equal length. Keys must be strings or integers. If `keys` contains duplicates, the later value wins.
### SENATVS
`SENATVS(bool, ...)` or `SENATVS([bool])`

View File

@@ -1522,6 +1522,29 @@ class BuiltIn(Node):
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 "NECTE":
if len(params) != 2:
raise CentvrionError("NECTE takes exactly II arguments")
if not isinstance(params[0], ValList) or not isinstance(params[1], ValList):
raise CentvrionError("NECTE requires two arrays")
a, b = list(params[0].value()), list(params[1].value())
if len(a) != len(b):
raise CentvrionError("NECTE requires arrays of equal length")
return vtable, ValList([ValList([x, y]) for x, y in zip(a, b)])
case "IVNGE":
if len(params) != 2:
raise CentvrionError("IVNGE takes exactly II arguments")
if not isinstance(params[0], ValList) or not isinstance(params[1], ValList):
raise CentvrionError("IVNGE requires two arrays")
keys, vals = list(params[0].value()), list(params[1].value())
if len(keys) != len(vals):
raise CentvrionError("IVNGE requires arrays of equal length")
d = {}
for k, v in zip(keys, vals):
if not isinstance(k, (ValStr, ValInt)):
raise CentvrionError("Dict keys must be strings or integers")
d[k.value()] = v
return vtable, ValDict(d)
case "ORDINA":
if not isinstance(params[0], ValList):
raise CentvrionError("ORDINA requires an array")

View File

@@ -322,6 +322,12 @@ def _emit_builtin(node, ctx):
case "INSERE":
lines.append(f"CentValue {tmp} = cent_insere({param_vars[0]}, {param_vars[1]}, {param_vars[2]});")
case "NECTE":
lines.append(f"CentValue {tmp} = cent_necte({param_vars[0]}, {param_vars[1]});")
case "IVNGE":
lines.append(f"CentValue {tmp} = cent_ivnge({param_vars[0]}, {param_vars[1]});")
case "EVERRE":
lines.append("cent_everre();")
lines.append(f"CentValue {tmp} = cent_null();")

View File

@@ -898,6 +898,38 @@ CentValue cent_insere(CentValue lst, CentValue idx, CentValue v) {
return result;
}
CentValue cent_necte(CentValue a, CentValue b) {
if (a.type != CENT_LIST || b.type != CENT_LIST)
cent_type_error("'NECTE' requires two arrays");
if (a.lval.len != b.lval.len)
cent_runtime_error("'NECTE' requires arrays of equal length");
int len = a.lval.len;
CentValue result = cent_list_new(len);
for (int i = 0; i < len; i++) {
CentValue pair = cent_list_new(2);
cent_list_push(&pair, a.lval.items[i]);
cent_list_push(&pair, b.lval.items[i]);
cent_list_push(&result, pair);
}
return result;
}
CentValue cent_ivnge(CentValue keys, CentValue vals) {
if (keys.type != CENT_LIST || vals.type != CENT_LIST)
cent_type_error("'IVNGE' requires two arrays");
if (keys.lval.len != vals.lval.len)
cent_runtime_error("'IVNGE' requires arrays of equal length");
int len = keys.lval.len;
CentValue result = cent_dict_new(len);
for (int i = 0; i < len; i++) {
CentValue k = keys.lval.items[i];
if (k.type != CENT_INT && k.type != CENT_STR)
cent_runtime_error("Dict keys must be strings or integers");
cent_dict_set(&result, k, vals.lval.items[i]);
}
return result;
}
/* ------------------------------------------------------------------ */
/* Array helpers */
/* ------------------------------------------------------------------ */

View File

@@ -242,6 +242,8 @@ 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_necte(CentValue a, CentValue b); /* NECTE */
CentValue cent_ivnge(CentValue keys, CentValue vals); /* IVNGE */
CentValue cent_lege(CentValue path); /* LEGE */
void cent_scribe(CentValue path, CentValue content); /* SCRIBE */
void cent_adivnge(CentValue path, CentValue content); /* ADIVNGE */

View File

@@ -57,10 +57,12 @@ builtin_tokens = [("BUILTIN", i) for i in [
"FORTVITVS_NVMERVS",
"FORTVITA_ELECTIO",
"INSERE",
"IVNGE",
"LITTERA",
"LONGITVDO",
"MAIVSCVLA",
"MINVSCVLA",
"NECTE",
"NVMERVS",
"ORDINA",
"SEMEN",

View File

@@ -70,7 +70,7 @@ contexts:
scope: constant.language.centvrion
builtins:
- 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'
- match: '\b(ADDE|ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|INSERE|IVNGE|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NECTE|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TOLLE|TYPVS)\b'
scope: support.function.builtin.centvrion
modules:

View File

@@ -242,6 +242,30 @@ builtin_tests = [
('MINVSCVLA("A,B!1")', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [String("A,B!1")]))]), ValStr("a,b!1")),
# MINVSCVLA round-trips MAIVSCVLA on lowercase input
('MINVSCVLA(MAIVSCVLA("hi"))', Program([], [ExpressionStatement(BuiltIn("MINVSCVLA", [BuiltIn("MAIVSCVLA", [String("hi")])]))]), ValStr("hi")),
# NECTE: zip two integer arrays
("NECTE([I, II, III], [IV, V, VI])", Program([], [ExpressionStatement(BuiltIn("NECTE", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), DataArray([Numeral("IV"), Numeral("V"), Numeral("VI")])]))]), ValList([ValList([ValInt(1), ValInt(4)]), ValList([ValInt(2), ValInt(5)]), ValList([ValInt(3), ValInt(6)])])),
# NECTE: empty + empty
("NECTE([], [])", Program([], [ExpressionStatement(BuiltIn("NECTE", [DataArray([]), DataArray([])]))]), ValList([])),
# NECTE: single element
("NECTE([I], [II])", Program([], [ExpressionStatement(BuiltIn("NECTE", [DataArray([Numeral("I")]), DataArray([Numeral("II")])]))]), ValList([ValList([ValInt(1), ValInt(2)])])),
# NECTE: mixed types (numerals paired with strings)
('NECTE([I, II], ["a", "b"])', Program([], [ExpressionStatement(BuiltIn("NECTE", [DataArray([Numeral("I"), Numeral("II")]), DataArray([String("a"), String("b")])]))]), ValList([ValList([ValInt(1), ValStr("a")]), ValList([ValInt(2), ValStr("b")])])),
# NECTE: print form
('DIC(NECTE([I, II], [III, IV]))', Program([], [ExpressionStatement(BuiltIn("DIC", [BuiltIn("NECTE", [DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("III"), Numeral("IV")])])]))]), ValStr("[[I III] [II IV]]"), "[[I III] [II IV]]\n"),
# NECTE: via variables
("DESIGNA a VT [I, II]\nDESIGNA b VT [III, IV]\nNECTE(a, b)", Program([], [Designa(ID("a"), DataArray([Numeral("I"), Numeral("II")])), Designa(ID("b"), DataArray([Numeral("III"), Numeral("IV")])), ExpressionStatement(BuiltIn("NECTE", [ID("a"), ID("b")]))]), ValList([ValList([ValInt(1), ValInt(3)]), ValList([ValInt(2), ValInt(4)])])),
# IVNGE: string keys
('IVNGE(["a", "b"], [I, II])', Program([], [ExpressionStatement(BuiltIn("IVNGE", [DataArray([String("a"), String("b")]), DataArray([Numeral("I"), Numeral("II")])]))]), ValDict({"a": ValInt(1), "b": ValInt(2)})),
# IVNGE: integer keys
('IVNGE([I, II], ["one", "two"])', Program([], [ExpressionStatement(BuiltIn("IVNGE", [DataArray([Numeral("I"), Numeral("II")]), DataArray([String("one"), String("two")])]))]), ValDict({1: ValStr("one"), 2: ValStr("two")})),
# IVNGE: empty + empty
("IVNGE([], [])", Program([], [ExpressionStatement(BuiltIn("IVNGE", [DataArray([]), DataArray([])]))]), ValDict({})),
# IVNGE: duplicate keys → last wins
('IVNGE(["a", "a"], [I, II])', Program([], [ExpressionStatement(BuiltIn("IVNGE", [DataArray([String("a"), String("a")]), DataArray([Numeral("I"), Numeral("II")])]))]), ValDict({"a": ValInt(2)})),
# IVNGE: print form
('DIC(IVNGE(["a", "b"], [I, II]))', Program([], [ExpressionStatement(BuiltIn("DIC", [BuiltIn("IVNGE", [DataArray([String("a"), String("b")]), DataArray([Numeral("I"), Numeral("II")])])]))]), ValStr("{a VT I, b VT II}"), "{a VT I, b VT II}\n"),
# IVNGE composes with CLAVES (round-trip keys)
('CLAVES(IVNGE(["a", "b"], [I, II]))', Program([], [ExpressionStatement(BuiltIn("CLAVES", [BuiltIn("IVNGE", [DataArray([String("a"), String("b")]), DataArray([Numeral("I"), Numeral("II")])])]))]), ValList([ValStr("a"), ValStr("b")])),
]
class TestBuiltins(unittest.TestCase):

View File

@@ -132,6 +132,14 @@ error_tests = [
('CVM RETE\nPETITVR("/", FVNCTIO (r) VT {\nREDI("hi")\n})\nAVSCVLTA("text")', CentvrionError), # AVSCVLTA port must be integer
("DONICVM i VT I VSQVE X GRADV I - I FAC { DIC(i) }", CentvrionError), # GRADV zero step
('DONICVM i VT I VSQVE X GRADV "foo" FAC { DIC(i) }', CentvrionError), # GRADV non-integer step
("NECTE([I, II], [III])", CentvrionError), # NECTE length mismatch
('NECTE(I, [II])', CentvrionError), # NECTE first arg not a list
('NECTE([I], II)', CentvrionError), # NECTE second arg not a list
("IVNGE([I, II], [III])", CentvrionError), # IVNGE length mismatch
('IVNGE(I, [II])', CentvrionError), # IVNGE first arg not a list
('IVNGE(["a"], II)', CentvrionError), # IVNGE second arg not a list
("IVNGE([VERITAS], [I])", CentvrionError), # IVNGE invalid key type (bool)
("IVNGE([[I]], [II])", CentvrionError), # IVNGE invalid key type (list)
]
class TestErrors(unittest.TestCase):

View File

@@ -65,7 +65,7 @@
"patterns": [
{
"name": "support.function.builtin.cent",
"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"
"match": "\\b(ADDE|ADIVNGE|AVDI_NVMERVS|AVDI|AVSCVLTA|CLAVES|DECIMATIO|DIC|DORMI|EVERRE|FORTVITVS_NVMERVS|FORTVITA_ELECTIO|INSERE|IVNGE|LEGE|LITTERA|LONGITVDO|MAIVSCVLA|MINVSCVLA|NECTE|NVMERVS|ORDINA|PETE|PETITVR|QVAERE|SCINDE|SCRIBE|SEMEN|SENATVS|SVBSTITVE|TOLLE|TYPVS)\\b"
}
]
},