File size: 1,032 Bytes
521b329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
"""JavaScript regex semantics via embedded QuickJS."""
import json
import quickjs

_JS = r"""
function compiles(p, f) { try { new RegExp(p, f); return true } catch (e) { return false } }
function tests(p, f, xs) { const r = new RegExp(p, f); return JSON.stringify(JSON.parse(xs).map(s => r.test(s))) }
"""

_ctx = quickjs.Context()
_ctx.set_time_limit(0.05)  # seconds; guards against catastrophic backtracking
_ctx.eval(_JS)
_compiles, _tests = _ctx.get("compiles"), _ctx.get("tests")


def compiles(pattern: str, flags: str = "") -> bool:
    return bool(_compiles(pattern, flags))


def test(pattern: str, flags: str, strings: list[str]) -> list[bool]:
    return json.loads(_tests(pattern, flags, json.dumps(strings)))


def literal(pattern: str, flags: str = "") -> str:
    return f"/{pattern}/{flags}"


def parse_literal(s: str) -> tuple[str, str]:
    s = s.strip()
    if not s.startswith("/") or s.rfind("/") == 0:
        raise ValueError(f"not a regex literal: {s!r}")
    i = s.rfind("/")
    return s[1:i], s[i + 1:]