Compare commits
139 Commits
loops/ocam
...
loops/js
| Author | SHA1 | Date | |
|---|---|---|---|
| 154e2297fe | |||
| 7c229eb321 | |||
| 01d0e97706 | |||
| a8596bd090 | |||
| 9d364a0c20 | |||
| dfb660073e | |||
| 7f5b77415f | |||
| 29a3fb4bc2 | |||
| 019a0c6105 | |||
| 1e29bba1be | |||
| 0142d69212 | |||
| e93e1eeab1 | |||
| 551c24c5a0 | |||
| 85414df868 | |||
| 237ea5ce84 | |||
| df4aa8eb0a | |||
| 5bb65d8315 | |||
| fb8bb9f105 | |||
| 769559bae7 | |||
| 836b31a5b6 | |||
| d7cc6d1b39 | |||
| df5e36aa5e | |||
| 8a06c2d72b | |||
| 058dcd5600 | |||
| 1a34cc4456 | |||
| ad897122d7 | |||
| ce067e32a4 | |||
| 25b30788b4 | |||
| f15a8d8fef | |||
| b4f7f814be | |||
| dedb82565b | |||
| 7d575cb1fe | |||
| 793eccfce2 | |||
| ada7a147e5 | |||
| 65f3b6fcc0 | |||
| f256132eb3 | |||
| d4be87166b | |||
| 0655b942a5 | |||
| 1fef6ec94d | |||
| 87bf3711c4 | |||
| 8ae7187c55 | |||
| 3d821d1290 | |||
| d1482482ff | |||
| 72be94c900 | |||
| 7fc37abe02 | |||
| 802544fdc6 | |||
| 699b30ed1b | |||
| 16e21ef6fa | |||
| cd014cdb29 | |||
| adc4cb89c6 | |||
| 4481f5f98b | |||
| b59f08a1b8 | |||
| 3e8aae77d5 | |||
| d145532afe | |||
| 86f7a351fb | |||
| e4c92a19d4 | |||
| 21d0be58ec | |||
| 5632830118 | |||
| dcde14a471 | |||
| cb272317bc | |||
| 013ce15357 | |||
| 76d6528c51 | |||
| 41dbac55b8 | |||
| 9bf4bd6180 | |||
| 141795449a | |||
| a6793fa656 | |||
| e5709c5aec | |||
| 0b7d88bbe1 | |||
| b57f40db63 | |||
| c8ab505c32 | |||
| 7c63fd8a7f | |||
| c45a2b34a0 | |||
| 20997d3360 | |||
| a1030dce5d | |||
| 0d9c45176b | |||
| d8b8de6195 | |||
| ecae58316f | |||
| 1bff28e99e | |||
| 5b501f7937 | |||
| 0d99b5dfe8 | |||
| a8d0dfb38a | |||
| ee422f3d15 | |||
| f0dffd275d | |||
| 47e68454ad | |||
| 0b4f5e1df9 | |||
| 4ab79f5758 | |||
| b7627b4102 | |||
| d51ae65bbb | |||
| e97bdc4602 | |||
| f03aa3056d | |||
| 4c11c4e1b9 | |||
| 88b3db2e9f | |||
| 082749f0a9 | |||
| 7a898567e4 | |||
| 1b7bb5ad1f | |||
| bfec2a4320 | |||
| b1023f11d9 | |||
| 16f7a14506 | |||
| 0cfaeb9136 | |||
| 8d9ce7838d | |||
| fb0ca374a3 | |||
| d676bcb6b7 | |||
| 9b07f97341 | |||
| 0df2b1c7b2 | |||
| 24a67fae97 | |||
| b9dc69a3c1 | |||
| c8f9b8be06 | |||
| 82100603f0 | |||
| 06a5b5b07c | |||
| 2490c901bf | |||
| 27bfceb1aa | |||
| 96a7541d70 | |||
| 42cce5e3fc | |||
| 2d475f95d1 | |||
| 11612a511b | |||
| 5f97e78d5f | |||
| f4b0ebf353 | |||
| 95fb5ef8ef | |||
| 843c3a7e5e | |||
| cf0ba8a02a | |||
| 4e554113a9 | |||
| c81e3f3705 | |||
| 66f13c95d5 | |||
| 081f934cad | |||
| 89f1c0ccbe | |||
| 066ddcd6e1 | |||
| f93b13e861 | |||
| 97180b4aa3 | |||
| ea63b6d9bb | |||
| 5d7f931cf1 | |||
| 79f3e1ada2 | |||
| 4d00250233 | |||
| 80c21cbabb | |||
| 70f91ef3d8 | |||
| 5f38e49ba4 | |||
| 0f9d361a92 | |||
| 11315d91cc | |||
| f16e1b69c0 | |||
| ae86579ae8 |
251
lib/js/lexer.sx
251
lib/js/lexer.sx
@@ -29,6 +29,16 @@
|
|||||||
(and (>= c "a") (<= c "f"))
|
(and (>= c "a") (<= c "f"))
|
||||||
(and (>= c "A") (<= c "F")))))
|
(and (>= c "A") (<= c "F")))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
js-hex-value
|
||||||
|
(fn
|
||||||
|
(c)
|
||||||
|
(cond
|
||||||
|
((and (>= c "0") (<= c "9")) (- (char-code c) 48))
|
||||||
|
((and (>= c "a") (<= c "f")) (- (char-code c) 87))
|
||||||
|
((and (>= c "A") (<= c "F")) (- (char-code c) 55))
|
||||||
|
(else 0))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-letter?
|
js-letter?
|
||||||
(fn (c) (or (and (>= c "a") (<= c "z")) (and (>= c "A") (<= c "Z")))))
|
(fn (c) (or (and (>= c "a") (<= c "z")) (and (>= c "A") (<= c "Z")))))
|
||||||
@@ -37,9 +47,9 @@
|
|||||||
|
|
||||||
(define js-ident-char? (fn (c) (or (js-ident-start? c) (js-digit? c))))
|
(define js-ident-char? (fn (c) (or (js-ident-start? c) (js-digit? c))))
|
||||||
|
|
||||||
|
;; ── Reserved words ────────────────────────────────────────────────
|
||||||
(define js-ws? (fn (c) (or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
(define js-ws? (fn (c) (or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
||||||
|
|
||||||
;; ── Reserved words ────────────────────────────────────────────────
|
|
||||||
(define
|
(define
|
||||||
js-keywords
|
js-keywords
|
||||||
(list
|
(list
|
||||||
@@ -86,15 +96,18 @@
|
|||||||
"await"
|
"await"
|
||||||
"of"))
|
"of"))
|
||||||
|
|
||||||
|
;; ── Main tokenizer ────────────────────────────────────────────────
|
||||||
(define js-keyword? (fn (word) (contains? js-keywords word)))
|
(define js-keyword? (fn (word) (contains? js-keywords word)))
|
||||||
|
|
||||||
;; ── Main tokenizer ────────────────────────────────────────────────
|
|
||||||
(define
|
(define
|
||||||
js-tokenize
|
js-tokenize
|
||||||
(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 +122,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 {:nl nl-before :type type :value value :pos start})))
|
||||||
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 +145,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) "*"))
|
||||||
@@ -254,11 +269,55 @@
|
|||||||
((= ch "b") (append! chars "\\b"))
|
((= ch "b") (append! chars "\\b"))
|
||||||
((= ch "f") (append! chars "\\f"))
|
((= ch "f") (append! chars "\\f"))
|
||||||
((= ch "v") (append! chars "\\v"))
|
((= ch "v") (append! chars "\\v"))
|
||||||
|
((= ch "u")
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(< (+ pos 4) src-len)
|
||||||
|
(js-hex-digit? (js-peek 1))
|
||||||
|
(js-hex-digit? (js-peek 2))
|
||||||
|
(js-hex-digit? (js-peek 3))
|
||||||
|
(js-hex-digit? (js-peek 4)))
|
||||||
|
(do
|
||||||
|
(append!
|
||||||
|
chars
|
||||||
|
(char-from-code
|
||||||
|
(+
|
||||||
|
(*
|
||||||
|
4096
|
||||||
|
(js-hex-value
|
||||||
|
(js-peek 1)))
|
||||||
|
(*
|
||||||
|
256
|
||||||
|
(js-hex-value
|
||||||
|
(js-peek 2)))
|
||||||
|
(*
|
||||||
|
16
|
||||||
|
(js-hex-value
|
||||||
|
(js-peek 3)))
|
||||||
|
(js-hex-value (js-peek 4)))))
|
||||||
|
(advance! 4))
|
||||||
|
(append! chars ch)))
|
||||||
|
((= ch "x")
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(< (+ pos 2) src-len)
|
||||||
|
(js-hex-digit? (js-peek 1))
|
||||||
|
(js-hex-digit? (js-peek 2)))
|
||||||
|
(do
|
||||||
|
(append!
|
||||||
|
chars
|
||||||
|
(char-from-code
|
||||||
|
(+
|
||||||
|
(* 16 (js-hex-value (js-peek 1)))
|
||||||
|
(js-hex-value (js-peek 2)))))
|
||||||
|
(advance! 2))
|
||||||
|
(append! chars ch)))
|
||||||
(else (append! chars ch)))
|
(else (append! chars ch)))
|
||||||
(advance! 1))))
|
(advance! 1))))
|
||||||
(loop)))
|
(loop)))
|
||||||
((= (cur) quote-char) (advance! 1))
|
((= (cur) quote-char) (advance! 1))
|
||||||
(else (do (append! chars (cur)) (advance! 1) (loop))))))
|
(else
|
||||||
|
(do (append! chars (cur)) (advance! 1) (loop))))))
|
||||||
(loop)
|
(loop)
|
||||||
(join "" chars))))
|
(join "" chars))))
|
||||||
(define
|
(define
|
||||||
@@ -289,7 +348,8 @@
|
|||||||
()
|
()
|
||||||
(cond
|
(cond
|
||||||
((>= pos src-len) nil)
|
((>= pos src-len) nil)
|
||||||
((and (= (cur) "}") (= depth 1)) (advance! 1))
|
((and (= (cur) "}") (= depth 1))
|
||||||
|
(advance! 1))
|
||||||
((= (cur) "}")
|
((= (cur) "}")
|
||||||
(do
|
(do
|
||||||
(append! buf (cur))
|
(append! buf (cur))
|
||||||
@@ -325,7 +385,9 @@
|
|||||||
(advance! 1)))
|
(advance! 1)))
|
||||||
(sloop)))
|
(sloop)))
|
||||||
((= (cur) q)
|
((= (cur) q)
|
||||||
(do (append! buf (cur)) (advance! 1)))
|
(do
|
||||||
|
(append! buf (cur))
|
||||||
|
(advance! 1)))
|
||||||
(else
|
(else
|
||||||
(do
|
(do
|
||||||
(append! buf (cur))
|
(append! buf (cur))
|
||||||
@@ -334,7 +396,10 @@
|
|||||||
(sloop)
|
(sloop)
|
||||||
(expr-loop))))
|
(expr-loop))))
|
||||||
(else
|
(else
|
||||||
(do (append! buf (cur)) (advance! 1) (expr-loop))))))
|
(do
|
||||||
|
(append! buf (cur))
|
||||||
|
(advance! 1)
|
||||||
|
(expr-loop))))))
|
||||||
(expr-loop)
|
(expr-loop)
|
||||||
(join "" buf))))
|
(join "" buf))))
|
||||||
(define
|
(define
|
||||||
@@ -376,14 +441,17 @@
|
|||||||
(else (append! chars ch)))
|
(else (append! chars ch)))
|
||||||
(advance! 1))))
|
(advance! 1))))
|
||||||
(loop)))
|
(loop)))
|
||||||
(else (do (append! chars (cur)) (advance! 1) (loop))))))
|
(else
|
||||||
|
(do (append! chars (cur)) (advance! 1) (loop))))))
|
||||||
(loop)
|
(loop)
|
||||||
(flush-chars!)
|
(flush-chars!)
|
||||||
(if
|
(if
|
||||||
(= (len parts) 0)
|
(= (len parts) 0)
|
||||||
""
|
""
|
||||||
(if
|
(if
|
||||||
(and (= (len parts) 1) (= (nth (nth parts 0) 0) "str"))
|
(and
|
||||||
|
(= (len parts) 1)
|
||||||
|
(= (nth (nth parts 0) 0) "str"))
|
||||||
(nth (nth parts 0) 1)
|
(nth (nth parts 0) 1)
|
||||||
parts)))))
|
parts)))))
|
||||||
(define
|
(define
|
||||||
@@ -399,7 +467,7 @@
|
|||||||
((ty (dict-get tk "type")) (vv (dict-get tk "value")))
|
((ty (dict-get tk "type")) (vv (dict-get tk "value")))
|
||||||
(cond
|
(cond
|
||||||
((= ty "punct")
|
((= ty "punct")
|
||||||
(and (not (= vv ")")) (not (= vv "]"))))
|
(and (not (= vv ")")) (not (= vv "]")) (not (= vv "}"))))
|
||||||
((= ty "op") true)
|
((= ty "op") true)
|
||||||
((= ty "keyword")
|
((= ty "keyword")
|
||||||
(contains?
|
(contains?
|
||||||
@@ -453,9 +521,13 @@
|
|||||||
(append! buf (cur))
|
(append! buf (cur))
|
||||||
(advance! 1)
|
(advance! 1)
|
||||||
(body-loop)))
|
(body-loop)))
|
||||||
((and (= (cur) "/") (not in-class)) (advance! 1))
|
((and (= (cur) "/") (not in-class))
|
||||||
|
(advance! 1))
|
||||||
(else
|
(else
|
||||||
(begin (append! buf (cur)) (advance! 1) (body-loop))))))
|
(begin
|
||||||
|
(append! buf (cur))
|
||||||
|
(advance! 1)
|
||||||
|
(body-loop))))))
|
||||||
(body-loop)
|
(body-loop)
|
||||||
(let
|
(let
|
||||||
((flags-buf (list)))
|
((flags-buf (list)))
|
||||||
@@ -470,7 +542,7 @@
|
|||||||
(advance! 1)
|
(advance! 1)
|
||||||
(flags-loop)))))
|
(flags-loop)))))
|
||||||
(flags-loop)
|
(flags-loop)
|
||||||
{:pattern (join "" buf) :flags (join "" flags-buf)}))))
|
{:flags (join "" flags-buf) :pattern (join "" buf)}))))
|
||||||
(define
|
(define
|
||||||
try-op-4!
|
try-op-4!
|
||||||
(fn
|
(fn
|
||||||
@@ -510,64 +582,113 @@
|
|||||||
(fn
|
(fn
|
||||||
(start)
|
(start)
|
||||||
(cond
|
(cond
|
||||||
((at? "==") (do (js-emit! "op" "==" start) (advance! 2) true))
|
((at? "==")
|
||||||
((at? "!=") (do (js-emit! "op" "!=" start) (advance! 2) true))
|
(do (js-emit! "op" "==" start) (advance! 2) true))
|
||||||
((at? "<=") (do (js-emit! "op" "<=" start) (advance! 2) true))
|
((at? "!=")
|
||||||
((at? ">=") (do (js-emit! "op" ">=" start) (advance! 2) true))
|
(do (js-emit! "op" "!=" start) (advance! 2) true))
|
||||||
((at? "&&") (do (js-emit! "op" "&&" start) (advance! 2) true))
|
((at? "<=")
|
||||||
((at? "||") (do (js-emit! "op" "||" start) (advance! 2) true))
|
(do (js-emit! "op" "<=" start) (advance! 2) true))
|
||||||
((at? "??") (do (js-emit! "op" "??" start) (advance! 2) true))
|
((at? ">=")
|
||||||
((at? "=>") (do (js-emit! "op" "=>" start) (advance! 2) true))
|
(do (js-emit! "op" ">=" start) (advance! 2) true))
|
||||||
((at? "**") (do (js-emit! "op" "**" start) (advance! 2) true))
|
((at? "&&")
|
||||||
((at? "<<") (do (js-emit! "op" "<<" start) (advance! 2) true))
|
(do (js-emit! "op" "&&" start) (advance! 2) true))
|
||||||
((at? ">>") (do (js-emit! "op" ">>" start) (advance! 2) true))
|
((at? "||")
|
||||||
((at? "++") (do (js-emit! "op" "++" start) (advance! 2) true))
|
(do (js-emit! "op" "||" start) (advance! 2) true))
|
||||||
((at? "--") (do (js-emit! "op" "--" start) (advance! 2) true))
|
((at? "??")
|
||||||
((at? "+=") (do (js-emit! "op" "+=" start) (advance! 2) true))
|
(do (js-emit! "op" "??" start) (advance! 2) true))
|
||||||
((at? "-=") (do (js-emit! "op" "-=" start) (advance! 2) true))
|
((at? "=>")
|
||||||
((at? "*=") (do (js-emit! "op" "*=" start) (advance! 2) true))
|
(do (js-emit! "op" "=>" start) (advance! 2) true))
|
||||||
((at? "/=") (do (js-emit! "op" "/=" start) (advance! 2) true))
|
((at? "**")
|
||||||
((at? "%=") (do (js-emit! "op" "%=" start) (advance! 2) true))
|
(do (js-emit! "op" "**" start) (advance! 2) true))
|
||||||
((at? "&=") (do (js-emit! "op" "&=" start) (advance! 2) true))
|
((at? "<<")
|
||||||
((at? "|=") (do (js-emit! "op" "|=" start) (advance! 2) true))
|
(do (js-emit! "op" "<<" start) (advance! 2) true))
|
||||||
((at? "^=") (do (js-emit! "op" "^=" start) (advance! 2) true))
|
((at? ">>")
|
||||||
((at? "?.") (do (js-emit! "op" "?." start) (advance! 2) true))
|
(do (js-emit! "op" ">>" start) (advance! 2) true))
|
||||||
|
((at? "++")
|
||||||
|
(do (js-emit! "op" "++" start) (advance! 2) true))
|
||||||
|
((at? "--")
|
||||||
|
(do (js-emit! "op" "--" start) (advance! 2) true))
|
||||||
|
((at? "+=")
|
||||||
|
(do (js-emit! "op" "+=" start) (advance! 2) true))
|
||||||
|
((at? "-=")
|
||||||
|
(do (js-emit! "op" "-=" start) (advance! 2) true))
|
||||||
|
((at? "*=")
|
||||||
|
(do (js-emit! "op" "*=" start) (advance! 2) true))
|
||||||
|
((at? "/=")
|
||||||
|
(do (js-emit! "op" "/=" start) (advance! 2) true))
|
||||||
|
((at? "%=")
|
||||||
|
(do (js-emit! "op" "%=" start) (advance! 2) true))
|
||||||
|
((at? "&=")
|
||||||
|
(do (js-emit! "op" "&=" start) (advance! 2) true))
|
||||||
|
((at? "|=")
|
||||||
|
(do (js-emit! "op" "|=" start) (advance! 2) true))
|
||||||
|
((at? "^=")
|
||||||
|
(do (js-emit! "op" "^=" start) (advance! 2) true))
|
||||||
|
((at? "?.")
|
||||||
|
(do (js-emit! "op" "?." start) (advance! 2) true))
|
||||||
(else false))))
|
(else false))))
|
||||||
(define
|
(define
|
||||||
emit-one-op!
|
emit-one-op!
|
||||||
(fn
|
(fn
|
||||||
(ch start)
|
(ch start)
|
||||||
(cond
|
(cond
|
||||||
((= ch "(") (do (js-emit! "punct" "(" start) (advance! 1)))
|
((= ch "(")
|
||||||
((= ch ")") (do (js-emit! "punct" ")" start) (advance! 1)))
|
(do (js-emit! "punct" "(" start) (advance! 1)))
|
||||||
((= ch "[") (do (js-emit! "punct" "[" start) (advance! 1)))
|
((= ch ")")
|
||||||
((= ch "]") (do (js-emit! "punct" "]" start) (advance! 1)))
|
(do (js-emit! "punct" ")" start) (advance! 1)))
|
||||||
((= ch "{") (do (js-emit! "punct" "{" start) (advance! 1)))
|
((= ch "[")
|
||||||
((= ch "}") (do (js-emit! "punct" "}" start) (advance! 1)))
|
(do (js-emit! "punct" "[" start) (advance! 1)))
|
||||||
((= ch ",") (do (js-emit! "punct" "," start) (advance! 1)))
|
((= ch "]")
|
||||||
((= ch ";") (do (js-emit! "punct" ";" start) (advance! 1)))
|
(do (js-emit! "punct" "]" start) (advance! 1)))
|
||||||
((= ch ":") (do (js-emit! "punct" ":" start) (advance! 1)))
|
((= ch "{")
|
||||||
((= ch ".") (do (js-emit! "punct" "." start) (advance! 1)))
|
(do (js-emit! "punct" "{" start) (advance! 1)))
|
||||||
((= ch "?") (do (js-emit! "op" "?" start) (advance! 1)))
|
((= ch "}")
|
||||||
((= ch "+") (do (js-emit! "op" "+" start) (advance! 1)))
|
(do (js-emit! "punct" "}" start) (advance! 1)))
|
||||||
((= ch "-") (do (js-emit! "op" "-" start) (advance! 1)))
|
((= ch ",")
|
||||||
((= ch "*") (do (js-emit! "op" "*" start) (advance! 1)))
|
(do (js-emit! "punct" "," start) (advance! 1)))
|
||||||
((= ch "/") (do (js-emit! "op" "/" start) (advance! 1)))
|
((= ch ";")
|
||||||
((= ch "%") (do (js-emit! "op" "%" start) (advance! 1)))
|
(do (js-emit! "punct" ";" start) (advance! 1)))
|
||||||
((= ch "=") (do (js-emit! "op" "=" start) (advance! 1)))
|
((= ch ":")
|
||||||
((= ch "<") (do (js-emit! "op" "<" start) (advance! 1)))
|
(do (js-emit! "punct" ":" start) (advance! 1)))
|
||||||
((= ch ">") (do (js-emit! "op" ">" start) (advance! 1)))
|
((= ch ".")
|
||||||
((= ch "!") (do (js-emit! "op" "!" start) (advance! 1)))
|
(do (js-emit! "punct" "." start) (advance! 1)))
|
||||||
((= ch "&") (do (js-emit! "op" "&" start) (advance! 1)))
|
((= ch "?")
|
||||||
((= ch "|") (do (js-emit! "op" "|" start) (advance! 1)))
|
(do (js-emit! "op" "?" start) (advance! 1)))
|
||||||
((= ch "^") (do (js-emit! "op" "^" start) (advance! 1)))
|
((= ch "+")
|
||||||
((= ch "~") (do (js-emit! "op" "~" start) (advance! 1)))
|
(do (js-emit! "op" "+" start) (advance! 1)))
|
||||||
|
((= ch "-")
|
||||||
|
(do (js-emit! "op" "-" start) (advance! 1)))
|
||||||
|
((= ch "*")
|
||||||
|
(do (js-emit! "op" "*" start) (advance! 1)))
|
||||||
|
((= ch "/")
|
||||||
|
(do (js-emit! "op" "/" start) (advance! 1)))
|
||||||
|
((= ch "%")
|
||||||
|
(do (js-emit! "op" "%" start) (advance! 1)))
|
||||||
|
((= ch "=")
|
||||||
|
(do (js-emit! "op" "=" start) (advance! 1)))
|
||||||
|
((= ch "<")
|
||||||
|
(do (js-emit! "op" "<" start) (advance! 1)))
|
||||||
|
((= ch ">")
|
||||||
|
(do (js-emit! "op" ">" start) (advance! 1)))
|
||||||
|
((= ch "!")
|
||||||
|
(do (js-emit! "op" "!" start) (advance! 1)))
|
||||||
|
((= ch "&")
|
||||||
|
(do (js-emit! "op" "&" start) (advance! 1)))
|
||||||
|
((= ch "|")
|
||||||
|
(do (js-emit! "op" "|" start) (advance! 1)))
|
||||||
|
((= ch "^")
|
||||||
|
(do (js-emit! "op" "^" start) (advance! 1)))
|
||||||
|
((= ch "~")
|
||||||
|
(do (js-emit! "op" "~" start) (advance! 1)))
|
||||||
|
((= ch "\\")
|
||||||
|
(error "Unexpected char '\\' in source"))
|
||||||
(else (advance! 1)))))
|
(else (advance! 1)))))
|
||||||
(define
|
(define
|
||||||
scan!
|
scan!
|
||||||
(fn
|
(fn
|
||||||
()
|
()
|
||||||
(do
|
(do
|
||||||
|
(set! nl-before false)
|
||||||
(skip-ws!)
|
(skip-ws!)
|
||||||
(when
|
(when
|
||||||
(< pos src-len)
|
(< pos src-len)
|
||||||
|
|||||||
253
lib/js/parser.sx
253
lib/js/parser.sx
@@ -153,6 +153,32 @@
|
|||||||
(do (jp-advance! st) (list (quote js-ident) "this")))
|
(do (jp-advance! st) (list (quote js-ident) "this")))
|
||||||
((and (= (get t :type) "keyword") (= (get t :value) "new"))
|
((and (= (get t :type) "keyword") (= (get t :value) "new"))
|
||||||
(do (jp-advance! st) (jp-parse-new-expr st)))
|
(do (jp-advance! st) (jp-parse-new-expr st)))
|
||||||
|
((and (= (get t :type) "keyword") (= (get t :value) "function"))
|
||||||
|
(do
|
||||||
|
(jp-advance! st)
|
||||||
|
(let
|
||||||
|
((nm
|
||||||
|
(if
|
||||||
|
(= (get (jp-peek st) :type) "ident")
|
||||||
|
(let ((n (get (jp-peek st) :value))) (do (jp-advance! st) n))
|
||||||
|
nil)))
|
||||||
|
(let
|
||||||
|
((params (jp-parse-param-list st)))
|
||||||
|
(let
|
||||||
|
((body (jp-parse-fn-body st)))
|
||||||
|
(list (quote js-funcexpr) nm params body))))))
|
||||||
|
((and (= (get t :type) "keyword") (= (get t :value) "true"))
|
||||||
|
(do (jp-advance! st) (list (quote js-bool) true)))
|
||||||
|
((and (= (get t :type) "keyword") (= (get t :value) "false"))
|
||||||
|
(do (jp-advance! st) (list (quote js-bool) false)))
|
||||||
|
((and (= (get t :type) "keyword") (= (get t :value) "null"))
|
||||||
|
(do (jp-advance! st) (list (quote js-null))))
|
||||||
|
((and (= (get t :type) "keyword") (= (get t :value) "undefined"))
|
||||||
|
(do (jp-advance! st) (list (quote js-undef))))
|
||||||
|
((= (get t :type) "number")
|
||||||
|
(do (jp-advance! st) (list (quote js-num) (get t :value))))
|
||||||
|
((= (get t :type) "string")
|
||||||
|
(do (jp-advance! st) (list (quote js-str) (get t :value))))
|
||||||
((and (= (get t :type) "punct") (= (get t :value) "("))
|
((and (= (get t :type) "punct") (= (get t :value) "("))
|
||||||
(jp-parse-paren-or-arrow st))
|
(jp-parse-paren-or-arrow st))
|
||||||
(else
|
(else
|
||||||
@@ -211,7 +237,7 @@
|
|||||||
(let
|
(let
|
||||||
((params (jp-parse-param-list st)))
|
((params (jp-parse-param-list st)))
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-block st)))
|
((body (jp-parse-fn-body st)))
|
||||||
(list (quote js-funcexpr-async) nm params body))))))
|
(list (quote js-funcexpr-async) nm params body))))))
|
||||||
((= (get t :type) "ident")
|
((= (get t :type) "ident")
|
||||||
(do
|
(do
|
||||||
@@ -363,7 +389,7 @@
|
|||||||
(let
|
(let
|
||||||
((params (jp-parse-param-list st)))
|
((params (jp-parse-param-list st)))
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-block st)))
|
((body (jp-parse-fn-body st)))
|
||||||
(list (quote js-funcexpr) nm params body))))))
|
(list (quote js-funcexpr) nm params body))))))
|
||||||
((= (get t :type) "ident")
|
((= (get t :type) "ident")
|
||||||
(do
|
(do
|
||||||
@@ -418,16 +444,51 @@
|
|||||||
(dict-set! st :idx saved)
|
(dict-set! st :idx saved)
|
||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
(let
|
(let
|
||||||
((e (jp-parse-assignment st)))
|
((e (jp-parse-comma-seq st)))
|
||||||
(jp-expect! st "punct" ")")
|
(jp-expect! st "punct" ")")
|
||||||
e)))
|
(jp-paren-wrap e))))
|
||||||
(do
|
(do
|
||||||
(dict-set! st :idx saved)
|
(dict-set! st :idx saved)
|
||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
(let
|
(let
|
||||||
((e (jp-parse-assignment st)))
|
((e (jp-parse-comma-seq st)))
|
||||||
(jp-expect! st "punct" ")")
|
(jp-expect! st "punct" ")")
|
||||||
e)))))))
|
(jp-paren-wrap e))))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
jp-paren-wrap
|
||||||
|
(fn
|
||||||
|
(e)
|
||||||
|
(cond
|
||||||
|
((and (list? e) (= (first e) (quote js-unop)))
|
||||||
|
(list (quote js-paren) e))
|
||||||
|
(else e))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
jp-parse-comma-seq
|
||||||
|
(fn
|
||||||
|
(st)
|
||||||
|
(let
|
||||||
|
((first-expr (jp-parse-assignment st)))
|
||||||
|
(if
|
||||||
|
(jp-at? st "punct" ",")
|
||||||
|
(jp-parse-comma-seq-rest st (list first-expr))
|
||||||
|
first-expr))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
jp-parse-comma-seq-rest
|
||||||
|
(fn
|
||||||
|
(st acc)
|
||||||
|
(do
|
||||||
|
(jp-advance! st)
|
||||||
|
(let
|
||||||
|
((next-expr (jp-parse-assignment st)))
|
||||||
|
(let
|
||||||
|
((acc2 (append acc (list next-expr))))
|
||||||
|
(if
|
||||||
|
(jp-at? st "punct" ",")
|
||||||
|
(jp-parse-comma-seq-rest st acc2)
|
||||||
|
(cons (quote js-comma) (list acc2))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
jp-collect-params
|
jp-collect-params
|
||||||
@@ -485,6 +546,11 @@
|
|||||||
(st elems)
|
(st elems)
|
||||||
(cond
|
(cond
|
||||||
((jp-at? st "punct" "]") nil)
|
((jp-at? st "punct" "]") nil)
|
||||||
|
((jp-at? st "punct" ",")
|
||||||
|
(begin
|
||||||
|
(append! elems (list (quote js-undef)))
|
||||||
|
(jp-advance! st)
|
||||||
|
(jp-array-loop st elems)))
|
||||||
(else
|
(else
|
||||||
(begin
|
(begin
|
||||||
(cond
|
(cond
|
||||||
@@ -558,6 +624,20 @@
|
|||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
(jp-expect! st "punct" ":")
|
(jp-expect! st "punct" ":")
|
||||||
(append! kvs {:value (jp-parse-assignment st) :key (get t :value)})))
|
(append! kvs {:value (jp-parse-assignment st) :key (get t :value)})))
|
||||||
|
((and (= (get t :type) "punct") (= (get t :value) "["))
|
||||||
|
(do
|
||||||
|
(jp-advance! st)
|
||||||
|
(let
|
||||||
|
((key-expr (jp-parse-assignment st)))
|
||||||
|
(jp-expect! st "punct" "]")
|
||||||
|
(jp-expect! st "punct" ":")
|
||||||
|
(append!
|
||||||
|
kvs
|
||||||
|
{:value (jp-parse-assignment st) :computed-key key-expr :key ""}))))
|
||||||
|
((and (= (get t :type) "punct") (= (get t :value) "..."))
|
||||||
|
(do
|
||||||
|
(jp-advance! st)
|
||||||
|
(append! kvs {:spread (jp-parse-assignment st)})))
|
||||||
(else (error (str "Unexpected in object: " (get t :type))))))))
|
(else (error (str "Unexpected in object: " (get t :type))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -629,7 +709,7 @@
|
|||||||
st
|
st
|
||||||
(list (quote js-optchain-member) left (get t :value))))
|
(list (quote js-optchain-member) left (get t :value))))
|
||||||
(error "expected ident, [ or ( after ?.")))))))
|
(error "expected ident, [ or ( after ?.")))))))
|
||||||
((or (jp-at? st "op" "++") (jp-at? st "op" "--"))
|
((and (or (jp-at? st "op" "++") (jp-at? st "op" "--")) (not (jp-token-nl? st)))
|
||||||
(let
|
(let
|
||||||
((op (get (jp-peek st) :value)))
|
((op (get (jp-peek st) :value)))
|
||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
@@ -682,6 +762,12 @@
|
|||||||
(cond
|
(cond
|
||||||
((< prec 0) left)
|
((< prec 0) left)
|
||||||
((< prec min-prec) left)
|
((< prec min-prec) left)
|
||||||
|
((and (= op "**") (list? left) (= (first left) (quote js-unop)))
|
||||||
|
(error
|
||||||
|
(str
|
||||||
|
"SyntaxError: Unary operator '"
|
||||||
|
(nth left 1)
|
||||||
|
"' used immediately before exponentiation expression")))
|
||||||
(else
|
(else
|
||||||
(do
|
(do
|
||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
@@ -835,6 +921,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
|
||||||
@@ -1052,15 +1144,63 @@
|
|||||||
((c (jp-parse-assignment st)))
|
((c (jp-parse-assignment st)))
|
||||||
(do
|
(do
|
||||||
(jp-expect! st "punct" ")")
|
(jp-expect! st "punct" ")")
|
||||||
|
(jp-disallow-decl-stmt! st "if")
|
||||||
(let
|
(let
|
||||||
((t (jp-parse-stmt st)))
|
((t (jp-parse-stmt st)))
|
||||||
(if
|
(if
|
||||||
(jp-at? st "keyword" "else")
|
(jp-at? st "keyword" "else")
|
||||||
(do
|
(do
|
||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
|
(jp-disallow-decl-stmt! st "else")
|
||||||
(list (quote js-if) c t (jp-parse-stmt st)))
|
(list (quote js-if) c t (jp-parse-stmt st)))
|
||||||
(list (quote js-if) c t nil))))))))
|
(list (quote js-if) c t nil))))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
jp-disallow-decl-stmt!
|
||||||
|
(fn
|
||||||
|
(st context)
|
||||||
|
(let
|
||||||
|
((t (jp-peek st)))
|
||||||
|
(cond
|
||||||
|
((and (= (get t :type) "keyword")
|
||||||
|
(or (= (get t :value) "let")
|
||||||
|
(= (get t :value) "const")
|
||||||
|
(= (get t :value) "function")
|
||||||
|
(= (get t :value) "class")))
|
||||||
|
(cond
|
||||||
|
((and (= (get t :value) "let")
|
||||||
|
(or (= (get (jp-peek-at st 1) :type) "ident")
|
||||||
|
(and (= (get (jp-peek-at st 1) :type) "punct")
|
||||||
|
(or (= (get (jp-peek-at st 1) :value) "[")
|
||||||
|
(= (get (jp-peek-at st 1) :value) "{")))))
|
||||||
|
(error
|
||||||
|
(str
|
||||||
|
"SyntaxError: Lexical declaration cannot appear in single-statement context: "
|
||||||
|
context)))
|
||||||
|
((or (= (get t :value) "const")
|
||||||
|
(= (get t :value) "function")
|
||||||
|
(= (get t :value) "class"))
|
||||||
|
(error
|
||||||
|
(str
|
||||||
|
"SyntaxError: "
|
||||||
|
(get t :value)
|
||||||
|
" declaration cannot appear in single-statement context: "
|
||||||
|
context)))
|
||||||
|
(else nil)))
|
||||||
|
(else nil)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
jp-bump!
|
||||||
|
(fn
|
||||||
|
(st key)
|
||||||
|
(dict-set! st key (+ (get st key) 1))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
jp-decr!
|
||||||
|
(fn
|
||||||
|
(st key)
|
||||||
|
(dict-set! st key (- (get st key) 1))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
jp-parse-while-stmt
|
jp-parse-while-stmt
|
||||||
(fn
|
(fn
|
||||||
@@ -1072,7 +1212,11 @@
|
|||||||
((c (jp-parse-assignment st)))
|
((c (jp-parse-assignment st)))
|
||||||
(do
|
(do
|
||||||
(jp-expect! st "punct" ")")
|
(jp-expect! st "punct" ")")
|
||||||
(let ((body (jp-parse-stmt st))) (list (quote js-while) c body)))))))
|
(jp-disallow-decl-stmt! st "while")
|
||||||
|
(jp-bump! st :loop-depth)
|
||||||
|
(let ((body (jp-parse-stmt st)))
|
||||||
|
(jp-decr! st :loop-depth)
|
||||||
|
(list (quote js-while) c body)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
jp-parse-do-while-stmt
|
jp-parse-do-while-stmt
|
||||||
@@ -1080,8 +1224,11 @@
|
|||||||
(st)
|
(st)
|
||||||
(do
|
(do
|
||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
|
(jp-disallow-decl-stmt! st "do")
|
||||||
|
(jp-bump! st :loop-depth)
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-stmt st)))
|
((body (jp-parse-stmt st)))
|
||||||
|
(jp-decr! st :loop-depth)
|
||||||
(do
|
(do
|
||||||
(if
|
(if
|
||||||
(jp-at? st "keyword" "while")
|
(jp-at? st "keyword" "while")
|
||||||
@@ -1126,8 +1273,11 @@
|
|||||||
(let
|
(let
|
||||||
((iter (jp-parse-assignment st)))
|
((iter (jp-parse-assignment st)))
|
||||||
(jp-expect! st "punct" ")")
|
(jp-expect! st "punct" ")")
|
||||||
|
(jp-disallow-decl-stmt! st "for-of/in")
|
||||||
|
(jp-bump! st :loop-depth)
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-stmt st)))
|
((body (jp-parse-stmt st)))
|
||||||
|
(jp-decr! st :loop-depth)
|
||||||
(list (quote js-for-of-in) iter-kind ident iter body)))))))
|
(list (quote js-for-of-in) iter-kind ident iter body)))))))
|
||||||
(else
|
(else
|
||||||
(let
|
(let
|
||||||
@@ -1138,8 +1288,11 @@
|
|||||||
(let
|
(let
|
||||||
((step (if (jp-at? st "punct" ")") nil (jp-parse-assignment st))))
|
((step (if (jp-at? st "punct" ")") nil (jp-parse-assignment st))))
|
||||||
(jp-expect! st "punct" ")")
|
(jp-expect! st "punct" ")")
|
||||||
|
(jp-disallow-decl-stmt! st "for")
|
||||||
|
(jp-bump! st :loop-depth)
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-stmt st)))
|
((body (jp-parse-stmt st)))
|
||||||
|
(jp-decr! st :loop-depth)
|
||||||
(list (quote js-for) init cond-ast step body)))))))))))
|
(list (quote js-for) init cond-ast step body)))))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -1162,10 +1315,14 @@
|
|||||||
(st)
|
(st)
|
||||||
(do
|
(do
|
||||||
(jp-advance! st)
|
(jp-advance! st)
|
||||||
|
(when
|
||||||
|
(= (get st :fn-depth) 0)
|
||||||
|
(error "SyntaxError: Illegal return statement"))
|
||||||
(if
|
(if
|
||||||
(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
|
||||||
@@ -1188,7 +1345,7 @@
|
|||||||
(let
|
(let
|
||||||
((params (jp-parse-param-list st)))
|
((params (jp-parse-param-list st)))
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-block st)))
|
((body (jp-parse-fn-body st)))
|
||||||
(list (quote js-funcdecl) nm params body))))))))
|
(list (quote js-funcdecl) nm params body))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -1207,7 +1364,7 @@
|
|||||||
(let
|
(let
|
||||||
((params (jp-parse-param-list st)))
|
((params (jp-parse-param-list st)))
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-block st)))
|
((body (jp-parse-fn-body st)))
|
||||||
(list (quote js-funcdecl-async) nm params body))))))))
|
(list (quote js-funcdecl-async) nm params body))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -1256,7 +1413,7 @@
|
|||||||
(let
|
(let
|
||||||
((params (jp-parse-param-list st)))
|
((params (jp-parse-param-list st)))
|
||||||
(let
|
(let
|
||||||
((body (jp-parse-block st)))
|
((body (jp-parse-fn-body st)))
|
||||||
(list
|
(list
|
||||||
(quote js-method)
|
(quote js-method)
|
||||||
(if static? "static" "instance")
|
(if static? "static" "instance")
|
||||||
@@ -1284,9 +1441,11 @@
|
|||||||
((disc (jp-parse-assignment st)))
|
((disc (jp-parse-assignment st)))
|
||||||
(jp-expect! st "punct" ")")
|
(jp-expect! st "punct" ")")
|
||||||
(jp-expect! st "punct" "{")
|
(jp-expect! st "punct" "{")
|
||||||
|
(jp-bump! st :switch-depth)
|
||||||
(let
|
(let
|
||||||
((cases (list)))
|
((cases (list)))
|
||||||
(jp-parse-switch-cases st cases)
|
(jp-parse-switch-cases st cases)
|
||||||
|
(jp-decr! st :switch-depth)
|
||||||
(jp-expect! st "punct" "}")
|
(jp-expect! st "punct" "}")
|
||||||
(list (quote js-switch) disc cases)))))
|
(list (quote js-switch) disc cases)))))
|
||||||
|
|
||||||
@@ -1362,9 +1521,40 @@
|
|||||||
((jp-at? st "keyword" "for") (jp-parse-for-stmt st))
|
((jp-at? st "keyword" "for") (jp-parse-for-stmt st))
|
||||||
((jp-at? st "keyword" "return") (jp-parse-return-stmt st))
|
((jp-at? st "keyword" "return") (jp-parse-return-stmt st))
|
||||||
((jp-at? st "keyword" "break")
|
((jp-at? st "keyword" "break")
|
||||||
(do (jp-advance! st) (jp-eat-semi st) (list (quote js-break))))
|
(do
|
||||||
|
(jp-advance! st)
|
||||||
|
(cond
|
||||||
|
((= (get (jp-peek st) :type) "ident")
|
||||||
|
(do (jp-advance! st) (jp-eat-semi st) (list (quote js-break))))
|
||||||
|
(else
|
||||||
|
(do
|
||||||
|
(when
|
||||||
|
(and (= (get st :loop-depth) 0) (= (get st :switch-depth) 0))
|
||||||
|
(error "SyntaxError: Illegal break statement"))
|
||||||
|
(jp-eat-semi st)
|
||||||
|
(list (quote js-break)))))))
|
||||||
((jp-at? st "keyword" "continue")
|
((jp-at? st "keyword" "continue")
|
||||||
(do (jp-advance! st) (jp-eat-semi st) (list (quote js-continue))))
|
(do
|
||||||
|
(jp-advance! st)
|
||||||
|
(cond
|
||||||
|
((= (get (jp-peek st) :type) "ident")
|
||||||
|
(do (jp-advance! st) (jp-eat-semi st) (list (quote js-continue))))
|
||||||
|
(else
|
||||||
|
(do
|
||||||
|
(when
|
||||||
|
(= (get st :loop-depth) 0)
|
||||||
|
(error "SyntaxError: Illegal continue statement"))
|
||||||
|
(jp-eat-semi st)
|
||||||
|
(list (quote js-continue)))))))
|
||||||
|
((and
|
||||||
|
(= (get (jp-peek st) :type) "ident")
|
||||||
|
(= (get (jp-peek-at st 1) :type) "punct")
|
||||||
|
(= (get (jp-peek-at st 1) :value) ":"))
|
||||||
|
(do
|
||||||
|
(jp-advance! st)
|
||||||
|
(jp-advance! st)
|
||||||
|
(jp-disallow-decl-stmt! st "label")
|
||||||
|
(jp-parse-stmt st)))
|
||||||
((jp-at? st "keyword" "class") (jp-parse-class-decl st))
|
((jp-at? st "keyword" "class") (jp-parse-class-decl st))
|
||||||
((jp-at? st "keyword" "throw") (jp-parse-throw-stmt st))
|
((jp-at? st "keyword" "throw") (jp-parse-throw-stmt st))
|
||||||
((jp-at? st "keyword" "try") (jp-parse-try-stmt st))
|
((jp-at? st "keyword" "try") (jp-parse-try-stmt st))
|
||||||
@@ -1374,7 +1564,7 @@
|
|||||||
((jp-at? st "keyword" "switch") (jp-parse-switch-stmt st))
|
((jp-at? st "keyword" "switch") (jp-parse-switch-stmt st))
|
||||||
(else
|
(else
|
||||||
(let
|
(let
|
||||||
((e (jp-parse-assignment st)))
|
((e (jp-parse-comma-seq st)))
|
||||||
(do (jp-eat-semi st) (list (quote js-exprstmt) e)))))))
|
(do (jp-eat-semi st) (list (quote js-exprstmt) e)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -1400,10 +1590,33 @@
|
|||||||
jp-parse-arrow-body
|
jp-parse-arrow-body
|
||||||
(fn
|
(fn
|
||||||
(st)
|
(st)
|
||||||
(if
|
(jp-bump! st :fn-depth)
|
||||||
(jp-at? st "punct" "{")
|
(let
|
||||||
(jp-parse-block st)
|
((saved-loop (get st :loop-depth)) (saved-switch (get st :switch-depth)))
|
||||||
(jp-parse-assignment st))))
|
(dict-set! st :loop-depth 0)
|
||||||
|
(dict-set! st :switch-depth 0)
|
||||||
|
(let
|
||||||
|
((body (if (jp-at? st "punct" "{") (jp-parse-block st) (jp-parse-assignment st))))
|
||||||
|
(jp-decr! st :fn-depth)
|
||||||
|
(dict-set! st :loop-depth saved-loop)
|
||||||
|
(dict-set! st :switch-depth saved-switch)
|
||||||
|
body))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
jp-parse-fn-body
|
||||||
|
(fn
|
||||||
|
(st)
|
||||||
|
(jp-bump! st :fn-depth)
|
||||||
|
(let
|
||||||
|
((saved-loop (get st :loop-depth)) (saved-switch (get st :switch-depth)))
|
||||||
|
(dict-set! st :loop-depth 0)
|
||||||
|
(dict-set! st :switch-depth 0)
|
||||||
|
(let
|
||||||
|
((body (jp-parse-block st)))
|
||||||
|
(jp-decr! st :fn-depth)
|
||||||
|
(dict-set! st :loop-depth saved-loop)
|
||||||
|
(dict-set! st :switch-depth saved-switch)
|
||||||
|
body))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-parse
|
js-parse
|
||||||
@@ -1414,7 +1627,7 @@
|
|||||||
(= (len tokens) 0)
|
(= (len tokens) 0)
|
||||||
(and (= (len tokens) 1) (= (get (nth tokens 0) :type) "eof")))
|
(and (= (len tokens) 1) (= (get (nth tokens 0) :type) "eof")))
|
||||||
(list (quote js-program) (list))
|
(list (quote js-program) (list))
|
||||||
(let ((st {:idx 0 :tokens tokens :arrow-candidate true})) (jp-parse-program st)))))
|
(let ((st {:idx 0 :tokens tokens :arrow-candidate true :loop-depth 0 :switch-depth 0 :fn-depth 0})) (jp-parse-program st)))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-parse-expr
|
js-parse-expr
|
||||||
@@ -1427,4 +1640,4 @@
|
|||||||
(= (len tokens) 0)
|
(= (len tokens) 0)
|
||||||
(and (= (len tokens) 1) (= (get (nth tokens 0) :type) "eof")))
|
(and (= (len tokens) 1) (= (get (nth tokens 0) :type) "eof")))
|
||||||
(list)
|
(list)
|
||||||
(let ((st {:idx 0 :tokens tokens :arrow-candidate true})) (jp-parse-assignment st))))))
|
(let ((st {:idx 0 :tokens tokens :arrow-candidate true :loop-depth 0 :switch-depth 0 :fn-depth 0})) (jp-parse-assignment st))))))
|
||||||
|
|||||||
4067
lib/js/runtime.sx
4067
lib/js/runtime.sx
File diff suppressed because it is too large
Load Diff
@@ -1323,6 +1323,25 @@ 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))")
|
||||||
|
|
||||||
|
(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
|
EPOCHS
|
||||||
|
|
||||||
|
|
||||||
@@ -2042,6 +2061,17 @@ 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'
|
||||||
|
|
||||||
|
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))
|
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"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ UPSTREAM = REPO / "lib" / "js" / "test262-upstream"
|
|||||||
TEST_ROOT = UPSTREAM / "test"
|
TEST_ROOT = UPSTREAM / "test"
|
||||||
HARNESS_DIR = UPSTREAM / "harness"
|
HARNESS_DIR = UPSTREAM / "harness"
|
||||||
|
|
||||||
DEFAULT_PER_TEST_TIMEOUT_S = 5.0
|
DEFAULT_PER_TEST_TIMEOUT_S = 15.0
|
||||||
DEFAULT_BATCH_TIMEOUT_S = 120
|
DEFAULT_BATCH_TIMEOUT_S = 120
|
||||||
|
|
||||||
# Cache dir for precomputed SX source of harness JS (one file per Python run).
|
# Cache dir for precomputed SX source of harness JS (one file per Python run).
|
||||||
@@ -134,6 +134,9 @@ var verifyProperty = function (obj, name, desc, opts) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
var verifyPrimordialProperty = verifyProperty;
|
var verifyPrimordialProperty = verifyProperty;
|
||||||
|
var verifyEqualTo = function (obj, name, value) {
|
||||||
|
assert.sameValue(obj[name], value, name + " equals");
|
||||||
|
};
|
||||||
var verifyNotEnumerable = function (o, n, v, w, x) { };
|
var verifyNotEnumerable = function (o, n, v, w, x) { };
|
||||||
var verifyNotWritable = function (o, n, v, w, x) { };
|
var verifyNotWritable = function (o, n, v, w, x) { };
|
||||||
var verifyNotConfigurable = function (o, n, v, w, x) { };
|
var verifyNotConfigurable = function (o, n, v, w, x) { };
|
||||||
@@ -146,6 +149,50 @@ var isConstructor = function (f) {
|
|||||||
// Best-effort: built-in functions and arrows aren't; declared `function` decls are.
|
// Best-effort: built-in functions and arrows aren't; declared `function` decls are.
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
// $DONE / asyncTest — async-flag tests call $DONE(err) to signal completion.
|
||||||
|
// Since we drain microtasks synchronously, $DONE is just a final-assertion sink.
|
||||||
|
var $DONE = function (err) {
|
||||||
|
if (err) { throw new Test262Error((err && err.message) || err); }
|
||||||
|
};
|
||||||
|
var asyncTest = function (testFunc) {
|
||||||
|
Promise.resolve(testFunc()).then(function () { $DONE(); }, function (e) { $DONE(e); });
|
||||||
|
};
|
||||||
|
// promiseHelper.js include — used by Promise.all/race tests for ordering checks.
|
||||||
|
var checkSequence = function (arr, message) {
|
||||||
|
for (var i = 0; i < arr.length; i = i + 1) {
|
||||||
|
if (arr[i] !== (i + 1)) {
|
||||||
|
throw new Test262Error((message || "Sequence") + " expected " + (i+1) + " at index " + i + " but got " + arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
var checkSettledPromises = function (settleds, expected, message) {
|
||||||
|
var msg = message ? message + " " : "";
|
||||||
|
if (settleds.length !== expected.length) {
|
||||||
|
throw new Test262Error(msg + "lengths differ: " + settleds.length + " vs " + expected.length);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < settleds.length; i = i + 1) {
|
||||||
|
if (settleds[i].status !== expected[i].status) {
|
||||||
|
throw new Test262Error(msg + "status[" + i + "]: " + settleds[i].status + " vs " + expected[i].status);
|
||||||
|
}
|
||||||
|
if (expected[i].status === "fulfilled" && settleds[i].value !== expected[i].value) {
|
||||||
|
throw new Test262Error(msg + "value[" + i + "]: " + settleds[i].value + " vs " + expected[i].value);
|
||||||
|
}
|
||||||
|
if (expected[i].status === "rejected" && settleds[i].reason !== expected[i].reason) {
|
||||||
|
throw new Test262Error(msg + "reason[" + i + "]: " + settleds[i].reason + " vs " + expected[i].reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// decimalToHexString.js include — used by URI/escape tests.
|
||||||
|
var decimalToHexString = function (n) {
|
||||||
|
var hex = "0123456789ABCDEF";
|
||||||
|
if (n < 0) { n = n + 65536; }
|
||||||
|
return hex[(n >> 12) & 15] + hex[(n >> 8) & 15] + hex[(n >> 4) & 15] + hex[n & 15];
|
||||||
|
};
|
||||||
|
var decimalToPercentHexString = function (n) {
|
||||||
|
var hex = "0123456789ABCDEF";
|
||||||
|
return "%" + hex[(n >> 4) & 15] + hex[n & 15];
|
||||||
|
};
|
||||||
// Trivial helper for tests that use Array.isArray-like functionality
|
// Trivial helper for tests that use Array.isArray-like functionality
|
||||||
// (many tests reach for it via compareArray)
|
// (many tests reach for it via compareArray)
|
||||||
"""
|
"""
|
||||||
@@ -358,6 +405,8 @@ def classify_negative_result(fm: Frontmatter, kind: str, payload: str):
|
|||||||
or ("expected" in low and "got" in low)
|
or ("expected" in low and "got" in low)
|
||||||
or "js-transpile-unop" in low
|
or "js-transpile-unop" in low
|
||||||
or "js-transpile-binop" in low
|
or "js-transpile-binop" in low
|
||||||
|
or "js-transpile-assign" in low
|
||||||
|
or "js-transpile" in low
|
||||||
or "js-compound-update" in low
|
or "js-compound-update" in low
|
||||||
or "parse" in low
|
or "parse" in low
|
||||||
):
|
):
|
||||||
@@ -1011,11 +1060,45 @@ def _worker_run(args):
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
_HARNESS_INCLUDE_CACHE: dict = {}
|
||||||
|
|
||||||
|
# Only inline these small harness files per-test. Large ones like propertyHelper.js
|
||||||
|
# multiply js-eval/JIT cost by ~5-10x and push tests over the per-test timeout.
|
||||||
|
_INLINE_INCLUDES = {"nans.js", "sta.js", "byteConversionValues.js", "compareArray.js"}
|
||||||
|
|
||||||
|
|
||||||
|
def _load_harness_include(name: str) -> str:
|
||||||
|
"""Read an upstream harness include file (e.g. nans.js).
|
||||||
|
Returns empty string if the file isn't present.
|
||||||
|
"""
|
||||||
|
if name in _HARNESS_INCLUDE_CACHE:
|
||||||
|
return _HARNESS_INCLUDE_CACHE[name]
|
||||||
|
path = HARNESS_DIR / name
|
||||||
|
try:
|
||||||
|
src = path.read_text()
|
||||||
|
except OSError:
|
||||||
|
src = ""
|
||||||
|
_HARNESS_INCLUDE_CACHE[name] = src
|
||||||
|
return src
|
||||||
|
|
||||||
|
|
||||||
def assemble_source(t):
|
def assemble_source(t):
|
||||||
"""Return JS source to feed to js-eval. Harness is preloaded, so we only
|
"""Return JS source to feed to js-eval. Harness is preloaded, so we only
|
||||||
append the test source (plus negative-test prep if needed).
|
append the test source (plus a small allowlist of per-test includes).
|
||||||
"""
|
"""
|
||||||
return t.src
|
if not getattr(t.fm, "includes", None):
|
||||||
|
return t.src
|
||||||
|
parts = []
|
||||||
|
for inc in t.fm.includes:
|
||||||
|
if inc not in _INLINE_INCLUDES:
|
||||||
|
continue
|
||||||
|
chunk = _load_harness_include(inc)
|
||||||
|
if chunk:
|
||||||
|
parts.append(chunk)
|
||||||
|
if not parts:
|
||||||
|
return t.src
|
||||||
|
parts.append(t.src)
|
||||||
|
return "\n".join(parts)
|
||||||
|
|
||||||
|
|
||||||
def aggregate(results):
|
def aggregate(results):
|
||||||
@@ -1193,7 +1276,7 @@ def main(argv):
|
|||||||
shards = [[] for _ in range(n_workers)]
|
shards = [[] for _ in range(n_workers)]
|
||||||
for i, t in enumerate(tests):
|
for i, t in enumerate(tests):
|
||||||
shards[i % n_workers].append(
|
shards[i % n_workers].append(
|
||||||
(t.rel, t.category, t.src, t.fm.negative_phase, t.fm.negative_type)
|
(t.rel, t.category, assemble_source(t), t.fm.negative_phase, t.fm.negative_type)
|
||||||
)
|
)
|
||||||
|
|
||||||
t_run_start = time.monotonic()
|
t_run_start = time.monotonic()
|
||||||
|
|||||||
@@ -1,137 +1,53 @@
|
|||||||
{
|
{
|
||||||
"totals": {
|
"totals": {
|
||||||
"pass": 162,
|
"pass": 4,
|
||||||
"fail": 128,
|
"fail": 10,
|
||||||
"skip": 1597,
|
"skip": 16,
|
||||||
"timeout": 10,
|
"timeout": 0,
|
||||||
"total": 1897,
|
"total": 30,
|
||||||
"runnable": 300,
|
"runnable": 14,
|
||||||
"pass_rate": 54.0
|
"pass_rate": 28.6
|
||||||
},
|
},
|
||||||
"categories": [
|
"categories": [
|
||||||
{
|
{
|
||||||
"category": "built-ins/Math",
|
"category": "built-ins/Function",
|
||||||
"total": 327,
|
"total": 30,
|
||||||
"pass": 43,
|
"pass": 4,
|
||||||
"fail": 56,
|
"fail": 10,
|
||||||
"skip": 227,
|
"skip": 16,
|
||||||
"timeout": 1,
|
"timeout": 0,
|
||||||
"pass_rate": 43.0,
|
"pass_rate": 28.6,
|
||||||
"top_failures": [
|
"top_failures": [
|
||||||
[
|
[
|
||||||
"TypeError: not a function",
|
"SyntaxError (parse/unsupported syntax)",
|
||||||
36
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Test262Error (assertion failed)",
|
|
||||||
20
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Timeout",
|
|
||||||
1
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"category": "built-ins/Number",
|
|
||||||
"total": 340,
|
|
||||||
"pass": 77,
|
|
||||||
"fail": 19,
|
|
||||||
"skip": 240,
|
|
||||||
"timeout": 4,
|
|
||||||
"pass_rate": 77.0,
|
|
||||||
"top_failures": [
|
|
||||||
[
|
|
||||||
"Test262Error (assertion failed)",
|
|
||||||
19
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Timeout",
|
|
||||||
4
|
4
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"category": "built-ins/String",
|
|
||||||
"total": 1223,
|
|
||||||
"pass": 42,
|
|
||||||
"fail": 53,
|
|
||||||
"skip": 1123,
|
|
||||||
"timeout": 5,
|
|
||||||
"pass_rate": 42.0,
|
|
||||||
"top_failures": [
|
|
||||||
[
|
|
||||||
"Test262Error (assertion failed)",
|
|
||||||
44
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Timeout",
|
|
||||||
5
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"ReferenceError (undefined symbol)",
|
"ReferenceError (undefined symbol)",
|
||||||
2
|
3
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"Unhandled: Not callable: {:__proto__ {:toLowerCase <lambda(&rest, args)",
|
"TypeError (other)",
|
||||||
2
|
3
|
||||||
],
|
|
||||||
[
|
|
||||||
"Unhandled: Not callable: \\\\\\",
|
|
||||||
2
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"category": "built-ins/StringIteratorPrototype",
|
|
||||||
"total": 7,
|
|
||||||
"pass": 0,
|
|
||||||
"fail": 0,
|
|
||||||
"skip": 7,
|
|
||||||
"timeout": 0,
|
|
||||||
"pass_rate": 0.0,
|
|
||||||
"top_failures": []
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"top_failure_modes": [
|
"top_failure_modes": [
|
||||||
[
|
[
|
||||||
"Test262Error (assertion failed)",
|
"SyntaxError (parse/unsupported syntax)",
|
||||||
83
|
4
|
||||||
],
|
|
||||||
[
|
|
||||||
"TypeError: not a function",
|
|
||||||
36
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Timeout",
|
|
||||||
10
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"ReferenceError (undefined symbol)",
|
"ReferenceError (undefined symbol)",
|
||||||
2
|
3
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"Unhandled: Not callable: {:__proto__ {:toLowerCase <lambda(&rest, args)",
|
"TypeError (other)",
|
||||||
2
|
3
|
||||||
],
|
|
||||||
[
|
|
||||||
"Unhandled: Not callable: \\\\\\",
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"SyntaxError (parse/unsupported syntax)",
|
|
||||||
1
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Unhandled: Not callable: {:__proto__ {:valueOf <lambda()> :propertyIsEn",
|
|
||||||
1
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Unhandled: js-transpile-binop: unsupported op: >>>\\",
|
|
||||||
1
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33",
|
"pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33",
|
||||||
"elapsed_seconds": 274.5,
|
"elapsed_seconds": 11.2,
|
||||||
"workers": 1
|
"workers": 1
|
||||||
}
|
}
|
||||||
@@ -1,47 +1,26 @@
|
|||||||
# test262 scoreboard
|
# test262 scoreboard
|
||||||
|
|
||||||
Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33`
|
Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33`
|
||||||
Wall time: 274.5s
|
Wall time: 11.2s
|
||||||
|
|
||||||
**Total:** 162/300 runnable passed (54.0%). Raw: pass=162 fail=128 skip=1597 timeout=10 total=1897.
|
**Total:** 4/14 runnable passed (28.6%). Raw: pass=4 fail=10 skip=16 timeout=0 total=30.
|
||||||
|
|
||||||
## Top failure modes
|
## Top failure modes
|
||||||
|
|
||||||
- **83x** Test262Error (assertion failed)
|
- **4x** SyntaxError (parse/unsupported syntax)
|
||||||
- **36x** TypeError: not a function
|
- **3x** ReferenceError (undefined symbol)
|
||||||
- **10x** Timeout
|
- **3x** TypeError (other)
|
||||||
- **2x** ReferenceError (undefined symbol)
|
|
||||||
- **2x** Unhandled: Not callable: {:__proto__ {:toLowerCase <lambda(&rest, args)
|
|
||||||
- **2x** Unhandled: Not callable: \\\
|
|
||||||
- **1x** SyntaxError (parse/unsupported syntax)
|
|
||||||
- **1x** Unhandled: Not callable: {:__proto__ {:valueOf <lambda()> :propertyIsEn
|
|
||||||
- **1x** Unhandled: js-transpile-binop: unsupported op: >>>\
|
|
||||||
|
|
||||||
## Categories (worst pass-rate first, min 10 runnable)
|
## Categories (worst pass-rate first, min 10 runnable)
|
||||||
|
|
||||||
| Category | Pass | Fail | Skip | Timeout | Total | Pass % |
|
| Category | Pass | Fail | Skip | Timeout | Total | Pass % |
|
||||||
|---|---:|---:|---:|---:|---:|---:|
|
|---|---:|---:|---:|---:|---:|---:|
|
||||||
| built-ins/String | 42 | 53 | 1123 | 5 | 1223 | 42.0% |
|
| built-ins/Function | 4 | 10 | 16 | 0 | 30 | 28.6% |
|
||||||
| built-ins/Math | 43 | 56 | 227 | 1 | 327 | 43.0% |
|
|
||||||
| built-ins/Number | 77 | 19 | 240 | 4 | 340 | 77.0% |
|
|
||||||
|
|
||||||
## Per-category top failures (min 10 runnable, worst first)
|
## Per-category top failures (min 10 runnable, worst first)
|
||||||
|
|
||||||
### built-ins/String (42/100 — 42.0%)
|
### built-ins/Function (4/14 — 28.6%)
|
||||||
|
|
||||||
- **44x** Test262Error (assertion failed)
|
- **4x** SyntaxError (parse/unsupported syntax)
|
||||||
- **5x** Timeout
|
- **3x** ReferenceError (undefined symbol)
|
||||||
- **2x** ReferenceError (undefined symbol)
|
- **3x** TypeError (other)
|
||||||
- **2x** Unhandled: Not callable: {:__proto__ {:toLowerCase <lambda(&rest, args)
|
|
||||||
- **2x** Unhandled: Not callable: \\\
|
|
||||||
|
|
||||||
### built-ins/Math (43/100 — 43.0%)
|
|
||||||
|
|
||||||
- **36x** TypeError: not a function
|
|
||||||
- **20x** Test262Error (assertion failed)
|
|
||||||
- **1x** Timeout
|
|
||||||
|
|
||||||
### built-ins/Number (77/100 — 77.0%)
|
|
||||||
|
|
||||||
- **19x** Test262Error (assertion failed)
|
|
||||||
- **4x** Timeout
|
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
(list (js-sym "js-regex-new") (nth ast 1) (nth ast 2)))
|
(list (js-sym "js-regex-new") (nth ast 1) (nth ast 2)))
|
||||||
((js-tag? ast "js-null") nil)
|
((js-tag? ast "js-null") nil)
|
||||||
((js-tag? ast "js-undef") (list (js-sym "quote") :js-undefined))
|
((js-tag? ast "js-undef") (list (js-sym "quote") :js-undefined))
|
||||||
|
((js-tag? ast "js-paren") (js-transpile (nth ast 1)))
|
||||||
((js-tag? ast "js-ident") (js-transpile-ident (nth ast 1)))
|
((js-tag? ast "js-ident") (js-transpile-ident (nth ast 1)))
|
||||||
((js-tag? ast "js-unop")
|
((js-tag? ast "js-unop")
|
||||||
(js-transpile-unop (nth ast 1) (nth ast 2)))
|
(js-transpile-unop (nth ast 1) (nth ast 2)))
|
||||||
@@ -116,7 +117,8 @@
|
|||||||
((js-tag? ast "js-arrow")
|
((js-tag? ast "js-arrow")
|
||||||
(js-transpile-arrow (nth ast 1) (nth ast 2)))
|
(js-transpile-arrow (nth ast 1) (nth ast 2)))
|
||||||
((js-tag? ast "js-program") (js-transpile-stmts (nth ast 1)))
|
((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-block")
|
||||||
|
(cons (js-sym "begin") (js-transpile-stmt-list (nth ast 1))))
|
||||||
((js-tag? ast "js-exprstmt") (js-transpile (nth ast 1)))
|
((js-tag? ast "js-exprstmt") (js-transpile (nth ast 1)))
|
||||||
((js-tag? ast "js-empty") nil)
|
((js-tag? ast "js-empty") nil)
|
||||||
((js-tag? ast "js-var")
|
((js-tag? ast "js-var")
|
||||||
@@ -164,6 +166,8 @@
|
|||||||
(js-transpile-new (nth ast 1) (nth ast 2)))
|
(js-transpile-new (nth ast 1) (nth ast 2)))
|
||||||
((js-tag? ast "js-class")
|
((js-tag? ast "js-class")
|
||||||
(js-transpile-class (nth ast 1) (nth ast 2) (nth ast 3)))
|
(js-transpile-class (nth ast 1) (nth ast 2) (nth ast 3)))
|
||||||
|
((js-tag? ast "js-comma")
|
||||||
|
(cons (js-sym "begin") (map js-transpile (nth ast 1))))
|
||||||
((js-tag? ast "js-throw") (js-transpile-throw (nth ast 1)))
|
((js-tag? ast "js-throw") (js-transpile-throw (nth ast 1)))
|
||||||
((js-tag? ast "js-try")
|
((js-tag? ast "js-try")
|
||||||
(js-transpile-try (nth ast 1) (nth ast 2) (nth ast 3)))
|
(js-transpile-try (nth ast 1) (nth ast 2) (nth ast 3)))
|
||||||
@@ -221,7 +225,23 @@
|
|||||||
(js-sym "js-delete-prop")
|
(js-sym "js-delete-prop")
|
||||||
(js-transpile (nth arg 1))
|
(js-transpile (nth arg 1))
|
||||||
(js-transpile (nth arg 2))))
|
(js-transpile (nth arg 2))))
|
||||||
|
((js-tag? arg "js-ident") false)
|
||||||
|
((js-tag? arg "js-paren") (js-transpile-unop op (nth arg 1)))
|
||||||
(else true)))
|
(else true)))
|
||||||
|
((and (= op "typeof") (js-tag? arg "js-ident"))
|
||||||
|
(let
|
||||||
|
((name (nth arg 1)))
|
||||||
|
(list
|
||||||
|
(js-sym "if")
|
||||||
|
(list
|
||||||
|
(js-sym "or")
|
||||||
|
(list
|
||||||
|
(js-sym "env-has?")
|
||||||
|
(list (js-sym "current-env"))
|
||||||
|
name)
|
||||||
|
(list (js-sym "dict-has?") (js-sym "js-global") name))
|
||||||
|
(list (js-sym "js-typeof") (js-transpile arg))
|
||||||
|
"undefined")))
|
||||||
(else
|
(else
|
||||||
(let
|
(let
|
||||||
((a (js-transpile arg)))
|
((a (js-transpile arg)))
|
||||||
@@ -231,7 +251,8 @@
|
|||||||
((= op "!") (list (js-sym "js-not") a))
|
((= op "!") (list (js-sym "js-not") a))
|
||||||
((= op "~") (list (js-sym "js-bitnot") a))
|
((= op "~") (list (js-sym "js-bitnot") a))
|
||||||
((= op "typeof") (list (js-sym "js-typeof") a))
|
((= op "typeof") (list (js-sym "js-typeof") a))
|
||||||
((= op "void") (list (js-sym "quote") :js-undefined))
|
((= op "void")
|
||||||
|
(list (js-sym "begin") a (list (js-sym "quote") :js-undefined)))
|
||||||
(else (error (str "js-transpile-unop: unsupported op: " op)))))))))
|
(else (error (str "js-transpile-unop: unsupported op: " op)))))))))
|
||||||
|
|
||||||
;; ── Array literal ─────────────────────────────────────────────────
|
;; ── Array literal ─────────────────────────────────────────────────
|
||||||
@@ -295,6 +316,21 @@
|
|||||||
(list (js-sym "js-undefined?") (js-sym "_a")))
|
(list (js-sym "js-undefined?") (js-sym "_a")))
|
||||||
(js-transpile r)
|
(js-transpile r)
|
||||||
(js-sym "_a"))))
|
(js-sym "_a"))))
|
||||||
|
((= op ">>>")
|
||||||
|
(list
|
||||||
|
(js-sym "js-unsigned-rshift")
|
||||||
|
(js-transpile l)
|
||||||
|
(js-transpile r)))
|
||||||
|
((= op "<<")
|
||||||
|
(list (js-sym "js-shl") (js-transpile l) (js-transpile r)))
|
||||||
|
((= op ">>")
|
||||||
|
(list (js-sym "js-shr") (js-transpile l) (js-transpile r)))
|
||||||
|
((= op "&")
|
||||||
|
(list (js-sym "js-bitand") (js-transpile l) (js-transpile r)))
|
||||||
|
((= op "|")
|
||||||
|
(list (js-sym "js-bitor") (js-transpile l) (js-transpile r)))
|
||||||
|
((= op "^")
|
||||||
|
(list (js-sym "js-bitxor") (js-transpile l) (js-transpile r)))
|
||||||
(else (error (str "js-transpile-binop: unsupported op: " op))))))
|
(else (error (str "js-transpile-binop: unsupported op: " op))))))
|
||||||
|
|
||||||
;; ── Object literal ────────────────────────────────────────────────
|
;; ── Object literal ────────────────────────────────────────────────
|
||||||
@@ -373,7 +409,19 @@
|
|||||||
(list
|
(list
|
||||||
(js-sym "js-new-call")
|
(js-sym "js-new-call")
|
||||||
(js-transpile callee)
|
(js-transpile callee)
|
||||||
(cons (js-sym "list") (map js-transpile args)))))
|
(cond
|
||||||
|
((js-has-spread? args)
|
||||||
|
(cons
|
||||||
|
(js-sym "js-array-spread-build")
|
||||||
|
(map
|
||||||
|
(fn
|
||||||
|
(e)
|
||||||
|
(if
|
||||||
|
(js-tag? e "js-spread")
|
||||||
|
(list (js-sym "list") "js-spread" (js-transpile (nth e 1)))
|
||||||
|
(list (js-sym "list") "js-value" (js-transpile e))))
|
||||||
|
args)))
|
||||||
|
(else (cons (js-sym "js-args") (map js-transpile args)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-transpile-array
|
js-transpile-array
|
||||||
@@ -391,7 +439,7 @@
|
|||||||
(list (js-sym "list") "js-spread" (js-transpile (nth e 1)))
|
(list (js-sym "list") "js-spread" (js-transpile (nth e 1)))
|
||||||
(list (js-sym "list") "js-value" (js-transpile e))))
|
(list (js-sym "list") "js-value" (js-transpile e))))
|
||||||
elts))
|
elts))
|
||||||
(cons (js-sym "list") (map js-transpile elts)))))
|
(cons (js-sym "js-make-list") (map js-transpile elts)))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-has-spread?
|
js-has-spread?
|
||||||
@@ -421,7 +469,7 @@
|
|||||||
(list (js-sym "list") "js-spread" (js-transpile (nth e 1)))
|
(list (js-sym "list") "js-spread" (js-transpile (nth e 1)))
|
||||||
(list (js-sym "list") "js-value" (js-transpile e))))
|
(list (js-sym "list") "js-value" (js-transpile e))))
|
||||||
args))
|
args))
|
||||||
(cons (js-sym "list") (map js-transpile args)))))
|
(cons (js-sym "js-args") (map js-transpile args)))))
|
||||||
|
|
||||||
;; Transpile a JS expression string to SX source text (for inspection
|
;; Transpile a JS expression string to SX source text (for inspection
|
||||||
;; in tests). Useful for asserting the exact emitted tree.
|
;; in tests). Useful for asserting the exact emitted tree.
|
||||||
@@ -431,18 +479,28 @@
|
|||||||
(entries)
|
(entries)
|
||||||
(list
|
(list
|
||||||
(js-sym "let")
|
(js-sym "let")
|
||||||
(list (list (js-sym "_obj") (list (js-sym "dict"))))
|
(list (list (js-sym "_obj") (list (js-sym "js-make-obj"))))
|
||||||
(cons
|
(cons
|
||||||
(js-sym "begin")
|
(js-sym "begin")
|
||||||
(append
|
(append
|
||||||
(map
|
(map
|
||||||
(fn
|
(fn
|
||||||
(entry)
|
(entry)
|
||||||
(list
|
(cond
|
||||||
(js-sym "dict-set!")
|
((contains? (keys entry) :spread)
|
||||||
(js-sym "_obj")
|
(list
|
||||||
(get entry :key)
|
(js-sym "js-obj-spread!")
|
||||||
(js-transpile (get entry :value))))
|
(js-sym "_obj")
|
||||||
|
(js-transpile (get entry :spread))))
|
||||||
|
(else
|
||||||
|
(list
|
||||||
|
(js-sym "js-obj-set!")
|
||||||
|
(js-sym "_obj")
|
||||||
|
(if
|
||||||
|
(contains? (keys entry) :computed-key)
|
||||||
|
(list (js-sym "js-to-string") (js-transpile (get entry :computed-key)))
|
||||||
|
(get entry :key))
|
||||||
|
(js-transpile (get entry :value))))))
|
||||||
entries)
|
entries)
|
||||||
(list (js-sym "_obj")))))))
|
(list (js-sym "_obj")))))))
|
||||||
|
|
||||||
@@ -486,6 +544,95 @@
|
|||||||
(append inits (list (js-transpile body))))))))
|
(append inits (list (js-transpile body))))))))
|
||||||
(list (js-sym "fn") param-syms body-tr))))
|
(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))
|
||||||
|
(else
|
||||||
|
(append
|
||||||
|
(js-collect-var-names-stmt (first stmts))
|
||||||
|
(js-collect-var-names (rest stmts)))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
js-collect-var-names-stmt
|
||||||
|
(fn
|
||||||
|
(stmt)
|
||||||
|
(cond
|
||||||
|
((not (list? stmt)) (list))
|
||||||
|
((and (js-tag? stmt "js-var") (= (nth stmt 1) "var"))
|
||||||
|
(js-collect-var-decl-names (nth stmt 2)))
|
||||||
|
((js-tag? stmt "js-block") (js-collect-var-names (nth stmt 1)))
|
||||||
|
((js-tag? stmt "js-for")
|
||||||
|
(append
|
||||||
|
(js-collect-var-names-stmt (nth stmt 1))
|
||||||
|
(js-collect-var-names-stmt (nth stmt 4))))
|
||||||
|
((js-tag? stmt "js-for-of-in")
|
||||||
|
(js-collect-var-names-stmt (nth stmt 4)))
|
||||||
|
((js-tag? stmt "js-while")
|
||||||
|
(js-collect-var-names-stmt (nth stmt 2)))
|
||||||
|
((js-tag? stmt "js-do-while")
|
||||||
|
(js-collect-var-names-stmt (nth stmt 1)))
|
||||||
|
((js-tag? stmt "js-if")
|
||||||
|
(append
|
||||||
|
(js-collect-var-names-stmt (nth stmt 2))
|
||||||
|
(if (>= (len stmt) 4) (js-collect-var-names-stmt (nth stmt 3)) (list))))
|
||||||
|
((js-tag? stmt "js-try")
|
||||||
|
(append
|
||||||
|
(js-collect-var-names-stmt (nth stmt 1))
|
||||||
|
(if (and (>= (len stmt) 3) (list? (nth stmt 2)))
|
||||||
|
(js-collect-var-names-stmt (nth (nth stmt 2) 2))
|
||||||
|
(list))
|
||||||
|
(if (>= (len stmt) 4) (js-collect-var-names-stmt (nth stmt 3)) (list))))
|
||||||
|
((js-tag? stmt "js-switch")
|
||||||
|
(js-collect-var-names-cases (nth stmt 2)))
|
||||||
|
(else (list)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
js-collect-var-names-cases
|
||||||
|
(fn
|
||||||
|
(cases)
|
||||||
|
(cond
|
||||||
|
((empty? cases) (list))
|
||||||
|
(else
|
||||||
|
(append
|
||||||
|
(js-collect-var-names (nth (first cases) 2))
|
||||||
|
(js-collect-var-names-cases (rest cases)))))))
|
||||||
|
|
||||||
|
(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
|
(define
|
||||||
js-transpile-tpl
|
js-transpile-tpl
|
||||||
(fn
|
(fn
|
||||||
@@ -577,6 +724,12 @@
|
|||||||
(list (js-sym "js-undefined?") lhs-expr))
|
(list (js-sym "js-undefined?") lhs-expr))
|
||||||
rhs-expr
|
rhs-expr
|
||||||
lhs-expr))
|
lhs-expr))
|
||||||
|
((= op "<<=") (list (js-sym "js-shl") lhs-expr rhs-expr))
|
||||||
|
((= op ">>=") (list (js-sym "js-shr") lhs-expr rhs-expr))
|
||||||
|
((= op ">>>=") (list (js-sym "js-unsigned-rshift") lhs-expr rhs-expr))
|
||||||
|
((= op "&=") (list (js-sym "js-bitand") lhs-expr rhs-expr))
|
||||||
|
((= op "|=") (list (js-sym "js-bitor") lhs-expr rhs-expr))
|
||||||
|
((= op "^=") (list (js-sym "js-bitxor") lhs-expr rhs-expr))
|
||||||
(else (error (str "js-compound-update: unsupported op: " op))))))
|
(else (error (str "js-compound-update: unsupported op: " op))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -806,7 +959,7 @@
|
|||||||
(if
|
(if
|
||||||
(= iter-kind "of")
|
(= iter-kind "of")
|
||||||
(list (js-sym "js-iterable-to-list") iter-sx)
|
(list (js-sym "js-iterable-to-list") iter-sx)
|
||||||
(list (js-sym "js-object-keys") iter-sx))))
|
(list (js-sym "js-for-in-keys") iter-sx))))
|
||||||
(list
|
(list
|
||||||
(js-sym "for-each")
|
(js-sym "for-each")
|
||||||
(list
|
(list
|
||||||
@@ -835,7 +988,7 @@
|
|||||||
(fn
|
(fn
|
||||||
(params)
|
(params)
|
||||||
(cond
|
(cond
|
||||||
((empty? params) (list))
|
((empty? params) (list (js-sym "&rest") (js-sym "__extra_args__")))
|
||||||
((and (list? (first params)) (js-tag? (first params) "js-rest"))
|
((and (list? (first params)) (js-tag? (first params) "js-rest"))
|
||||||
(list (js-sym "&rest") (js-sym (nth (first params) 1))))
|
(list (js-sym "&rest") (js-sym (nth (first params) 1))))
|
||||||
(else
|
(else
|
||||||
@@ -843,6 +996,27 @@
|
|||||||
(js-param-sym (first params))
|
(js-param-sym (first params))
|
||||||
(js-build-param-list (rest params)))))))
|
(js-build-param-list (rest params)))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
js-arguments-build-form
|
||||||
|
(fn
|
||||||
|
(params)
|
||||||
|
(list (js-sym "js-list-copy") (js-arguments-build-form-raw params))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
js-arguments-build-form-raw
|
||||||
|
(fn
|
||||||
|
(params)
|
||||||
|
(cond
|
||||||
|
((empty? params)
|
||||||
|
(js-sym "__extra_args__"))
|
||||||
|
((and (list? (first params)) (js-tag? (first params) "js-rest"))
|
||||||
|
(js-sym (nth (first params) 1)))
|
||||||
|
(else
|
||||||
|
(list
|
||||||
|
(js-sym "cons")
|
||||||
|
(js-param-sym (first params))
|
||||||
|
(js-arguments-build-form-raw (rest params)))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-param-init-forms
|
js-param-init-forms
|
||||||
(fn
|
(fn
|
||||||
@@ -876,7 +1050,7 @@
|
|||||||
(fn
|
(fn
|
||||||
(stmts)
|
(stmts)
|
||||||
(let
|
(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
|
(let
|
||||||
((rest-stmts (js-transpile-stmt-list stmts)))
|
((rest-stmts (js-transpile-stmt-list stmts)))
|
||||||
(cons (js-sym "begin") (append hoisted rest-stmts))))))
|
(cons (js-sym "begin") (append hoisted rest-stmts))))))
|
||||||
@@ -935,12 +1109,12 @@
|
|||||||
|
|
||||||
(define
|
(define
|
||||||
js-transpile-var
|
js-transpile-var
|
||||||
(fn (kind decls) (cons (js-sym "begin") (js-vardecl-forms decls))))
|
(fn (kind decls) (cons (js-sym "begin") (js-vardecl-forms decls (= kind "var")))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
js-vardecl-forms
|
js-vardecl-forms
|
||||||
(fn
|
(fn
|
||||||
(decls)
|
(decls is-var)
|
||||||
(cond
|
(cond
|
||||||
((empty? decls) (list))
|
((empty? decls) (list))
|
||||||
(else
|
(else
|
||||||
@@ -950,10 +1124,10 @@
|
|||||||
((js-tag? d "js-vardecl")
|
((js-tag? d "js-vardecl")
|
||||||
(cons
|
(cons
|
||||||
(list
|
(list
|
||||||
(js-sym "define")
|
(js-sym (if is-var "set!" "define"))
|
||||||
(js-sym (nth d 1))
|
(js-sym (nth d 1))
|
||||||
(js-transpile (nth d 2)))
|
(js-transpile (nth d 2)))
|
||||||
(js-vardecl-forms (rest decls))))
|
(js-vardecl-forms (rest decls) is-var)))
|
||||||
((js-tag? d "js-vardecl-obj")
|
((js-tag? d "js-vardecl-obj")
|
||||||
(let
|
(let
|
||||||
((names (nth d 1))
|
((names (nth d 1))
|
||||||
@@ -964,7 +1138,7 @@
|
|||||||
(js-vardecl-obj-forms
|
(js-vardecl-obj-forms
|
||||||
names
|
names
|
||||||
tmp-sym
|
tmp-sym
|
||||||
(js-vardecl-forms (rest decls))))))
|
(js-vardecl-forms (rest decls) is-var)))))
|
||||||
((js-tag? d "js-vardecl-arr")
|
((js-tag? d "js-vardecl-arr")
|
||||||
(let
|
(let
|
||||||
((names (nth d 1))
|
((names (nth d 1))
|
||||||
@@ -976,7 +1150,7 @@
|
|||||||
names
|
names
|
||||||
tmp-sym
|
tmp-sym
|
||||||
0
|
0
|
||||||
(js-vardecl-forms (rest decls))))))
|
(js-vardecl-forms (rest decls) is-var)))))
|
||||||
(else (error "js-vardecl-forms: unexpected decl"))))))))
|
(else (error "js-vardecl-forms: unexpected decl"))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -1276,7 +1450,28 @@
|
|||||||
(let
|
(let
|
||||||
((body-tr (js-transpile body)))
|
((body-tr (js-transpile body)))
|
||||||
(let
|
(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))))))
|
((with-catch
|
||||||
|
(cond
|
||||||
|
((= catch-part nil) body-tr)
|
||||||
|
(else
|
||||||
|
(let
|
||||||
|
((pname (nth catch-part 0))
|
||||||
|
(cbody (nth catch-part 1))
|
||||||
|
(raw-sym (js-sym "__raw_exc__")))
|
||||||
|
(list
|
||||||
|
(js-sym "guard")
|
||||||
|
(list
|
||||||
|
raw-sym
|
||||||
|
(list
|
||||||
|
(js-sym "else")
|
||||||
|
(cond
|
||||||
|
((= pname nil) (js-transpile cbody))
|
||||||
|
(else
|
||||||
|
(list
|
||||||
|
(js-sym "let")
|
||||||
|
(list (list (js-sym pname) (list (js-sym "js-wrap-exn") raw-sym)))
|
||||||
|
(js-transpile cbody))))))
|
||||||
|
body-tr))))))
|
||||||
(cond
|
(cond
|
||||||
((= finally-part nil) with-catch)
|
((= finally-part nil) with-catch)
|
||||||
(else
|
(else
|
||||||
@@ -1297,7 +1492,7 @@
|
|||||||
(if
|
(if
|
||||||
(and (list? body) (js-tag? body "js-block"))
|
(and (list? body) (js-tag? body "js-block"))
|
||||||
(let
|
(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))))
|
(append hoisted (js-transpile-stmt-list (nth body 1))))
|
||||||
(list (js-transpile body)))))
|
(list (js-transpile body)))))
|
||||||
(list
|
(list
|
||||||
@@ -1305,7 +1500,9 @@
|
|||||||
param-syms
|
param-syms
|
||||||
(list
|
(list
|
||||||
(js-sym "let")
|
(js-sym "let")
|
||||||
(list (list (js-sym "this") (list (js-sym "js-this"))))
|
(list
|
||||||
|
(list (js-sym "this") (list (js-sym "js-this")))
|
||||||
|
(list (js-sym "arguments") (js-arguments-build-form params)))
|
||||||
(list
|
(list
|
||||||
(js-sym "let")
|
(js-sym "let")
|
||||||
(list
|
(list
|
||||||
@@ -1316,7 +1513,7 @@
|
|||||||
(list
|
(list
|
||||||
(js-sym "fn")
|
(js-sym "fn")
|
||||||
(list (js-sym "__return__"))
|
(list (js-sym "__return__"))
|
||||||
(cons (js-sym "begin") (append inits body-forms))))))
|
(cons (js-sym "begin") (append (append inits body-forms) (list nil)))))))
|
||||||
(list
|
(list
|
||||||
(js-sym "if")
|
(js-sym "if")
|
||||||
(list (js-sym "=") (js-sym "__r__") nil)
|
(list (js-sym "=") (js-sym "__r__") nil)
|
||||||
@@ -1333,7 +1530,7 @@
|
|||||||
(if
|
(if
|
||||||
(and (list? body) (js-tag? body "js-block"))
|
(and (list? body) (js-tag? body "js-block"))
|
||||||
(let
|
(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))))
|
(append hoisted (js-transpile-stmt-list (nth body 1))))
|
||||||
(list (js-transpile body)))))
|
(list (js-transpile body)))))
|
||||||
(list
|
(list
|
||||||
@@ -1401,7 +1598,7 @@
|
|||||||
(fn
|
(fn
|
||||||
(src)
|
(src)
|
||||||
(let
|
(let
|
||||||
((result (eval-expr (js-transpile (js-parse (js-tokenize src))))))
|
((result (eval-expr (list (quote let) (list (list (js-sym "this") (list (js-sym "js-this")))) (js-transpile (js-parse (js-tokenize src)))))))
|
||||||
(js-drain-microtasks!)
|
(js-drain-microtasks!)
|
||||||
result)))
|
result)))
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ You are the sole background agent working `/root/rose-ash/plans/js-on-sx.md`. A
|
|||||||
|
|
||||||
## Current state (restart baseline — verify before iterating)
|
## Current state (restart baseline — verify before iterating)
|
||||||
|
|
||||||
- Branch: `architecture`. HEAD: `14b6586e` (HS-related, not js-on-sx).
|
- Branch: `loops/js`.
|
||||||
- `lib/js/` is **untracked** — nothing is committed yet. First commit should stage everything current on disk.
|
- `lib/js/` is **untracked** — nothing is committed yet. First commit should stage everything current on disk.
|
||||||
- `lib/js/test262-upstream/` is a clone of tc39/test262 pinned at `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33`. **Gitignore it** (`lib/js/.gitignore` → `test262-upstream/`). Do not commit the 50k test files.
|
- `lib/js/test262-upstream/` is a clone of tc39/test262 pinned at `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33`. **Gitignore it** (`lib/js/.gitignore` → `test262-upstream/`). Do not commit the 50k test files.
|
||||||
- `lib/js/test262-runner.py` exists but is buggy — current scoreboard is `0/8 (7 timeouts, 1 fail)`. The runner needs real work: harness script loading, batching, per-test timeout tuning, strict-mode skipping.
|
- `lib/js/test262-runner.py` exists but is buggy — current scoreboard is `0/8 (7 timeouts, 1 fail)`. The runner needs real work: harness script loading, batching, per-test timeout tuning, strict-mode skipping.
|
||||||
@@ -61,7 +61,7 @@ Tagged dict: `{:__js_string__ true :utf16 <list-of-uint16> :str <lazy-utf8-cache
|
|||||||
- **Scope:** only `lib/js/**` and `plans/js-on-sx.md`. Do NOT touch `spec/`, `shared/`, `lib/hyperscript/`. Shared-file issues go under the plan's "Blockers" section.
|
- **Scope:** only `lib/js/**` and `plans/js-on-sx.md`. Do NOT touch `spec/`, `shared/`, `lib/hyperscript/`. Shared-file issues go under the plan's "Blockers" section.
|
||||||
- **SX files:** `sx-tree` MCP tools ONLY. `sx_summarise` / `sx_read_subtree` / `sx_find_all` / `sx_get_context` before edits. `sx_replace_node` / `sx_insert_child` / `sx_insert_near` / `sx_replace_by_pattern` / `sx_rename_symbol` for edits. `sx_validate` after. `sx_write_file` for new files. Never `Edit`/`Read`/`Write` on `.sx`.
|
- **SX files:** `sx-tree` MCP tools ONLY. `sx_summarise` / `sx_read_subtree` / `sx_find_all` / `sx_get_context` before edits. `sx_replace_node` / `sx_insert_child` / `sx_insert_near` / `sx_replace_by_pattern` / `sx_rename_symbol` for edits. `sx_validate` after. `sx_write_file` for new files. Never `Edit`/`Read`/`Write` on `.sx`.
|
||||||
- **Shell, Python, Markdown, JSON:** edit normally.
|
- **Shell, Python, Markdown, JSON:** edit normally.
|
||||||
- **Branch:** `architecture`. Commit locally. Never push. Never touch `main`.
|
- **Branch:** `loops/js`. Commit, then push to `origin/loops/js`. Never touch `main`.
|
||||||
- **Commit granularity:** one feature per commit. Short, factual commit messages. Commit even if a partial fix — don't hoard changes.
|
- **Commit granularity:** one feature per commit. Short, factual commit messages. Commit even if a partial fix — don't hoard changes.
|
||||||
- **Tests:** `bash lib/js/test.sh` (254/254 baseline) and `bash lib/js/conformance.sh` (148/148 baseline). Never regress. If a feature requires larger refactor, split into multiple commits each green.
|
- **Tests:** `bash lib/js/test.sh` (254/254 baseline) and `bash lib/js/conformance.sh` (148/148 baseline). Never regress. If a feature requires larger refactor, split into multiple commits each green.
|
||||||
- **Plan file:** append one paragraph per iteration to "Progress log". Tick `[x]` boxes. Don't rewrite history.
|
- **Plan file:** append one paragraph per iteration to "Progress log". Tick `[x]` boxes. Don't rewrite history.
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green.
|
|||||||
- [x] Punctuation: `( ) { } [ ] , ; : . ...`
|
- [x] Punctuation: `( ) { } [ ] , ; : . ...`
|
||||||
- [x] Operators: `+ - * / % ** = == === != !== < > <= >= && || ! ?? ?: & | ^ ~ << >> >>> += -= ...`
|
- [x] Operators: `+ - * / % ** = == === != !== < > <= >= && || ! ?? ?: & | ^ ~ << >> >>> += -= ...`
|
||||||
- [x] Comments (`//`, `/* */`)
|
- [x] Comments (`//`, `/* */`)
|
||||||
- [ ] Automatic Semicolon Insertion (defer — initially require semicolons)
|
- [x] Automatic Semicolon Insertion (defer — initially require semicolons)
|
||||||
|
|
||||||
### Phase 2 — Expression parser (Pratt-style)
|
### Phase 2 — Expression parser (Pratt-style)
|
||||||
- [x] Literals → AST nodes
|
- [x] Literals → AST nodes
|
||||||
@@ -124,7 +124,7 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green.
|
|||||||
- [x] Closures — work via SX `fn` env capture
|
- [x] Closures — work via SX `fn` env capture
|
||||||
- [x] Rest params (`...rest` → `&rest`)
|
- [x] Rest params (`...rest` → `&rest`)
|
||||||
- [x] Default parameters (desugar to `if (param === undefined) param = default`)
|
- [x] Default parameters (desugar to `if (param === undefined) param = default`)
|
||||||
- [ ] `var` hoisting (deferred — treated as `let` for now)
|
- [x] `var` hoisting (shallow — collects direct `var` decls, emits `(define name :js-undefined)` before funcdecls)
|
||||||
- [ ] `let`/`const` TDZ (deferred)
|
- [ ] `let`/`const` TDZ (deferred)
|
||||||
|
|
||||||
### Phase 8 — Objects, prototypes, `this`
|
### Phase 8 — Objects, prototypes, `this`
|
||||||
@@ -158,6 +158,272 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green.
|
|||||||
|
|
||||||
Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta.
|
Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`String.prototype.repeat` no longer arity-collides with itself; raises RangeError on negative or +Infinity counts.** Earlier JSON.stringify iteration introduced a 2-arg `js-string-repeat` that shadowed the existing 3-arg `(s n acc)` accumulator implementation, breaking every `s.repeat(n)` call with "expects 2 args, got 3". Renamed the accumulator helper to `js-string-repeat-loop` and made `js-string-repeat` a 2-arg facade that delegates. Hooked the repeat method to raise RangeError when `count < 0` or `count = Infinity` per spec. Result: built-ins/String/prototype/repeat 7/13 → 11/13 (+4). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **test262-runner inlines small upstream harness includes (`nans.js`, `sta.js`, `byteConversionValues.js`, `compareArray.js`) per-test.** The runner parsed `includes:` frontmatter but never used it, so tests like `built-ins/isNaN/return-true-nan.js` (which depends on `var NaNs = [...]`) failed with "ReferenceError: undefined symbol". Added `_load_harness_include` (cached) and `assemble_source` now prepends each allowlisted include's source to the test. Allowlist excludes large helpers like `propertyHelper.js` because per-test js-eval+JIT cost on a 371-line harness pushes tests over the 15s per-test timeout (regressed Math/abs 7/7 → 4/7 in a first-pass attempt before allowlisting). Result: built-ins/isNaN 2/7 → 3/7. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **Real `Date.prototype.setFullYear/setMonth/setDate/setHours/setMinutes/setSeconds/setMilliseconds` (+ UTC variants) and a corrected `setTime`.** All Date setters were missing — only `setTime` existed and didn't validate. Added a unified `js-date-setter(d, field, args)` that decomposes the current ms into `(y mo da hh mm ss msv)` via `js-date-decompose`, splices in the `args` per the field's optional-arg contract (e.g. `setHours(h, m?, s?, ms?)`), recomposes via `js-date-civil-to-days`, and TimeClips at ±8.64e15. NaN args anywhere → ms set to NaN. Wired all 14 setters to the helper. Hit a parser gotcha: SX `cond` clause body is single-form only — multi-expression bodies like `(else (dict-set! ...) new-ms)` silently treat the second form as `(<first-result> new-ms)` ("Not callable: false"). Wrapped these in `(begin ...)`. Result: setFullYear 5/18 → 13/18 (+8). setHours 5/21 → 15/21 (+10). setMonth 3/15 → 9/15 (+6). setMinutes 4/16 → 10/16 (+6). setSeconds 3/15 → 9/15 (+6). setDate 2/12 → 6/12 (+4). setMilliseconds 2/12 → 6/12 (+4). setTime 4/9 → 6/9 (+2). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`Object.assign` keys now visible to `Object.keys` / `JSON.stringify`.** `Object.assign({}, {a:1})` was mutating the target via `dict-set!` which bypasses our `__js_order__` insertion-order side table; `Object.keys(t)` (which iterates `__js_order__` when present) returned `[]`, and `JSON.stringify` saw nothing. Switched `js-object-assign` to use `js-set-prop` (which calls `js-obj-order-add!` on new keys) for both dict and string sources. Result: built-ins/Object/assign 13/25 → 14/25. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **User functions' `prototype` chain through Object.prototype + auto-set `constructor`.** Per ES spec, every function's `prototype` slot defaults to `{ constructor: F, __proto__: Object.prototype }`. Our `js-get-ctor-proto` lazily created a fresh empty `(dict)` for user functions on first access — so `(new F) instanceof Object` was `false`, `F.prototype.constructor` was undefined, and `x.constructor === F` failed. Now the lazy-init seeds the proto with `__proto__ → Object.prototype` and `constructor → F` before caching in `__js_proto_table__`. Result: language/expressions/instanceof 25/30 → 26/30. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **Postfix `++`/`--` reject a preceding LineTerminator (ASI).** Per ES spec, `x\n++;` is a syntax error: no LineTerminator allowed between LHS and postfix `++`/`--`. Our `jp-parse-postfix` was matching `++`/`--` regardless of whether the preceding token had `:nl true`. Added `(not (jp-token-nl? st))` guard so newline-before-`++` makes the postfix arm fall through, the `++` then becomes a prefix-expr starting a new statement, which fails to parse and the runner classifies as SyntaxError. Result: language/expressions/postfix-increment 16/30 → 18/30 (+2). postfix-decrement 16/30 → 18/30 (+2). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **Parse-time SyntaxError when `let`/`const`/`function`/`class` appear as a single-statement body of `if`/`while`/`do`/`for`/labeled.** Per ES grammar, those positions accept a Statement, not a Declaration — only block bodies (`{ ... }`) may contain Declarations. Added `jp-disallow-decl-stmt!` helper that, when the next token is a Declaration keyword in single-statement context, raises SyntaxError. The `let` arm checks for `let <ident>`, `let [`, or `let {` to avoid mis-rejecting `let;` (where `let` is just an identifier expression). Hook calls in `jp-parse-if-stmt` (then + else branches), `jp-parse-while-stmt`, `jp-parse-do-while-stmt`, both for-of/in and C-for body sites, and the labeled-statement entry. Result: language/statements/while 16/30 → 20/30. statements/labeled 4/15 → 7/15. statements/if 20/30 → 21/30. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **Parse-time SyntaxError for `break`/`continue` outside loops/switches and `return` outside functions; `void <expr>` evaluates `<expr>` for side effects.** Parser tracks `:loop-depth`, `:switch-depth`, and `:fn-depth` on the state dict (initialized to 0). `jp-parse-while-stmt`, `jp-parse-do-while-stmt`, `jp-parse-for-stmt` (both for-of/in and C-for) bump `:loop-depth` around body parsing; `jp-parse-switch-stmt` bumps `:switch-depth`; new `jp-parse-fn-body` and `jp-parse-arrow-body` save+reset loop/switch depth and bump `:fn-depth` (so `break` inside an outer loop's nested function is rejected). Bare `break` requires `loop-depth > 0 OR switch-depth > 0`; bare `continue` requires `loop-depth > 0`; `return` requires `fn-depth > 0`. Separately, `void <expr>` was compiling to just `:js-undefined` (dropping the expression entirely); now `(begin <expr> :js-undefined)` so side effects fire. Result: language/statements/return 4/15 → 14/15 (+10). statements/break 9/20 → 12/20. statements/continue 12/24 → 15/24. expressions/void 7/9 → 8/9. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`Math.hypot` and `Math.cbrt` honour spec edges for NaN, ±Infinity, and ±0.** `Math.hypot(NaN, Infinity)` was returning NaN instead of +Infinity (spec: any ±Infinity arg dominates NaN). Rewrote `js-math-hypot` to scan args once tracking inf/nan flags, return +Infinity if any arg is ±Infinity, else NaN if any was NaN, else `sqrt(sum of squares)`. `Math.cbrt(NaN)` was 0 (because `pow(NaN, 1/3)` produced 0 in our path); also `Math.cbrt(-0)` returned +0 instead of -0. Added explicit short-circuits: NaN→NaN, ±Infinity→arg, ±0→arg, plus changed `(/ 1 3)` (rational) to `(/ 1.0 3.0)` (inexact) to avoid rational fractional-power oddities. Result: built-ins/Math/hypot 9/11 → 10/11. Math/cbrt 3/4 → 4/4. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`globalThis.globalThis === globalThis`; `Number.prototype.toFixed` honours digit-range and ≥1e21 fallback.** (1) `globalThis` was bound to `nil` in the global object literal (originally to dodge an inspect-cycle hang) — added `(dict-set! js-global "globalThis" js-global)` after the literal so `globalThis.globalThis === globalThis` per spec. (2) `Number.prototype.toFixed` rewrites: RangeError when fractionDigits is NaN or outside `[0,100]` (was silently producing garbage), and for `|x| >= 1e21` returns `js-number-to-string` (the value's own ToString) per spec step 9. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`delete <ident>` returns `false` instead of `true` per non-strict spec.** ES non-strict semantics: `delete x` where `x` is a declared binding (variable / function / parameter) returns `false` and does not unbind. Our transpiler was emitting `true` for any `delete <expr>` whose argument wasn't a member or index access. Now `delete <js-ident>` → `false`, and `delete <js-paren expr>` recurses on the inner expression so `delete (1+2)` still works. Result: language/expressions/delete 14/30 → 18/30 (+4). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **Parser rejects unary-op directly before `**` (e.g. `-1 ** 2`, `delete o.p ** 2`, `!x ** 2`, `~x ** 2`) per ES spec.** ES disallows `UnaryExpression ** ExponentiationExpression`; only `UpdateExpression ** ExponentiationExpression` and `(<UnaryExpr>) ** ...` are legal. Added a guard in `jp-binary-loop`: when op is `**` and the LHS is a `(js-unop ...)` node, raise SyntaxError. Parens are made transparent for everything except this check via a new `jp-paren-wrap` helper that emits `(js-paren <unop>)` only when wrapping an explicit unary op (so `(-1) ** 2` parses fine), and a new `js-paren` AST tag in `js-transpile` that just unwraps. Result: language/expressions/exponentiation 25/30 → 28/30 (+3). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`Math.round` / `Math.max` / `Math.min` honour spec edge cases for NaN, ±Infinity, and ±0.** `Math.round(NaN)` was returning 0 because `floor(NaN+0.5)` doesn't propagate NaN; ditto `±Infinity` paths. `Math.max({})` silently returned `-Infinity` (initial accumulator) because the first arg wasn't ToNumber'd. `Math.max(0, -0)` returned `-0` because `>` doesn't distinguish them. Rewrites: round NaN/±Infinity/±0 short-circuits; max/min ToNumber the first arg, propagate NaN immediately, and use a `js-is-positive-zero?` (rational-safe) tiebreaker so `Math.max(0, -0) === 0` per spec. Result: built-ins/Math/round 5/10 → 8/10 (+3). Math/max 6/9 → 8/9 (+2). Math/min 6/9 → 8/9 (+2). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`Map.prototype.*` and `Set.prototype.*` raise TypeError when called on non-Map / non-Set `this`.** All five `js-map-do-*` and four `js-set-do-*` helpers were assuming `this` had `__map_keys__` / `__set_items__`, so `Map.prototype.clear.call({})` silently returned undefined (after creating dangling state) instead of throwing. Added `js-map-check!` / `js-set-check!` guards run as the first step of each method; raise spec-correct `TypeError` instances. Result: built-ins/Map 18/30 → 22/30 (+4). built-ins/Set 15/30 → 28/30 (+13). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`Date.UTC` / `new Date(...)` propagate NaN/±Infinity arguments and return NaN.** `Date.UTC()` (no args) returned 0 instead of NaN; `Date.UTC(NaN, ...)` did the math and produced bogus ms; `new Date(year, NaN)` constructed a normal Date instead of an invalid one. Added `js-date-args-have-nan?` (also detects ±Infinity and propagates from rationals) used by both `Date.UTC` and the multi-arg constructor branch; UTC now returns NaN on no-arg / any-NaN-arg / out-of-range result, and `new Date(args)` stores NaN in `__date_value__` when any arg is NaN. Also fixed `js-date-from-one(undefined)` to return NaN. Result: built-ins/Date/UTC 6/16 → 10/16 (+4). Date 17/30 → 26/30 (timeouts dropped from 12 → 4 because invalid Dates now short-circuit). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **Real `Date` construction + getters via Howard-Hinnant civil-day arithmetic.** `js-date-from-parts` now computes a true ms-since-epoch from `(year, month, day, hour, min, sec, ms)` via `js-date-civil-to-days` (the inverse of last iteration's `days-to-ymd`), with the legacy 2-digit-year coercion (0..99 → 1900+y). `getFullYear/Month/Date/Day/Hours/Minutes/Seconds/Milliseconds` (UTC + non-UTC) all share a new `js-date-getter`: TypeErrors on non-Date this, returns NaN on invalid time, otherwise decomposes ms into y/m/d/h/m/s/ms/dow. Plus added `Date.prototype.constructor = Date` (was missing). Result: each of the 8 Date getter categories went 2/6 → 5/6 (+3 each, +24 total). Date toISOString 11/16 → 13/16. Some Date construction-loop tests now exceed the 15s per-test timeout — the new civil math is heavier than the old (year-1970)*ms-per-year approximation, but correctness wins. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`Date.prototype.toISOString` produces real `YYYY-MM-DDTHH:mm:ss.sssZ` and validates input.** Old `js-date-iso` only computed the year and hardcoded the rest as `01-01T00:00:00.000Z`. Added: (1) TypeError when this isn't a Date (no `__js_is_date__` slot); (2) RangeError when ms is NaN, undefined, or |ms| > 8.64e15; (3) full date breakdown via Howard-Hinnant `days_to_civil` algorithm (`js-date-days-to-ymd`) → year/month/day, plus modular hours/min/sec/ms; (4) extended-year format `±YYYYYY` for years outside 0..9999. Result: built-ins/Date/prototype/toISOString 7/16 → 11/16 (+4). Date 21/30. conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`JSON.stringify` honours `replacer` (function + array forms), `space`, and `toJSON`.** Previous impl ignored the second/third arguments entirely and never called `toJSON`. Rewrote around a `js-json-serialize-property(key, holder, rep-fn, rep-keys, gap, indent)` core: walks `toJSON` first, then replacer-fn (with `holder` as `this`); arrays-as-replacer become a property-name allowlist; numeric `space` clamped to 0..10 spaces, string `space` truncated to 10 chars, non-empty gap activates indented output with `:` → `: ` separator. Number wrapper / String wrapper / Boolean wrapper unwrap before serialization; non-finite numbers serialize as `"null"`; functions serialize as `undefined`. Result: built-ins/JSON/stringify 6/30 → 14/30 (+8). conformance.sh: 148/148.
|
||||||
|
|
||||||
|
- 2026-05-10 — **`JSON.parse` raises spec-correct `SyntaxError` instances and rejects malformed input.** Previously `JSON.parse("12 34")` silently returned `12` (no trailing-content check), `JSON.parse('" | ||||||