Skip to content

Commit bdb7ebc

Browse files
committed
Destructurer dispatcher v2: admission-keyed buckets, two levels deep
The v1 dispatcher tried arms in declaration order, so the late pratt op forms paid ~20 failed unifications per expression node. v2 derives each arm's FIRST-CHILD admission keys from its step plan (node rule / leaf tokenType / literal first charCode, with nullable-first arms admitting everything) and dispatches through nested switches: node child -> rule bucket, leaf -> tokenType bucket ($keyword/$punct sub-switched on the first character). Big node-rule buckets (a pratt rule's self bucket holds every led + op form) sub-dispatch one level deeper on c[1] - the connector position. Buckets are SUPERSET filters (every arm fn still verifies exactly) and preserve declaration order internally, so tie semantics are unchanged. Destructure-EVERY-node overhead over the bare CST pass: +64% (v1) -> +39% (one level) -> +31% (two levels); totality unchanged (32,336 nodes, 0 misses across all seven grammars + the TS corpus sample) and the tsc-structural oracle stays node-identical (32/32).
1 parent 61d9966 commit bdb7ebc

8 files changed

Lines changed: 6017 additions & 810 deletions

html.cst-match.ts

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,25 @@ function _Element$lt4(c: readonly CstChild[], src: string): ElementMatch | null
158158

159159
export function matchElement(n: ElementNode, src: string): ElementMatch {
160160
const c = n.children;
161-
{ const m = _Element$lt(c, src); if (m !== null) return m; }
162-
{ const m = _Element$lt2(c, src); if (m !== null) return m; }
163-
{ const m = _Element$lt3(c, src); if (m !== null) return m; }
164-
{ const m = _Element$lt4(c, src); if (m !== null) return m; }
161+
const k0 = c[0] as (CstChild & { tokenType?: string; rule?: string }) | undefined;
162+
if (k0 === undefined) {
163+
} else if (k0.tokenType === undefined) {
164+
switch (k0.rule) {
165+
}
166+
} else if (k0.tokenType === '$keyword' || k0.tokenType === '$punct') {
167+
switch (src.charCodeAt(k0.offset)) {
168+
case 60: {
169+
{ const m = _Element$lt(c, src); if (m !== null) return m; }
170+
{ const m = _Element$lt2(c, src); if (m !== null) return m; }
171+
{ const m = _Element$lt3(c, src); if (m !== null) return m; }
172+
{ const m = _Element$lt4(c, src); if (m !== null) return m; }
173+
break;
174+
}
175+
}
176+
} else {
177+
switch (k0.tokenType) {
178+
}
179+
}
165180
throw new Error("matchElement: no arm matches" + ' @' + n.offset);
166181
}
167182

@@ -222,7 +237,22 @@ function _Attr$name(c: readonly CstChild[], src: string): AttrMatch | null {
222237

223238
export function matchAttr(n: AttrNode, src: string): AttrMatch {
224239
const c = n.children;
225-
{ const m = _Attr$name(c, src); if (m !== null) return m; }
240+
const k0 = c[0] as (CstChild & { tokenType?: string; rule?: string }) | undefined;
241+
if (k0 === undefined) {
242+
} else if (k0.tokenType === undefined) {
243+
switch (k0.rule) {
244+
}
245+
} else if (k0.tokenType === '$keyword' || k0.tokenType === '$punct') {
246+
switch (src.charCodeAt(k0.offset)) {
247+
}
248+
} else {
249+
switch (k0.tokenType) {
250+
case "Name": {
251+
{ const m = _Attr$name(c, src); if (m !== null) return m; }
252+
break;
253+
}
254+
}
255+
}
226256
throw new Error("matchAttr: no arm matches" + ' @' + n.offset);
227257
}
228258

@@ -274,10 +304,34 @@ function _Node$text(c: readonly CstChild[], src: string): NodeMatch | null {
274304

275305
export function matchNode(n: NodeNode, src: string): NodeMatch {
276306
const c = n.children;
277-
{ const m = _Node$element(c, src); if (m !== null) return m; }
278-
{ const m = _Node$comment(c, src); if (m !== null) return m; }
279-
{ const m = _Node$rawText(c, src); if (m !== null) return m; }
280-
{ const m = _Node$text(c, src); if (m !== null) return m; }
307+
const k0 = c[0] as (CstChild & { tokenType?: string; rule?: string }) | undefined;
308+
if (k0 === undefined) {
309+
} else if (k0.tokenType === undefined) {
310+
switch (k0.rule) {
311+
case "Element": {
312+
{ const m = _Node$element(c, src); if (m !== null) return m; }
313+
break;
314+
}
315+
}
316+
} else if (k0.tokenType === '$keyword' || k0.tokenType === '$punct') {
317+
switch (src.charCodeAt(k0.offset)) {
318+
}
319+
} else {
320+
switch (k0.tokenType) {
321+
case "Comment": {
322+
{ const m = _Node$comment(c, src); if (m !== null) return m; }
323+
break;
324+
}
325+
case "RawText": {
326+
{ const m = _Node$rawText(c, src); if (m !== null) return m; }
327+
break;
328+
}
329+
case "Text": {
330+
{ const m = _Node$text(c, src); if (m !== null) return m; }
331+
break;
332+
}
333+
}
334+
}
281335
throw new Error("matchNode: no arm matches" + ' @' + n.offset);
282336
}
283337

@@ -333,7 +387,31 @@ function _Document$element(c: readonly CstChild[], src: string): DocumentMatch |
333387

334388
export function matchDocument(n: DocumentNode, src: string): DocumentMatch {
335389
const c = n.children;
336-
{ const m = _Document$element(c, src); if (m !== null) return m; }
390+
const k0 = c[0] as (CstChild & { tokenType?: string; rule?: string }) | undefined;
391+
if (k0 === undefined) {
392+
{ const m = _Document$element(c, src); if (m !== null) return m; }
393+
} else if (k0.tokenType === undefined) {
394+
switch (k0.rule) {
395+
case "Element": {
396+
{ const m = _Document$element(c, src); if (m !== null) return m; }
397+
break;
398+
}
399+
}
400+
} else if (k0.tokenType === '$keyword' || k0.tokenType === '$punct') {
401+
switch (src.charCodeAt(k0.offset)) {
402+
}
403+
} else {
404+
switch (k0.tokenType) {
405+
case "Comment": {
406+
{ const m = _Document$element(c, src); if (m !== null) return m; }
407+
break;
408+
}
409+
case "Text": {
410+
{ const m = _Document$element(c, src); if (m !== null) return m; }
411+
break;
412+
}
413+
}
414+
}
337415
throw new Error("matchDocument: no arm matches" + ' @' + n.offset);
338416
}
339417

0 commit comments

Comments
 (0)