From 861448cb048ddbaf584828deb79542b4acfd5a14 Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Tue, 21 Apr 2026 15:18:39 +0200 Subject: [PATCH] :goat: DECIMATIO --- README.md | 4 +++- centvrion/ast_nodes.py | 10 ++++++++++ centvrion/compiler/emit_expr.py | 7 +++++++ centvrion/compiler/runtime/cent_runtime.c | 19 +++++++++++++++++++ centvrion/compiler/runtime/cent_runtime.h | 1 + centvrion/lexer.py | 1 + snippets/syntaxes/centvrion.sublime-syntax | 2 +- tests.py | 10 ++++++++++ .../syntaxes/cent.tmLanguage.json | 2 +- 9 files changed, 53 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7aa77a..a378bcd 100644 --- a/README.md +++ b/README.md @@ -248,12 +248,14 @@ Vnlike many other programming languages with modules, the modules in `CENTVRION` ### FORS ![CVM FORS](snippets/fors.png) -The `FORS` module allows you to add randomness to your `CENTVRION` program. It adds 3 new built-in functions: `FORTIS_NVMERVS int int`, `FORTIS_ELECTIONIS ['a]`, and `SEMEN int`. +The `FORS` module allows you to add randomness to your `CENTVRION` program. It adds 4 new built-in functions: `FORTIS_NVMERVS int int`, `FORTIS_ELECTIONIS ['a]`, `DECIMATIO ['a]`, and `SEMEN int`. `FORTIS_NVMERVS int int` picks a random int in the (inclusive) range of the two given ints. `FORTIS_ELECTIONIS ['a]` picks a random element from the given array. `FORTIS_ELECTIONIS array` is identical to ```array[FORTIS_NVMERVS NVLLVS ((LONGITVDO array)-I)]```. +`DECIMATIO ['a]` returns a copy of the given array with a random tenth of its elements removed. Arrays with fewer than 10 elements are returned unchanged. + `SEMEN int` seeds the random number generator for reproducibility. ### FRACTIO diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index 4adae8b..3d2a430 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -957,6 +957,16 @@ class BuiltIn(Node): raise CentvrionError("SEMEN requires an integer seed") random.seed(seed) return vtable, ValNul() + case "DECIMATIO": + if "FORS" not in vtable["#modules"]: + raise CentvrionError("Cannot use 'DECIMATIO' without module 'FORS'") + if not isinstance(params[0], ValList): + raise CentvrionError("DECIMATIO requires an array") + arr = list(params[0].value()) + to_remove = len(arr) // 10 + for _ in range(to_remove): + arr.pop(random.randint(0, len(arr) - 1)) + return vtable, ValList(arr) 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 84dc9a6..935ba4e 100644 --- a/centvrion/compiler/emit_expr.py +++ b/centvrion/compiler/emit_expr.py @@ -186,6 +186,13 @@ def _emit_builtin(node, ctx): else: lines.append(f"CentValue {tmp} = cent_fortis_electionis({param_vars[0]});") + case "DECIMATIO": + if not ctx.has_module("FORS"): + lines.append('cent_runtime_error("FORS module required for DECIMATIO");') + lines.append(f"CentValue {tmp} = cent_null();") + else: + lines.append(f"CentValue {tmp} = cent_decimatio({param_vars[0]});") + case "SEMEN": if not ctx.has_module("FORS"): lines.append('cent_runtime_error("FORS module required for SEMEN");') diff --git a/centvrion/compiler/runtime/cent_runtime.c b/centvrion/compiler/runtime/cent_runtime.c index 2a28084..ed06505 100644 --- a/centvrion/compiler/runtime/cent_runtime.c +++ b/centvrion/compiler/runtime/cent_runtime.c @@ -533,6 +533,25 @@ CentValue cent_fortis_electionis(CentValue lst) { return lst.lval.items[rand() % lst.lval.len]; } +CentValue cent_decimatio(CentValue lst) { + if (lst.type != CENT_LIST) + cent_type_error("'DECIMATIO' requires a list"); + int len = lst.lval.len; + /* Copy the list so we can remove in-place */ + CentValue result = cent_list_new(len); + for (int i = 0; i < len; i++) + cent_list_push(&result, lst.lval.items[i]); + int to_remove = result.lval.len / 10; + for (int i = 0; i < to_remove; i++) { + int idx = rand() % result.lval.len; + /* Shift remaining elements left */ + for (int j = idx; j < result.lval.len - 1; j++) + result.lval.items[j] = result.lval.items[j + 1]; + result.lval.len--; + } + return result; +} + void cent_semen(CentValue seed) { if (seed.type != CENT_INT) cent_type_error("'SEMEN' requires an integer seed"); diff --git a/centvrion/compiler/runtime/cent_runtime.h b/centvrion/compiler/runtime/cent_runtime.h index b37dbb4..0813567 100644 --- a/centvrion/compiler/runtime/cent_runtime.h +++ b/centvrion/compiler/runtime/cent_runtime.h @@ -177,6 +177,7 @@ CentValue cent_avdi_numerus(void); /* AVDI_NVMERVS */ CentValue cent_longitudo(CentValue v); /* LONGITVDO */ CentValue cent_fortis_numerus(CentValue lo, CentValue hi); /* FORTIS_NVMERVS */ 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 */ diff --git a/centvrion/lexer.py b/centvrion/lexer.py index 21e7776..144f8cb 100644 --- a/centvrion/lexer.py +++ b/centvrion/lexer.py @@ -39,6 +39,7 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [ builtin_tokens = [("BUILTIN", i) for i in [ "AVDI_NVMERVS", "AVDI", + "DECIMATIO", "DICE", "EVERRO", "FORTIS_NVMERVS", diff --git a/snippets/syntaxes/centvrion.sublime-syntax b/snippets/syntaxes/centvrion.sublime-syntax index be8072f..64934c4 100644 --- a/snippets/syntaxes/centvrion.sublime-syntax +++ b/snippets/syntaxes/centvrion.sublime-syntax @@ -57,7 +57,7 @@ contexts: scope: constant.language.centvrion builtins: - - match: '\b(AVDI_NVMERVS|AVDI|DICE|FORTIS_NVMERVS|FORTIS_ELECTIONIS|LONGITVDO|SEMEN)\b' + - match: '\b(AVDI_NVMERVS|AVDI|DECIMATIO|DICE|FORTIS_NVMERVS|FORTIS_ELECTIONIS|LONGITVDO|SEMEN)\b' scope: support.function.builtin.centvrion modules: diff --git a/tests.py b/tests.py index e16d192..a7f1857 100644 --- a/tests.py +++ b/tests.py @@ -537,6 +537,14 @@ builtin_tests = [ ('LONGITVDO("")', Program([], [ExpressionStatement(BuiltIn("LONGITVDO", [String("")]))]), ValInt(0)), ("CVM FORS\nFORTIS_ELECTIONIS([I, II, III])", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("FORTIS_ELECTIONIS", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")])]))]), ValInt(1)), ("CVM FORS\nSEMEN(XLII)\nFORTIS_NVMERVS(I, C)", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("SEMEN", [Numeral("XLII")])), ExpressionStatement(BuiltIn("FORTIS_NVMERVS", [Numeral("I"), Numeral("C")]))]), ValInt(82)), + # DECIMATIO: seed 42, 10 elements → removes 1 (element II) + ("CVM FORS\nSEMEN(XLII)\nDECIMATIO([I, II, III, IV, V, VI, VII, VIII, IX, X])", 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")])]))]), ValList([ValInt(1), ValInt(3), ValInt(4), ValInt(5), ValInt(6), ValInt(7), ValInt(8), ValInt(9), ValInt(10)])), + # DECIMATIO: seed 1, 3 elements → 3//10=0, nothing removed + ("CVM FORS\nSEMEN(I)\nDECIMATIO([I, II, III])", Program([ModuleCall("FORS")], [ExpressionStatement(BuiltIn("SEMEN", [Numeral("I")])), ExpressionStatement(BuiltIn("DECIMATIO", [DataArray([Numeral("I"), Numeral("II"), Numeral("III")])]))]), ValList([ValInt(1), ValInt(2), ValInt(3)])), + # DECIMATIO: empty array → empty array + ("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)])), ] class TestBuiltins(unittest.TestCase): @@ -577,6 +585,8 @@ error_tests = [ ("CVM FORS\nFORTIS_ELECTIONIS([])", CentvrionError), # FORTIS_ELECTIONIS on empty array ("CVM FORS\nFORTIS_NVMERVS(X, I)", CentvrionError), # FORTIS_NVMERVS a > b ("PER i IN I FACE { DICE(i) }", CentvrionError), # PER over non-array + ("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 ("DESIGNA x VT I\nINVOCA x ()", CentvrionError), # invoking a non-function ("SI I TVNC { DESIGNA r VT I }", CentvrionError), # non-bool SI condition: int diff --git a/vscode-extension/syntaxes/cent.tmLanguage.json b/vscode-extension/syntaxes/cent.tmLanguage.json index a4724e2..7f279ae 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(AVDI_NVMERVS|AVDI|DICE|EVERRO|FORTIS_NVMERVS|FORTIS_ELECTIONIS|LONGITVDO)\\b" + "match": "\\b(AVDI_NVMERVS|AVDI|DECIMATIO|DICE|EVERRO|FORTIS_NVMERVS|FORTIS_ELECTIONIS|LONGITVDO|SEMEN)\\b" } ] },