🐐 DORMI

This commit is contained in:
2026-04-21 21:30:59 +02:00
parent 78b1dd7667
commit 108e69291d
7 changed files with 130 additions and 0 deletions

View File

@@ -314,6 +314,11 @@ Returns VERITAS if a strict majority of the arguments are VERITAS, FALSITAS othe
Returns the type of `value` as a string: `NVMERVS` (integer), `LITTERA` (string), `VERAX` (boolean), `CATALOGVS` (list), `FRACTIO` (fraction), `TABVLA` (dict), `FVNCTIO` (function), or `NVLLVS` (null).
### DORMI
`DORMI(n)`
Sleeps for `n` seconds, where `n` is an integer, fraction, or NVLLVS (treated as 0). Returns nothing meaningful.
## Modules
Modules are additions to the base `CENTVRION` syntax. They add or change certain features. Modules are included in your code by having

View File

@@ -1,5 +1,6 @@
import re
import random
import time
from fractions import Fraction
from rply.token import BaseBox
@@ -1128,6 +1129,18 @@ class BuiltIn(Node):
ValFunc: "FVNCTIO", ValNul: "NVLLVS",
}
return vtable, ValStr(type_map[type(params[0])])
case "DORMI":
v = params[0]
if isinstance(v, ValNul):
seconds = 0
elif isinstance(v, ValInt):
seconds = v.value()
elif isinstance(v, ValFrac):
seconds = float(v.value())
else:
raise CentvrionError("DORMI requires a number or NVLLVS")
time.sleep(seconds)
return vtable, ValNul()
case _:
raise NotImplementedError(self.builtin)

View File

@@ -258,6 +258,10 @@ def _emit_builtin(node, ctx):
case "TYPVS":
lines.append(f"CentValue {tmp} = cent_typvs({param_vars[0]});")
case "DORMI":
lines.append(f"cent_dormi({param_vars[0]});")
lines.append(f"CentValue {tmp} = cent_null();")
case _:
raise NotImplementedError(node.builtin)

View File

@@ -559,6 +559,23 @@ CentValue cent_typvs(CentValue v) {
return cent_str("IGNOTA"); /* unreachable */
}
void cent_dormi(CentValue n) {
struct timespec ts;
if (n.type == CENT_NULL) {
ts.tv_sec = 0; ts.tv_nsec = 0;
} else if (n.type == CENT_INT) {
ts.tv_sec = n.ival; ts.tv_nsec = 0;
} else if (n.type == CENT_FRAC) {
long sec = n.fval.num / n.fval.den;
long rem = n.fval.num % n.fval.den;
ts.tv_sec = sec;
ts.tv_nsec = rem * 1000000000L / n.fval.den;
} else {
cent_type_error("'DORMI' requires a number or NVLLVS");
}
nanosleep(&ts, NULL);
}
CentValue cent_fortis_numerus(CentValue lo, CentValue hi) {
if (lo.type != CENT_INT || hi.type != CENT_INT)
cent_type_error("'FORTIS_NVMERVS' requires two integers");

View File

@@ -221,6 +221,7 @@ void cent_semen(CentValue seed); /* SEMEN */
void cent_everro(void); /* EVERRO */
CentValue cent_senatus(CentValue *args, int n); /* SENATVS */
CentValue cent_typvs(CentValue v); /* TYPVS */
void cent_dormi(CentValue n); /* DORMI */
/* ------------------------------------------------------------------ */
/* Array helpers */

View File

@@ -44,6 +44,7 @@ builtin_tokens = [("BUILTIN", i) for i in [
"CLAVES",
"DECIMATIO",
"DICE",
"DORMI",
"EVERRO",
"FORTIS_NVMERVS",
"FORTIS_ELECTIONIS",

View File

@@ -2,6 +2,7 @@ import os
import random
import subprocess
import tempfile
import time
import unittest
from io import StringIO
from unittest.mock import patch
@@ -2217,5 +2218,93 @@ class TestFvnctio(unittest.TestCase):
run_test(self, source, nodes, value, output)
# --- DORMI ---
dormi_tests = [
("DORMI(NVLLVS)",
Program([], [ExpressionStatement(BuiltIn("DORMI", [Nullus()]))]),
ValNul()),
]
class TestDormi(unittest.TestCase):
@parameterized.expand(dormi_tests)
def test_dormi(self, source, nodes, value, output=""):
run_test(self, source, nodes, value, output)
def test_dormi_timing_int(self):
source = "DORMI(I)\n"
lexer = Lexer().get_lexer()
tokens = lexer.lex(source)
program = Parser().parse(tokens)
start = time.time()
program.eval()
elapsed = time.time() - start
self.assertAlmostEqual(elapsed, 1.0, delta=0.5)
def test_dormi_timing_int_compiled(self):
source = "DORMI(I)\n"
lexer = Lexer().get_lexer()
tokens = lexer.lex(source)
program = Parser().parse(tokens)
c_source = compile_program(program)
with tempfile.NamedTemporaryFile(suffix=".c", delete=False, mode="w") as tmp_c:
tmp_c.write(c_source)
tmp_c_path = tmp_c.name
with tempfile.NamedTemporaryFile(suffix="", delete=False) as tmp_bin:
tmp_bin_path = tmp_bin.name
try:
subprocess.run(
["gcc", "-O2", tmp_c_path, _RUNTIME_C, "-o", tmp_bin_path],
check=True, capture_output=True,
)
start = time.time()
proc = subprocess.run([tmp_bin_path], capture_output=True, text=True)
elapsed = time.time() - start
self.assertEqual(proc.returncode, 0)
self.assertAlmostEqual(elapsed, 1.0, delta=0.5)
finally:
os.unlink(tmp_c_path)
os.unlink(tmp_bin_path)
def test_dormi_timing_frac(self):
source = "CVM FRACTIO\nDORMI(S)\n"
lexer = Lexer().get_lexer()
tokens = lexer.lex(source)
program = Parser().parse(tokens)
start = time.time()
program.eval()
elapsed = time.time() - start
self.assertAlmostEqual(elapsed, 0.5, delta=0.5)
def test_dormi_timing_frac_compiled(self):
source = "CVM FRACTIO\nDORMI(S)\n"
lexer = Lexer().get_lexer()
tokens = lexer.lex(source)
program = Parser().parse(tokens)
c_source = compile_program(program)
with tempfile.NamedTemporaryFile(suffix=".c", delete=False, mode="w") as tmp_c:
tmp_c.write(c_source)
tmp_c_path = tmp_c.name
with tempfile.NamedTemporaryFile(suffix="", delete=False) as tmp_bin:
tmp_bin_path = tmp_bin.name
try:
subprocess.run(
["gcc", "-O2", tmp_c_path, _RUNTIME_C, "-o", tmp_bin_path],
check=True, capture_output=True,
)
start = time.time()
proc = subprocess.run([tmp_bin_path], capture_output=True, text=True)
elapsed = time.time() - start
self.assertEqual(proc.returncode, 0)
self.assertAlmostEqual(elapsed, 0.5, delta=0.5)
finally:
os.unlink(tmp_c_path)
os.unlink(tmp_bin_path)
if __name__ == "__main__":
unittest.main()