From bdf72b2bcc419fcbfd942d0138dcc42f379be30e Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Fri, 24 Apr 2026 16:02:03 +0200 Subject: [PATCH] :goat: LTE/GTE --- README.md | 2 +- centvrion/ast_nodes.py | 10 +++++ centvrion/compiler/emit_expr.py | 2 + centvrion/compiler/runtime/cent_runtime.c | 22 +++++++++ centvrion/compiler/runtime/cent_runtime.h | 2 + centvrion/lexer.py | 2 + centvrion/parser.py | 5 ++- language/main.pdf | Bin 25934 -> 26526 bytes language/main.tex | 2 +- snippets/syntaxes/centvrion.sublime-syntax | 2 +- tests.py | 42 ++++++++++++++++++ .../syntaxes/cent.tmLanguage.json | 2 +- 12 files changed, 88 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a8a4eee..2faf99d 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index cc44400..2788188 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -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)): diff --git a/centvrion/compiler/emit_expr.py b/centvrion/compiler/emit_expr.py index 0b2317a..61863c9 100644 --- a/centvrion/compiler/emit_expr.py +++ b/centvrion/compiler/emit_expr.py @@ -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", } diff --git a/centvrion/compiler/runtime/cent_runtime.c b/centvrion/compiler/runtime/cent_runtime.c index 3045a8f..c29e086 100644 --- a/centvrion/compiler/runtime/cent_runtime.c +++ b/centvrion/compiler/runtime/cent_runtime.c @@ -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"); diff --git a/centvrion/compiler/runtime/cent_runtime.h b/centvrion/compiler/runtime/cent_runtime.h index 237f509..b3707dc 100644 --- a/centvrion/compiler/runtime/cent_runtime.h +++ b/centvrion/compiler/runtime/cent_runtime.h @@ -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 */ diff --git a/centvrion/lexer.py b/centvrion/lexer.py index 48dcc77..61f2a52 100644 --- a/centvrion/lexer.py +++ b/centvrion/lexer.py @@ -20,6 +20,8 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [ "FAC", "FALSITAS", "FVNCTIO", + "HAVD_MINVS", + "HAVD_PLVS", "INVOCA", "IN", "MINVE", diff --git a/centvrion/parser.py b/centvrion/parser.py index 3b2bceb..9f741f4 100644 --- a/centvrion/parser.py +++ b/centvrion/parser.py @@ -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): diff --git a/language/main.pdf b/language/main.pdf index d99df9fdf240c7417ba24ebce0a01ab7831c12eb..abd10f8ec7aa2e13ee0e0621204545cc5758a7b1 100644 GIT binary patch delta 12106 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 delta 11498 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, \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} diff --git a/snippets/syntaxes/centvrion.sublime-syntax b/snippets/syntaxes/centvrion.sublime-syntax index 56d1be0..9e71361 100644 --- a/snippets/syntaxes/centvrion.sublime-syntax +++ b/snippets/syntaxes/centvrion.sublime-syntax @@ -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: diff --git a/tests.py b/tests.py index f19c046..020ad4b 100644 --- a/tests.py +++ b/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")], [ diff --git a/vscode-extension/syntaxes/cent.tmLanguage.json b/vscode-extension/syntaxes/cent.tmLanguage.json index 75d07f7..96d8953 100644 --- a/vscode-extension/syntaxes/cent.tmLanguage.json +++ b/vscode-extension/syntaxes/cent.tmLanguage.json @@ -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",