🐐 Modulo
This commit is contained in:
@@ -38,6 +38,8 @@ Strings are concatenated with `&`:
|
|||||||
|
|
||||||
`NVLLVS` coerces to an empty string when used with `&`. Note: `+` is for arithmetic only — using it on strings raises an error.
|
`NVLLVS` coerces to an empty string when used with `&`. Note: `+` is for arithmetic only — using it on strings raises an error.
|
||||||
|
|
||||||
|
Integer modulo is `RELIQVVM`: `VII RELIQVVM III` evaluates to `I`. Under the `FRACTIO` module it returns a fraction, so `IIIS RELIQVVM IS` is `S` (i.e. 1/2).
|
||||||
|
|
||||||
### Integers
|
### Integers
|
||||||
Integers must be written in roman numerals using the following symbols:
|
Integers must be written in roman numerals using the following symbols:
|
||||||
|
|
||||||
@@ -92,7 +94,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.
|
Will return `I` (1), as the conditional evaluates `x` to be true.
|
||||||
|
|
||||||
### Boolean expressions
|
### Boolean expressions
|
||||||
In conditionals, `EST` functions as an equality evaluation, and `MINVS` (<) and `PLVS` (>) function as inequality evaluation.
|
In conditionals, `EST` functions as an equality evaluation, `DISPAR` as not-equal, and `MINVS` (<) and `PLVS` (>) function as inequality evaluation.
|
||||||
|
|
||||||
### ALVID
|
### ALVID
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ OP_STR = {
|
|||||||
"SYMBOL_PLUS": "+", "SYMBOL_MINUS": "-",
|
"SYMBOL_PLUS": "+", "SYMBOL_MINUS": "-",
|
||||||
"SYMBOL_TIMES": "*", "SYMBOL_DIVIDE": "/",
|
"SYMBOL_TIMES": "*", "SYMBOL_DIVIDE": "/",
|
||||||
"SYMBOL_AMPERSAND": "&",
|
"SYMBOL_AMPERSAND": "&",
|
||||||
"KEYWORD_EST": "EST", "KEYWORD_MINVS": "MINVS",
|
"KEYWORD_RELIQVVM": "RELIQVVM",
|
||||||
|
"KEYWORD_EST": "EST", "KEYWORD_DISPAR": "DISPAR",
|
||||||
|
"KEYWORD_MINVS": "MINVS",
|
||||||
"KEYWORD_PLVS": "PLVS", "KEYWORD_ET": "ET", "KEYWORD_AVT": "AVT",
|
"KEYWORD_PLVS": "PLVS", "KEYWORD_ET": "ET", "KEYWORD_AVT": "AVT",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,6 +588,14 @@ class BinOp(Node):
|
|||||||
if isinstance(lv, Fraction) or isinstance(rv, Fraction) or "FRACTIO" in vtable["#modules"]:
|
if isinstance(lv, Fraction) or isinstance(rv, Fraction) or "FRACTIO" in vtable["#modules"]:
|
||||||
return vtable, ValFrac(Fraction(lv or 0) / Fraction(rv or 0))
|
return vtable, ValFrac(Fraction(lv or 0) / Fraction(rv or 0))
|
||||||
return vtable, ValInt((lv or 0) // (rv or 0))
|
return vtable, ValInt((lv or 0) // (rv or 0))
|
||||||
|
case "KEYWORD_RELIQVVM":
|
||||||
|
if isinstance(lv, (str, list)) or isinstance(rv, (str, list)):
|
||||||
|
raise CentvrionError("Cannot use RELIQVVM on strings or arrays")
|
||||||
|
if (rv or 0) == 0:
|
||||||
|
raise CentvrionError("Modulo by zero")
|
||||||
|
if isinstance(lv, Fraction) or isinstance(rv, Fraction) or "FRACTIO" in vtable["#modules"]:
|
||||||
|
return vtable, ValFrac(Fraction(lv or 0) % Fraction(rv or 0))
|
||||||
|
return vtable, ValInt((lv or 0) % (rv or 0))
|
||||||
case "KEYWORD_MINVS":
|
case "KEYWORD_MINVS":
|
||||||
if isinstance(lv, (str, list)) or isinstance(rv, (str, list)):
|
if isinstance(lv, (str, list)) or isinstance(rv, (str, list)):
|
||||||
raise CentvrionError("Cannot compare strings or arrays with MINVS")
|
raise CentvrionError("Cannot compare strings or arrays with MINVS")
|
||||||
@@ -596,6 +606,8 @@ class BinOp(Node):
|
|||||||
return vtable, ValBool((lv or 0) > (rv or 0))
|
return vtable, ValBool((lv or 0) > (rv or 0))
|
||||||
case "KEYWORD_EST":
|
case "KEYWORD_EST":
|
||||||
return vtable, ValBool(lv == rv)
|
return vtable, ValBool(lv == rv)
|
||||||
|
case "KEYWORD_DISPAR":
|
||||||
|
return vtable, ValBool(lv != rv)
|
||||||
case "KEYWORD_ET":
|
case "KEYWORD_ET":
|
||||||
return vtable, ValBool(bool(left) and bool(right))
|
return vtable, ValBool(bool(left) and bool(right))
|
||||||
case "KEYWORD_AVT":
|
case "KEYWORD_AVT":
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ _BINOP_FN = {
|
|||||||
"SYMBOL_TIMES": "cent_mul",
|
"SYMBOL_TIMES": "cent_mul",
|
||||||
"SYMBOL_DIVIDE": "cent_div",
|
"SYMBOL_DIVIDE": "cent_div",
|
||||||
"SYMBOL_AMPERSAND": "cent_concat",
|
"SYMBOL_AMPERSAND": "cent_concat",
|
||||||
|
"KEYWORD_RELIQVVM": "cent_mod",
|
||||||
"KEYWORD_EST": "cent_eq",
|
"KEYWORD_EST": "cent_eq",
|
||||||
|
"KEYWORD_DISPAR": "cent_neq",
|
||||||
"KEYWORD_MINVS": "cent_lt",
|
"KEYWORD_MINVS": "cent_lt",
|
||||||
"KEYWORD_PLVS": "cent_gt",
|
"KEYWORD_PLVS": "cent_gt",
|
||||||
"KEYWORD_ET": "cent_and",
|
"KEYWORD_ET": "cent_and",
|
||||||
@@ -77,6 +79,8 @@ def emit_expr(node, ctx):
|
|||||||
tmp = ctx.fresh_tmp()
|
tmp = ctx.fresh_tmp()
|
||||||
if node.op == "SYMBOL_DIVIDE" and ctx.has_module("FRACTIO"):
|
if node.op == "SYMBOL_DIVIDE" and ctx.has_module("FRACTIO"):
|
||||||
fn = "cent_div_frac"
|
fn = "cent_div_frac"
|
||||||
|
elif node.op == "KEYWORD_RELIQVVM" and ctx.has_module("FRACTIO"):
|
||||||
|
fn = "cent_mod_frac"
|
||||||
else:
|
else:
|
||||||
fn = _BINOP_FN[node.op]
|
fn = _BINOP_FN[node.op]
|
||||||
return l_lines + r_lines + [f"CentValue {tmp} = {fn}({l_var}, {r_var});"], tmp
|
return l_lines + r_lines + [f"CentValue {tmp} = {fn}({l_var}, {r_var});"], tmp
|
||||||
|
|||||||
@@ -396,6 +396,30 @@ CentValue cent_div_frac(CentValue a, CentValue b) {
|
|||||||
return frac_reduce(an * bd, ad * bn);
|
return frac_reduce(an * bd, ad * bn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CentValue cent_mod(CentValue a, CentValue b) {
|
||||||
|
if (a.type != CENT_INT || b.type != CENT_INT)
|
||||||
|
cent_type_error("'RELIQVVM' requires two integers");
|
||||||
|
if (b.ival == 0)
|
||||||
|
cent_runtime_error("modulo by zero");
|
||||||
|
return cent_int(a.ival % b.ival);
|
||||||
|
}
|
||||||
|
|
||||||
|
CentValue cent_mod_frac(CentValue a, CentValue b) {
|
||||||
|
long an, ad, bn, bd;
|
||||||
|
to_frac(a, &an, &ad); to_frac(b, &bn, &bd);
|
||||||
|
if (bn == 0) cent_runtime_error("modulo by zero");
|
||||||
|
/* a/b mod c/d over a common denominator ad*bd:
|
||||||
|
num_a = an*bd, num_b = bn*ad
|
||||||
|
result = (num_a - floor(num_a/num_b) * num_b) / (ad*bd)
|
||||||
|
Use floored division so the result matches Python's Fraction.__mod__. */
|
||||||
|
long num_a = an * bd;
|
||||||
|
long num_b = bn * ad;
|
||||||
|
long q = num_a / num_b;
|
||||||
|
if ((num_a % num_b != 0) && ((num_a < 0) != (num_b < 0))) q -= 1;
|
||||||
|
long new_num = num_a - q * num_b;
|
||||||
|
return frac_reduce(new_num, ad * bd);
|
||||||
|
}
|
||||||
|
|
||||||
CentValue cent_eq(CentValue a, CentValue b) {
|
CentValue cent_eq(CentValue a, CentValue b) {
|
||||||
if ((a.type == CENT_INT || a.type == CENT_FRAC) &&
|
if ((a.type == CENT_INT || a.type == CENT_FRAC) &&
|
||||||
(b.type == CENT_INT || b.type == CENT_FRAC)) {
|
(b.type == CENT_INT || b.type == CENT_FRAC)) {
|
||||||
@@ -414,6 +438,11 @@ CentValue cent_eq(CentValue a, CentValue b) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CentValue cent_neq(CentValue a, CentValue b) {
|
||||||
|
CentValue r = cent_eq(a, b);
|
||||||
|
return cent_bool(!r.bval);
|
||||||
|
}
|
||||||
|
|
||||||
CentValue cent_lt(CentValue a, CentValue b) {
|
CentValue cent_lt(CentValue a, CentValue b) {
|
||||||
if ((a.type == CENT_INT || a.type == CENT_FRAC || a.type == CENT_NULL) &&
|
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)) {
|
(b.type == CENT_INT || b.type == CENT_FRAC || b.type == CENT_NULL)) {
|
||||||
|
|||||||
@@ -158,7 +158,10 @@ CentValue cent_sub(CentValue a, CentValue b); /* INT-INT or FRAC-FRAC/INT */
|
|||||||
CentValue cent_mul(CentValue a, CentValue b); /* INT*INT or FRAC*FRAC/INT */
|
CentValue cent_mul(CentValue a, CentValue b); /* INT*INT or FRAC*FRAC/INT */
|
||||||
CentValue cent_div(CentValue a, CentValue b); /* INT/INT integer div */
|
CentValue cent_div(CentValue a, CentValue b); /* INT/INT integer div */
|
||||||
CentValue cent_div_frac(CentValue a, CentValue b); /* FRACTIO: exact div → FRAC */
|
CentValue cent_div_frac(CentValue a, CentValue b); /* FRACTIO: exact div → FRAC */
|
||||||
|
CentValue cent_mod(CentValue a, CentValue b); /* INT%INT integer modulo */
|
||||||
|
CentValue cent_mod_frac(CentValue a, CentValue b); /* FRACTIO: floored mod → FRAC */
|
||||||
CentValue cent_eq (CentValue a, CentValue b); /* EST → BOOL */
|
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_lt (CentValue a, CentValue b); /* MINVS → BOOL */
|
||||||
CentValue cent_gt (CentValue a, CentValue b); /* PLVS → BOOL */
|
CentValue cent_gt (CentValue a, CentValue b); /* PLVS → BOOL */
|
||||||
CentValue cent_and(CentValue a, CentValue b); /* ET → BOOL */
|
CentValue cent_and(CentValue a, CentValue b); /* ET → BOOL */
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [
|
|||||||
"AVT",
|
"AVT",
|
||||||
"DEFINI",
|
"DEFINI",
|
||||||
"DESIGNA",
|
"DESIGNA",
|
||||||
|
"DISPAR",
|
||||||
"DONICVM",
|
"DONICVM",
|
||||||
"DVM",
|
"DVM",
|
||||||
"CONTINVA",
|
"CONTINVA",
|
||||||
@@ -23,6 +24,7 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [
|
|||||||
"PER",
|
"PER",
|
||||||
"PLVS",
|
"PLVS",
|
||||||
"REDI",
|
"REDI",
|
||||||
|
"RELIQVVM",
|
||||||
"SI",
|
"SI",
|
||||||
"TVNC",
|
"TVNC",
|
||||||
"VSQVE",
|
"VSQVE",
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ class Parser():
|
|||||||
precedence=[
|
precedence=[
|
||||||
('left', ["KEYWORD_AVT"]),
|
('left', ["KEYWORD_AVT"]),
|
||||||
('left', ["KEYWORD_ET"]),
|
('left', ["KEYWORD_ET"]),
|
||||||
('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST"]),
|
('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST", "KEYWORD_DISPAR"]),
|
||||||
('left', ["SYMBOL_AMPERSAND", "SYMBOL_PLUS", "SYMBOL_MINUS"]),
|
('left', ["SYMBOL_AMPERSAND", "SYMBOL_PLUS", "SYMBOL_MINUS"]),
|
||||||
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]),
|
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE", "KEYWORD_RELIQVVM"]),
|
||||||
('right', ["UMINUS", "UNOT"]),
|
('right', ["UMINUS", "UNOT"]),
|
||||||
('left', ["SYMBOL_LBRACKET", "INDEX"]),
|
('left', ["SYMBOL_LBRACKET", "INDEX"]),
|
||||||
]
|
]
|
||||||
@@ -191,7 +191,9 @@ class Parser():
|
|||||||
@self.pg.production('expression : expression SYMBOL_PLUS expression')
|
@self.pg.production('expression : expression SYMBOL_PLUS expression')
|
||||||
@self.pg.production('expression : expression SYMBOL_TIMES expression')
|
@self.pg.production('expression : expression SYMBOL_TIMES expression')
|
||||||
@self.pg.production('expression : expression SYMBOL_DIVIDE expression')
|
@self.pg.production('expression : expression SYMBOL_DIVIDE expression')
|
||||||
|
@self.pg.production('expression : expression KEYWORD_RELIQVVM expression')
|
||||||
@self.pg.production('expression : expression KEYWORD_EST expression')
|
@self.pg.production('expression : expression KEYWORD_EST expression')
|
||||||
|
@self.pg.production('expression : expression KEYWORD_DISPAR expression')
|
||||||
@self.pg.production('expression : expression KEYWORD_MINVS expression')
|
@self.pg.production('expression : expression KEYWORD_MINVS expression')
|
||||||
@self.pg.production('expression : expression KEYWORD_PLVS expression')
|
@self.pg.production('expression : expression KEYWORD_PLVS expression')
|
||||||
@self.pg.production('expression : expression KEYWORD_ET expression')
|
@self.pg.production('expression : expression KEYWORD_ET expression')
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
\item \textbf{string}: \\ Any text encased in " characters.
|
\item \textbf{string}: \\ Any text encased in " characters.
|
||||||
\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{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{bool}: \\ VERITAS or FALSITAS.
|
||||||
\item \textbf{binop}: \\ Binary operators: \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}, \texttt{EST} (equality), \texttt{MINVS} (<), \texttt{PLVS} (>), \texttt{ET} (and), \texttt{AVT} (or), \texttt{\&} (string 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{ET} (and), \texttt{AVT} (or), \texttt{\&} (string concatenation).
|
||||||
\item \textbf{unop}: \\ Unary operators: \texttt{-} (negation), \texttt{NON} (boolean not).
|
\item \textbf{unop}: \\ Unary operators: \texttt{-} (negation), \texttt{NON} (boolean not).
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ contexts:
|
|||||||
scope: support.class.module.centvrion
|
scope: support.class.module.centvrion
|
||||||
|
|
||||||
keywords:
|
keywords:
|
||||||
- match: '\b(ALVID|AVT|DEFINI|DESIGNA|DONICVM|DVM|ERVMPE|EST|ET|FACE|INVOCA|IN|MINVS|NON|PER|PLVS|REDI|SI|TVNC|VSQVE|VT|CVM)\b'
|
- match: '\b(ALVID|AVT|DEFINI|DESIGNA|DONICVM|DVM|ERVMPE|EST|ET|FACE|INVOCA|IN|MINVS|NON|PER|PLVS|REDI|RELIQVVM|SI|TVNC|VSQVE|VT|CVM)\b'
|
||||||
scope: keyword.control.centvrion
|
scope: keyword.control.centvrion
|
||||||
|
|
||||||
operators:
|
operators:
|
||||||
|
|||||||
63
tests.py
63
tests.py
@@ -136,6 +136,9 @@ arithmetic_tests = [
|
|||||||
("III * IV", Program([], [ExpressionStatement(BinOp(Numeral("III"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(12)),
|
("III * IV", Program([], [ExpressionStatement(BinOp(Numeral("III"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(12)),
|
||||||
("X / II", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))]), ValInt(5)),
|
("X / II", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))]), ValInt(5)),
|
||||||
("X / III", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "SYMBOL_DIVIDE"))]), ValInt(3)), # integer division: 10 // 3 = 3
|
("X / III", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "SYMBOL_DIVIDE"))]), ValInt(3)), # integer division: 10 // 3 = 3
|
||||||
|
("X RELIQVVM III", Program([], [ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "KEYWORD_RELIQVVM"))]), ValInt(1)), # 10 % 3 = 1
|
||||||
|
("IX RELIQVVM III", Program([], [ExpressionStatement(BinOp(Numeral("IX"), Numeral("III"), "KEYWORD_RELIQVVM"))]), ValInt(0)), # exact divisor
|
||||||
|
("VII RELIQVVM X", Program([], [ExpressionStatement(BinOp(Numeral("VII"), Numeral("X"), "KEYWORD_RELIQVVM"))]), ValInt(7)), # dividend < divisor
|
||||||
("II + III * IV", Program([], [ExpressionStatement(BinOp(Numeral("II"), BinOp(Numeral("III"), Numeral("IV"), "SYMBOL_TIMES"), "SYMBOL_PLUS"))]), ValInt(14)), # precedence: 2 + (3*4) = 14
|
("II + III * IV", Program([], [ExpressionStatement(BinOp(Numeral("II"), BinOp(Numeral("III"), Numeral("IV"), "SYMBOL_TIMES"), "SYMBOL_PLUS"))]), ValInt(14)), # precedence: 2 + (3*4) = 14
|
||||||
("(II + III) * IV", Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(20)), # parens: (2+3)*4 = 20
|
("(II + III) * IV", Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), Numeral("IV"), "SYMBOL_TIMES"))]), ValInt(20)), # parens: (2+3)*4 = 20
|
||||||
("CVM SVBNVLLA\n- III", Program([ModuleCall("SVBNVLLA")], [ExpressionStatement(UnaryMinus(Numeral("III")))]), ValInt(-3)), # unary negation
|
("CVM SVBNVLLA\n- III", Program([ModuleCall("SVBNVLLA")], [ExpressionStatement(UnaryMinus(Numeral("III")))]), ValInt(-3)), # unary negation
|
||||||
@@ -153,7 +156,7 @@ class TestArithmetic(unittest.TestCase):
|
|||||||
# --- Precedence and associativity ---
|
# --- Precedence and associativity ---
|
||||||
#
|
#
|
||||||
# Precedence (lowest → highest):
|
# Precedence (lowest → highest):
|
||||||
# AVT < ET < (EST, PLVS, MINVS) < (+ -) < (* /) < UMINUS < INDEX
|
# AVT < ET < (EST, DISPAR, PLVS, MINVS) < (+ -) < (* / RELIQVVM) < UMINUS < INDEX
|
||||||
|
|
||||||
precedence_tests = [
|
precedence_tests = [
|
||||||
# * binds tighter than -: 10 - (2*3) = 4, not (10-2)*3 = 24
|
# * binds tighter than -: 10 - (2*3) = 4, not (10-2)*3 = 24
|
||||||
@@ -176,6 +179,14 @@ precedence_tests = [
|
|||||||
("I EST II ET II EST II",
|
("I EST II ET II EST II",
|
||||||
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"), BinOp(Numeral("II"), Numeral("II"), "KEYWORD_EST"), "KEYWORD_ET"))]),
|
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_EST"), BinOp(Numeral("II"), Numeral("II"), "KEYWORD_EST"), "KEYWORD_ET"))]),
|
||||||
ValBool(False)),
|
ValBool(False)),
|
||||||
|
# + binds tighter than DISPAR: (2+3)!=5 = False, not 2+(3!=5) = type error
|
||||||
|
("II + III DISPAR V",
|
||||||
|
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), Numeral("V"), "KEYWORD_DISPAR"))]),
|
||||||
|
ValBool(False)),
|
||||||
|
# DISPAR binds tighter than ET: (1!=2) AND (2!=2) = True AND False = False
|
||||||
|
("I DISPAR II ET II DISPAR II",
|
||||||
|
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_DISPAR"), BinOp(Numeral("II"), Numeral("II"), "KEYWORD_DISPAR"), "KEYWORD_ET"))]),
|
||||||
|
ValBool(False)),
|
||||||
# ET binds tighter than AVT: True OR (False AND False) = True
|
# ET binds tighter than AVT: True OR (False AND False) = True
|
||||||
("VERITAS AVT FALSITAS ET FALSITAS",
|
("VERITAS AVT FALSITAS ET FALSITAS",
|
||||||
Program([], [ExpressionStatement(BinOp(Bool(True), BinOp(Bool(False), Bool(False), "KEYWORD_ET"), "KEYWORD_AVT"))]),
|
Program([], [ExpressionStatement(BinOp(Bool(True), BinOp(Bool(False), Bool(False), "KEYWORD_ET"), "KEYWORD_AVT"))]),
|
||||||
@@ -208,6 +219,19 @@ precedence_tests = [
|
|||||||
("XII / II / III",
|
("XII / II / III",
|
||||||
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("XII"), Numeral("II"), "SYMBOL_DIVIDE"), Numeral("III"), "SYMBOL_DIVIDE"))]),
|
Program([], [ExpressionStatement(BinOp(BinOp(Numeral("XII"), Numeral("II"), "SYMBOL_DIVIDE"), Numeral("III"), "SYMBOL_DIVIDE"))]),
|
||||||
ValInt(2)),
|
ValInt(2)),
|
||||||
|
# RELIQVVM same precedence as *, /; left-associative: (17 % 5) % 2 = 0
|
||||||
|
("XVII RELIQVVM V RELIQVVM II",
|
||||||
|
Program([], [ExpressionStatement(
|
||||||
|
BinOp(BinOp(Numeral("XVII"), Numeral("V"), "KEYWORD_RELIQVVM"),
|
||||||
|
Numeral("II"), "KEYWORD_RELIQVVM"))]),
|
||||||
|
ValInt(0)),
|
||||||
|
# RELIQVVM binds tighter than +: 2 + (7 % 3) = 3, not (2+7) % 3 = 0
|
||||||
|
("II + VII RELIQVVM III",
|
||||||
|
Program([], [ExpressionStatement(
|
||||||
|
BinOp(Numeral("II"),
|
||||||
|
BinOp(Numeral("VII"), Numeral("III"), "KEYWORD_RELIQVVM"),
|
||||||
|
"SYMBOL_PLUS"))]),
|
||||||
|
ValInt(3)),
|
||||||
# left-associativity of AVT: (False OR True) OR False = True
|
# left-associativity of AVT: (False OR True) OR False = True
|
||||||
("FALSITAS AVT VERITAS AVT FALSITAS",
|
("FALSITAS AVT VERITAS AVT FALSITAS",
|
||||||
Program([], [ExpressionStatement(BinOp(BinOp(Bool(False), Bool(True), "KEYWORD_AVT"), Bool(False), "KEYWORD_AVT"))]),
|
Program([], [ExpressionStatement(BinOp(BinOp(Bool(False), Bool(True), "KEYWORD_AVT"), Bool(False), "KEYWORD_AVT"))]),
|
||||||
@@ -719,6 +743,15 @@ comparison_tests = [
|
|||||||
# NVLLVS coerces to 0 in comparisons
|
# NVLLVS coerces to 0 in comparisons
|
||||||
("V PLVS NVLLVS", Program([], [ExpressionStatement(BinOp(Numeral("V"), Nullus(), "KEYWORD_PLVS"))]), ValBool(True)),
|
("V PLVS NVLLVS", Program([], [ExpressionStatement(BinOp(Numeral("V"), Nullus(), "KEYWORD_PLVS"))]), ValBool(True)),
|
||||||
("NVLLVS MINVS V", Program([], [ExpressionStatement(BinOp(Nullus(), Numeral("V"), "KEYWORD_MINVS"))]), ValBool(True)),
|
("NVLLVS MINVS V", Program([], [ExpressionStatement(BinOp(Nullus(), Numeral("V"), "KEYWORD_MINVS"))]), ValBool(True)),
|
||||||
|
# DISPAR (not-equal): mirrors EST semantics, negated
|
||||||
|
("I DISPAR II", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("II"), "KEYWORD_DISPAR"))]), ValBool(True)),
|
||||||
|
("I DISPAR I", Program([], [ExpressionStatement(BinOp(Numeral("I"), Numeral("I"), "KEYWORD_DISPAR"))]), ValBool(False)),
|
||||||
|
('"hello" DISPAR "hello"', Program([], [ExpressionStatement(BinOp(String("hello"), String("hello"), "KEYWORD_DISPAR"))]), ValBool(False)),
|
||||||
|
('"hello" DISPAR "world"', Program([], [ExpressionStatement(BinOp(String("hello"), String("world"), "KEYWORD_DISPAR"))]), ValBool(True)),
|
||||||
|
("VERITAS DISPAR FALSITAS", Program([], [ExpressionStatement(BinOp(Bool(True), Bool(False), "KEYWORD_DISPAR"))]), ValBool(True)),
|
||||||
|
("NVLLVS DISPAR NVLLVS", Program([], [ExpressionStatement(BinOp(Nullus(), Nullus(), "KEYWORD_DISPAR"))]), ValBool(False)),
|
||||||
|
# cross-type: an int and a string are never equal
|
||||||
|
('I DISPAR "I"', Program([], [ExpressionStatement(BinOp(Numeral("I"), String("I"), "KEYWORD_DISPAR"))]), ValBool(True)),
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestComparisons(unittest.TestCase):
|
class TestComparisons(unittest.TestCase):
|
||||||
@@ -1431,6 +1464,34 @@ fractio_tests = [
|
|||||||
]),
|
]),
|
||||||
ValFrac(Fraction(5))
|
ValFrac(Fraction(5))
|
||||||
),
|
),
|
||||||
|
# Modulo on fractions: 7/2 RELIQVVM 3/2 = 1/2 (7/2 / 3/2 = 7/3, floor=2, 7/2 - 3 = 1/2)
|
||||||
|
("CVM FRACTIO\nIIIS RELIQVVM IS",
|
||||||
|
Program([ModuleCall("FRACTIO")], [
|
||||||
|
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IS"), "KEYWORD_RELIQVVM"))
|
||||||
|
]),
|
||||||
|
ValFrac(Fraction(1, 2))
|
||||||
|
),
|
||||||
|
# Modulo with mixed operand types: 5/2 RELIQVVM 1 = 1/2
|
||||||
|
("CVM FRACTIO\nIIS RELIQVVM I",
|
||||||
|
Program([ModuleCall("FRACTIO")], [
|
||||||
|
ExpressionStatement(BinOp(Fractio("IIS"), Numeral("I"), "KEYWORD_RELIQVVM"))
|
||||||
|
]),
|
||||||
|
ValFrac(Fraction(1, 2))
|
||||||
|
),
|
||||||
|
# Int operands under FRACTIO still return a fraction: 10 RELIQVVM 3 = 1 (as Fraction)
|
||||||
|
("CVM FRACTIO\nX RELIQVVM III",
|
||||||
|
Program([ModuleCall("FRACTIO")], [
|
||||||
|
ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "KEYWORD_RELIQVVM"))
|
||||||
|
]),
|
||||||
|
ValFrac(Fraction(1))
|
||||||
|
),
|
||||||
|
# Exact multiple under FRACTIO: 3 RELIQVVM 3/2 = 0
|
||||||
|
("CVM FRACTIO\nIII RELIQVVM IS",
|
||||||
|
Program([ModuleCall("FRACTIO")], [
|
||||||
|
ExpressionStatement(BinOp(Numeral("III"), Fractio("IS"), "KEYWORD_RELIQVVM"))
|
||||||
|
]),
|
||||||
|
ValFrac(Fraction(0))
|
||||||
|
),
|
||||||
# String concatenation with fraction
|
# String concatenation with fraction
|
||||||
("CVM FRACTIO\nDICE(IIIS & \"!\")",
|
("CVM FRACTIO\nDICE(IIIS & \"!\")",
|
||||||
Program([ModuleCall("FRACTIO")], [
|
Program([ModuleCall("FRACTIO")], [
|
||||||
|
|||||||
Reference in New Issue
Block a user