Skip to content

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

peg
Start = value:<Identifier> !. { return value; }
Identifier = [\pL_] [\pL\p{Nd}_]*

main.ts

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:

powershell
pegtool -t ts -o parser.ts identifier.peg
tsc --strict --target ES2020 --module NodeNext --moduleResolution NodeNext parser.ts main.ts
node main.js

Output:

text
name_世界

Generated API

The generated module exports parse, Parser, ParserOptions, PTParseError, Position, Current, Grammar, and the grammar variable. A complete options call looks like:

ts
const value = parse(source, {
  filename: "input.txt",
  entrypoint: "Start",
  maxExpressions: 1_000_000,
  customData: { featureEnabled: true },
});

Actions

peg
{
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.

Released under the BSD 3-Clause License