diff --git a/lib/js/lexer.sx b/lib/js/lexer.sx index 1e72bce1..abf28b75 100644 --- a/lib/js/lexer.sx +++ b/lib/js/lexer.sx @@ -94,7 +94,7 @@ (fn (src) (let - ((tokens (list)) (pos 0) (src-len (len src))) + ((tokens (list)) (pos 0) (src-len (len src)) (nl-before false)) (define js-peek (fn @@ -109,11 +109,7 @@ (let ((sl (len s))) (and (<= (+ pos sl) src-len) (= (slice src pos (+ pos sl)) s))))) - (define - js-emit! - (fn - (type value start) - (append! tokens (js-make-token type value start)))) + (define js-emit! (fn (type value start) (append! tokens {:pos start :value value :type type :nl nl-before}))) (define skip-line-comment! (fn @@ -136,7 +132,13 @@ () (cond ((>= pos src-len) nil) - ((js-ws? (cur)) (do (advance! 1) (skip-ws!))) + ((js-ws? (cur)) + (do + (when + (or (= (cur) "\n") (= (cur) "\r")) + (set! nl-before true)) + (advance! 1) + (skip-ws!))) ((and (= (cur) "/") (< (+ pos 1) src-len) (= (js-peek 1) "/")) (do (advance! 2) (skip-line-comment!) (skip-ws!))) ((and (= (cur) "/") (< (+ pos 1) src-len) (= (js-peek 1) "*")) @@ -568,6 +570,7 @@ (fn () (do + (set! nl-before false) (skip-ws!) (when (< pos src-len) diff --git a/lib/js/parser.sx b/lib/js/parser.sx index a3cc71ce..5b1d1bb7 100644 --- a/lib/js/parser.sx +++ b/lib/js/parser.sx @@ -835,6 +835,12 @@ jp-eat-semi (fn (st) (if (jp-at? st "punct" ";") (do (jp-advance! st) nil) nil))) +(define + jp-token-nl? + (fn + (st) + (let ((tok (jp-peek st))) (if tok (= (get tok :nl) true) false)))) + (define jp-parse-vardecl (fn @@ -1166,6 +1172,7 @@ (or (jp-at? st "punct" ";") (jp-at? st "punct" "}") + (jp-token-nl? st) (jp-at? st "eof" nil)) (do (jp-eat-semi st) (list (quote js-return) nil)) (let diff --git a/lib/js/test.sh b/lib/js/test.sh index de6caea5..751da07b 100755 --- a/lib/js/test.sh +++ b/lib/js/test.sh @@ -1323,6 +1323,16 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 3505) (eval "(js-eval \"var a = {length: 3, 0: 10, 1: 20, 2: 30}; var sum = 0; Array.prototype.forEach.call(a, function(x){sum += x;}); sum\")") +;; ── Phase 1.ASI: automatic semicolon insertion ───────────────── +(epoch 4200) +(eval "(js-eval \"function f() { return\n42\n} f()\")") +(epoch 4201) +(eval "(js-eval \"function g() { return 42 } g()\")") +(epoch 4202) +(eval "(let ((toks (js-tokenize \"a\nb\"))) (get (nth toks 1) :nl))") +(epoch 4203) +(eval "(let ((toks (js-tokenize \"a b\"))) (get (nth toks 1) :nl))") + EPOCHS @@ -2042,6 +2052,12 @@ check 3503 "indexOf.call arrLike" '1' check 3504 "filter.call arrLike" '"2,3"' check 3505 "forEach.call arrLike sum" '60' +# ── Phase 1.ASI: automatic semicolon insertion ──────────────────── +check 4200 "return+newline → undefined" '"js-undefined"' +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' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "✓ $PASS/$TOTAL JS-on-SX tests passed"