🐐 NON operator

This commit is contained in:
2026-04-01 13:21:07 +02:00
parent 0b712f5040
commit 77a4f8ae2b
4 changed files with 64 additions and 2 deletions

View File

@@ -462,6 +462,24 @@ class UnaryMinus(Node):
return vtable, ValInt(-val.value()) return vtable, ValInt(-val.value())
class UnaryNot(Node):
def __init__(self, expr):
self.expr = expr
def __eq__(self, other):
return type(self) == type(other) and self.expr == other.expr
def __repr__(self):
return f"UnaryNot({self.expr!r})"
def print(self):
return f"(NON {self.expr.print()})"
def _eval(self, vtable):
vtable, val = self.expr.eval(vtable)
return vtable, ValBool(not bool(val))
class ArrayIndex(Node): class ArrayIndex(Node):
def __init__(self, array, index) -> None: def __init__(self, array, index) -> None:
self.array = array self.array = array

View File

@@ -17,6 +17,7 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [
"INVOCA", "INVOCA",
"IN", "IN",
"MINVS", "MINVS",
"NON",
"NVLLVS", "NVLLVS",
"PER", "PER",
"PLVS", "PLVS",

View File

@@ -15,7 +15,7 @@ class Parser():
('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST"]), ('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST"]),
('left', ["SYMBOL_COLON", "SYMBOL_PLUS", "SYMBOL_MINUS"]), ('left', ["SYMBOL_COLON", "SYMBOL_PLUS", "SYMBOL_MINUS"]),
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]), ('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]),
('right', ["UMINUS"]), ('right', ["UMINUS", "UNOT"]),
('left', ["INDEX"]), ('left', ["INDEX"]),
] ]
) )
@@ -191,6 +191,10 @@ class Parser():
def unary_minus(tokens): def unary_minus(tokens):
return ast_nodes.UnaryMinus(tokens[1]) return ast_nodes.UnaryMinus(tokens[1])
@self.pg.production('expression : KEYWORD_NON expression', precedence='UNOT')
def unary_not(tokens):
return ast_nodes.UnaryNot(tokens[1])
@self.pg.production('expression : KEYWORD_INVOCA id expressions') @self.pg.production('expression : KEYWORD_INVOCA id expressions')
def invoca(tokens): def invoca(tokens):
return ast_nodes.Invoca(tokens[1], tokens[2]) return ast_nodes.Invoca(tokens[1], tokens[2])

View File

@@ -8,7 +8,7 @@ from centvrion.ast_nodes import (
ArrayIndex, Bool, BinOp, BuiltIn, DataArray, DataRangeArray, Defini, ArrayIndex, Bool, BinOp, BuiltIn, DataArray, DataRangeArray, Defini,
Designa, DumStatement, Erumpe, ExpressionStatement, ID, Designa, DumStatement, Erumpe, ExpressionStatement, ID,
Invoca, ModuleCall, Nullus, Numeral, PerStatement, Invoca, ModuleCall, Nullus, Numeral, PerStatement,
Program, Redi, SiStatement, String, UnaryMinus, Program, Redi, SiStatement, String, UnaryMinus, UnaryNot,
num_to_int, int_to_num, make_string, num_to_int, int_to_num, make_string,
) )
from centvrion.lexer import Lexer from centvrion.lexer import Lexer
@@ -160,6 +160,10 @@ precedence_tests = [
("CVM SVBNVLLA\n- [I, II, III][I]", ("CVM SVBNVLLA\n- [I, II, III][I]",
Program([ModuleCall("SVBNVLLA")], [ExpressionStatement(UnaryMinus(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("I"))))]), Program([ModuleCall("SVBNVLLA")], [ExpressionStatement(UnaryMinus(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("I"))))]),
ValInt(-1)), ValInt(-1)),
# INDEX binds tighter than NON: NON (arr[I]) = NON VERITAS = False
("NON [VERITAS, FALSITAS][I]",
Program([], [ExpressionStatement(UnaryNot(ArrayIndex(DataArray([Bool(True), Bool(False)]), Numeral("I"))))]),
ValBool(False)),
# INDEX binds tighter than +: (arr[II]) + X = 2 + 10 = 12 # INDEX binds tighter than +: (arr[II]) + X = 2 + 10 = 12
("[I, II, III][II] + X", ("[I, II, III][II] + X",
Program([], [ExpressionStatement(BinOp(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("II")), Numeral("X"), "SYMBOL_PLUS"))]), Program([], [ExpressionStatement(BinOp(ArrayIndex(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), Numeral("II")), Numeral("X"), "SYMBOL_PLUS"))]),
@@ -1080,5 +1084,40 @@ class TestScope(unittest.TestCase):
run_test(self, source, nodes, value) run_test(self, source, nodes, value)
# --- NON (boolean not) ---
non_tests = [
("NON VERITAS",
Program([], [ExpressionStatement(UnaryNot(Bool(True)))]),
ValBool(False)),
("NON FALSITAS",
Program([], [ExpressionStatement(UnaryNot(Bool(False)))]),
ValBool(True)),
("NON NON VERITAS",
Program([], [ExpressionStatement(UnaryNot(UnaryNot(Bool(True))))]),
ValBool(True)),
("NON I",
Program([], [ExpressionStatement(UnaryNot(Numeral("I")))]),
ValBool(False)),
# zero int is falsy, so NON gives True
("DESIGNA z VT I - I\nNON z",
Program([], [Designa(ID("z"), BinOp(Numeral("I"), Numeral("I"), "SYMBOL_MINUS")), ExpressionStatement(UnaryNot(ID("z")))]),
ValBool(True)),
# NON binds tighter than AVT: (NON VERITAS) AVT FALSITAS → FALSITAS AVT FALSITAS → False
("NON VERITAS AVT FALSITAS",
Program([], [ExpressionStatement(BinOp(UnaryNot(Bool(True)), Bool(False), "KEYWORD_AVT"))]),
ValBool(False)),
# NON binds tighter than EST: (NON I) EST I → FALSITAS EST I → False
("NON I EST I",
Program([], [ExpressionStatement(BinOp(UnaryNot(Numeral("I")), Numeral("I"), "KEYWORD_EST"))]),
ValBool(False)),
]
class TestNon(unittest.TestCase):
@parameterized.expand(non_tests)
def test_non(self, source, nodes, value):
run_test(self, source, nodes, value)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()