This commit is contained in:
2026-04-25 22:03:30 +02:00
parent ff1c392dd6
commit d2d09c770d
20 changed files with 782 additions and 16 deletions

View File

@@ -8,7 +8,7 @@ from tests._helpers import (
String, TemptaStatement, UnaryMinus, UnaryNot, Fractio, frac_to_fraction,
fraction_to_frac, num_to_int, int_to_num, make_string,
ValInt, ValStr, ValBool, ValList, ValDict, ValNul, ValFunc, ValFrac,
CentvrionError, _RUNTIME_C, _cent_rng,
CentvrionError, _RUNTIME_C, _IASON_C, _cent_rng,
Lexer, Parser, compile_program,
os, subprocess, tempfile, StringIO, patch,
)
@@ -160,6 +160,24 @@ error_tests = [
('IVNGE(["a"], II)', CentvrionError), # IVNGE second arg not a list
("IVNGE([VERITAS], [I])", CentvrionError), # IVNGE invalid key type (bool)
("IVNGE([[I]], [II])", CentvrionError), # IVNGE invalid key type (list)
("IASON_LEGE('null')", CentvrionError), # IASON module required for IASON_LEGE
("IASON_SCRIBE(NVLLVS)", CentvrionError), # IASON module required for IASON_SCRIBE
("CVM IASON\nIASON_LEGE(I)", CentvrionError), # IASON_LEGE non-string arg
("CVM IASON\nIASON_LEGE()", CentvrionError), # IASON_LEGE no args
("CVM IASON\nIASON_LEGE('null', 'null')", CentvrionError), # IASON_LEGE too many args
("CVM IASON\nIASON_LEGE('not json')", CentvrionError), # invalid JSON
("CVM IASON\nIASON_LEGE('[1,]')", CentvrionError), # trailing comma in array
("CVM IASON\nIASON_LEGE('{\"a\":}')", CentvrionError), # missing value in object
("CVM IASON\nIASON_LEGE('{\"a\" 1}')", CentvrionError), # missing colon in object
("CVM IASON\nIASON_LEGE('[1, 2')", CentvrionError), # unterminated array
("CVM IASON\nIASON_LEGE('{')", CentvrionError), # unterminated object
("CVM IASON\nIASON_LEGE('\"abc')", CentvrionError), # unterminated string
("CVM IASON\nIASON_LEGE('[1] junk')", CentvrionError), # trailing data
("CVM IASON\nIASON_LEGE('[\"a\\\\x\"]')", CentvrionError), # invalid escape
("CVM IASON\nIASON_SCRIBE()", CentvrionError), # IASON_SCRIBE no args
("CVM IASON\nIASON_SCRIBE(I, II)", CentvrionError), # IASON_SCRIBE too many args
('CVM IASON\nIASON_SCRIBE(IVNGE([I], ["v"]))', CentvrionError), # IASON_SCRIBE int dict keys
("CVM IASON\nIASON_SCRIBE(FVNCTIO (x) VT { REDI(x) })", CentvrionError), # IASON_SCRIBE function
]
class TestErrors(unittest.TestCase):
@@ -196,7 +214,7 @@ class TestErrorLineNumbers(unittest.TestCase):
tmp_bin_path = tmp_bin.name
try:
subprocess.run(
["gcc", "-O2", tmp_c_path, _RUNTIME_C, "-o", tmp_bin_path, "-lcurl", "-lmicrohttpd"],
["gcc", "-O2", tmp_c_path, _RUNTIME_C, _IASON_C, "-o", tmp_bin_path, "-lcurl", "-lmicrohttpd", "-lm"],
check=True, capture_output=True,
)
proc = subprocess.run([tmp_bin_path], capture_output=True, text=True)

173
tests/13_test_iason___.py Normal file
View File

@@ -0,0 +1,173 @@
from tests._helpers import (
unittest, parameterized, Fraction,
run_test,
Bool, BuiltIn, DataArray, DataDict, Designa, ExpressionStatement, ID,
ModuleCall, Nullus, Numeral, Program, String,
ValInt, ValStr, ValBool, ValList, ValDict, ValNul, ValFrac,
)
def _scribe(arg, modules=("IASON",)):
return Program(
[ModuleCall(m) for m in modules],
[ExpressionStatement(BuiltIn("IASON_SCRIBE", [arg]))],
)
def _lege(arg, modules=("IASON",)):
return Program(
[ModuleCall(m) for m in modules],
[ExpressionStatement(BuiltIn("IASON_LEGE", [String(arg)]))],
)
def _src_lege(arg, extra_modules=()):
modules = ("IASON",) + tuple(extra_modules)
prefix = "\n".join(f"CVM {m}" for m in modules) + "\n"
return prefix + f"IASON_LEGE('{arg}')"
def _src_scribe(arg_text, extra_modules=()):
modules = ("IASON",) + tuple(extra_modules)
prefix = "\n".join(f"CVM {m}" for m in modules) + "\n"
return prefix + f"IASON_SCRIBE({arg_text})"
iason_tests = [
# ---- Parse: scalars ----
(_src_lege("null"), _lege("null"), ValNul()),
(_src_lege("true"), _lege("true"), ValBool(True)),
(_src_lege("false"), _lege("false"), ValBool(False)),
(_src_lege("42"), _lege("42"), ValInt(42)),
(_src_lege('"hello"'), _lege('"hello"'), ValStr("hello")),
# ---- Parse: empty containers ----
(_src_lege("[]"), _lege("[]"), ValList([])),
(_src_lege("{}"), _lege("{}"), ValDict({})),
# ---- Parse: array of mixed types ----
(_src_lege('[1, true, null, "x"]'),
_lege('[1, true, null, "x"]'),
ValList([ValInt(1), ValBool(True), ValNul(), ValStr("x")])),
# ---- Parse: nested ----
(_src_lege('{"a": [1, 2], "b": {"c": 3}}'),
_lege('{"a": [1, 2], "b": {"c": 3}}'),
ValDict({
"a": ValList([ValInt(1), ValInt(2)]),
"b": ValDict({"c": ValInt(3)}),
})),
# ---- Parse: numbers ----
(_src_lege("-7"), _lege("-7"), ValInt(-7)),
(_src_lege("0"), _lege("0"), ValInt(0)),
# ---- Parse: string escapes ----
# NB: single-quoted CENTVRION strings unescape \n / \" / \\ before the
# JSON parser sees them, so direct parse tests for those escapes would
# have ambiguous semantics. Serialize tests below cover the inverse, and
# this \u test exercises the JSON parser's escape path.
(_src_lege('"\\u00e9"'),
_lege('"\\u00e9"'),
ValStr("é")),
# ---- Parse: float without FRACTIO floors ----
(_src_lege("3.7"), _lege("3.7"), ValInt(3)),
(_src_lege("-2.5"), _lege("-2.5"), ValInt(-3)),
(_src_lege("1e2"), _lege("1e2"), ValInt(100)),
# ---- Parse: float with FRACTIO is exact ----
(_src_lege("0.5", extra_modules=("FRACTIO",)),
_lege("0.5", modules=("IASON", "FRACTIO")),
ValFrac(Fraction(1, 2))),
(_src_lege("0.1", extra_modules=("FRACTIO",)),
_lege("0.1", modules=("IASON", "FRACTIO")),
ValFrac(Fraction(1, 10))),
(_src_lege("-0.25", extra_modules=("FRACTIO",)),
_lege("-0.25", modules=("IASON", "FRACTIO")),
ValFrac(Fraction(-1, 4))),
(_src_lege("5", extra_modules=("FRACTIO",)),
_lege("5", modules=("IASON", "FRACTIO")),
ValInt(5)),
(_src_lege("3.0", extra_modules=("FRACTIO",)),
_lege("3.0", modules=("IASON", "FRACTIO")),
ValInt(3)),
# ---- Serialize: scalars ----
(_src_scribe("NVLLVS"), _scribe(Nullus()), ValStr("null")),
(_src_scribe("VERITAS"), _scribe(Bool(True)), ValStr("true")),
(_src_scribe("FALSITAS"), _scribe(Bool(False)), ValStr("false")),
(_src_scribe("XLII"), _scribe(Numeral("XLII")), ValStr("42")),
(_src_scribe('"hello"'), _scribe(String("hello")), ValStr('"hello"')),
(_src_scribe("[]"), _scribe(DataArray([])), ValStr("[]")),
(_src_scribe("TABVLA {}"), _scribe(DataDict([])), ValStr("{}")),
# ---- Serialize: nested ----
(_src_scribe("[I, II, III]"),
_scribe(DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
ValStr("[1, 2, 3]")),
(_src_scribe('TABVLA {"a" VT I, "b" VT VERITAS}'),
_scribe(DataDict([(String("a"), Numeral("I")), (String("b"), Bool(True))])),
ValStr('{"a": 1, "b": true}')),
# ---- Serialize: special chars ----
(_src_scribe('"a\\nb"'),
_scribe(String("a\nb")),
ValStr('"a\\nb"')),
(_src_scribe('"a\\"b"'),
_scribe(String('a"b')),
ValStr('"a\\"b"')),
(_src_scribe('"a\\\\b"'),
_scribe(String("a\\b")),
ValStr('"a\\\\b"')),
# ---- Round-trip ----
("CVM IASON\nDIC(IASON_LEGE('[1, 2, 3]'))",
Program([ModuleCall("IASON")], [ExpressionStatement(BuiltIn("DIC",
[BuiltIn("IASON_LEGE", [String("[1, 2, 3]")])]))]),
ValStr("[I II III]"), "[I II III]\n"),
("CVM IASON\nDIC(IASON_SCRIBE(IASON_LEGE('{\"a\": [1, true, null]}')))",
Program([ModuleCall("IASON")], [ExpressionStatement(BuiltIn("DIC",
[BuiltIn("IASON_SCRIBE",
[BuiltIn("IASON_LEGE", [String('{"a": [1, true, null]}')])])]))]),
ValStr('{"a": [1, true, null]}'),
'{"a": [1, true, null]}\n'),
("CVM IASON\nCVM FRACTIO\nDIC(IASON_SCRIBE(IASON_LEGE('0.5')))",
Program([ModuleCall("IASON"), ModuleCall("FRACTIO")],
[ExpressionStatement(BuiltIn("DIC",
[BuiltIn("IASON_SCRIBE",
[BuiltIn("IASON_LEGE", [String("0.5")])])]))]),
ValStr("0.5"), "0.5\n"),
("CVM IASON\nCVM FRACTIO\nDIC(IASON_SCRIBE(IASON_LEGE('0.1')))",
Program([ModuleCall("IASON"), ModuleCall("FRACTIO")],
[ExpressionStatement(BuiltIn("DIC",
[BuiltIn("IASON_SCRIBE",
[BuiltIn("IASON_LEGE", [String("0.1")])])]))]),
ValStr("0.1"), "0.1\n"),
# ---- Serialize: insertion order preserved ----
(_src_scribe('TABVLA {"b" VT II, "a" VT I, "c" VT III}'),
_scribe(DataDict([
(String("b"), Numeral("II")),
(String("a"), Numeral("I")),
(String("c"), Numeral("III")),
])),
ValStr('{"b": 2, "a": 1, "c": 3}')),
# ---- Whitespace-tolerant parse ----
(_src_lege(" [ 1 , 2 ] "),
_lege(" [ 1 , 2 ] "),
ValList([ValInt(1), ValInt(2)])),
# ---- Unicode passes through serialize (ensure_ascii=False) ----
('CVM IASON\nDIC(IASON_SCRIBE("café"))',
Program([ModuleCall("IASON")], [ExpressionStatement(BuiltIn("DIC",
[BuiltIn("IASON_SCRIBE", [String("café")])]))]),
ValStr('"café"'), '"café"\n'),
]
class TestIason(unittest.TestCase):
@parameterized.expand(iason_tests)
def test_iason(self, source, nodes, value, output="", input_lines=[]):
run_test(self, source, nodes, value, output, input_lines)

View File

@@ -24,10 +24,12 @@ from centvrion.lexer import Lexer
from centvrion.parser import Parser
from centvrion.values import ValInt, ValStr, ValBool, ValList, ValDict, ValNul, ValFunc, ValFrac
_RUNTIME_C = os.path.join(
_RUNTIME_DIR = os.path.join(
os.path.dirname(__file__), "..",
"centvrion", "compiler", "runtime", "cent_runtime.c"
"centvrion", "compiler", "runtime"
)
_RUNTIME_C = os.path.join(_RUNTIME_DIR, "cent_runtime.c")
_IASON_C = os.path.join(_RUNTIME_DIR, "cent_iason.c")
def run_test(self, source, target_nodes, target_value, target_output="", input_lines=[]):
_cent_rng.seed(1)
@@ -92,7 +94,7 @@ def run_test(self, source, target_nodes, target_value, target_output="", input_l
tmp_bin_path = tmp_bin.name
try:
subprocess.run(
["gcc", "-O2", tmp_c_path, _RUNTIME_C, "-o", tmp_bin_path, "-lcurl", "-lmicrohttpd"],
["gcc", "-O2", tmp_c_path, _RUNTIME_C, _IASON_C, "-o", tmp_bin_path, "-lcurl", "-lmicrohttpd", "-lm"],
check=True, capture_output=True,
)
stdin_data = "".join(f"{l}\n" for l in input_lines)
@@ -124,7 +126,7 @@ def run_compiler_error_test(self, source):
tmp_bin_path = tmp_bin.name
try:
subprocess.run(
["gcc", "-O2", tmp_c_path, _RUNTIME_C, "-o", tmp_bin_path, "-lcurl", "-lmicrohttpd"],
["gcc", "-O2", tmp_c_path, _RUNTIME_C, _IASON_C, "-o", tmp_bin_path, "-lcurl", "-lmicrohttpd", "-lm"],
check=True, capture_output=True,
)
proc = subprocess.run([tmp_bin_path], capture_output=True, text=True)