js-on-sx: regex literal lex+parse+transpile+runtime stub

Lexer: js-regex-context? disambiguates / based on prior token;
read-regex handles [...] classes and \ escapes. Emits
{:type "regex" :value {:pattern :flags}}.

Parser: new primary branch → (js-regex pat flags).

Transpile: (js-regex-new pat flags).

Runtime: js-regex? predicate, js-regex-new builds tagged dict with
source/flags/global/ignoreCase/multiline/sticky/unicode/dotAll/
hasIndices/lastIndex. js-regex-invoke-method dispatches .test/.exec/
.toString. js-invoke-method detects regex receivers. Stub engine
uses js-string-index-of; __js_regex_platform__ + override! let a
real engine plug in later.

Runner: repeatable --filter flags (OR'd).

308/310 unit (+30 regex tests), 148/148 slice unchanged.
This commit is contained in:
2026-04-23 20:27:19 +00:00
parent 6b0334affe
commit ce46420c2e
7 changed files with 337 additions and 4 deletions

View File

@@ -386,6 +386,91 @@
(and (= (len parts) 1) (= (nth (nth parts 0) 0) "str"))
(nth (nth parts 0) 1)
parts)))))
(define
js-regex-context?
(fn
()
(if
(= (len tokens) 0)
true
(let
((tk (nth tokens (- (len tokens) 1))))
(let
((ty (dict-get tk "type")) (vv (dict-get tk "value")))
(cond
((= ty "punct")
(and (not (= vv ")")) (not (= vv "]"))))
((= ty "op") true)
((= ty "keyword")
(contains?
(list
"return"
"typeof"
"in"
"of"
"throw"
"new"
"delete"
"instanceof"
"void"
"yield"
"await"
"case"
"do"
"else")
vv))
(else false)))))))
(define
read-regex
(fn
()
(let
((buf (list)) (in-class false))
(advance! 1)
(define
body-loop
(fn
()
(cond
((>= pos src-len) nil)
((= (cur) "\\")
(begin
(append! buf (cur))
(advance! 1)
(when
(< pos src-len)
(begin (append! buf (cur)) (advance! 1)))
(body-loop)))
((= (cur) "[")
(begin
(set! in-class true)
(append! buf (cur))
(advance! 1)
(body-loop)))
((= (cur) "]")
(begin
(set! in-class false)
(append! buf (cur))
(advance! 1)
(body-loop)))
((and (= (cur) "/") (not in-class)) (advance! 1))
(else
(begin (append! buf (cur)) (advance! 1) (body-loop))))))
(body-loop)
(let
((flags-buf (list)))
(define
flags-loop
(fn
()
(when
(and (< pos src-len) (js-ident-char? (cur)))
(begin
(append! flags-buf (cur))
(advance! 1)
(flags-loop)))))
(flags-loop)
{:pattern (join "" buf) :flags (join "" flags-buf)}))))
(define
try-op-4!
(fn
@@ -510,6 +595,11 @@
word
start))
(scan!)))
((and (= ch "/") (js-regex-context?))
(let
((rx (read-regex)))
(js-emit! "regex" rx start)
(scan!)))
((try-op-4! start) (scan!))
((try-op-3! start) (scan!))
((try-op-2! start) (scan!))