Compare commits
6 Commits
4f67b1b2dc
...
6413d9fd1c
| Author | SHA1 | Date | |
|---|---|---|---|
| 6413d9fd1c | |||
| f62a7dda1c | |||
| 861448cb04 | |||
| 63c35605a2 | |||
| c28ffbbf45 | |||
| ad46f189c0 |
38
README.md
38
README.md
@@ -22,6 +22,33 @@ Variables are set with the `DESIGNA` and `VT` keywords. Type is inferred.
|
||||
|
||||
Variable can consist of lower-case letters, numbers, as well as `_`.
|
||||
|
||||
### Compound assignment
|
||||
|
||||
`AVGE` (+=) and `MINVE` (-=) are shorthand for incrementing or decrementing a variable:
|
||||
|
||||

|
||||
|
||||
```
|
||||
> VIII
|
||||
```
|
||||
|
||||
`x AVGE III` is equivalent to `DESIGNA x VT x + III`.
|
||||
|
||||
### Destructuring
|
||||
|
||||
Multiple variables can be assigned at once by unpacking an array or multi-return function:
|
||||
|
||||
```
|
||||
DEFINI pair (a, b) VT { REDI (a, b) }
|
||||
DESIGNA x, y VT INVOCA pair (III, VII)
|
||||
```
|
||||
|
||||
The number of targets must match the length of the array. This also works with array literals:
|
||||
|
||||
```
|
||||
DESIGNA a, b, c VT [I, II, III]
|
||||
```
|
||||
|
||||
## Data types
|
||||
### NVLLVS
|
||||
`NVLLVS` is a special kind of data type in `CENTVRION`, similar to the `null` value in many other languages. `NVLLVS` can be 0 if evaluated as an int or float, or an empty string if evaluated as a string. `NVLLVS` cannot be evaluated as a boolean.
|
||||
@@ -209,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 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
|
||||
|
||||
@@ -221,12 +253,16 @@ Vnlike many other programming languages with modules, the modules in `CENTVRION`
|
||||
### FORS
|
||||

|
||||
|
||||
The `FORS` module allows you to add randomness to your `CENTVRION` program. It adds 2 new built-in functions: `FORTIS_NVMERVS int int` and `FORTIS_ELECTIONIS ['a]`.
|
||||
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
|
||||

|
||||
|
||||
|
||||
@@ -431,6 +431,35 @@ class DesignaIndex(Node):
|
||||
return vtable, ValNul()
|
||||
|
||||
|
||||
class DesignaDestructure(Node):
|
||||
def __init__(self, variables: list, value: Node) -> None:
|
||||
self.ids = variables
|
||||
self.value = value
|
||||
|
||||
def __eq__(self, other):
|
||||
return type(self) == type(other) and self.ids == other.ids and self.value == other.value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
ids_string = ", ".join(repr(i) for i in self.ids)
|
||||
value_string = repr(self.value).replace('\n', '\n ')
|
||||
return f"DesignaDestructure(\n [{ids_string}],\n {value_string}\n)"
|
||||
|
||||
def print(self):
|
||||
ids_str = ", ".join(i.print() for i in self.ids)
|
||||
return f"DESIGNA {ids_str} VT {self.value.print()}"
|
||||
|
||||
def _eval(self, vtable):
|
||||
vtable, val = self.value.eval(vtable)
|
||||
if not isinstance(val, ValList):
|
||||
raise CentvrionError("Cannot destructure non-array value")
|
||||
if len(val.value()) != len(self.ids):
|
||||
raise CentvrionError(
|
||||
f"Destructuring mismatch: {len(self.ids)} targets, {len(val.value())} values")
|
||||
for id_node, item in zip(self.ids, val.value()):
|
||||
vtable[id_node.name] = item
|
||||
return vtable, ValNul()
|
||||
|
||||
|
||||
class Defini(Node):
|
||||
def __init__(self, name: ID, parameters: list[ID], statements: list[Node]) -> None:
|
||||
self.name = name
|
||||
@@ -920,6 +949,34 @@ class BuiltIn(Node):
|
||||
if len(lst) == 0:
|
||||
raise CentvrionError("FORTIS_ELECTIONIS: cannot select from an empty array")
|
||||
return vtable, lst[random.randint(0, len(lst) - 1)]
|
||||
case "SEMEN":
|
||||
if "FORS" not in vtable["#modules"]:
|
||||
raise CentvrionError("Cannot use 'SEMEN' without module 'FORS'")
|
||||
seed = params[0].value()
|
||||
if not isinstance(seed, int):
|
||||
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 "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()))
|
||||
|
||||
@@ -186,6 +186,29 @@ 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");')
|
||||
lines.append(f"CentValue {tmp} = cent_null();")
|
||||
else:
|
||||
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;")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from centvrion.ast_nodes import (
|
||||
Designa, DesignaIndex, SiStatement, DumStatement, PerStatement,
|
||||
Defini, Redi, Erumpe, Continva, ExpressionStatement, ID,
|
||||
Designa, DesignaIndex, DesignaDestructure, SiStatement, DumStatement,
|
||||
PerStatement, Defini, Redi, Erumpe, Continva, ExpressionStatement, ID,
|
||||
)
|
||||
from centvrion.compiler.emit_expr import emit_expr
|
||||
|
||||
@@ -29,6 +29,18 @@ def emit_stmt(node, ctx):
|
||||
]
|
||||
)
|
||||
|
||||
if isinstance(node, DesignaDestructure):
|
||||
n = len(node.ids)
|
||||
val_lines, val_var = emit_expr(node.value, ctx)
|
||||
lines = val_lines[:]
|
||||
lines.append(f'if ({val_var}.type != CENT_LIST) cent_type_error("Cannot destructure non-array value");')
|
||||
lines.append(f'if ({val_var}.lval.len != {n}) cent_runtime_error("Destructuring mismatch");')
|
||||
for i, id_node in enumerate(node.ids):
|
||||
tmp = ctx.fresh_tmp()
|
||||
lines.append(f"CentValue {tmp} = cent_list_index({val_var}, cent_int({i + 1}));")
|
||||
lines.append(f'cent_scope_set(&_scope, "{id_node.name}", {tmp});')
|
||||
return lines
|
||||
|
||||
if isinstance(node, SiStatement):
|
||||
cond_lines, cond_var = emit_expr(node.test, ctx)
|
||||
then_lines = _emit_body(node.statements, ctx)
|
||||
|
||||
@@ -533,6 +533,46 @@ 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");
|
||||
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");
|
||||
srand((unsigned)seed.ival);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Array helpers */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
@@ -177,7 +177,10 @@ 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 */
|
||||
CentValue cent_senatus(CentValue *args, int n); /* SENATVS */
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Array helpers */
|
||||
|
||||
@@ -5,6 +5,7 @@ valid_characters = '|'.join(list("abcdefghiklmnopqrstvxyz_"))
|
||||
keyword_tokens = [("KEYWORD_"+i, i) for i in [
|
||||
"AETERNVM",
|
||||
"ALVID",
|
||||
"AVGE",
|
||||
"AVT",
|
||||
"DEFINI",
|
||||
"DESIGNA",
|
||||
@@ -19,6 +20,7 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [
|
||||
"FALSITAS",
|
||||
"INVOCA",
|
||||
"IN",
|
||||
"MINVE",
|
||||
"MINVS",
|
||||
"NON",
|
||||
"NVLLVS",
|
||||
@@ -37,11 +39,14 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [
|
||||
builtin_tokens = [("BUILTIN", i) for i in [
|
||||
"AVDI_NVMERVS",
|
||||
"AVDI",
|
||||
"DECIMATIO",
|
||||
"DICE",
|
||||
"EVERRO",
|
||||
"FORTIS_NVMERVS",
|
||||
"FORTIS_ELECTIONIS",
|
||||
"LONGITVDO"
|
||||
"LONGITVDO",
|
||||
"SEMEN",
|
||||
"SENATVS"
|
||||
]]
|
||||
|
||||
data_tokens = [
|
||||
|
||||
@@ -74,6 +74,18 @@ class Parser():
|
||||
def statement_designa_index(tokens):
|
||||
return ast_nodes.DesignaIndex(tokens[1], tokens[3], tokens[6])
|
||||
|
||||
@self.pg.production('statement : KEYWORD_DESIGNA id SYMBOL_COMMA id_list_rest KEYWORD_VT expression')
|
||||
def statement_designa_destructure(tokens):
|
||||
return ast_nodes.DesignaDestructure([tokens[1]] + tokens[3], tokens[5])
|
||||
|
||||
@self.pg.production('statement : id KEYWORD_AVGE expression')
|
||||
def statement_avge(tokens):
|
||||
return ast_nodes.Designa(tokens[0], ast_nodes.BinOp(tokens[0], tokens[2], "SYMBOL_PLUS"))
|
||||
|
||||
@self.pg.production('statement : id KEYWORD_MINVE expression')
|
||||
def statement_minve(tokens):
|
||||
return ast_nodes.Designa(tokens[0], ast_nodes.BinOp(tokens[0], tokens[2], "SYMBOL_MINUS"))
|
||||
|
||||
@self.pg.production('statement : expression')
|
||||
def statement_expression(tokens):
|
||||
return ast_nodes.ExpressionStatement(tokens[0])
|
||||
@@ -250,6 +262,14 @@ class Parser():
|
||||
else:
|
||||
return [calls[0]] + calls[2]
|
||||
|
||||
@self.pg.production('id_list_rest : id')
|
||||
@self.pg.production('id_list_rest : id SYMBOL_COMMA id_list_rest')
|
||||
def id_list_rest(calls):
|
||||
if len(calls) == 1:
|
||||
return [calls[0]]
|
||||
else:
|
||||
return [calls[0]] + calls[2]
|
||||
|
||||
@self.pg.production("id : ID")
|
||||
def id_expression(tokens):
|
||||
return ast_nodes.ID(tokens[0].value)
|
||||
|
||||
Binary file not shown.
@@ -30,6 +30,9 @@
|
||||
\multicolumn{3}{|c|}{\textbf{Statements}} \\ \hline
|
||||
\languageline{statement}{\textit{expression}} \\
|
||||
\languageline{statement}{\texttt{DESIGNA} \textbf{id} \texttt{VT} \textit{expression}} \\
|
||||
\languageline{statement}{\texttt{DESIGNA} \textbf{id} \texttt{,} \textit{ids} \texttt{VT} \textit{expression}} \\
|
||||
\languageline{statement}{\textbf{id} \texttt{AVGE} \textit{expression}} \\
|
||||
\languageline{statement}{\textbf{id} \texttt{MINVE} \textit{expression}} \\
|
||||
\languageline{statement}{\texttt{DEFINI} \textbf{id} \texttt{(} \textit{optional-ids} \texttt{)} \texttt{VT} \textit{scope}} \\
|
||||
\languageline{statement}{\textit{if-statement}} \\
|
||||
\languageline{statement}{\texttt{DVM} \textit{expression} \texttt{FACE} \textit{scope}} \\
|
||||
|
||||
3
snippets/compound.cent
Normal file
3
snippets/compound.cent
Normal file
@@ -0,0 +1,3 @@
|
||||
DESIGNA x VT V
|
||||
x AVGE III
|
||||
DICE(x)
|
||||
BIN
snippets/compound.png
Normal file
BIN
snippets/compound.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -57,7 +57,7 @@ contexts:
|
||||
scope: constant.language.centvrion
|
||||
|
||||
builtins:
|
||||
- match: '\b(AVDI_NVMERVS|AVDI|DICE|FORTIS_NVMERVS|FORTIS_ELECTIONIS|LONGITVDO)\b'
|
||||
- match: '\b(AVDI_NVMERVS|AVDI|DECIMATIO|DICE|FORTIS_NVMERVS|FORTIS_ELECTIONIS|LONGITVDO|SEMEN)\b'
|
||||
scope: support.function.builtin.centvrion
|
||||
|
||||
modules:
|
||||
@@ -65,7 +65,7 @@ contexts:
|
||||
scope: support.class.module.centvrion
|
||||
|
||||
keywords:
|
||||
- match: '\b(AETERNVM|ALVID|AVT|DEFINI|DESIGNA|DONICVM|DVM|ERVMPE|EST|ET|FACE|INVOCA|IN|MINVS|NON|PER|PLVS|REDI|RELIQVVM|SI|TVNC|VSQVE|VT|CVM)\b'
|
||||
- match: '\b(AETERNVM|ALVID|AVGE|AVT|CONTINVA|DEFINI|DESIGNA|DONICVM|DVM|ERVMPE|EST|ET|FACE|INVOCA|IN|MINVE|MINVS|NON|PER|PLVS|REDI|RELIQVVM|SI|TVNC|VSQVE|VT|CVM)\b'
|
||||
scope: keyword.control.centvrion
|
||||
|
||||
operators:
|
||||
|
||||
121
tests.py
121
tests.py
@@ -11,8 +11,8 @@ from fractions import Fraction
|
||||
|
||||
from centvrion.ast_nodes import (
|
||||
ArrayIndex, Bool, BinOp, BuiltIn, DataArray, DataRangeArray, Defini,
|
||||
Continva, Designa, DesignaIndex, DumStatement, Erumpe, ExpressionStatement, ID,
|
||||
Invoca, ModuleCall, Nullus, Numeral, PerStatement,
|
||||
Continva, Designa, DesignaDestructure, DesignaIndex, DumStatement, Erumpe,
|
||||
ExpressionStatement, ID, Invoca, ModuleCall, Nullus, Numeral, PerStatement,
|
||||
Program, Redi, SiStatement, String, UnaryMinus, UnaryNot,
|
||||
Fractio, frac_to_fraction, fraction_to_frac,
|
||||
num_to_int, int_to_num, make_string,
|
||||
@@ -265,6 +265,31 @@ assignment_tests = [
|
||||
Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")),
|
||||
ExpressionStatement(ID("x"))]),
|
||||
ValInt(3)),
|
||||
# Compound assignment — AVGE (+=)
|
||||
("DESIGNA x VT V\nx AVGE III\nx",
|
||||
Program([], [Designa(ID("x"), Numeral("V")),
|
||||
Designa(ID("x"), BinOp(ID("x"), Numeral("III"), "SYMBOL_PLUS")),
|
||||
ExpressionStatement(ID("x"))]),
|
||||
ValInt(8)),
|
||||
# Compound assignment — MINVE (-=)
|
||||
("DESIGNA x VT X\nx MINVE III\nx",
|
||||
Program([], [Designa(ID("x"), Numeral("X")),
|
||||
Designa(ID("x"), BinOp(ID("x"), Numeral("III"), "SYMBOL_MINUS")),
|
||||
ExpressionStatement(ID("x"))]),
|
||||
ValInt(7)),
|
||||
# AVGE with complex expression
|
||||
("DESIGNA x VT I\nx AVGE II + III\nx",
|
||||
Program([], [Designa(ID("x"), Numeral("I")),
|
||||
Designa(ID("x"), BinOp(ID("x"), BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), "SYMBOL_PLUS")),
|
||||
ExpressionStatement(ID("x"))]),
|
||||
ValInt(6)),
|
||||
# AVGE inside a loop (DONICVM range is exclusive of upper bound: I VSQVE III = [1, 2])
|
||||
("DESIGNA s VT NVLLVS\nDONICVM i VT I VSQVE III FACE {\ns AVGE i\n}\ns",
|
||||
Program([], [Designa(ID("s"), Nullus()),
|
||||
PerStatement(DataRangeArray(Numeral("I"), Numeral("III")), ID("i"),
|
||||
[Designa(ID("s"), BinOp(ID("s"), ID("i"), "SYMBOL_PLUS"))]),
|
||||
ExpressionStatement(ID("s"))]),
|
||||
ValInt(3)),
|
||||
]
|
||||
|
||||
class TestAssignment(unittest.TestCase):
|
||||
@@ -273,6 +298,57 @@ class TestAssignment(unittest.TestCase):
|
||||
run_test(self, source, nodes, value)
|
||||
|
||||
|
||||
# --- Destructuring ---
|
||||
|
||||
destructuring_tests = [
|
||||
# basic: unpack multi-return function
|
||||
(
|
||||
"DEFINI pair (a, b) VT { REDI (a, b) }\nDESIGNA x, y VT INVOCA pair (III, VII)\nx + y",
|
||||
Program([], [
|
||||
Defini(ID("pair"), [ID("a"), ID("b")], [Redi([ID("a"), ID("b")])]),
|
||||
DesignaDestructure([ID("x"), ID("y")], Invoca(ID("pair"), [Numeral("III"), Numeral("VII")])),
|
||||
ExpressionStatement(BinOp(ID("x"), ID("y"), "SYMBOL_PLUS")),
|
||||
]),
|
||||
ValInt(10),
|
||||
),
|
||||
# unpack array literal
|
||||
(
|
||||
"DESIGNA a, b VT [I, II]\na + b",
|
||||
Program([], [
|
||||
DesignaDestructure([ID("a"), ID("b")], DataArray([Numeral("I"), Numeral("II")])),
|
||||
ExpressionStatement(BinOp(ID("a"), ID("b"), "SYMBOL_PLUS")),
|
||||
]),
|
||||
ValInt(3),
|
||||
),
|
||||
# three variables
|
||||
(
|
||||
"DESIGNA a, b, c VT [X, XX, XXX]\na + b + c",
|
||||
Program([], [
|
||||
DesignaDestructure([ID("a"), ID("b"), ID("c")], DataArray([Numeral("X"), Numeral("XX"), Numeral("XXX")])),
|
||||
ExpressionStatement(BinOp(BinOp(ID("a"), ID("b"), "SYMBOL_PLUS"), ID("c"), "SYMBOL_PLUS")),
|
||||
]),
|
||||
ValInt(60),
|
||||
),
|
||||
# destructure into individual use
|
||||
(
|
||||
"DEFINI pair (a, b) VT { REDI (a, b) }\nDESIGNA x, y VT INVOCA pair (V, II)\nDICE(x)\nDICE(y)",
|
||||
Program([], [
|
||||
Defini(ID("pair"), [ID("a"), ID("b")], [Redi([ID("a"), ID("b")])]),
|
||||
DesignaDestructure([ID("x"), ID("y")], Invoca(ID("pair"), [Numeral("V"), Numeral("II")])),
|
||||
ExpressionStatement(BuiltIn("DICE", [ID("x")])),
|
||||
ExpressionStatement(BuiltIn("DICE", [ID("y")])),
|
||||
]),
|
||||
ValStr("II"),
|
||||
"V\nII\n",
|
||||
),
|
||||
]
|
||||
|
||||
class TestDestructuring(unittest.TestCase):
|
||||
@parameterized.expand(destructuring_tests)
|
||||
def test_destructuring(self, source, nodes, value, output=""):
|
||||
run_test(self, source, nodes, value, output)
|
||||
|
||||
|
||||
# --- Control flow ---
|
||||
|
||||
control_tests = [
|
||||
@@ -460,6 +536,37 @@ builtin_tests = [
|
||||
('LONGITVDO("salve")', Program([], [ExpressionStatement(BuiltIn("LONGITVDO", [String("salve")]))]), ValInt(5)),
|
||||
('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)])),
|
||||
# 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 <20><> 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):
|
||||
@@ -494,11 +601,18 @@ error_tests = [
|
||||
("\"hello\" MINVS \"world\"", CentvrionError), # comparison with strings
|
||||
("I[I]", CentvrionError), # indexing a non-array
|
||||
("DESIGNA x VT I\nDESIGNA x[I] VT II", CentvrionError), # index-assign to non-array
|
||||
("SEMEN(I)", CentvrionError), # requires FORS module
|
||||
('CVM FORS\nSEMEN("abc")', CentvrionError), # SEMEN requires integer seed
|
||||
("FORTIS_ELECTIONIS([])", CentvrionError), # FORS required for FORTIS_ELECTIONIS
|
||||
("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
|
||||
("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
|
||||
@@ -511,6 +625,9 @@ error_tests = [
|
||||
("NON I", CentvrionError), # NON on integer
|
||||
("DESIGNA z VT I - I\nNON z", CentvrionError), # NON on zero integer
|
||||
('NON "hello"', CentvrionError), # NON on string
|
||||
("DESIGNA a, b VT III", CentvrionError), # destructure non-array
|
||||
("DESIGNA a, b VT [I]", CentvrionError), # destructure length mismatch: too many targets
|
||||
("DESIGNA a, b VT [I, II, III]", CentvrionError), # destructure length mismatch: too few targets
|
||||
]
|
||||
|
||||
class TestErrors(unittest.TestCase):
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user