From 11315d91cc33cf0d05a4d0359c9976fa190fef9d Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 25 Apr 2026 12:18:42 +0000 Subject: [PATCH] =?UTF-8?q?js-on-sx:=20var=20hoisting=20=E2=80=94=20hoist?= =?UTF-8?q?=20var=20names=20as=20undefined=20before=20funcdecls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/js/test.sh | 14 +++++++++++++ lib/js/transpile.sx | 51 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/lib/js/test.sh b/lib/js/test.sh index 751da07b..9080735e 100755 --- a/lib/js/test.sh +++ b/lib/js/test.sh @@ -1333,6 +1333,15 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 4203) (eval "(let ((toks (js-tokenize \"a b\"))) (get (nth toks 1) :nl))") +(epoch 4300) +(eval "(js-eval \"var x = 5; x\")") +(epoch 4301) +(eval "(js-eval \"function f() { return x; var x = 42; } f()\")") +(epoch 4302) +(eval "(js-eval \"function f() { var y = 7; return y; } f()\")") +(epoch 4303) +(eval "(js-eval \"function f() { var z; z = 3; return z; } f()\")") + EPOCHS @@ -2058,6 +2067,11 @@ check 4201 "return+space+val → val" '42' check 4202 "nl-before flag set after newline" 'true' check 4203 "nl-before flag false on same line" 'false' +check 4300 "var decl program-level" '5' +check 4301 "var hoisted before use → undef" '"js-undefined"' +check 4302 "var in function body" '7' +check 4303 "var then set in function" '3' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "✓ $PASS/$TOTAL JS-on-SX tests passed" diff --git a/lib/js/transpile.sx b/lib/js/transpile.sx index 619d796f..69c6c4e3 100644 --- a/lib/js/transpile.sx +++ b/lib/js/transpile.sx @@ -486,6 +486,51 @@ (append inits (list (js-transpile body)))))))) (list (js-sym "fn") param-syms body-tr)))) +(define + js-collect-var-decl-names + (fn + (decls) + (cond + ((empty? decls) (list)) + ((js-tag? (first decls) "js-vardecl") + (cons + (nth (first decls) 1) + (js-collect-var-decl-names (rest decls)))) + (else (js-collect-var-decl-names (rest decls)))))) + +(define + js-collect-var-names + (fn + (stmts) + (cond + ((empty? stmts) (list)) + ((and (list? (first stmts)) (js-tag? (first stmts) "js-var") (= (nth (first stmts) 1) "var")) + (append + (js-collect-var-decl-names (nth (first stmts) 2)) + (js-collect-var-names (rest stmts)))) + (else (js-collect-var-names (rest stmts)))))) + +(define + js-dedup-names + (fn + (names seen) + (cond + ((empty? names) (list)) + ((some (fn (s) (= s (first names))) seen) + (js-dedup-names (rest names) seen)) + (else + (cons + (first names) + (js-dedup-names (rest names) (cons (first names) seen))))))) + +(define + js-var-hoist-forms + (fn + (names) + (map + (fn (name) (list (js-sym "define") (js-sym name) :js-undefined)) + names))) + (define js-transpile-tpl (fn @@ -876,7 +921,7 @@ (fn (stmts) (let - ((hoisted (js-collect-funcdecls stmts))) + ((hoisted (append (js-var-hoist-forms (js-dedup-names (js-collect-var-names stmts) (list))) (js-collect-funcdecls stmts)))) (let ((rest-stmts (js-transpile-stmt-list stmts))) (cons (js-sym "begin") (append hoisted rest-stmts)))))) @@ -1297,7 +1342,7 @@ (if (and (list? body) (js-tag? body "js-block")) (let - ((hoisted (js-collect-funcdecls (nth body 1)))) + ((hoisted (append (js-var-hoist-forms (js-dedup-names (js-collect-var-names (nth body 1)) (list))) (js-collect-funcdecls (nth body 1))))) (append hoisted (js-transpile-stmt-list (nth body 1)))) (list (js-transpile body))))) (list @@ -1333,7 +1378,7 @@ (if (and (list? body) (js-tag? body "js-block")) (let - ((hoisted (js-collect-funcdecls (nth body 1)))) + ((hoisted (append (js-var-hoist-forms (js-dedup-names (js-collect-var-names (nth body 1)) (list))) (js-collect-funcdecls (nth body 1))))) (append hoisted (js-transpile-stmt-list (nth body 1)))) (list (js-transpile body))))) (list