Files
rose-ash/lib/js/transpile.sx
giles 9e568ad886 js-on-sx: baseline commit (278/280 unit, 148/148 slice, runner stub)
Initial commit of the lib/js/ tree and plans/ directory. A previous
session left template-string work in progress — 278/280 unit tests pass
(2 failing: tpl part-count off-by-one, escaped-backtick ident lookup).
test262-runner.py and scoreboard are placeholders (0/8 with 7 timeouts);
fixing the runner is the next queue item.
2026-04-23 19:42:16 +00:00

916 lines
29 KiB
Plaintext

;; lib/js/transpile.sx — JS AST → SX AST
;;
;; Produces SX trees the existing CEK/VM can evaluate directly.
;; Reuses lib/js/runtime.sx shims for JS-specific semantics
;; (coercions, prototype lookup, abstract equality, etc.).
;;
;; Input AST node shapes (from lib/js/parser.sx):
;; (js-num n) (js-str s) (js-bool b)
;; (js-null) (js-undef)
;; (js-ident "name")
;; (js-unop op expr)
;; (js-binop op l r)
;; (js-member obj "key") (js-index obj expr)
;; (js-call fn (args...))
;; (js-array (elts...)) (js-object ({:key :value}...))
;; (js-cond c t f)
;; (js-arrow (params...) body)
;; (js-assign op target rhs)
;;
;; Output is plain SX the evaluator can run, built with `list`/`cons`/
;; `make-symbol`. The entry point `js-eval-ast` calls `eval-expr` on
;; the transpiled tree.
;; ── tiny helpers ──────────────────────────────────────────────────
(define js-sym (fn (name) (make-symbol name)))
(define
js-tag?
(fn
(ast tag)
(and
(= (type-of ast) "list")
(not (empty? ast))
(= (type-of (first ast)) "symbol")
(= (symbol-name (first ast)) tag))))
(define js-ast-tag (fn (ast) (symbol-name (first ast))))
;; ── main dispatcher ───────────────────────────────────────────────
(define
js-transpile
(fn
(ast)
(cond
((= (type-of ast) "number") ast)
((= (type-of ast) "string") ast)
((= (type-of ast) "boolean") ast)
((= ast nil) ast)
((= (type-of ast) "list")
(cond
((empty? ast) (list))
((js-tag? ast "js-num") (nth ast 1))
((js-tag? ast "js-str") (nth ast 1))
((js-tag? ast "js-bool") (nth ast 1))
((js-tag? ast "js-null") nil)
((js-tag? ast "js-undef") (list (js-sym "quote") :js-undefined))
((js-tag? ast "js-ident") (js-transpile-ident (nth ast 1)))
((js-tag? ast "js-unop")
(js-transpile-unop (nth ast 1) (nth ast 2)))
((js-tag? ast "js-binop")
(js-transpile-binop (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-member")
(js-transpile-member (nth ast 1) (nth ast 2)))
((js-tag? ast "js-index")
(js-transpile-index (nth ast 1) (nth ast 2)))
((js-tag? ast "js-call")
(js-transpile-call (nth ast 1) (nth ast 2)))
((js-tag? ast "js-array") (js-transpile-array (nth ast 1)))
((js-tag? ast "js-object") (js-transpile-object (nth ast 1)))
((js-tag? ast "js-cond")
(js-transpile-cond (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-arrow")
(js-transpile-arrow (nth ast 1) (nth ast 2)))
((js-tag? ast "js-program") (js-transpile-stmts (nth ast 1)))
((js-tag? ast "js-block") (js-transpile-stmts (nth ast 1)))
((js-tag? ast "js-exprstmt") (js-transpile (nth ast 1)))
((js-tag? ast "js-empty") nil)
((js-tag? ast "js-var")
(js-transpile-var (nth ast 1) (nth ast 2)))
((js-tag? ast "js-if")
(js-transpile-if-stmt (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-while")
(js-transpile-while (nth ast 1) (nth ast 2)))
((js-tag? ast "js-do-while")
(js-transpile-do-while (nth ast 1) (nth ast 2)))
((js-tag? ast "js-for")
(js-transpile-for
(nth ast 1)
(nth ast 2)
(nth ast 3)
(nth ast 4)))
((js-tag? ast "js-return") (js-transpile-return (nth ast 1)))
((js-tag? ast "js-break") (js-transpile-break))
((js-tag? ast "js-continue") (js-transpile-continue))
((js-tag? ast "js-funcdecl")
(js-transpile-funcdecl (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-funcexpr")
(js-transpile-funcexpr (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-assign")
(js-transpile-assign (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-new")
(js-transpile-new (nth ast 1) (nth ast 2)))
((js-tag? ast "js-class")
(js-transpile-class (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-throw") (js-transpile-throw (nth ast 1)))
((js-tag? ast "js-try")
(js-transpile-try (nth ast 1) (nth ast 2) (nth ast 3)))
((js-tag? ast "js-await")
(list (js-sym "js-await-value") (js-transpile (nth ast 1))))
((js-tag? ast "js-funcdecl-async")
(js-transpile-funcdecl-async
(nth ast 1)
(nth ast 2)
(nth ast 3)))
((js-tag? ast "js-funcexpr-async")
(js-transpile-funcexpr-async
(nth ast 1)
(nth ast 2)
(nth ast 3)))
((js-tag? ast "js-arrow-async")
(js-transpile-arrow-async (nth ast 1) (nth ast 2)))
((js-tag? ast "js-tpl") (js-transpile-tpl (nth ast 1)))
(else
(error (str "js-transpile: unknown AST tag: " (js-ast-tag ast))))))
(else
(error (str "js-transpile: unexpected value type: " (type-of ast)))))))
;; ── Identifier lookup ─────────────────────────────────────────────
;; `undefined` in JS is really a global binding. If the parser emits
;; (js-undef) we handle that above. A bare `undefined` ident also maps
;; to the same sentinel.
(define
js-transpile-ident
(fn
(name)
(cond
((= name "undefined") (list (js-sym "quote") :js-undefined))
(else (js-sym name)))))
;; ── Unary ops ─────────────────────────────────────────────────────
(define
js-transpile-unop
(fn
(op arg)
(let
((a (js-transpile arg)))
(cond
((= op "-") (list (js-sym "js-neg") a))
((= op "+") (list (js-sym "js-pos") a))
((= op "!") (list (js-sym "js-not") a))
((= op "~") (list (js-sym "js-bitnot") a))
((= op "typeof") (list (js-sym "js-typeof") a))
((= op "void") (list (js-sym "quote") :js-undefined))
(else (error (str "js-transpile-unop: unsupported op: " op)))))))
;; ── Binary ops ────────────────────────────────────────────────────
(define
js-transpile-binop
(fn
(op l r)
(cond
((= op "+")
(list (js-sym "js-add") (js-transpile l) (js-transpile r)))
((= op "-")
(list (js-sym "js-sub") (js-transpile l) (js-transpile r)))
((= op "*")
(list (js-sym "js-mul") (js-transpile l) (js-transpile r)))
((= op "/")
(list (js-sym "js-div") (js-transpile l) (js-transpile r)))
((= op "%")
(list (js-sym "js-mod") (js-transpile l) (js-transpile r)))
((= op "**")
(list (js-sym "js-pow") (js-transpile l) (js-transpile r)))
((= op "===")
(list (js-sym "js-strict-eq") (js-transpile l) (js-transpile r)))
((= op "!==")
(list (js-sym "js-strict-neq") (js-transpile l) (js-transpile r)))
((= op "==")
(list (js-sym "js-loose-eq") (js-transpile l) (js-transpile r)))
((= op "!=")
(list (js-sym "js-loose-neq") (js-transpile l) (js-transpile r)))
((= op "<")
(list (js-sym "js-lt") (js-transpile l) (js-transpile r)))
((= op ">")
(list (js-sym "js-gt") (js-transpile l) (js-transpile r)))
((= op "<=")
(list (js-sym "js-le") (js-transpile l) (js-transpile r)))
((= op ">=")
(list (js-sym "js-ge") (js-transpile l) (js-transpile r)))
((= op "&&")
(list
(js-sym "js-and")
(js-transpile l)
(list (js-sym "fn") (list) (js-transpile r))))
((= op "||")
(list
(js-sym "js-or")
(js-transpile l)
(list (js-sym "fn") (list) (js-transpile r))))
((= op "instanceof")
(list (js-sym "js-instanceof") (js-transpile l) (js-transpile r)))
((= op "in")
(list (js-sym "js-in") (js-transpile l) (js-transpile r)))
((= op "??")
(list
(js-sym "let")
(list (list (js-sym "_a") (js-transpile l)))
(list
(js-sym "if")
(list
(js-sym "or")
(list (js-sym "=") (js-sym "_a") nil)
(list (js-sym "js-undefined?") (js-sym "_a")))
(js-transpile r)
(js-sym "_a"))))
(else (error (str "js-transpile-binop: unsupported op: " op))))))
;; ── Member / index ────────────────────────────────────────────────
(define
js-transpile-member
(fn (obj key) (list (js-sym "js-get-prop") (js-transpile obj) key)))
(define
js-transpile-index
(fn
(obj idx)
(list (js-sym "js-get-prop") (js-transpile obj) (js-transpile idx))))
;; ── Call ──────────────────────────────────────────────────────────
;; JS `f(a, b, c)` → `(f a b c)` after transpile. Works for both
;; identifier calls and computed callee (arrow fn, member access).
(define
js-transpile-call
(fn
(callee args)
(cond
((and (list? callee) (js-tag? callee "js-member"))
(let
((recv (js-transpile (nth callee 1))) (key (nth callee 2)))
(list
(js-sym "js-invoke-method")
recv
key
(cons (js-sym "list") (map js-transpile args)))))
((and (list? callee) (js-tag? callee "js-index"))
(let
((recv (js-transpile (nth callee 1)))
(key (js-transpile (nth callee 2))))
(list
(js-sym "js-invoke-method-dyn")
recv
key
(cons (js-sym "list") (map js-transpile args)))))
(else
(list
(js-sym "js-call-plain")
(js-transpile callee)
(cons (js-sym "list") (map js-transpile args)))))))
;; ── Array literal ─────────────────────────────────────────────────
(define
js-transpile-new
(fn
(callee args)
(list
(js-sym "js-new-call")
(js-transpile callee)
(cons (js-sym "list") (map js-transpile args)))))
;; ── Object literal ────────────────────────────────────────────────
;; Build a dict by `(dict)` + `dict-set!` inside a `let` that yields
;; the dict as its final expression. This keeps keys in JS insertion
;; order and allows computed values.
(define
js-transpile-array
(fn (elts) (cons (js-sym "list") (map js-transpile elts))))
;; ── Conditional ───────────────────────────────────────────────────
(define
js-transpile-object
(fn
(entries)
(list
(js-sym "let")
(list (list (js-sym "_obj") (list (js-sym "dict"))))
(cons
(js-sym "begin")
(append
(map
(fn
(entry)
(list
(js-sym "dict-set!")
(js-sym "_obj")
(get entry :key)
(js-transpile (get entry :value))))
entries)
(list (js-sym "_obj")))))))
;; ── Arrow function ────────────────────────────────────────────────
(define
js-transpile-cond
(fn
(c t f)
(list
(js-sym "if")
(list (js-sym "js-to-boolean") (js-transpile c))
(js-transpile t)
(js-transpile f))))
;; ── Assignment ────────────────────────────────────────────────────
;; `a = b` on an ident → (set! a b).
;; `a += b` on an ident → (set! a (js-add a b)).
;; `obj.k = v` / `obj[k] = v` → (js-set-prop obj "k" v).
(define
js-transpile-arrow
(fn
(params body)
(let
((param-syms (js-build-param-list params))
(inits (js-param-init-forms params))
(body-tr
(if
(and (list? body) (js-tag? body "js-block"))
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__return__"))
(cons
(js-sym "begin")
(append
inits
(append
(js-collect-funcdecls (nth body 1))
(js-transpile-stmt-list (nth body 1)))))))
(if
(empty? inits)
(js-transpile body)
(cons
(js-sym "begin")
(append inits (list (js-transpile body))))))))
(list (js-sym "fn") param-syms body-tr))))
(define
js-transpile-tpl
(fn
(parts)
(cond
((empty? parts) (list (quote quote) ""))
((= (len parts) 1)
(list (js-sym "js-to-string") (js-transpile (first parts))))
(else
(cons (js-sym "js-template-concat") (js-transpile-tpl-parts parts))))))
(define
js-transpile-tpl-parts
(fn
(parts)
(if
(empty? parts)
(list)
(cons
(js-transpile (first parts))
(js-transpile-tpl-parts (rest parts))))))
;; ── End-to-end entry points ───────────────────────────────────────
;; Transpile + eval a single JS expression string.
(define
js-transpile-assign
(fn
(op target rhs)
(cond
((js-tag? target "js-ident")
(let
((name (nth target 1)))
(let
((sxname (js-sym name)))
(cond
((= op "=") (list (js-sym "set!") sxname (js-transpile rhs)))
(else
(list
(js-sym "set!")
sxname
(js-compound-update op sxname (js-transpile rhs))))))))
((js-tag? target "js-member")
(list
(js-sym "js-set-prop")
(js-transpile (nth target 1))
(nth target 2)
(js-compound-update-or-plain
op
(js-transpile target)
(js-transpile rhs))))
((js-tag? target "js-index")
(list
(js-sym "js-set-prop")
(js-transpile (nth target 1))
(js-transpile (nth target 2))
(js-compound-update-or-plain
op
(js-transpile target)
(js-transpile rhs))))
(else (error "js-transpile-assign: unsupported target")))))
;; Transpile a JS expression string to SX source text (for inspection
;; in tests). Useful for asserting the exact emitted tree.
(define
js-compound-update
(fn
(op lhs-expr rhs-expr)
(cond
((= op "+=") (list (js-sym "js-add") lhs-expr rhs-expr))
((= op "-=") (list (js-sym "js-sub") lhs-expr rhs-expr))
((= op "*=") (list (js-sym "js-mul") lhs-expr rhs-expr))
((= op "/=") (list (js-sym "js-div") lhs-expr rhs-expr))
((= op "%=") (list (js-sym "js-mod") lhs-expr rhs-expr))
((= op "**=") (list (js-sym "js-pow") lhs-expr rhs-expr))
(else (error (str "js-compound-update: unsupported op: " op))))))
(define
js-compound-update-or-plain
(fn
(op lhs-expr rhs-expr)
(cond
((= op "=") rhs-expr)
(else (js-compound-update op lhs-expr rhs-expr)))))
(define
js-param-sym
(fn
(p)
(cond
((string? p) (js-sym p))
((and (list? p) (js-tag? p "js-param")) (js-sym (nth p 1)))
((and (list? p) (js-tag? p "js-rest")) (js-sym (nth p 1)))
(else (js-sym p)))))
(define
js-build-param-list
(fn
(params)
(cond
((empty? params) (list))
((and (list? (first params)) (js-tag? (first params) "js-rest"))
(list (js-sym "&rest") (js-sym (nth (first params) 1))))
(else
(cons
(js-param-sym (first params))
(js-build-param-list (rest params)))))))
(define
js-param-init-forms
(fn
(params)
(cond
((empty? params) (list))
((and (list? (first params)) (js-tag? (first params) "js-param"))
(let
((nm (js-sym (nth (first params) 1)))
(dv (js-transpile (nth (first params) 2))))
(cons
(list
(js-sym "set!")
nm
(list
(js-sym "if")
(list
(js-sym "or")
(list (js-sym "=") nm nil)
(list
(js-sym "=")
nm
(list (js-sym "quote") :js-undefined)))
dv
nm))
(js-param-init-forms (rest params)))))
(else (js-param-init-forms (rest params))))))
(define
js-transpile-stmts
(fn
(stmts)
(let
((hoisted (js-collect-funcdecls stmts)))
(let
((rest-stmts (js-transpile-stmt-list stmts)))
(cons (js-sym "begin") (append hoisted rest-stmts))))))
(define
js-collect-funcdecls
(fn
(stmts)
(cond
((empty? stmts) (list))
((and (list? (first stmts)) (js-tag? (first stmts) "js-funcdecl"))
(cons
(js-transpile-funcdecl
(nth (first stmts) 1)
(nth (first stmts) 2)
(nth (first stmts) 3))
(js-collect-funcdecls (rest stmts))))
(else (js-collect-funcdecls (rest stmts))))))
(define
js-transpile-stmt-list
(fn
(stmts)
(cond
((empty? stmts) (list))
((and (list? (first stmts)) (js-tag? (first stmts) "js-funcdecl"))
(cons nil (js-transpile-stmt-list (rest stmts))))
(else
(cons
(js-transpile (first stmts))
(js-transpile-stmt-list (rest stmts)))))))
(define
js-transpile-var
(fn (kind decls) (cons (js-sym "begin") (js-vardecl-forms decls))))
(define
js-vardecl-forms
(fn
(decls)
(cond
((empty? decls) (list))
(else
(let
((d (first decls)))
(cons
(list
(js-sym "define")
(js-sym (nth d 1))
(js-transpile (nth d 2)))
(js-vardecl-forms (rest decls))))))))
(define
js-transpile-if-stmt
(fn
(c t e)
(let
((c-tr (list (js-sym "js-to-boolean") (js-transpile c)))
(t-tr (if (= t nil) nil (js-transpile t))))
(if
(= e nil)
(list (js-sym "if") c-tr t-tr nil)
(list (js-sym "if") c-tr t-tr (js-transpile e))))))
(define
js-transpile-while
(fn
(c body)
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__break__"))
(list
(js-sym "letrec")
(list
(list
(js-sym "__loop__")
(list
(js-sym "fn")
(list)
(list
(js-sym "if")
(list (js-sym "js-to-boolean") (js-transpile c))
(list
(js-sym "begin")
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__continue__"))
(js-transpile body)))
(list (js-sym "__loop__")))
nil))))
(list (js-sym "__loop__")))))))
(define
js-transpile-do-while
(fn
(body c)
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__break__"))
(list
(js-sym "letrec")
(list
(list
(js-sym "__loop__")
(list
(js-sym "fn")
(list)
(list
(js-sym "begin")
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__continue__"))
(js-transpile body)))
(list
(js-sym "if")
(list (js-sym "js-to-boolean") (js-transpile c))
(list (js-sym "__loop__"))
nil)))))
(list (js-sym "__loop__")))))))
(define
js-transpile-for
(fn
(init cond-ast step body)
(let
((init-form (if (= init nil) nil (js-transpile init)))
(cond-form
(if
(= cond-ast nil)
(list (js-sym "quote") true)
(list (js-sym "js-to-boolean") (js-transpile cond-ast))))
(step-form (if (= step nil) nil (js-transpile step)))
(body-tr (js-transpile body)))
(list
(js-sym "begin")
(if (= init-form nil) nil init-form)
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__break__"))
(list
(js-sym "letrec")
(list
(list
(js-sym "__loop__")
(list
(js-sym "fn")
(list)
(list
(js-sym "if")
cond-form
(list
(js-sym "begin")
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__continue__"))
body-tr))
(if (= step-form nil) nil step-form)
(list (js-sym "__loop__")))
nil))))
(list (js-sym "__loop__")))))))))
(define
js-transpile-return
(fn
(e)
(list
(js-sym "__return__")
(if (= e nil) (list (js-sym "quote") :js-undefined) (js-transpile e)))))
(define js-transpile-break (fn () (list (js-sym "__break__") nil)))
(define js-transpile-continue (fn () (list (js-sym "__continue__") nil)))
(define
js-transpile-funcdecl
(fn
(name params body)
(list
(js-sym "define")
(js-sym name)
(js-transpile-funcexpr name params body))))
(define
js-transpile-class
(fn
(name parent methods)
(let
((ctor-method (js-find-ctor methods))
(instance-methods (js-filter-methods methods "instance"))
(name-sym (js-sym name)))
(let
((ctor-body (if (= ctor-method nil) (if parent (js-default-ctor-with-super parent) (js-default-ctor-noop)) (js-transpile-funcexpr name (nth ctor-method 3) (nth ctor-method 4)))))
(cons
(js-sym "begin")
(append
(list (list (js-sym "define") name-sym ctor-body))
(list (list (js-sym "js-reset-ctor-proto!") name-sym))
(if
parent
(list
(list
(js-sym "dict-set!")
(list (js-sym "js-get-ctor-proto") name-sym)
"__proto__"
(list (js-sym "js-get-ctor-proto") (js-sym parent))))
(list))
(map
(fn
(m)
(let
((mname (nth m 2))
(mparams (nth m 3))
(mbody (nth m 4)))
(list
(js-sym "dict-set!")
(list (js-sym "js-get-ctor-proto") name-sym)
mname
(js-transpile-funcexpr mname mparams mbody))))
instance-methods)
(list
(list
(js-sym "dict-set!")
(list (js-sym "js-get-ctor-proto") name-sym)
"constructor"
name-sym))))))))
(define
js-find-ctor
(fn
(methods)
(cond
((empty? methods) nil)
((and (js-tag? (first methods) "js-method") (= (nth (first methods) 1) "instance") (= (nth (first methods) 2) "constructor"))
(first methods))
(else (js-find-ctor (rest methods))))))
(define
js-filter-methods
(fn
(methods kind)
(cond
((empty? methods) (list))
((and (js-tag? (first methods) "js-method") (= (nth (first methods) 1) kind) (not (= (nth (first methods) 2) "constructor")))
(cons (first methods) (js-filter-methods (rest methods) kind)))
(else (js-filter-methods (rest methods) kind)))))
(define
js-default-ctor-noop
(fn
()
(list
(js-sym "fn")
(list (js-sym "&rest") (js-sym "__args__"))
(list
(js-sym "let")
(list (list (js-sym "this") (list (js-sym "js-this"))))
:js-undefined))))
(define
js-default-ctor-with-super
(fn
(parent)
(list
(js-sym "fn")
(list (js-sym "&rest") (js-sym "__args__"))
(list
(js-sym "let")
(list (list (js-sym "this") (list (js-sym "js-this"))))
(list
(js-sym "js-call-with-this")
(js-sym "this")
(js-sym parent)
(js-sym "__args__"))))))
(define
js-transpile-throw
(fn (e) (list (js-sym "raise") (js-transpile e))))
(define
js-transpile-try
(fn
(body catch-part finally-part)
(let
((body-tr (js-transpile body)))
(let
((with-catch (cond ((= catch-part nil) body-tr) (else (let ((pname (nth catch-part 0)) (cbody (nth catch-part 1))) (list (js-sym "guard") (list (if (= pname nil) (js-sym "__exc__") (js-sym pname)) (list (js-sym "else") (js-transpile cbody))) body-tr))))))
(cond
((= finally-part nil) with-catch)
(else
(list
(js-sym "let")
(list (list (js-sym "__try_result__") with-catch))
(js-transpile finally-part)
(js-sym "__try_result__"))))))))
(define
js-transpile-funcexpr
(fn
(name params body)
(let
((param-syms (js-build-param-list params))
(inits (js-param-init-forms params))
(body-forms
(if
(and (list? body) (js-tag? body "js-block"))
(let
((hoisted (js-collect-funcdecls (nth body 1))))
(append hoisted (js-transpile-stmt-list (nth body 1))))
(list (js-transpile body)))))
(list
(js-sym "fn")
param-syms
(list
(js-sym "let")
(list (list (js-sym "this") (list (js-sym "js-this"))))
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__return__"))
(cons (js-sym "begin") (append inits body-forms)))))))))
(define
js-transpile-funcexpr-async
(fn
(name params body)
(let
((param-syms (js-build-param-list params))
(inits (js-param-init-forms params))
(body-forms
(if
(and (list? body) (js-tag? body "js-block"))
(let
((hoisted (js-collect-funcdecls (nth body 1))))
(append hoisted (js-transpile-stmt-list (nth body 1))))
(list (js-transpile body)))))
(list
(js-sym "fn")
param-syms
(list
(js-sym "let")
(list (list (js-sym "this") (list (js-sym "js-this"))))
(list
(js-sym "js-async-wrap")
(list
(js-sym "fn")
(list)
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__return__"))
(cons (js-sym "begin") (append inits body-forms)))))))))))
(define
js-transpile-funcdecl-async
(fn
(name params body)
(list
(js-sym "define")
(js-sym name)
(js-transpile-funcexpr-async name params body))))
(define
js-transpile-arrow-async
(fn
(params body)
(let
((param-syms (js-build-param-list params))
(inits (js-param-init-forms params))
(body-tr
(if
(and (list? body) (js-tag? body "js-block"))
(list
(js-sym "call/cc")
(list
(js-sym "fn")
(list (js-sym "__return__"))
(cons
(js-sym "begin")
(append
inits
(append
(js-collect-funcdecls (nth body 1))
(js-transpile-stmt-list (nth body 1)))))))
(if
(empty? inits)
(js-transpile body)
(cons
(js-sym "begin")
(append inits (list (js-transpile body))))))))
(list
(js-sym "fn")
param-syms
(list (js-sym "js-async-wrap") (list (js-sym "fn") (list) body-tr))))))
(define
js-eval
(fn
(src)
(let
((result (eval-expr (js-transpile (js-parse (js-tokenize src))))))
(js-drain-microtasks!)
result)))
(define js-compile-expr (fn (src) (js-transpile (js-parse-expr src))))