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 0000000..8dabd8d Binary files /dev/null and b/snippets/compound.png differ 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):