From c28ffbbf45bea3b2f8c6b2e4e249504433bf2acf Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Tue, 21 Apr 2026 14:39:42 +0200 Subject: [PATCH] :goat: Compound assignment --- README.md | 12 ++++++++++ centvrion/lexer.py | 2 ++ centvrion/parser.py | 8 +++++++ language/main.tex | 2 ++ snippets/compound.cent | 3 +++ snippets/compound.png | Bin 0 -> 10510 bytes snippets/syntaxes/centvrion.sublime-syntax | 2 +- tests.py | 25 +++++++++++++++++++++ 8 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 snippets/compound.cent create mode 100644 snippets/compound.png diff --git a/README.md b/README.md index 2fbf8a4..edb68c6 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,18 @@ Variables are set with the `DESIGNA` and `VT` keywords. Type is inferred. Variable can consist of lower-case letters, numbers, as well as `_`. +### Compound assignment + +`AVGE` (+=) and `MINVE` (-=) are shorthand for incrementing or decrementing a variable: + +![Compound assignment](snippets/compound.png) + +``` +> VIII +``` + +`x AVGE III` is equivalent to `DESIGNA x VT x + III`. + ## Data types ### NVLLVS `NVLLVS` is a special kind of data type in `CENTVRION`, similar to the `null` value in many other languages. `NVLLVS` can be 0 if evaluated as an int or float, or an empty string if evaluated as a string. `NVLLVS` cannot be evaluated as a boolean. diff --git a/centvrion/lexer.py b/centvrion/lexer.py index b441ffd..21e7776 100644 --- a/centvrion/lexer.py +++ b/centvrion/lexer.py @@ -5,6 +5,7 @@ valid_characters = '|'.join(list("abcdefghiklmnopqrstvxyz_")) keyword_tokens = [("KEYWORD_"+i, i) for i in [ "AETERNVM", "ALVID", + "AVGE", "AVT", "DEFINI", "DESIGNA", @@ -19,6 +20,7 @@ keyword_tokens = [("KEYWORD_"+i, i) for i in [ "FALSITAS", "INVOCA", "IN", + "MINVE", "MINVS", "NON", "NVLLVS", diff --git a/centvrion/parser.py b/centvrion/parser.py index 993b236..6f7fc81 100644 --- a/centvrion/parser.py +++ b/centvrion/parser.py @@ -74,6 +74,14 @@ class Parser(): def statement_designa_index(tokens): return ast_nodes.DesignaIndex(tokens[1], tokens[3], tokens[6]) + @self.pg.production('statement : id KEYWORD_AVGE expression') + def statement_avge(tokens): + return ast_nodes.Designa(tokens[0], ast_nodes.BinOp(tokens[0], tokens[2], "SYMBOL_PLUS")) + + @self.pg.production('statement : id KEYWORD_MINVE expression') + def statement_minve(tokens): + return ast_nodes.Designa(tokens[0], ast_nodes.BinOp(tokens[0], tokens[2], "SYMBOL_MINUS")) + @self.pg.production('statement : expression') def statement_expression(tokens): return ast_nodes.ExpressionStatement(tokens[0]) diff --git a/language/main.tex b/language/main.tex index e2f8866..5410620 100644 --- a/language/main.tex +++ b/language/main.tex @@ -30,6 +30,8 @@ \multicolumn{3}{|c|}{\textbf{Statements}} \\ \hline \languageline{statement}{\textit{expression}} \\ \languageline{statement}{\texttt{DESIGNA} \textbf{id} \texttt{VT} \textit{expression}} \\ + \languageline{statement}{\textbf{id} \texttt{AVGE} \textit{expression}} \\ + \languageline{statement}{\textbf{id} \texttt{MINVE} \textit{expression}} \\ \languageline{statement}{\texttt{DEFINI} \textbf{id} \texttt{(} \textit{optional-ids} \texttt{)} \texttt{VT} \textit{scope}} \\ \languageline{statement}{\textit{if-statement}} \\ \languageline{statement}{\texttt{DVM} \textit{expression} \texttt{FACE} \textit{scope}} \\ diff --git a/snippets/compound.cent b/snippets/compound.cent new file mode 100644 index 0000000..216003d --- /dev/null +++ b/snippets/compound.cent @@ -0,0 +1,3 @@ +DESIGNA x VT V +x AVGE III +DICE(x) diff --git a/snippets/compound.png b/snippets/compound.png new file mode 100644 index 0000000000000000000000000000000000000000..8dabd8d57672f1eb4a50ee12204ab53b55106b4e GIT binary patch literal 10510 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 literal 0 HcmV?d00001 diff --git a/snippets/syntaxes/centvrion.sublime-syntax b/snippets/syntaxes/centvrion.sublime-syntax index 0afa2ba..be8072f 100644 --- a/snippets/syntaxes/centvrion.sublime-syntax +++ b/snippets/syntaxes/centvrion.sublime-syntax @@ -65,7 +65,7 @@ contexts: scope: support.class.module.centvrion keywords: - - match: '\b(AETERNVM|ALVID|AVT|DEFINI|DESIGNA|DONICVM|DVM|ERVMPE|EST|ET|FACE|INVOCA|IN|MINVS|NON|PER|PLVS|REDI|RELIQVVM|SI|TVNC|VSQVE|VT|CVM)\b' + - match: '\b(AETERNVM|ALVID|AVGE|AVT|CONTINVA|DEFINI|DESIGNA|DONICVM|DVM|ERVMPE|EST|ET|FACE|INVOCA|IN|MINVE|MINVS|NON|PER|PLVS|REDI|RELIQVVM|SI|TVNC|VSQVE|VT|CVM)\b' scope: keyword.control.centvrion operators: diff --git a/tests.py b/tests.py index 582cd5a..34c669b 100644 --- a/tests.py +++ b/tests.py @@ -265,6 +265,31 @@ assignment_tests = [ Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")), ExpressionStatement(ID("x"))]), ValInt(3)), + # Compound assignment — AVGE (+=) + ("DESIGNA x VT V\nx AVGE III\nx", + Program([], [Designa(ID("x"), Numeral("V")), + Designa(ID("x"), BinOp(ID("x"), Numeral("III"), "SYMBOL_PLUS")), + ExpressionStatement(ID("x"))]), + ValInt(8)), + # Compound assignment — MINVE (-=) + ("DESIGNA x VT X\nx MINVE III\nx", + Program([], [Designa(ID("x"), Numeral("X")), + Designa(ID("x"), BinOp(ID("x"), Numeral("III"), "SYMBOL_MINUS")), + ExpressionStatement(ID("x"))]), + ValInt(7)), + # AVGE with complex expression + ("DESIGNA x VT I\nx AVGE II + III\nx", + Program([], [Designa(ID("x"), Numeral("I")), + Designa(ID("x"), BinOp(ID("x"), BinOp(Numeral("II"), Numeral("III"), "SYMBOL_PLUS"), "SYMBOL_PLUS")), + ExpressionStatement(ID("x"))]), + ValInt(6)), + # AVGE inside a loop (DONICVM range is exclusive of upper bound: I VSQVE III = [1, 2]) + ("DESIGNA s VT NVLLVS\nDONICVM i VT I VSQVE III FACE {\ns AVGE i\n}\ns", + Program([], [Designa(ID("s"), Nullus()), + PerStatement(DataRangeArray(Numeral("I"), Numeral("III")), ID("i"), + [Designa(ID("s"), BinOp(ID("s"), ID("i"), "SYMBOL_PLUS"))]), + ExpressionStatement(ID("s"))]), + ValInt(3)), ] class TestAssignment(unittest.TestCase):