🐐 LTE/GTE
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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)):
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [
|
||||
"FAC",
|
||||
"FALSITAS",
|
||||
"FVNCTIO",
|
||||
"HAVD_MINVS",
|
||||
"HAVD_PLVS",
|
||||
"INVOCA",
|
||||
"IN",
|
||||
"MINVE",
|
||||
|
||||
@@ -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.
@@ -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}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
42
tests.py
42
tests.py
@@ -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")], [
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user