code wiki / (root) / nx_js_parse.nx

nx_js_parse.nx

buildroot/runtime/nx_js_parse.nx

124093 B2485 linesdepth 4pulls 4 transitivereach 143 importersview sourcekind tooltopic js
docsdependenciesstructsconstsfunctions

about

nx_js_parse.nx -- R-JS-PARSE (WB-JS-001 rung 1): the ECMAScript PARSER, riding the rung-0 tokenizer (nx_js_lex.nx) per the no-floating law -- the lexer is built + gated BEFORE this parser composes it. Shape = RECURSIVE-DESCENT over the flat token stream, producing an AST in a FLAT i64 ARENA (parallel slots per node, NO struct complexity). Founds R-JS-EVAL above it (the interpreter rides the AST). Consumes nx_js_lex's token stream (3 i64/token: kind, start-offset, byte-length) and js_lexeme_eq for keyword/punct matching. Produces nodes in a node arena (NODE_SLOTS i64 each) + a child-index arena for variable-arity lists (PROGRAM statement list, BLOCK body, CALL arguments, function params). SCOPE (rung 1) -- every sub-feature below is now GATED by a KAT that asserts its node (council BLOCKER fix: the prior 'ALL gated' header listed paths with ZERO coverage -- string/null/bool/typeof/unary +-/ the '/ %' '< <= > >=' '!= === !==' operator families were code-present-but-unasserted; KAT9/10 now assert them): - primary: identifier, number, STRING, true/false (BOOL), null, ( expr ) - postfix chains (left-assoc): member a.b, computed index a[b], call f(args...) - unary prefix: ! - + typeof - binary with CORRECT precedence + associativity (highest -> lowest): (* / %) > (+ -) > (< <= > >=) > (== != === !==) > (&&) > (||) all left-associative; assignment `=` is RIGHT-assoc and LOWEST. - statements: var/let/const decl (optional initializer), expression statement, return [expr] ;, block { ... }, if (cond) stmt [else stmt], while (cond) stmt, function decl function name(params){body} - PROGRAM = list of statements ERROR CONTRACT (honest -- what the parser DOES and does NOT enforce): - Semicolons are OPTIONAL via ASI-lite: a statement may end without ';' ONLY at a line terminator, before '}', or at EOF. Two statements on ONE line with no separator (`a b`, `1 2`) are REJECTED (ERROR node + err flag) -- real JS SyntaxError. (Council BLOCKER fix: previously `;` was unconditionally optional so adjacent same-line statements were silently accepted.) - Assignment requires a valid lvalue target (IDENT / MEMBER / INDEX). '1 = 2' and '(a+b) = c' are REJECTED (real JS: Invalid left-hand side). (BLOCKER fix.) - Unbalanced parens, missing operands, declarations with no name, dangling operators, unterminated blocks -> ERROR node + err flag. - NOT enforced (honest OPEN, NOT claimed): full ASI restricted-production rules (e.g. `return`-newline-expr nuance), strict-mode lvalue refinements, label scoping. These are rung-1b; the gate does NOT assert them.

dependencies 2 imports · 2 importers

nx_js_lex.nx nx_itoa_lib.nx nx_js_parse.nx nx_js_eval.nx nx_js_octane_next_diag.nx

imports: nx_js_lex.nxnx_itoa_lib.nx

imported by: nx_js_eval.nxnx_js_octane_next_diag.nx

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main jq_puts jq_parse jp_parse_source js_lex js_skip_trivia js_is_space js_lex_one js_is_idstart js_is_alpha js_scan_ident js_is_idpart js_is_keyword js_lexeme_eq js_emit js_num_starts js_is_digit js_scan_number js_scan_while_hex js_scan_while_digit js_scan_exp js_lex_string js_scan_string js_emit ↻ js_lex_template js_emit ↻ js_regex_context js_lexeme_eq ↻ js_lex_regex js_emit ↻ js_punct_len js_is_digit ↻ jp_parse_program jp_tok_kind jp_pst jp_toks jp_cur jp_pst ↻ jp_err jp_pst ↻

structs

none

consts

86const ND_MAGIC_1024: i64 = 1024
87const ND_MAGIC_2048: i64 = 2048
88const ND_MAGIC_4096: i64 = 4096
89const ND_MAGIC_65536: i64 = 65536
90const ND_MAGIC_131072: i64 = 131072
93const ND_ERROR: i64 = 0
94const ND_PROGRAM: i64 = 1
95const ND_NUMBER: i64 = 2
96const ND_STRING: i64 = 3
97const ND_IDENT: i64 = 4
98const ND_BOOL: i64 = 5
99const ND_NULL: i64 = 6
100const ND_BINARY: i64 = 7
101const ND_UNARY: i64 = 8
102const ND_ASSIGN: i64 = 9
103const ND_CALL: i64 = 10
104const ND_MEMBER: i64 = 11 // a.b (b is an IDENT node)
105const ND_INDEX: i64 = 12 // a[b] (b is an expr node)
106const ND_VAR_DECL: i64 = 13 // a=name IDENT node, b=init expr node (-1 if none)
107const ND_EXPR_STMT: i64 = 14
108const ND_RETURN: i64 = 15 // a=expr node (-1 if bare `return;`)
109const ND_BLOCK: i64 = 16 // list-node: a=child_start, b=child_count
110const ND_IF: i64 = 17 // a=cond, b=then-stmt, c=else-stmt (-1 if none)
111const ND_WHILE: i64 = 18 // a=cond, b=body
112const ND_FUNC_DECL: i64 = 19 // a=name IDENT, b=params list-node, c=body BLOCK
113const ND_PARAMS: i64 = 20 // list-node: a=child_start, b=child_count
114const ND_OBJECT: i64 = 21 // object literal {k:v,...}: list-node a=child_start, b=prop_count (children are ND_PROP)
115const ND_ARRAY: i64 = 22 // array literal [e0,e1,...]: list-node a=child_start, b=elem_count
116const ND_PROP: i64 = 23 // object property: tokidx=key token (IDENT or STRING), a=value-expr node
118const ND_FOR: i64 = 24 // for(init;cond;update) body: a=init, b=cond, c=update, tokidx-slot=body (all may be -1 except body)
119const ND_TERNARY: i64 = 25 // cond ? a : b -> a=cond, b=then-expr, c=else-expr
120const ND_BREAK: i64 = 26 // break; (statement, no children)
121const ND_CONTINUE: i64 = 27 // continue; (statement, no children)
122const ND_DOWHILE: i64 = 28 // do body while(cond): a=cond, b=body
124const ND_FOR_OF: i64 = 29 // for(var x of iter) body: a=var-target(VAR_DECL|IDENT), b=iterable-expr, c=body
125const ND_FOR_IN: i64 = 30 // for(var k in obj) body: a=var-target(VAR_DECL|IDENT), b=object-expr, c=body
126const ND_SWITCH: i64 = 31 // switch(disc){...}: LIST-node a=case_list_child_start, b=case_count, c=disc-expr (children=ND_CASE; a/b match jp_child_at)
127const ND_CASE: i64 = 32 // one case/default clause: LIST-node a=stmt_list_child_start, b=stmt_count, c=case-expr(-1 for default)
128const ND_SPREAD: i64 = 33 // ...expr (spread element in an array literal; a=inner expr)
129const ND_ARRAY_PAT: i64 = 34 // [a,b] destructuring pattern (var-decl target); children = name IDENTs
130const ND_OBJ_PAT: i64 = 35 // {a,b} destructuring pattern (shorthand); children = name IDENTs
131const ND_TEMPLATE: i64 = 36 // template literal (tokidx = whole template token; interior decoded at eval)
132const ND_THIS: i64 = 37 // the `this` keyword (eval resolves via the call frame's this binding)
133const ND_NEW: i64 = 38 // new C(args): extra=0 -> a=ND_CALL node; extra=1 -> a=callee (bare `new C`)
134const ND_UPDATE: i64 = 39 // ++x/--x/x++/x-- : a=lvalue (IDENT/MEMBER/INDEX); extra: 1=++pre 2=--pre 3=++post 4=--post
135const ND_TRY: i64 = 40 // try{a}catch(b){c}finally{slot4}: a=try BLOCK, b=catch-param IDENT(-1), c=catch BLOCK(-1), tokidx-slot=finally BLOCK(-1)
136const ND_THROW: i64 = 41 // throw expr: a=expr
137const ND_VAR_LIST: i64 = 42 // multi-declarator `var a, b=c, d;` -- LIST-node a=child_start b=count (children=ND_VAR_DECL)
138const ND_CLASS: i64 = 43 // class Name{constructor(){} m(){}...}: a=name IDENT, b=ctor ND_FUNC_DECL, c=methods ND_BLOCK (each child = a method ND_FUNC_DECL whose name IDENT is the method name); prototype methods -> instances via func_prototype+obj_set
139const ND_REGEX: i64 = 44 // regex literal: tokidx = whole /pattern/flags token; pattern+flags extracted + compiled (nx_rxfull) at eval
140const ND_SUPER: i64 = 45 // the `super` keyword -- only valid as a call callee (super(...)=parent ctor) or member object (super.m(...)=parent method); eval routes both in js_eval_call
141const ND_SEQ: i64 = 46 // comma/sequence expr `a, b, c` -- LIST node a=child_start b=count; eval each, value = last
142const ND_HOLE: i64 = 47 // ARRAY ELISION `[0,4,,5]` -- an omitted element. Evaluates to undefined
143const ND_YIELD: i64 = 48 // `yield [expr]` / `yield* expr` (generators, JS-SOTA phase 1 2026-07-29):
154const OP_ADD: i64 = 1 // +
155const OP_SUB: i64 = 2 // -
156const OP_MUL: i64 = 3 // *
157const OP_DIV: i64 = 4 // /
158const OP_MOD: i64 = 5 // %
159const OP_LT: i64 = 6 // <
160const OP_LE: i64 = 7 // <=
161const OP_GT: i64 = 8 // >
162const OP_GE: i64 = 9 // >=
163const OP_EQ: i64 = 10 // ==
164const OP_NE: i64 = 11 // !=
165const OP_SEQ: i64 = 12 // ===
166const OP_SNE: i64 = 13 // !==
167const OP_AND: i64 = 14 // &&
168const OP_OR: i64 = 15 // ||
169const OP_NOT: i64 = 16 // ! (unary)
170const OP_NEG: i64 = 17 // - (unary)
171const OP_POS: i64 = 18 // + (unary)
172const OP_TYPEOF: i64 = 19 // typeof (unary)
173const OP_NULLISH: i64 = 20 // ?? (nullish coalescing, short-circuit)
174const OP_BAND: i64 = 21 // & (bitwise AND)
175const OP_BOR: i64 = 22 // | (bitwise OR)
176const OP_BXOR: i64 = 23 // ^ (bitwise XOR)
177const OP_SHL: i64 = 24 // << (left shift)
178const OP_SHR: i64 = 25 // >> (signed right shift)
179const OP_USHR: i64 = 26 // >>> (unsigned right shift)
180const OP_BNOT: i64 = 27 // ~ (unary bitwise NOT)
181const OP_INSTANCEOF: i64 = 28 // `a instanceof B` -> true if B.prototype is on a's [[Prototype]] chain
182const OP_IN: i64 = 29 // `k in obj` -> true if obj has property k (own or inherited)
183const OP_DELETE: i64 = 30 // `delete obj[k]` / `delete obj.k` -> remove property, return true (unary, ref-based)
184const OP_VOID: i64 = 31 // `void x` -> evaluate x (side effects) then return undefined (minified `void 0`=undefined)
189const NODE_SLOTS: i64 = 6
196const PST_CUR: i64 = 0
197const PST_ERR: i64 = 1
198const PST_NNODE: i64 = 2
199const PST_NCHILD: i64 = 3
200const PST_ERR_POS: i64 = 4 // source byte offset of the FIRST error token (-1 = none); for parser-gap location
209const CTX_SRC: i64 = 0
210const CTX_TOKS: i64 = 1
211const CTX_NTOK: i64 = 2
212const CTX_NODES: i64 = 3
213const CTX_CHILDREN: i64 = 4
214const CTX_PST: i64 = 5
215const CTX_MAXNODE: i64 = 6
216const CTX_MAXCHILD: i64 = 7

functions

219func jp_pst(ctx: *i64) -> *i64 { return (ctx[CTX_PST]) as *i64 }
220func jp_toks(ctx: *i64) -> *i64 { return (ctx[CTX_TOKS]) as *i64 }
221func jp_nodes(ctx: *i64) -> *i64 { return (ctx[CTX_NODES]) as *i64 }
222func jp_children(ctx: *i64) -> *i64 { return (ctx[CTX_CHILDREN]) as *i64 }
223func jp_src(ctx: *i64) -> *u8 { return (ctx[CTX_SRC]) as *u8 }
225func jp_cur(ctx: *i64) -> i64 { let p: *i64 = jp_pst(ctx); return p[PST_CUR] }
226func jp_set_cur(ctx: *i64, v: i64) -> i64 { let p: *i64 = jp_pst(ctx); p[PST_CUR] = v; return 0 }
called by 1: jp_parse_assign calls 1: jp_pst
227func jp_err(ctx: *i64) -> i64 { let p: *i64 = jp_pst(ctx); return p[PST_ERR] }
228func jp_err_pos(ctx: *i64) -> i64 { let p: *i64 = jp_pst(ctx); return p[PST_ERR_POS] }
calls 1: jp_pst
229func jp_set_err(ctx: *i64) -> i64 { let p: *i64 = jp_pst(ctx); if p[PST_ERR] == 0 { p[PST_ERR_POS] = jp_tok_start(ctx) } p[PST_ERR] = 1; return 0 }
232func jp_tok_kind(ctx: *i64) -> i64
239func jp_tok_start(ctx: *i64) -> i64
246func jp_tok_len(ctx: *i64) -> i64
called by 1: jp_is_lex calls 2: jp_pstjp_toks
253func jp_advance(ctx: *i64) -> i64
260func jp_is_lex(ctx: *i64, kind: i64, lit: *u8) -> i64
264func jp_is_punct(ctx: *i64, lit: *u8) -> i64 { return jp_is_lex(ctx, JS_TOK_PUNCT, lit) }
265func jp_is_kw(ctx: *i64, lit: *u8) -> i64 { return jp_is_lex(ctx, JS_TOK_KEYWORD, lit) }
271func jp_kind_at(ctx: *i64, i: i64) -> i64
278func jp_punct_at(ctx: *i64, i: i64, lit: *u8) -> i64
290func jp_arrow_paren_ahead(ctx: *i64) -> i64
314func jp_eat_punct(ctx: *i64, lit: *u8) -> i64
327func jp_prev_end(ctx: *i64) -> i64
called by 1: jp_newline_before calls 2: jp_curjp_toks
338func jp_newline_before(ctx: *i64) -> i64
358func jp_can_asi(ctx: *i64) -> i64
368func jp_is_lvalue(ctx: *i64, idx: i64) -> i64
377func jp_new_node(ctx: *i64, nkind: i64, a: i64, b: i64, c: i64, tokidx: i64, extra: i64) -> i64
392func jp_error_node(ctx: *i64) -> i64
398func jp_nkind(ctx: *i64, idx: i64) -> i64 { let n: *i64 = jp_nodes(ctx); return n[idx * NODE_SLOTS + 0] }
399func jp_na(ctx: *i64, idx: i64) -> i64 { let n: *i64 = jp_nodes(ctx); return n[idx * NODE_SLOTS + 1] }
400func jp_nb(ctx: *i64, idx: i64) -> i64 { let n: *i64 = jp_nodes(ctx); return n[idx * NODE_SLOTS + 2] }
401func jp_nc(ctx: *i64, idx: i64) -> i64 { let n: *i64 = jp_nodes(ctx); return n[idx * NODE_SLOTS + 3] }
402func jp_nextra(ctx: *i64, idx: i64) -> i64 { let n: *i64 = jp_nodes(ctx); return n[idx * NODE_SLOTS + 5] }
405func jp_child_push(ctx: *i64, node_idx: i64) -> i64
415func jp_child_at(ctx: *i64, list_idx: i64, k: i64) -> i64
427func jp_child_commit(ctx: *i64, scratch: *i64, count: i64) -> i64
448func jp_parse_array(ctx: *i64) -> i64
493func jp_parse_object(ctx: *i64) -> i64
586func jp_parse_new(ctx: *i64) -> i64
627func jp_parse_class_decl(ctx: *i64) -> i64
710func jp_parse_primary(ctx: *i64) -> i64
789func jp_parse_postfix(ctx: *i64) -> i64
866func jp_parse_call_tail(ctx: *i64, callee: i64, opt: i64) -> i64 { // opt=1 -> optional call a?.(args)
909func jp_parse_unary(ctx: *i64) -> i64
1003func jp_mul_op(ctx: *i64) -> i64
called by 1: jp_parse_mul calls 2: jp_tok_kindjp_is_punct
1011func jp_add_op(ctx: *i64) -> i64
called by 1: jp_parse_add calls 2: jp_tok_kindjp_is_punct
1020func jp_rel_op(ctx: *i64) -> i64
1032func jp_eq_op(ctx: *i64) -> i64
called by 1: jp_parse_eq calls 2: jp_tok_kindjp_is_punct
1046func jp_parse_mul(ctx: *i64) -> i64
1062func jp_parse_add(ctx: *i64) -> i64
1080func jp_parse_shift(ctx: *i64) -> i64
1099func jp_parse_rel(ctx: *i64) -> i64
1115func jp_parse_eq(ctx: *i64) -> i64
1133func jp_parse_bitand(ctx: *i64) -> i64
1148func jp_parse_bitxor(ctx: *i64) -> i64
1163func jp_parse_bitor(ctx: *i64) -> i64
1178func jp_parse_and(ctx: *i64) -> i64
1193func jp_parse_or(ctx: *i64) -> i64
1213func jp_compound_op(ctx: *i64) -> i64
1237func jp_parse_nullish(ctx: *i64) -> i64
1252func jp_parse_ternary(ctx: *i64) -> i64
1276func jp_wrap_block1(ctx: *i64, stmt: i64) -> i64
1284func jp_parse_arrow_params(ctx: *i64) -> i64 { return jp_parse_params(ctx) }
1289func jp_parse_arrow_body(ctx: *i64) -> i64
1298func jp_parse_arrow_single(ctx: *i64) -> i64
1311func jp_parse_arrow_paren(ctx: *i64) -> i64
1323func jp_arrow_ahead(ctx: *i64) -> i64
1337func jp_parse_assign(ctx: *i64) -> i64
1383func jp_parse_expr(ctx: *i64) -> i64
1409func jp_parse_array_pattern(ctx: *i64) -> i64
1439func jp_parse_object_pattern(ctx: *i64) -> i64
1477func jp_parse_one_declarator(ctx: *i64) -> i64
1495func jp_parse_var_decl(ctx: *i64) -> i64
1523func jp_parse_return(ctx: *i64) -> i64
1543func jp_parse_block(ctx: *i64) -> i64
1568func jp_parse_if(ctx: *i64) -> i64
1584func jp_parse_while(ctx: *i64) -> i64
1600func jp_parse_for(ctx: *i64) -> i64
1650func jp_parse_for_decl(ctx: *i64) -> i64
1678func jp_parse_for_in_of(ctx: *i64, target: i64, nkind: i64) -> i64
1698func jp_parse_switch(ctx: *i64) -> i64
1751func jp_parse_case_body(ctx: *i64, case_expr: i64) -> i64
1779func jp_parse_dowhile(ctx: *i64) -> i64
1799func jp_parse_break(ctx: *i64) -> i64
1807func jp_parse_continue(ctx: *i64) -> i64
1817func jp_parse_params(ctx: *i64) -> i64
1866func jp_parse_func_decl(ctx: *i64) -> i64
1885func jp_parse_func_expr(ctx: *i64) -> i64
1906func jp_parse_expr_stmt(ctx: *i64) -> i64
1919func jp_parse_try(ctx: *i64) -> i64
1950func jp_parse_throw(ctx: *i64) -> i64
1959func jp_parse_stmt(ctx: *i64) -> i64
1997func jp_parse_program(ctx: *i64) -> i64
2019func jp_parse_source(src: *u8, srclen: i64, ctx_out: *i64) -> i64
2078func jq_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
called by 1: main
2083func jq_putn(v: i64) -> i64 { nxi_out(v); return 0 }
called by 1: main calls 1: nxi_out
2084func jq_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
called by 1: jq_parse
2089func jq_fdn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
called by 1: main calls 1: nxi_fd
2092func jq_parse(s: *u8, ctxbox: *i64) -> i64
2095func jq_ctx(ctxbox: *i64) -> *i64 { return (ctxbox[0]) as *i64 }
2096func jq_haserr(ctxbox: *i64) -> i64 { let c: *i64 = jq_ctx(ctxbox); let p: *i64 = jp_pst(c); return p[PST_ERR] }
2099func jq_expr0(ctxbox: *i64, prog: i64) -> i64
2105func jq_expr0_kind(ctxbox: *i64, prog: i64) -> i64
2110func jq_expr0_op(ctxbox: *i64, prog: i64) -> i64
2116func jq_binop_is(src: *u8, ctxbox: *i64, want: i64) -> i64
2125func jq_unop_is(src: *u8, ctxbox: *i64, want: i64) -> i64
2133func main() -> i64