Build the official TypeScript grammar for Rust
August 3, 2026 ยท View on GitHub
The official grammars-v4 TypeScript lexer and parser use grammar-specific
stateful actions and predicates, including the argument-taking p("of") and
n("get"|"set") helpers. This repository routes those calls through generated
typed Rust hooks and keeps the target-specific state in copyable base modules.
Prerequisites
- Rust 1.95 or newer
antlr/grammars-v4at the pinned parity commit- Java 17 and ANTLR 4.13.2 only when running the Java parity proof
The setup below keeps downloaded inputs under the repository's ignored
target/antlr-cleanroom/, which is not subject to operating-system temporary
directory cleanup. cargo clean removes it.
# The jar is an oracle dependency for the parity harness, not Rust generation.
ANTLR4_JAR=target/antlr-cleanroom/tools/antlr-4.13.2-complete.jar
ANTLR_JAR_SHA256=eae2dfa119a64327444672aff63e9ec35a20180dc5b8090b7a6ab85125df4d76
mkdir -p target/antlr-cleanroom/tools
curl -fLo "$ANTLR4_JAR" \
https://www.antlr.org/download/antlr-4.13.2-complete.jar
echo "${ANTLR_JAR_SHA256} ${ANTLR4_JAR}" | shasum -a 256 -c -
git clone --filter=blob:none --no-checkout \
https://github.com/antlr/grammars-v4.git \
target/antlr-cleanroom/grammars-v4
git -C target/antlr-cleanroom/grammars-v4 sparse-checkout init --cone
git -C target/antlr-cleanroom/grammars-v4 sparse-checkout set javascript/typescript
git -C target/antlr-cleanroom/grammars-v4 checkout \
284602b3f23ca54dc30778204ab7ae9e969145e9
Generate strict Rust modules
From this repository's root:
GRAMMAR=target/antlr-cleanroom/grammars-v4/javascript/typescript
BUILD=target/antlr-cleanroom/typescript-rust
mkdir -p "$BUILD/generated"
cargo run --locked --release -p antlr-rust-codegen --bin antlr4-rust-gen -- \
"$GRAMMAR/TypeScriptLexer.g4" \
"$GRAMMAR/TypeScriptParser.g4" \
--lib "$GRAMMAR" \
--sem-patterns patterns/javascript.toml \
--option-hook superClass=TypeScriptLexerBase \
--option-hook superClass=TypeScriptParserBase \
--sem-unknown error \
--require-full-semantics \
--out-dir "$BUILD/generated"
Every authored action and predicate is either translated or routed to a typed
hook. Do not pass --allow-unsupported-lexer-actions: the TypeScript base needs
all template, brace, strict-mode, regex, and token-lookaround helpers to retain
the official grammar's behavior.
The --option-hook acknowledgements record that those Rust hooks supply the
otherwise target-specific superclass behavior.
Copy these files into an application crate:
$BUILD/generated/type_script_lexer.rs$BUILD/generated/type_script_parser.rstests/typescript-parity/dumper/src/typescript_lexer_base.rstests/typescript-parity/dumper/src/typescript_parser_base.rs
The base files are examples rather than runtime modules. Adjust their module
paths if the generated files do not live under generated in the application.
Construct the typed lexer and parser
use antlr4_runtime::{CommonTokenStream, InputStream, Parser};
use generated::type_script_lexer::TypeScriptLexer;
use generated::type_script_parser::TypeScriptParser;
use typescript_lexer_base::TypeScriptLexerBase;
use typescript_parser_base::TypeScriptParserBase;
let source = "interface Box<T> { value: T }";
let lexer = TypeScriptLexer::with_typed_hooks(
InputStream::new(source),
TypeScriptLexerBase::with_strict_default(false),
);
let tokens = CommonTokenStream::new(lexer);
let mut parser = TypeScriptParser::with_typed_hooks(tokens, TypeScriptParserBase);
let tree = parser.program().expect("TypeScript parses");
assert_eq!(parser.number_of_syntax_errors(), 0);
assert!(!tree.text().is_empty());
program() is the compilation-unit entry rule. The lexer base tracks the last
default-channel token, strict scopes, brace depth, and nested template depth.
The parser base implements visible lookahead/lookbehind for n(...) and
p(...), automatic-semicolon-insertion checks, and the TypeScript
open-brace/function/interface guard.
For lower-level diagnostics, fill a CommonTokenStream and call
drain_source_errors() before parsing, or inspect
Parser::number_of_syntax_errors() after the entry rule.
The generator intentionally omits --require-generated-parser. Rules outside
the generated recursive-descent subset use the faithful runtime ATN
interpreter, which receives the same typed hooks.
Run the repository proof
tests/typescript-parity/run.sh \
--antlr-jar "$ANTLR4_JAR" \
--grammars-v4 target/antlr-cleanroom/grammars-v4
The harness regenerates the Rust recognizers and the official Java target, then
compares tokens and parse trees for every fixture under
tests/typescript-parity/snippets/.