PEGO

118. TypeScript Operator Nodes Are Built Directly

  • Status: Implemented
  • Date: 2026-10-24

Problem and Mechanism

An Operators Rule whose result is a named node type (type Expr = node[_]) makes every operator node of that type. In the TypeScript runtime an operator line without an action built the tuple node of (lhs, op, rhs) and then converted it: convertNode allocated a TypedNode that copied the range, the children and the fields and left the tuple node as garbage. The Go runtime builds the named kind directly.

operatorsRuleBuild now constructs the TypedNode of the rule's operator type from the children and fields it has just made, with the same range, an empty rule name and the same capture labels as the converted node. The constructor of TypedNode takes those parts instead of a node to copy from; convertNode keeps the copy for values that come from actions. The node that an action returns is still converted.

Results

Generated TypeScript parser of a grammar with five operator levels and a named result type, one input of 139 kB (79,036 nodes), Node 24.13.0 on darwin/arm64, eight paired runs alternating the parent and the change, 150 parses each after warm-up:

parent change
time per parse (median) 31.4 ms 29.8 ms (0.95x)
garbage collections per parse 0.74 0.60
time in garbage collection per parse 3.25 ms 2.5 ms

The change was faster in seven of the eight pairs; one pair was 0.6% slower. The trees hash identically (node type, constructor, rule, range, text, field names, child count) for the parent and the change.

Tests

The Operators Rule named-type cases run through the TypeScript backend, together with the typed conformance and replay tests, and parsers/test.sh, pass.