🐐 ORDINA with comparitor

This commit is contained in:
2026-04-25 18:50:37 +02:00
parent 8d06407527
commit 5e4c7350a9
9 changed files with 126 additions and 18 deletions
+39 -15
View File
@@ -1,3 +1,4 @@
import functools
import http.server
import re
import time
@@ -1328,6 +1329,22 @@ class TemptaStatement(Node):
return vtable, last_val
def _call_func(func: ValFunc, args: list, vtable: dict, callee_desc: str = "function"):
if len(args) != len(func.params):
raise CentvrionError(
f"{callee_desc} expects {len(func.params)} argument(s), got {len(args)}"
)
func_vtable = vtable.copy()
for i, param in enumerate(func.params):
func_vtable[param.name] = args[i]
func_vtable["#return"] = None
for statement in func.body:
func_vtable, _ = statement.eval(func_vtable)
if func_vtable["#return"] is not None:
return func_vtable["#return"]
return ValNul()
class Invoca(Node):
def __init__(self, callee, parameters) -> None:
self.callee = callee
@@ -1354,21 +1371,9 @@ class Invoca(Node):
callee_desc = (self.callee.name
if isinstance(self.callee, ID) else "expression")
raise CentvrionError(f"{callee_desc} is not a function")
if len(params) != len(func.params):
callee_desc = (self.callee.name
if isinstance(self.callee, ID) else "FVNCTIO")
raise CentvrionError(
f"{callee_desc} expects {len(func.params)} argument(s), got {len(params)}"
)
func_vtable = vtable.copy()
for i, param in enumerate(func.params):
func_vtable[param.name] = params[i]
func_vtable["#return"] = None
for statement in func.body:
func_vtable, _ = statement.eval(func_vtable)
if func_vtable["#return"] is not None:
return vtable, func_vtable["#return"]
return vtable, ValNul()
callee_desc = (self.callee.name
if isinstance(self.callee, ID) else "FVNCTIO")
return vtable, _call_func(func, params, vtable, callee_desc)
class BuiltIn(Node):
@@ -1546,11 +1551,30 @@ class BuiltIn(Node):
d[k.value()] = v
return vtable, ValDict(d)
case "ORDINA":
if not 1 <= len(params) <= 2:
raise CentvrionError("ORDINA takes 1 or 2 arguments")
if not isinstance(params[0], ValList):
raise CentvrionError("ORDINA requires an array")
items = list(params[0].value())
if not items:
return vtable, ValList([])
if len(params) == 2:
cmp = params[1]
if not isinstance(cmp, ValFunc):
raise CentvrionError("ORDINA comparator must be a function")
if len(cmp.params) != 2:
raise CentvrionError("ORDINA comparator must take 2 arguments")
def adapter(a, b):
r = _call_func(cmp, [a, b], vtable, "ORDINA comparator")
if not isinstance(r, ValBool):
raise CentvrionError("ORDINA comparator must return VERAX")
if r.value():
return -1
r2 = _call_func(cmp, [b, a], vtable, "ORDINA comparator")
if not isinstance(r2, ValBool):
raise CentvrionError("ORDINA comparator must return VERAX")
return 1 if r2.value() else 0
return vtable, ValList(sorted(items, key=functools.cmp_to_key(adapter)))
all_numeric = all(isinstance(i, (ValInt, ValFrac)) for i in items)
all_string = all(isinstance(i, ValStr) for i in items)
if not (all_numeric or all_string):
+8 -1
View File
@@ -311,7 +311,14 @@ def _emit_builtin(node, ctx):
lines.append(f"CentValue {tmp} = cent_dict_keys({param_vars[0]});")
case "ORDINA":
lines.append(f"CentValue {tmp} = cent_ordina({param_vars[0]});")
if len(param_vars) == 1:
lines.append(f"CentValue {tmp} = cent_ordina({param_vars[0]});")
elif len(param_vars) == 2:
lines.append(
f"CentValue {tmp} = cent_ordina_cmp({param_vars[0]}, {param_vars[1]}, _scope);"
)
else:
raise CentvrionError("ORDINA takes 1 or 2 arguments")
case "ADDE":
lines.append(f"CentValue {tmp} = cent_adde({param_vars[0]}, {param_vars[1]});")
+49
View File
@@ -848,6 +848,55 @@ CentValue cent_ordina(CentValue lst) {
return result;
}
/* User-comparator sort: single-threaded runtime, so the active comparator
and its calling scope are stashed in file-scope statics. Save/restore
them around the qsort call so nested ORDINA(..., cmp) calls inside a
comparator still work. */
static CentValue _cmp_active;
static CentScope _cmp_scope;
static int _ordina_user_comparator(const void *a, const void *b) {
const CentValue *va = (const CentValue *)a;
const CentValue *vb = (const CentValue *)b;
CentScope s1 = cent_scope_copy(&_cmp_scope);
cent_scope_set(&s1, _cmp_active.fnval.param_names[0], *va);
cent_scope_set(&s1, _cmp_active.fnval.param_names[1], *vb);
CentValue r1 = _cmp_active.fnval.fn(s1);
if (r1.type != CENT_BOOL)
cent_type_error("'ORDINA' comparator must return VERAX");
if (r1.bval) return -1;
CentScope s2 = cent_scope_copy(&_cmp_scope);
cent_scope_set(&s2, _cmp_active.fnval.param_names[0], *vb);
cent_scope_set(&s2, _cmp_active.fnval.param_names[1], *va);
CentValue r2 = _cmp_active.fnval.fn(s2);
if (r2.type != CENT_BOOL)
cent_type_error("'ORDINA' comparator must return VERAX");
return r2.bval ? 1 : 0;
}
CentValue cent_ordina_cmp(CentValue lst, CentValue cmp, CentScope scope) {
if (lst.type != CENT_LIST)
cent_type_error("'ORDINA' requires a list");
if (cmp.type != CENT_FUNC)
cent_type_error("'ORDINA' comparator must be a function");
if (cmp.fnval.param_count != 2)
cent_runtime_error("'ORDINA' comparator must take 2 arguments");
int len = lst.lval.len;
CentValue result = cent_list_new(len);
for (int i = 0; i < len; i++)
cent_list_push(&result, lst.lval.items[i]);
if (len > 1) {
CentValue saved_cmp = _cmp_active;
CentScope saved_scope = _cmp_scope;
_cmp_active = cmp;
_cmp_scope = scope;
qsort(result.lval.items, len, sizeof(CentValue), _ordina_user_comparator);
_cmp_active = saved_cmp;
_cmp_scope = saved_scope;
}
return result;
}
static long _index_arg(CentValue idx, const char *name) {
if (idx.type == CENT_INT)
return idx.ival;
@@ -239,6 +239,7 @@ 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_ordina_cmp(CentValue lst, CentValue cmp, CentScope scope); /* ORDINA w/ comparator */
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 */