🐐 LTE/GTE

This commit is contained in:
2026-04-24 16:02:03 +02:00
parent 92301f3ff6
commit bdf72b2bcc
12 changed files with 88 additions and 5 deletions

View File

@@ -183,7 +183,7 @@ If-then statements are denoted with the keywords `SI` (if) and `TVNC` (then). Th
Will return `I` (1), as the conditional evaluates `x` to be true.
### Boolean expressions
In conditionals, `EST` functions as an equality evaluation, `DISPAR` as not-equal, and `MINVS` (<) and `PLVS` (>) function as inequality evaluation.
In conditionals, `EST` functions as an equality evaluation, `DISPAR` as not-equal, and `MINVS` (<), `PLVS` (>), `HAVD_PLVS` (≤), and `HAVD_MINVS` () function as inequality evaluation.
### ALIVD

View File

@@ -64,6 +64,8 @@ OP_STR = {
"KEYWORD_EST": "EST", "KEYWORD_DISPAR": "DISPAR",
"KEYWORD_MINVS": "MINVS",
"KEYWORD_PLVS": "PLVS", "KEYWORD_ET": "ET", "KEYWORD_AVT": "AVT",
"KEYWORD_HAVD_PLVS": "HAVD_PLVS",
"KEYWORD_HAVD_MINVS": "HAVD_MINVS",
}
def single_num_to_int(i, m):
@@ -937,6 +939,14 @@ class BinOp(Node):
if isinstance(lv, (str, list)) or isinstance(rv, (str, list)):
raise CentvrionError("Cannot compare strings or arrays with PLVS")
return vtable, ValBool((lv or 0) > (rv or 0))
case "KEYWORD_HAVD_PLVS":
if isinstance(lv, (str, list)) or isinstance(rv, (str, list)):
raise CentvrionError("Cannot compare strings or arrays with HAVD_PLVS")
return vtable, ValBool((lv or 0) <= (rv or 0))
case "KEYWORD_HAVD_MINVS":
if isinstance(lv, (str, list)) or isinstance(rv, (str, list)):
raise CentvrionError("Cannot compare strings or arrays with HAVD_MINVS")
return vtable, ValBool((lv or 0) >= (rv or 0))
case "KEYWORD_EST":
if ((isinstance(left, ValInt) and lv == 0 and isinstance(right, ValNul)) or
(isinstance(left, ValNul) and isinstance(right, ValInt) and rv == 0)):

View File

@@ -19,6 +19,8 @@ _BINOP_FN = {
"KEYWORD_DISPAR": "cent_neq",
"KEYWORD_MINVS": "cent_lt",
"KEYWORD_PLVS": "cent_gt",
"KEYWORD_HAVD_PLVS": "cent_lte",
"KEYWORD_HAVD_MINVS": "cent_gte",
"KEYWORD_ET": "cent_and",
"KEYWORD_AVT": "cent_or",
}

View File

@@ -564,6 +564,28 @@ CentValue cent_gt(CentValue a, CentValue b) {
return cent_null();
}
CentValue cent_lte(CentValue a, CentValue b) {
if ((a.type == CENT_INT || a.type == CENT_FRAC || a.type == CENT_NULL) &&
(b.type == CENT_INT || b.type == CENT_FRAC || b.type == CENT_NULL)) {
long an, ad, bn, bd;
to_frac(a, &an, &ad); to_frac(b, &bn, &bd);
return cent_bool(an * bd <= bn * ad);
}
cent_type_error("'HAVD_PLVS' requires two integers");
return cent_null();
}
CentValue cent_gte(CentValue a, CentValue b) {
if ((a.type == CENT_INT || a.type == CENT_FRAC || a.type == CENT_NULL) &&
(b.type == CENT_INT || b.type == CENT_FRAC || b.type == CENT_NULL)) {
long an, ad, bn, bd;
to_frac(a, &an, &ad); to_frac(b, &bn, &bd);
return cent_bool(an * bd >= bn * ad);
}
cent_type_error("'HAVD_MINVS' requires two integers");
return cent_null();
}
CentValue cent_and(CentValue a, CentValue b) {
if (a.type != CENT_BOOL || b.type != CENT_BOOL)
cent_type_error("'ET' requires two booleans");

View File

@@ -210,6 +210,8 @@ CentValue cent_eq (CentValue a, CentValue b); /* EST → BOOL */
CentValue cent_neq(CentValue a, CentValue b); /* DISPAR → BOOL */
CentValue cent_lt (CentValue a, CentValue b); /* MINVS → BOOL */
CentValue cent_gt (CentValue a, CentValue b); /* PLVS → BOOL */
CentValue cent_lte(CentValue a, CentValue b); /* HAVD_PLVS → BOOL */
CentValue cent_gte(CentValue a, CentValue b); /* HAVD_MINVS → BOOL */
CentValue cent_and(CentValue a, CentValue b); /* ET → BOOL */
CentValue cent_or (CentValue a, CentValue b); /* AVT → BOOL */

View File

@@ -20,6 +20,8 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [
"FAC",
"FALSITAS",
"FVNCTIO",
"HAVD_MINVS",
"HAVD_PLVS",
"INVOCA",
"IN",
"MINVE",

View File

@@ -115,7 +115,8 @@ class Parser():
precedence=[
('left', ["KEYWORD_AVT"]),
('left', ["KEYWORD_ET"]),
('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST", "KEYWORD_DISPAR"]),
('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST", "KEYWORD_DISPAR",
"KEYWORD_HAVD_PLVS", "KEYWORD_HAVD_MINVS"]),
('left', ["SYMBOL_AMPERSAND", "SYMBOL_AT", "SYMBOL_PLUS", "SYMBOL_MINUS"]),
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE", "KEYWORD_RELIQVVM"]),
('right', ["UMINUS", "UNOT"]),
@@ -334,6 +335,8 @@ class Parser():
@self.pg.production('expression : expression KEYWORD_DISPAR expression')
@self.pg.production('expression : expression KEYWORD_MINVS expression')
@self.pg.production('expression : expression KEYWORD_PLVS expression')
@self.pg.production('expression : expression KEYWORD_HAVD_PLVS expression')
@self.pg.production('expression : expression KEYWORD_HAVD_MINVS expression')
@self.pg.production('expression : expression KEYWORD_ET expression')
@self.pg.production('expression : expression KEYWORD_AVT expression')
def binop(tokens):

Binary file not shown.

View File

@@ -106,7 +106,7 @@
\item \textbf{interpolated-string}: \\ A double-quoted string containing \texttt{\{}\textit{expression}\texttt{\}} segments. Each expression is evaluated and coerced to a string. Use \texttt{\{\{} and \texttt{\}\}} for literal braces.
\item \textbf{numeral}: \\ Roman numerals consisting of the uppercase characters I, V, X, L, C, D, and M. Can also include underscore if the module MAGNVM.
\item \textbf{bool}: \\ VERITAS or FALSITAS.
\item \textbf{binop}: \\ Binary operators: \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}, \texttt{RELIQVVM} (modulo), \texttt{EST} (equality), \texttt{DISPAR} (not-equal), \texttt{MINVS} (<), \texttt{PLVS} (>), \texttt{ET} (and), \texttt{AVT} (or), \texttt{\&} (string concatenation), \texttt{@} (array concatenation).
\item \textbf{binop}: \\ Binary operators: \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}, \texttt{RELIQVVM} (modulo), \texttt{EST} (equality), \texttt{DISPAR} (not-equal), \texttt{MINVS} (<), \texttt{PLVS} (>), \texttt{HAVD\_PLVS} ($\leq$), \texttt{HAVD\_MINVS} ($\geq$), \texttt{ET} (and), \texttt{AVT} (or), \texttt{\&} (string concatenation), \texttt{@} (array concatenation).
\item \textbf{unop}: \\ Unary operators: \texttt{-} (negation), \texttt{NON} (boolean not).
\end{itemize}

View File

@@ -78,7 +78,7 @@ contexts:
scope: support.class.module.centvrion
keywords:
- match: '\b(AETERNVM|ALIVD|AVGE|AVT|CAPE|CONTINVA|DEFINI|DESIGNA|DISPAR|DONICVM|DVM|ERVMPE|EST|ET|FAC|FVNCTIO|INVOCA|IN|MINVE|MINVS|NON|PER|PLVS|REDI|RELIQVVM|SI|TABVLA|TEMPTA|TVNC|VSQVE|VT|CVM)\b'
- match: '\b(HAVD_PLVS|HAVD_MINVS|AETERNVM|ALIVD|AVGE|AVT|CAPE|CONTINVA|DEFINI|DESIGNA|DISPAR|DONICVM|DVM|ERVMPE|EST|ET|FAC|FVNCTIO|INVOCA|IN|MINVE|MINVS|NON|PER|PLVS|REDI|RELIQVVM|SI|TABVLA|TEMPTA|TVNC|VSQVE|VT|CVM)\b'
scope: keyword.control.centvrion
operators:

View File

@@ -729,6 +729,8 @@ error_tests = [
("I - \"hello\"", CentvrionError), # subtraction with string
("I * \"hello\"", CentvrionError), # multiplication with string
("\"hello\" MINVS \"world\"", CentvrionError), # comparison with strings
('"a" HAVD_PLVS "b"', CentvrionError), # HAVD_PLVS on strings
('[I] HAVD_MINVS [II]', CentvrionError), # HAVD_MINVS on arrays
("I[I]", CentvrionError), # indexing a non-array
('"SALVTE"[VII]', CentvrionError), # string index out of range
('"SALVTE"[NVLLVS]', CentvrionError), # string index with non-integer
@@ -1302,6 +1304,27 @@ comparison_tests = [
("[] EST []", Program([], [ExpressionStatement(BinOp(DataArray([]), DataArray([]), "KEYWORD_EST"))]), ValBool(True)),
("[I, II] DISPAR [I, III]", Program([], [ExpressionStatement(BinOp(DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("I"), Numeral("III")]), "KEYWORD_DISPAR"))]), ValBool(True)),
("[I, II] DISPAR [I, II]", Program([], [ExpressionStatement(BinOp(DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("I"), Numeral("II")]), "KEYWORD_DISPAR"))]), ValBool(False)),
# HAVD_PLVS (<=) and HAVD_MINVS (>=)
("I HAVD_PLVS II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_HAVD_PLVS"))]), ValBool(True)),
("II HAVD_PLVS I", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("I"), "KEYWORD_HAVD_PLVS"))]), ValBool(False)),
# equality boundary — the only case that distinguishes <= from <
("II HAVD_PLVS II", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("II"), "KEYWORD_HAVD_PLVS"))]), ValBool(True)),
("II HAVD_MINVS I", Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("I"), "KEYWORD_HAVD_MINVS"))]), ValBool(True)),
("I HAVD_MINVS II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_HAVD_MINVS"))]), ValBool(False)),
# equality boundary — the only case that distinguishes >= from >
("II HAVD_MINVS II",Program([], [ExpressionStatement(BinOp(Numeral("II"), Numeral("II"), "KEYWORD_HAVD_MINVS"))]), ValBool(True)),
# NVLLVS coerces to 0
("V HAVD_MINVS NVLLVS", Program([], [ExpressionStatement(BinOp(Numeral("V"), Nullus(), "KEYWORD_HAVD_MINVS"))]), ValBool(True)),
("NVLLVS HAVD_PLVS V", Program([], [ExpressionStatement(BinOp(Nullus(), Numeral("V"), "KEYWORD_HAVD_PLVS"))]), ValBool(True)),
("NVLLVS HAVD_PLVS NVLLVS", Program([], [ExpressionStatement(BinOp(Nullus(), Nullus(), "KEYWORD_HAVD_PLVS"))]), ValBool(True)),
# precedence: * binds tighter, so II*III HAVD_PLVS VI parses as (II*III) HAVD_PLVS VI = 6 <= 6 = True
("II * III HAVD_PLVS VI",
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_TIMES"), Numeral("VI"), "KEYWORD_HAVD_PLVS"))]),
ValBool(True)),
# control flow: SI ... HAVD_MINVS
("SI II HAVD_MINVS II TVNC { DESIGNA r VT I } ALIVD { DESIGNA r VT II }\nr",
Program([], [SiStatement(BinOp(Numeral("II"), Numeral("II"), "KEYWORD_HAVD_MINVS"), [Designa(ID("r"), Numeral("I"))], [Designa(ID("r"), Numeral("II"))]), ExpressionStatement(ID("r"))]),
ValInt(1)),
]
class TestComparisons(unittest.TestCase):
@@ -2436,6 +2459,25 @@ fractio_comparison_tests = [
]),
ValBool(False)
),
# HAVD_PLVS / HAVD_MINVS on fractions — equality boundary distinguishes from MINVS / PLVS
("CVM FRACTIO\nIIIS HAVD_PLVS III",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("III"), "KEYWORD_HAVD_PLVS"))
]),
ValBool(False) # 3.5 <= 3 is false
),
("CVM FRACTIO\nIIIS HAVD_MINVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_HAVD_MINVS"))
]),
ValBool(True) # 3.5 >= 3.5 is true (equality boundary)
),
("CVM FRACTIO\nIIIS HAVD_PLVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_HAVD_PLVS"))
]),
ValBool(True) # 3.5 <= 3.5 is true (equality boundary)
),
# equality: fraction == fraction
("CVM FRACTIO\nIIIS EST IIIS",
Program([ModuleCall("FRACTIO")], [

View File

@@ -49,7 +49,7 @@
},
{
"name": "keyword.operator.comparison.cent",
"match": "\\b(DISPAR|EST|MINVS|PLVS)\\b"
"match": "\\b(HAVD_PLVS|HAVD_MINVS|DISPAR|EST|MINVS|PLVS)\\b"
},
{
"name": "keyword.operator.arithmetic.cent",