ANTLR4 TypeScript Runtime (antlr4ng)
raw JSON →antlr4ng is an alternative, TypeScript-first runtime for ANTLR4 grammars, specifically designed to be used with the `antlr-ng` parser generation tool. Currently stable at version `3.0.16`, it receives frequent point releases addressing bugs and introducing minor enhancements. Unlike the original ANTLR4 JavaScript runtime, `antlr4ng` operates identically across Node.js and browser environments and is built with strict TypeScript nullability checks. It requires an ES2022 (ES6) compatible runtime for features like static initialization blocks and private class fields. While implementing nearly all features of the Java ANTLR4 runtime, it notably omits the `UnbufferedCharStream` class. Developers migrating from other ANTLR4 JavaScript runtimes will need to adjust code for stricter null handling and renamed internal members.
Common errors
error TS2339: Property '_ctx' does not exist on type 'Parser'. Did you mean 'context'? ↓
parser._ctx with parser.context. Similarly, change _errHandler to errorHandler, _input to inputStream, and _interp to interpreter. error TypeError: Class constructor CharStream cannot be invoked without 'new' ↓
import { CharStream } from 'antlr4ng'; instead of const { CharStream } = require('antlr4ng');. error Error: Cannot find module './generated/ExpressionLexer' ↓
generated folder. For ESM in Node.js, ensure the import statement includes the .js extension: import { ExpressionLexer } from './generated/ExpressionLexer.js'; error SyntaxError: Private field '#field' must be declared in an enclosing class ↓
Warnings
breaking The `antlr4ng` runtime requires ES2022 (ES13) or newer JavaScript environments, utilizing features like static initialization blocks and private class fields (`#field`). Older JavaScript runtimes (e.g., Node.js < 16) will fail. ↓
breaking Migration from other ANTLR4 JavaScript runtimes requires adjusting code due to `antlr4ng`'s stricter TypeScript nullability. Many previously implicitly nullable members are now explicitly typed as nullable, aligning with Java runtime behavior. ↓
breaking Several internal parser and recognizer member variables have been renamed for consistency with TypeScript conventions (e.g., `_ctx` to `context`, `_errHandler` to `errorHandler`, `_input` to `inputStream`, `_interp` to `interpreter`). ↓
gotcha This `antlr4ng` runtime is designed to be used with parsers generated by the `antlr-ng` tool. While it implements ANTLR4, direct compatibility with parsers generated by the original ANTLR4 JavaScript target might not be guaranteed due to numerous bug fixes and internal changes. ↓
gotcha When importing generated lexer/parser/visitor files in an ESM Node.js environment, the `.js` extension must be explicitly included in the import path, even if the source file is `.ts`. ↓
Install
npm install antlr4ng yarn add antlr4ng pnpm add antlr4ng Imports
- CharStream wrong
const { CharStream } = require("antlr4ng");correctimport { CharStream } from "antlr4ng"; - CommonTokenStream wrong
const { CommonTokenStream } = require("antlr4ng");correctimport { CommonTokenStream } from "antlr4ng"; - ExpressionLexer wrong
import { ExpressionLexer } from "antlr4ng"; import { ExpressionLexer } from "./generated/ExpressionLexer";correctimport { ExpressionLexer } from "./generated/ExpressionLexer.js"; - Parser wrong
const { Parser } = require("antlr4ng");correctimport { Parser } from "antlr4ng";
Quickstart
import { CharStream, CommonTokenStream } from "antlr4ng";
import { ExpressionLexer } from "./generated/ExpressionLexer.js";
import { ExpressionParser } from "./generated/ExpressionParser.js";
const input = "1 + 2 * 3";
const inputStream = CharStream.fromString(input);
const lexer = new ExpressionLexer(inputStream);
const tokenStream = new CommonTokenStream(lexer);
const parser = new ExpressionParser(tokenStream);
// Optionally configure error handling or listeners
// parser.removeErrorListeners();
// parser.addErrorListener(new MyErrorListener());
const tree = parser.start();
// To demonstrate visitor usage (assuming MyVisitor is defined):
// import { ExpressionVisitor } from "./generated/ExpressionVisitor.js";
// import { AddContext } from "./generated/ExpressionParser.js"; // Example context type
//
// class MyVisitor extends ExpressionVisitor<number> {
// public visitAdd = (ctx: AddContext): number => {
// // Assuming visit is implemented for other rule contexts
// return this.visit(ctx.expression(0)) + this.visit(ctx.expression(1));
// };
// // ... implement other visit methods
// protected defaultResult(): number { return 0; }
// protected aggregateResult(aggregate: number, nextResult: number): number { return nextResult; }
// }
// const result = new MyVisitor().visit(tree);
// console.log(`Parse tree successfully created. Visitor result: ${result}`);
console.log("Parse tree successfully created.");