nx_rxfull.nx
buildroot/runtime/nx_rxfull.nx
about
nx_regex.nx -- SOVEREIGN regex engine (operator 2026-07-08/09: push the JS engine to run modern bundles;
regex is the last bundle-blocker + a PARSE-level one). Standalone + self-gated so it is proven before wiring
into the JS lexer/RegExp. Architecture = Cox's "regex VM" (compile the pattern to a flat INSTRUCTION PROGRAM,
then a backtracking matcher over it) -- flat dispatch dodges nx_cc's deep-else-nest miscompile, and it makes
captures + step-bounding clean. STEP + DEPTH bounded => a hostile bundle regex can NEVER hang/crash the crawler
(fails safe to no-match). Covers: literals, ., char classes [..] (ranges/negation/predef), \d\w\s\D\W\S,
anchors ^ $, quantifiers * + ? and {n} {n,} {n,m} (greedy + lazy ?), groups (capturing + (?:..)), alternation
|, escapes, \b \B word-boundary, flags i (icase) m (multiline) s (dotall). Leftmost match + capture groups.
license_tier: ORIGINAL
dependencies 1 imports · 2 importers
imports: nx_syscalls.nx
imported by: nx_js_eval.nxnx_regex_gate.nx
structs
| 69 | struct Regex { prog: *i64, nprog: i64, classes: *i64, ngroup: i64, flags: i64, nsave: i64, ok: i64, nmark: i64 } |
| 72 | struct RxP { pat: *u8, plen: i64, pos: i64, nodes: *i64, nn: i64, classes: *i64, nc: i64, ngroup: i64, flags: i64, err: i64 } |
| 75 | struct Rx { s: *u8, slen: i64, prog: *i64, classes: *i64, flags: i64, saves: *i64, fuel: *i64, marks: *i64 } |
consts
| 13 | const RE_CHAR: i64 = 1 // a = charcode (lowered if icase) |
| 14 | const RE_ANY: i64 = 2 // any char (not newline unless dotall) |
| 15 | const RE_CLASS: i64 = 3 // a = class-table base |
| 16 | const RE_SPLIT: i64 = 4 // a,b = two pcs to try (a first) |
| 17 | const RE_JMP: i64 = 5 // a = pc |
| 18 | const RE_SAVE: i64 = 6 // a = capture slot |
| 19 | const RE_MATCH: i64 = 7 |
| 20 | const RE_BOL: i64 = 8 // ^ |
| 21 | const RE_EOL: i64 = 9 // $ |
| 22 | const RE_WORDB: i64 = 10 // \b |
| 23 | const RE_NWORDB: i64 = 11 // \B |
| 24 | const RE_AHEAD: i64 = 12 // a=negate(0 pos/1 neg) b=after-pc; ZERO-WIDTH sub-match of [p+1 .. RE_AEND) |
| 25 | const RE_AEND: i64 = 13 // terminates a lookahead sub-match program (acts like MATCH for the sub-run) |
| 26 | const RE_BREF: i64 = 14 // a=groupnum; re-match the text a prior capture group matched (JS \1..\9) |
| 27 | const RE_MARK: i64 = 15 // a=slot; record current input pos into marks[slot] (loop-entry stamp) |
| 28 | const RE_BACK: i64 = 16 // a=slot b=target; take the loop back-edge to `target` ONLY if pos advanced since |
| 31 | const RE_LOOPA: i64 = 17 // a=min(0 star/1 plus) b=greedy; the SINGLE consuming atom insn sits at p+1 and the |
| 37 | const RX_CHAR: i64 = 1 // a=code |
| 38 | const RX_ANY: i64 = 2 |
| 39 | const RX_CLASS: i64 = 3 // a=class base |
| 40 | const RX_CONCAT: i64 = 4 // a=left b=right |
| 41 | const RX_ALT: i64 = 5 // a=left b=right |
| 42 | const RX_STAR: i64 = 6 // a=child b=greedy |
| 43 | const RX_PLUS: i64 = 7 // a=child b=greedy |
| 44 | const RX_QUEST: i64 = 8 // a=child b=greedy |
| 45 | const RX_REPEAT: i64 = 9 // a=child b=min c=max(-1=inf) d=greedy |
| 46 | const RX_GROUP: i64 = 10 // a=child b=capturing c=groupnum |
| 47 | const RX_BOL: i64 = 11 |
| 48 | const RX_EOL: i64 = 12 |
| 49 | const RX_WORDB: i64 = 13 |
| 50 | const RX_NWORDB: i64 = 14 |
| 51 | const RX_EMPTY: i64 = 15 |
| 52 | const RX_LOOKAHEAD: i64 = 16 // a=child b=negate(0 pos/1 neg) -- (?=..) / (?!..) |
| 53 | const RX_BACKREF: i64 = 17 // a=groupnum -- \1..\9 |
| 56 | const RXF_I: i64 = 1 |
| 57 | const RXF_M: i64 = 2 |
| 58 | const RXF_S: i64 = 4 |
| 59 | const RXF_G: i64 = 8 |
| 62 | const RX_MAXNODE: i64 = 8192 |
| 63 | const RX_MAXPROG: i64 = 16384 |
| 64 | const RX_MAXCLASS: i64 = 8192 |
| 65 | const RX_MAXREP: i64 = 1000 // {n,m} expansion clamp |
| 66 | const RX_MAXDEPTH: i64 = 6000 // backtracking recursion cap (stack-overflow guard) |
| 67 | const RX_FUEL: i64 = 5000000 // total backtracking steps (catastrophic-backtrack guard) |
| 70 | const REGEX_BYTES: i64 = 64 |
| 73 | const RXP_BYTES: i64 = 80 |
| 76 | const RX_BYTES: i64 = 64 |
functions
| 79 | func rx_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c } |
| 80 | func rx_swapcase(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } if c >= 97 { if c <= 122 { return c - 32 } } return c } called by 1: rx_class_match |
| 81 | func rx_isword(c: i64) -> i64 { if c >= 48 { if c <= 57 { return 1 } } if c >= 65 { if c <= 90 { return 1 } } if c >= 97 { if c <= 122 { return 1 } } if c == 95 { return 1 } return 0 } called by 1: rx_wordb |
| 84 | func rp_peek(p: *RxP) -> i64 { if p.pos < p.plen { return (p.pat[p.pos]) as i64 } return 0 - 1 } |
| 85 | func rp_at(p: *RxP, off: i64) -> i64 { let q: i64 = p.pos + off; if q < p.plen { return (p.pat[q]) as i64 } return 0 - 1 } |
| 86 | func rp_adv(p: *RxP) -> i64 { p.pos = p.pos + 1; return 0 } |
| 88 | func rp_node(p: *RxP, kind: i64, a: i64, b: i64, c: i64) -> i64 |
| 99 | func rp_add_range(p: *RxP, lo: i64, hi: i64) -> i64 { let cl: *i64 = p.classes; if p.nc + 2 > RX_MAXCLASS { p.err = 1; return 0 } cl[p.nc] = lo; cl[p.nc + 1] = hi; p.nc = p.nc + 2; return 0 } |
| 101 | func rp_add_predef(p: *RxP, which: i64) -> i64 |
| 107 | func rp_predef_node(p: *RxP, which: i64, neg: i64) -> i64 |
| 121 | func rp_class_esc_val(p: *RxP) -> i64 |
| 143 | func rp_class(p: *RxP) -> i64 |
| 177 | func rp_class_lit(p: *RxP, lo: i64) -> i64 |
| 191 | func rx_hexval(c: i64) -> i64 |
| 198 | func rx_esc_char(e: i64) -> i64 |
| 209 | func rp_escape(p: *RxP) -> i64 |
| 257 | func rp_backref_node(p: *RxP, g: i64) -> i64 { return rp_node(p, RX_BACKREF, g, 0, 0) } |
| 260 | func rp_atom(p: *RxP) -> i64 |
| 305 | func rp_brace(p: *RxP, box: *i64) -> i64 |
| 332 | func rp_repeat(p: *RxP) -> i64 |
| 349 | func rp_lazy(p: *RxP) -> i64 { if rp_peek(p) == 63 { rp_adv(p); return 0 } return 1 } |
| 351 | func rp_wrap(p: *RxP, kind: i64, child: i64, x: i64, y: i64, g: i64) -> i64 |
| 361 | func rp_concat(p: *RxP) -> i64 |
| 379 | func rp_alt(p: *RxP) -> i64 |
| 394 | func rc_emit(prog: *i64, npp: *i64, op: i64, a: i64, b: i64) -> i64 |
| 402 | func rx_single_atom(nodes: *i64, node: i64) -> i64 |
| 412 | func rc_star(nodes: *i64, child: i64, prog: *i64, npp: *i64, greedy: i64) -> i64 |
| 429 | func rc(nodes: *i64, node: i64, prog: *i64, npp: *i64) -> i64 |
| 527 | func rx_class_match(rx: *Rx, cbase: i64, ch: i64) -> i64 |
| 542 | func rx_wordb(rx: *Rx, i: i64) -> i64 |
| 552 | func re_bt(rx: *Rx, pc: i64, sp: i64, depth: i64) -> i64 |
| 702 | func nx_regex_compile(pat: *u8, plen: i64, flags: i64) -> *Regex |
| 725 | func nx_regex_exec(re: *Regex, s: *u8, slen: i64, start: i64, saves: *i64) -> i64 |
| 750 | func nx_regex_test(re: *Regex, s: *u8, slen: i64) -> i64 |
| 756 | func nx_regex_flags(fs: *u8, flen: i64) -> i64 called by 1: js_make_regex_val |