🐐 Aeternum loops

This commit is contained in:
2026-04-16 20:46:20 +02:00
parent c570d72b02
commit 7960f6e3da
7 changed files with 72 additions and 1 deletions

View File

@@ -141,6 +141,18 @@ The keyword `ET` can be used as a boolean "and". The keyword `AVT` can be used a
> XI
```
### AETERNVM loops
`AETERNVM FACE { ... }` is shorthand for an infinite loop — equivalent
to `DVM FALSITAS FACE { ... }` but without relying on `DVM`'s inverted
condition. Exit the loop with `ERVMPE` (or `REDI` from inside a function).
![AETERNVM loop](snippets/aeternvm.png)
```
> X
```
### PER loops
![PER loop](snippets/per.png)

View File

@@ -3,6 +3,7 @@ from rply import LexerGenerator
valid_characters = '|'.join(list("abcdefghiklmnopqrstvxyz_"))
keyword_tokens = [("KEYWORD_"+i, i) for i in [
"AETERNVM",
"ALVID",
"AVT",
"DEFINI",

View File

@@ -121,6 +121,11 @@ class Parser():
def dum(tokens):
return ast_nodes.DumStatement(tokens[1], tokens[4])
# AETERNVM is sugar for `DVM FALSITAS` — same AST, no observable difference.
@self.pg.production('dum_statement : KEYWORD_AETERNVM KEYWORD_FACE SYMBOL_LCURL statements SYMBOL_RCURL')
def aeternvm(tokens):
return ast_nodes.DumStatement(ast_nodes.Bool(False), tokens[3])
@self.pg.production('per_statement : KEYWORD_PER id KEYWORD_IN expression KEYWORD_FACE SYMBOL_LCURL statements SYMBOL_RCURL')
def per(tokens):
return ast_nodes.PerStatement(tokens[3], tokens[1], tokens[6])

View File

@@ -33,6 +33,7 @@
\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}} \\
\languageline{statement}{\texttt{AETERNVM} \texttt{FACE} \textit{scope}} \\
\languageline{statement}{\texttt{PER} \textbf{id} \texttt{IN} \textit{expression} \texttt{FACE} \textit{scope}} \\
\languageline{statement}{\texttt{DONICVM} \textbf{id} \texttt{VT} \textit{expression} \texttt{VSQVE} \textit{expression} \texttt{FACE} \textit{scope}} \\
\languageline{statement}{\texttt{REDI(} \textit{optional-expressions} \texttt{)}} \\

8
snippets/aeternvm.cent Normal file
View File

@@ -0,0 +1,8 @@
DESIGNA x VT NVLLVS
AETERNVM FACE {
DESIGNA x VT x+I
SI x EST X TVNC {
ERVMPE
}
}
DICE(x)

View File

@@ -65,7 +65,7 @@ contexts:
scope: support.class.module.centvrion
keywords:
- 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'
- 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'
scope: keyword.control.centvrion
operators:

View File

@@ -342,6 +342,50 @@ control_tests = [
DumStatement(Bool(False), [ExpressionStatement(BuiltIn("DICE", [ID("x")])), Erumpe()]),
]),
ValStr("I"), "I\n"),
# AETERNVM is sugar for DVM FALSITAS — must produce the same AST.
("DESIGNA x VT I\nAETERNVM FACE {\nDICE(x)\nERVMPE\n}",
Program([], [
Designa(ID("x"), Numeral("I")),
DumStatement(Bool(False), [ExpressionStatement(BuiltIn("DICE", [ID("x")])), Erumpe()]),
]),
ValStr("I"), "I\n"),
# AETERNVM with counter + ERVMPE on condition
("DESIGNA x VT I\nAETERNVM FACE {\nSI x EST III TVNC { ERVMPE }\nDESIGNA x VT x + I\n}\nx",
Program([], [
Designa(ID("x"), Numeral("I")),
DumStatement(Bool(False), [
SiStatement(BinOp(ID("x"), Numeral("III"), "KEYWORD_EST"), [Erumpe()], None),
Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")),
]),
ExpressionStatement(ID("x")),
]),
ValInt(3)),
# AETERNVM with CONTINVA — skip printing III; ERVMPE after V.
# Return value is ValNul because the iteration that triggers ERVMPE runs
# Designa first (resetting last_val); we test on output, which is the point.
("DESIGNA x VT NVLLVS\nAETERNVM FACE {\nDESIGNA x VT x + I\nSI x PLVS V TVNC { ERVMPE }\nSI x EST III TVNC { CONTINVA }\nDICE(x)\n}",
Program([], [
Designa(ID("x"), Nullus()),
DumStatement(Bool(False), [
Designa(ID("x"), BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")),
SiStatement(BinOp(ID("x"), Numeral("V"), "KEYWORD_PLVS"), [Erumpe()], None),
SiStatement(BinOp(ID("x"), Numeral("III"), "KEYWORD_EST"), [Continva()], None),
ExpressionStatement(BuiltIn("DICE", [ID("x")])),
]),
]),
ValNul(), "I\nII\nIV\nV\n"),
# REDI inside AETERNVM (inside DEFINI) — exits both loop and function
(
"DEFINI f () VT {\nDESIGNA x VT I\nAETERNVM FACE {\nREDI (x)\n}\n}\nINVOCA f ()",
Program([], [
Defini(ID("f"), [], [
Designa(ID("x"), Numeral("I")),
DumStatement(Bool(False), [Redi([ID("x")])]),
]),
ExpressionStatement(Invoca(ID("f"), [])),
]),
ValInt(1),
),
# PER foreach
("PER i IN [I, II, III] FACE { DICE(i) }",
Program([], [PerStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]), ID("i"), [ExpressionStatement(BuiltIn("DICE", [ID("i")]))])]),