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:
@@ -106,35 +106,56 @@
|
||||
(define aser-fragment
|
||||
(fn (children env)
|
||||
;; Serialize (<> child1 child2 ...) to sx source string
|
||||
(let ((parts (filter
|
||||
(fn (x) (not (nil? x)))
|
||||
(map (fn (c) (aser c env)) children))))
|
||||
;; Must flatten list results (e.g. from map/filter) to avoid nested parens
|
||||
(let ((parts (list)))
|
||||
(for-each
|
||||
(fn (c)
|
||||
(let ((result (aser c env)))
|
||||
(if (= (type-of result) "list")
|
||||
(for-each
|
||||
(fn (item)
|
||||
(when (not (nil? item))
|
||||
(append! parts (serialize item))))
|
||||
result)
|
||||
(when (not (nil? result))
|
||||
(append! parts (serialize result))))))
|
||||
children)
|
||||
(if (empty? parts)
|
||||
""
|
||||
(str "(<> " (join " " (map serialize parts)) ")")))))
|
||||
(str "(<> " (join " " parts) ")")))))
|
||||
|
||||
|
||||
(define aser-call
|
||||
(fn (name args env)
|
||||
;; Serialize (name :key val child ...) — evaluate args but keep as sx
|
||||
(let ((parts (list name)))
|
||||
(reduce
|
||||
(fn (state arg)
|
||||
(let ((skip (get state "skip")))
|
||||
(if skip
|
||||
(assoc state "skip" false "i" (inc (get state "i")))
|
||||
(if (and (= (type-of arg) "keyword")
|
||||
(< (inc (get state "i")) (len args)))
|
||||
(let ((val (aser (nth args (inc (get state "i"))) env)))
|
||||
(when (not (nil? val))
|
||||
(append! parts (str ":" (keyword-name arg)))
|
||||
(append! parts (serialize val)))
|
||||
(assoc state "skip" true "i" (inc (get state "i"))))
|
||||
(let ((val (aser arg env)))
|
||||
(when (not (nil? val))
|
||||
(append! parts (serialize val)))
|
||||
(assoc state "i" (inc (get state "i"))))))))
|
||||
(dict "i" 0 "skip" false)
|
||||
;; Uses for-each + mutable state (not reduce) so bootstrapper emits for-loops
|
||||
;; that can contain nested for-each for list flattening.
|
||||
(let ((parts (list name))
|
||||
(skip false)
|
||||
(i 0))
|
||||
(for-each
|
||||
(fn (arg)
|
||||
(if skip
|
||||
(do (set! skip false)
|
||||
(set! i (inc i)))
|
||||
(if (and (= (type-of arg) "keyword")
|
||||
(< (inc i) (len args)))
|
||||
(let ((val (aser (nth args (inc i)) env)))
|
||||
(when (not (nil? val))
|
||||
(append! parts (str ":" (keyword-name arg)))
|
||||
(append! parts (serialize val)))
|
||||
(set! skip true)
|
||||
(set! i (inc i)))
|
||||
(let ((val (aser arg env)))
|
||||
(when (not (nil? val))
|
||||
(if (= (type-of val) "list")
|
||||
(for-each
|
||||
(fn (item)
|
||||
(when (not (nil? item))
|
||||
(append! parts (serialize item))))
|
||||
val)
|
||||
(append! parts (serialize val))))
|
||||
(set! i (inc i))))))
|
||||
args)
|
||||
(str "(" (join " " parts) ")"))))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user