TypeScript
The TypeScript target generates a standalone module that compiles under --strict and works in browsers, Node.js, and other JavaScript runtimes.
Minimal working example
Create the following two files in an empty directory. parser.ts is generated by pegtool.
identifier.peg
Start = value:<Identifier> !. { return value; }
Identifier = [\pL_] [\pL\p{Nd}_]*main.ts
import { parse } from "./parser.js";
const value = parse("name_世界");
if (typeof value !== "string") {
throw new Error("unexpected parser result");
}
console.log(value);Generate, compile in strict mode, and run it:
pegtool -t ts -o parser.ts identifier.peg
tsc --strict --target ES2020 --module NodeNext --moduleResolution NodeNext parser.ts main.ts
node main.jsOutput:
name_世界Generated API
The generated module exports parse, Parser, ParserOptions, PTParseError, Position, Current, Grammar, and the grammar variable. A complete options call looks like:
const value = parse(source, {
filename: "input.txt",
entrypoint: "Start",
maxExpressions: 1_000_000,
customData: { featureEnabled: true },
});Actions
{
function toInteger(text: string): number {
return Number.parseInt(text, 10);
}
}
Input = value:Integer !. { return value; }
Integer = [0-9]+ { return toInteger(c.text); }Labels and action return values have type any. c.text is a JavaScript string, and c.data comes from ParserOptions.customData.
Unicode
Unicode classes are converted to compact shared number tables at generation time. The runtime matches against generator-fixed ranges rather than depending on browser support or the Unicode version of \p{...} regular expressions. Ordinary characters, ranges, case-insensitive matching, negation, and non-BMP code points share the same character-class semantics.
Optimization and runtime
The current TypeScript runtime does not use -optimize-parser; output with and without the option is byte-for-byte identical. TypeScript also has no runtime memoization switch.
-optimize-ref-expr-by-index can avoid rule-name map lookups, but inserting or reordering a rule then produces a large diff. Leave it off by default and enable it only when a real workload proves the benefit and the generated changes are acceptable.
Normal Node.js uses the V8 JIT. node --jitless is useful only for measuring the absolute path without JIT and is not a production performance option. tjs/QuickJS can test compatibility without V8; the target remains direct TypeScript and the generation command does not change.