Fix aser list flattening bug, add wire format test suite (41 tests)

The sync aser-call in adapter-sx.sx didn't flatten list results from
map/filter in positional children — serialize(list) wrapped in parens
creating ((div ...) ...) which re-parses as an invalid call. Rewrote
aser-call from reduce to for-each (bootstrapper can't nest for-each
inside reduce lambdas) and added list flattening in both aser-call
and aser-fragment.

Also adds test-aser.sx (41 tests), render-sx platform function,
expanded test-render.sx (+7 map/filter children tests), and specs
async-eval-slot-inner in adapter-async.sx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 14:59:31 +00:00
parent c95e19dcf2
commit e843602ac9
7 changed files with 465 additions and 41 deletions

View File

@@ -134,6 +134,28 @@ def render_html(sx_source):
return result
# --- Render SX (aser) platform function ---
def render_sx(sx_source):
"""Parse SX source and serialize to SX wire format via the bootstrapped evaluator."""
try:
from shared.sx.ref.sx_ref import aser as _aser, serialize as _serialize
except ImportError:
raise RuntimeError("aser not available — sx_ref.py not built")
exprs = parse_all(sx_source)
render_env = dict(env)
result = ""
for expr in exprs:
val = _aser(expr, render_env)
if isinstance(val, str):
result += val
elif val is None or val is NIL:
pass
else:
result += _serialize(val)
return result
# --- Signal platform primitives ---
# Implements the signal runtime platform interface for testing signals.sx
@@ -258,6 +280,7 @@ SPECS = {
"parser": {"file": "test-parser.sx", "needs": ["sx-parse"]},
"router": {"file": "test-router.sx", "needs": []},
"render": {"file": "test-render.sx", "needs": ["render-html"]},
"aser": {"file": "test-aser.sx", "needs": ["render-sx"]},
"deps": {"file": "test-deps.sx", "needs": []},
"engine": {"file": "test-engine.sx", "needs": []},
"orchestration": {"file": "test-orchestration.sx", "needs": []},
@@ -296,8 +319,9 @@ env = _Env({
"make-keyword": make_keyword,
"symbol-name": symbol_name,
"keyword-name": keyword_name,
# Render platform function
# Render platform functions
"render-html": render_html,
"render-sx": render_sx,
# Extra primitives needed by spec modules (router.sx, deps.sx)
"for-each-indexed": "_deferred", # replaced below
"dict-set!": "_deferred",
@@ -773,9 +797,9 @@ def main():
print(f"# --- {spec_name} ---")
eval_file(spec["file"], env)
# Reset render state after render tests to avoid leaking
# Reset render state after render/aser tests to avoid leaking
# into subsequent specs (bootstrapped evaluator checks render_active)
if spec_name == "render":
if spec_name in ("render", "aser"):
try:
from shared.sx.ref.sx_ref import set_render_active_b
set_render_active_b(False)