js-on-sx: ASI — :nl token flag + return restricted production (525/526 unit, 148/148 slice)
Lexer: adds :nl (newline-before) boolean to every token. scan! resets the flag before each skip-ws! call; skip-ws! sets it true when it consumes \n or \r. Parser: jp-token-nl? reads the flag; jp-parse-return-stmt stops before the expression when a newline precedes it (return\n42 → return undefined). Four new tests cover the restricted production and the raw flag. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -94,7 +94,7 @@
|
|||||||
(fn
|
(fn
|
||||||
(src)
|
(src)
|
||||||
(let
|
(let
|
||||||
((tokens (list)) (pos 0) (src-len (len src)))
|
((tokens (list)) (pos 0) (src-len (len src)) (nl-before false))
|
||||||
(define
|
(define
|
||||||
js-peek
|
js-peek
|
||||||
(fn
|
(fn
|
||||||
@@ -109,11 +109,7 @@
|
|||||||
(let
|
(let
|
||||||
((sl (len s)))
|
((sl (len s)))
|
||||||
(and (<= (+ pos sl) src-len) (= (slice src pos (+ pos sl)) s)))))
|
(and (<= (+ pos sl) src-len) (= (slice src pos (+ pos sl)) s)))))
|
||||||
(define
|
(define js-emit! (fn (type value start) (append! tokens {:pos start :value value :type type :nl nl-before})))
|
||||||
js-emit!
|
|
||||||
(fn
|
|
||||||
(type value start)
|
|
||||||
(append! tokens (js-make-token type value start))))
|
|
||||||
(define
|
(define
|
||||||
skip-line-comment!
|
skip-line-comment!
|
||||||
(fn
|
(fn
|
||||||
@@ -136,7 +132,13 @@
|
|||||||
()
|
()
|
||||||
(cond
|
(cond
|
||||||
((>= pos src-len) nil)
|
((>= 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) "/"))
|
((and (= (cur) "/") (< (+ pos 1) src-len) (= (js-peek 1) "/"))
|
||||||
(do (advance! 2) (skip-line-comment!) (skip-ws!)))
|
(do (advance! 2) (skip-line-comment!) (skip-ws!)))
|
||||||
((and (= (cur) "/") (< (+ pos 1) src-len) (= (js-peek 1) "*"))
|
((and (= (cur) "/") (< (+ pos 1) src-len) (= (js-peek 1) "*"))
|
||||||
@@ -568,6 +570,7 @@
|
|||||||
(fn
|
(fn
|
||||||
()
|
()
|
||||||
(do
|
(do
|
||||||
|
(set! nl-before false)
|
||||||
(skip-ws!)
|
(skip-ws!)
|
||||||
(when
|
(when
|
||||||
(< pos src-len)
|
(< pos src-len)
|
||||||
|
|||||||
@@ -835,6 +835,12 @@
|
|||||||
jp-eat-semi
|
jp-eat-semi
|
||||||
(fn (st) (if (jp-at? st "punct" ";") (do (jp-advance! st) nil) nil)))
|
(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
|
(define
|
||||||
jp-parse-vardecl
|
jp-parse-vardecl
|
||||||
(fn
|
(fn
|
||||||
@@ -1166,6 +1172,7 @@
|
|||||||
(or
|
(or
|
||||||
(jp-at? st "punct" ";")
|
(jp-at? st "punct" ";")
|
||||||
(jp-at? st "punct" "}")
|
(jp-at? st "punct" "}")
|
||||||
|
(jp-token-nl? st)
|
||||||
(jp-at? st "eof" nil))
|
(jp-at? st "eof" nil))
|
||||||
(do (jp-eat-semi st) (list (quote js-return) nil))
|
(do (jp-eat-semi st) (list (quote js-return) nil))
|
||||||
(let
|
(let
|
||||||
|
|||||||
@@ -1323,6 +1323,16 @@ cat > "$TMPFILE" << 'EPOCHS'
|
|||||||
(epoch 3505)
|
(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\")")
|
(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
|
EPOCHS
|
||||||
|
|
||||||
|
|
||||||
@@ -2042,6 +2052,12 @@ check 3503 "indexOf.call arrLike" '1'
|
|||||||
check 3504 "filter.call arrLike" '"2,3"'
|
check 3504 "filter.call arrLike" '"2,3"'
|
||||||
check 3505 "forEach.call arrLike sum" '60'
|
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))
|
TOTAL=$((PASS + FAIL))
|
||||||
if [ $FAIL -eq 0 ]; then
|
if [ $FAIL -eq 0 ]; then
|
||||||
echo "✓ $PASS/$TOTAL JS-on-SX tests passed"
|
echo "✓ $PASS/$TOTAL JS-on-SX tests passed"
|
||||||
|
|||||||
Reference in New Issue
Block a user