← board

The Pascal parser allocates a string per identifier token to throw it away

This ticket is a characterised job, not a request to do it. It exists so that on some later day the work is an afternoon rather than a rediscovery.

The shape

GetTokenStr(idx) calls GetTokenStrFromRaw, which does SetLength(s, len) and a copy — a heap allocation. CaseEqual(a, b) then rejects on length in its first two lines. So the cheap reject happens after the expensive part. Every identifier token a scan passes costs an allocation, a copy, a comparison and a free, in order to answer a question an integer comparison settles.

The remedy, already written and already proven

TokenCaseEqual(idx, nm) in ast_syminfer.inc compares TokChars in place after Tokens[idx].SLen <> Length(nm) rejects. Semantics are identical, edges included — an out-of-range index and an empty token both yield '' from GetTokenStr, and CaseEqual('', nm) is true exactly when nm is empty, which the replacement reproduces explicitly rather than by accident.

It carries -dPXX_TCE_CROSSCHECK (computes both ways, reports disagreements) and -dPXX_TCE_BREAK (deliberately wrong, to prove the crosscheck fires). On lekkerzeilen those read 0 and 67. So a converter does not have to trust the substitution; they can measure it in two builds.

The rewrite is mechanical: CaseEqual(GetTokenStr(X), Y) -> TokenCaseEqual(X, Y), and every occurrence in pyparser.inc had a simple index expression with no nested parentheses.

What is NOT claimed

Pascal parse time has never been profiled. The NilPy win came from routines that scan the whole token array per definition, where that array holds every imported module — a shape the Pascal frontend may simply not have. A reader who assumes 12.4% transfers is reasoning from an unmeasured analogy.

Before opening this, profile a large Pascal build. If GetTokenStrFromRaw or the allocator is not in the top few symbols, close this as not worth it and say so here.