From f62a7dda1c753eb158c9a2e7bb0d357bb2e241be Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Tue, 21 Apr 2026 15:49:53 +0200 Subject: [PATCH] :goat: SENATVS --- README.md | 5 +++++ centvrion/ast_nodes.py | 10 +++++++++ centvrion/compiler/emit_expr.py | 8 ++++++++ centvrion/compiler/runtime/cent_runtime.c | 15 ++++++++++++++ centvrion/compiler/runtime/cent_runtime.h | 1 + centvrion/lexer.py | 3 ++- tests.py | 25 +++++++++++++++++++++++ 7 files changed, 66 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a378bcd..66e9583 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,11 @@ Breaks out of the current loop (`DVM` or `PER`). Has no meaningful return value. Returns the length of `array` (element count) or `string` (character count) as an integer. +### SENATVS +`SENATVS(bool, ...)` or `SENATVS([bool])` + +Returns VERITAS if a strict majority of the arguments are VERITAS, FALSITAS otherwise. Also accepts a single array of booleans. All values must be booleans. Ties (even split) return FALSITAS. + ## Modules Modules are additions to the base `CENTVRION` syntax. They add or change certain features. Modules are included in your code by having diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index 3d2a430..02d71a8 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -967,6 +967,16 @@ class BuiltIn(Node): for _ in range(to_remove): arr.pop(random.randint(0, len(arr) - 1)) return vtable, ValList(arr) + case "SENATVS": + if len(params) == 1 and isinstance(params[0], ValList): + items = params[0].value() + else: + items = params + for p in items: + if not isinstance(p, ValBool): + raise CentvrionError("SENATVS requires boolean arguments") + true_count = sum(1 for p in items if p.value()) + return vtable, ValBool(true_count > len(items) / 2) case "LONGITVDO": if isinstance(params[0], (ValList, ValStr)): return vtable, ValInt(len(params[0].value())) diff --git a/centvrion/compiler/emit_expr.py b/centvrion/compiler/emit_expr.py index 935ba4e..4c6c03a 100644 --- a/centvrion/compiler/emit_expr.py +++ b/centvrion/compiler/emit_expr.py @@ -201,6 +201,14 @@ def _emit_builtin(node, ctx): lines.append(f"cent_semen({param_vars[0]});") lines.append(f"CentValue {tmp} = cent_null();") + case "SENATVS": + if param_vars: + arr_tmp = ctx.fresh_tmp() + "_arr" + lines.append(f"CentValue {arr_tmp}[] = {{{', '.join(param_vars)}}};") + lines.append(f"CentValue {tmp} = cent_senatus({arr_tmp}, {len(param_vars)});") + else: + lines.append(f"CentValue {tmp} = cent_senatus(NULL, 0);") + case "ERVMPE": # break as expression (side-effecting; result is unused) lines.append("break;") diff --git a/centvrion/compiler/runtime/cent_runtime.c b/centvrion/compiler/runtime/cent_runtime.c index ed06505..927e398 100644 --- a/centvrion/compiler/runtime/cent_runtime.c +++ b/centvrion/compiler/runtime/cent_runtime.c @@ -533,6 +533,21 @@ CentValue cent_fortis_electionis(CentValue lst) { return lst.lval.items[rand() % lst.lval.len]; } +CentValue cent_senatus(CentValue *args, int n) { + /* Single array argument: unpack it */ + if (n == 1 && args[0].type == CENT_LIST) { + n = args[0].lval.len; + args = args[0].lval.items; + } + int true_count = 0; + for (int i = 0; i < n; i++) { + if (args[i].type != CENT_BOOL) + cent_type_error("'SENATVS' requires boolean arguments"); + if (args[i].bval) true_count++; + } + return cent_bool(true_count * 2 > n); +} + CentValue cent_decimatio(CentValue lst) { if (lst.type != CENT_LIST) cent_type_error("'DECIMATIO' requires a list"); diff --git a/centvrion/compiler/runtime/cent_runtime.h b/centvrion/compiler/runtime/cent_runtime.h index 0813567..6ce3ff6 100644 --- a/centvrion/compiler/runtime/cent_runtime.h +++ b/centvrion/compiler/runtime/cent_runtime.h @@ -180,6 +180,7 @@ CentValue cent_fortis_electionis(CentValue lst); /* FORTIS_ELECTIONIS * CentValue cent_decimatio(CentValue lst); /* DECIMATIO */ void cent_semen(CentValue seed); /* SEMEN */ void cent_everro(void); /* EVERRO */ +CentValue cent_senatus(CentValue *args, int n); /* SENATVS */ /* ------------------------------------------------------------------ */ /* Array helpers */ diff --git a/centvrion/lexer.py b/centvrion/lexer.py index 144f8cb..9b7eb72 100644 --- a/centvrion/lexer.py +++ b/centvrion/lexer.py @@ -45,7 +45,8 @@ builtin_tokens = [("BUILTIN", i) for i in [ "FORTIS_NVMERVS", "FORTIS_ELECTIONIS", "LONGITVDO", - "SEMEN" + "SEMEN", + "SENATVS" ]] data_tokens = [ diff --git a/tests.py b/tests.py index a7f1857..1068868 100644 --- a/tests.py +++ b/tests.py @@ -545,6 +545,28 @@ builtin_tests = [ ("CVM FORS\nDECIMATIO([])", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("DECIMATIO", [DataArray([])]))]), ValList([])), # DECIMATIO: seed 42, 20 elements → removes 2 (elements I and IV) ("CVM FORS\nSEMEN(XLII)\nDECIMATIO([I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX])", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("SEMEN", [Numeral("XLII")])), ExpressionStatement(BuiltIn("DECIMATIO", [DataArray([Numeral("I"), Numeral("II"), Numeral("III"), Numeral("IV"), Numeral("V"), Numeral("VI"), Numeral("VII"), Numeral("VIII"), Numeral("IX"), Numeral("X"), Numeral("XI"), Numeral("XII"), Numeral("XIII"), Numeral("XIV"), Numeral("XV"), Numeral("XVI"), Numeral("XVII"), Numeral("XVIII"), Numeral("XIX"), Numeral("XX")])]))]), ValList([ValInt(2), ValInt(3), ValInt(5), ValInt(6), ValInt(7), ValInt(8), ValInt(9), ValInt(10), ValInt(11), ValInt(12), ValInt(13), ValInt(14), ValInt(15), ValInt(16), ValInt(17), ValInt(18), ValInt(19), ValInt(20)])), + # SENATVS: majority true → VERITAS + ("SENATVS(VERITAS, VERITAS, FALSITAS)", Program([], [ExpressionStatement(BuiltIn("SENATVS", [Bool(True), Bool(True), Bool(False)]))]), ValBool(True)), + # SENATVS: majority false → FALSITAS + ("SENATVS(FALSITAS, FALSITAS, VERITAS)", Program([], [ExpressionStatement(BuiltIn("SENATVS", [Bool(False), Bool(False), Bool(True)]))]), ValBool(False)), + # SENATVS: tie → FALSITAS + ("SENATVS(VERITAS, FALSITAS)", Program([], [ExpressionStatement(BuiltIn("SENATVS", [Bool(True), Bool(False)]))]), ValBool(False)), + # SENATVS: 4-arg tie → FALSITAS + ("SENATVS(VERITAS, VERITAS, FALSITAS, FALSITAS)", Program([], [ExpressionStatement(BuiltIn("SENATVS", [Bool(True), Bool(True), Bool(False), Bool(False)]))]), ValBool(False)), + # SENATVS: single true → VERITAS + ("SENATVS(VERITAS)", Program([], [ExpressionStatement(BuiltIn("SENATVS", [Bool(True)]))]), ValBool(True)), + # SENATVS: single false → FALSITAS + ("SENATVS(FALSITAS)", Program([], [ExpressionStatement(BuiltIn("SENATVS", [Bool(False)]))]), ValBool(False)), + # SENATVS: empty → FALSITAS (vacuous) + ("SENATVS()", Program([], [ExpressionStatement(BuiltIn("SENATVS", []))]), ValBool(False)), + # SENATVS: all true �� VERITAS + ("SENATVS(VERITAS, VERITAS, VERITAS, VERITAS, VERITAS)", Program([], [ExpressionStatement(BuiltIn("SENATVS", [Bool(True), Bool(True), Bool(True), Bool(True), Bool(True)]))]), ValBool(True)), + # SENATVS: array input, majority true → VERITAS + ("SENATVS([VERITAS, VERITAS, FALSITAS])", Program([], [ExpressionStatement(BuiltIn("SENATVS", [DataArray([Bool(True), Bool(True), Bool(False)])]))]), ValBool(True)), + # SENATVS: array input, majority false → FALSITAS + ("SENATVS([FALSITAS, FALSITAS, VERITAS])", Program([], [ExpressionStatement(BuiltIn("SENATVS", [DataArray([Bool(False), Bool(False), Bool(True)])]))]), ValBool(False)), + # SENATVS: array input, empty → FALSITAS + ("SENATVS([])", Program([], [ExpressionStatement(BuiltIn("SENATVS", [DataArray([])]))]), ValBool(False)), ] class TestBuiltins(unittest.TestCase): @@ -588,6 +610,9 @@ error_tests = [ ("DECIMATIO([I, II, III])", CentvrionError), # FORS required for DECIMATIO ("CVM FORS\nDECIMATIO(I)", CentvrionError), # DECIMATIO requires an array ("LONGITVDO(I)", CentvrionError), # LONGITVDO on non-array + ("SENATVS(I)", CentvrionError), # SENATVS requires booleans + ("SENATVS(VERITAS, I)", CentvrionError), # SENATVS mixed types + ("SENATVS([I, II, III])", CentvrionError), # SENATVS array of non-bools ("DESIGNA x VT I\nINVOCA x ()", CentvrionError), # invoking a non-function ("SI I TVNC { DESIGNA r VT I }", CentvrionError), # non-bool SI condition: int ("IIIS", CentvrionError), # fraction without FRACTIO module