Skip to content

Commit 5603660

Browse files
committed
Build parser tokens as one monomorphic literal instead of mutating interned fields
The no-inlining profile attributed ~14% of parse time to the token interning loop: internTok ADDED k/t to already-shaped lexer tokens (two hidden-class transitions per token, on top of the conditional newlineBefore stamp making shapes polymorphic at every tok.k/tok.t site). The tokenize wrapper now rebuilds each token as a single literal with every field the parser reads (type/text/offset/k/t + the three stamp flags normalized to booleans) — one shape from birth. The matchPuLit '>'-split tokens go through the same mkPunct shape. internTok is gone. Gates: full 18,805-file corpus byte-identical, 26/26 check. Bench: aggregate +3.1~5.4% across three runs.
1 parent 3d76210 commit 5603660

1 file changed

Lines changed: 28 additions & 22 deletions

File tree

src/emit-parser.ts

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -811,27 +811,34 @@ export function emitParser(grammar: CstGrammar): string {
811811
e.emit(`const K_NAMED_MIN = ${st.KIND_NAMED_MIN};`);
812812
e.emit(`const K_NAMED_FALLBACK = ${st.KIND_NAMED_FALLBACK};`);
813813
e.emit(``);
814-
// Intern tok.k / tok.t for one token (also used when matchLiteral splices a `>>`).
815-
// k = TYPE kind (PUNCT for '' tokens, else the declared/template kind; an unforeseen
816-
// named type → NAMED_FALLBACK, which is >= NAMED_MIN yet collides with no real
817-
// token-name kind). t = LITERAL kind: a '' token's text in the punct table, a named
818-
// token's text in the keyword table (keyMatchesTok's keyword branch needs tok.type !== '').
819-
e.emit(String.raw`function internTok(tok) {
820-
const ty = tok.type;
821-
if (ty === '') {
822-
tok.k = K_PUNCT;
823-
tok.t = LIT_PU[tok.text] | 0;
824-
} else {
825-
const k = TYPE_KIND[ty];
826-
tok.k = k === undefined ? K_NAMED_FALLBACK : k;
827-
tok.t = LIT_KW[tok.text] | 0;
814+
// Rebuild each lexer token as ONE object literal carrying every field the parser
815+
// reads — type/text/offset, the int kinds k (TYPE kind: PUNCT for '' tokens, else the
816+
// declared/template kind, NAMED_FALLBACK for an unforeseen type) and t (LITERAL kind:
817+
// a '' token's text in the punct table, a named token's text in the keyword table),
818+
// and the three stamp flags normalized to booleans (absent ≡ false for the parser's
819+
// truthiness reads). One monomorphic shape from birth: the old in-place interning
820+
// added k/t to already-shaped tokens — two hidden-class transitions per token, and
821+
// that loop dominated the parse() profile.
822+
e.emit(String.raw`function mkTok(type, text, offset, k, t, nl, cb, mf) {
823+
return { type, text, offset, k, t, newlineBefore: nl, commentBefore: cb, multilineFlowBefore: mf };
824+
}
825+
function mkPunct(text, offset) {
826+
return mkTok('', text, offset, K_PUNCT, LIT_PU[text] | 0, false, false, false);
827+
}
828+
function tokenize(source) {
829+
const raw = _lexTokenize(source);
830+
const out = new Array(raw.length);
831+
for (let i = 0; i < raw.length; i++) {
832+
const r = raw[i];
833+
const ty = r.type;
834+
let k, t;
835+
if (ty === '') { k = K_PUNCT; t = LIT_PU[r.text] | 0; }
836+
else { k = TYPE_KIND[ty]; if (k === undefined) k = K_NAMED_FALLBACK; t = LIT_KW[r.text] | 0; }
837+
out[i] = mkTok(ty, r.text, r.offset, k, t,
838+
r.newlineBefore === true, r.commentBefore === true, r.multilineFlowBefore === true);
828839
}
840+
return out;
829841
}`);
830-
e.emit(`function tokenize(source) {`);
831-
e.emit(` const toks = _lexTokenize(source);`);
832-
e.emit(` for (let i = 0; i < toks.length; i++) internTok(toks[i]);`);
833-
e.emit(` return toks;`);
834-
e.emit(`}`);
835842
e.emit(``);
836843
// Baked maps. Emit as object literals → Map.
837844
e.emit(`const opTable = new Map(${J([...a.opTable])});`);
@@ -933,9 +940,8 @@ function matchPuLit(value, pu) {
933940
}
934941
if (value === '>' && tok.k === K_PUNCT && tok.text.length > 1 && tok.text[0] === '>') {
935942
const rest = tok.text.slice(1);
936-
const a = { type: '', text: '>', offset: tok.offset };
937-
const b = { type: '', text: rest, offset: tok.offset + 1 };
938-
internTok(a); internTok(b);
943+
const a = mkPunct('>', tok.offset);
944+
const b = mkPunct(rest, tok.offset + 1);
939945
tokens.splice(pos, 1, a, b);
940946
memo.clear();
941947
pos++;

0 commit comments

Comments
 (0)