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

@@ -2015,16 +2015,49 @@ def aser_list(expr, env):
# aser-fragment
def aser_fragment(children, env):
parts = filter(lambda x: (not sx_truthy(is_nil(x))), map(lambda c: aser(c, env), children))
parts = []
for c in children:
result = aser(c, env)
if sx_truthy((type_of(result) == 'list')):
for item in result:
if sx_truthy((not sx_truthy(is_nil(item)))):
parts.append(serialize(item))
else:
if sx_truthy((not sx_truthy(is_nil(result)))):
parts.append(serialize(result))
if sx_truthy(empty_p(parts)):
return ''
else:
return sx_str('(<> ', join(' ', map(serialize, parts)), ')')
return sx_str('(<> ', join(' ', parts), ')')
# aser-call
def aser_call(name, args, env):
_cells = {}
parts = [name]
reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin((_sx_begin(_sx_append(parts, sx_str(':', keyword_name(arg))), _sx_append(parts, serialize(val))) if sx_truthy((not sx_truthy(is_nil(val)))) else NIL), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(aser(nth(args, (get(state, 'i') + 1)), env)) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else (lambda val: _sx_begin((_sx_append(parts, serialize(val)) if sx_truthy((not sx_truthy(is_nil(val)))) else NIL), assoc(state, 'i', (get(state, 'i') + 1))))(aser(arg, env)))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
_cells['skip'] = False
_cells['i'] = 0
for arg in args:
if sx_truthy(_cells['skip']):
_cells['skip'] = False
_cells['i'] = (_cells['i'] + 1)
else:
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
val = aser(nth(args, (_cells['i'] + 1)), env)
if sx_truthy((not sx_truthy(is_nil(val)))):
parts.append(sx_str(':', keyword_name(arg)))
parts.append(serialize(val))
_cells['skip'] = True
_cells['i'] = (_cells['i'] + 1)
else:
val = aser(arg, env)
if sx_truthy((not sx_truthy(is_nil(val)))):
if sx_truthy((type_of(val) == 'list')):
for item in val:
if sx_truthy((not sx_truthy(is_nil(item)))):
parts.append(serialize(item))
else:
parts.append(serialize(val))
_cells['i'] = (_cells['i'] + 1)
return sx_str('(', join(' ', parts), ')')
# SPECIAL_FORM_NAMES
@@ -2753,4 +2786,4 @@ def render(expr, env=None):
def make_env(**kwargs):
"""Create an environment with initial bindings."""
return _Env(dict(kwargs))
return _Env(dict(kwargs))