├── .eslintrc.json ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .npmignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE.txt ├── ReadMe.md ├── build └── generate-test-parsers.sh ├── cspell.json ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── release-notes.md ├── src ├── ANTLRErrorListener.ts ├── ANTLRErrorStrategy.ts ├── BailErrorStrategy.ts ├── BaseErrorListener.ts ├── BufferedTokenStream.ts ├── CharStream.ts ├── CommonToken.ts ├── CommonTokenFactory.ts ├── CommonTokenStream.ts ├── ConsoleErrorListener.ts ├── DefaultErrorStrategy.ts ├── DiagnosticErrorListener.ts ├── FailedPredicateException.ts ├── InputMismatchException.ts ├── IntStream.ts ├── InterpreterRuleContext.ts ├── Lexer.ts ├── LexerInterpreter.ts ├── LexerNoViableAltException.ts ├── ListTokenSource.ts ├── NoViableAltException.ts ├── Parser.ts ├── ParserInterpreter.ts ├── ParserRuleContext.ts ├── ProxyErrorListener.ts ├── RecognitionException.ts ├── Recognizer.ts ├── RuntimeMetaData.ts ├── Token.ts ├── TokenFactory.ts ├── TokenSource.ts ├── TokenStream.ts ├── TokenStreamRewriter.ts ├── TraceListener.ts ├── UnbufferedTokenStream.ts ├── Vocabulary.ts ├── WritableToken.ts ├── atn │ ├── ATN.ts │ ├── ATNConfig.ts │ ├── ATNConfigSet.ts │ ├── ATNDeserializationOptions.ts │ ├── ATNDeserializer.ts │ ├── ATNSerializer.ts │ ├── ATNSimulator.ts │ ├── ATNState.ts │ ├── AbstractPredicateTransition.ts │ ├── ActionTransition.ts │ ├── AmbiguityInfo.ts │ ├── ArrayPredictionContext.ts │ ├── AtomTransition.ts │ ├── BasicBlockStartState.ts │ ├── BasicState.ts │ ├── BlockEndState.ts │ ├── BlockStartState.ts │ ├── CodePointTransitions.ts │ ├── ContextSensitivityInfo.ts │ ├── DecisionEventInfo.ts │ ├── DecisionInfo.ts │ ├── DecisionState.ts │ ├── EmptyPredictionContext.ts │ ├── EpsilonTransition.ts │ ├── LL1Analyzer.ts │ ├── LexerATNConfig.ts │ ├── LexerATNSimulator.ts │ ├── LexerAction.ts │ ├── LexerActionExecutor.ts │ ├── LexerActionType.ts │ ├── LexerChannelAction.ts │ ├── LexerCustomAction.ts │ ├── LexerIndexedCustomAction.ts │ ├── LexerModeAction.ts │ ├── LexerMoreAction.ts │ ├── LexerPopModeAction.ts │ ├── LexerPushModeAction.ts │ ├── LexerSkipAction.ts │ ├── LexerTypeAction.ts │ ├── LookaheadEventInfo.ts │ ├── LoopEndState.ts │ ├── NotSetTransition.ts │ ├── OrderedATNConfigSet.ts │ ├── ParseInfo.ts │ ├── ParserATNSimulator.ts │ ├── PlusBlockStartState.ts │ ├── PlusLoopbackState.ts │ ├── PrecedencePredicateTransition.ts │ ├── PredicateEvalInfo.ts │ ├── PredicateTransition.ts │ ├── PredictionContext.ts │ ├── PredictionContextCache.ts │ ├── PredictionContextUtils.ts │ ├── PredictionMode.ts │ ├── ProfilingATNSimulator.ts │ ├── RangeTransition.ts │ ├── RuleStartState.ts │ ├── RuleStopState.ts │ ├── RuleTransition.ts │ ├── SemanticContext.ts │ ├── SetTransition.ts │ ├── SingletonPredictionContext.ts │ ├── StarBlockStartState.ts │ ├── StarLoopEntryState.ts │ ├── StarLoopbackState.ts │ ├── TokensStartState.ts │ ├── Transition.ts │ ├── WildcardTransition.ts │ ├── helpers.ts │ └── index.ts ├── dfa │ ├── DFA.ts │ ├── DFASerializer.ts │ ├── DFAState.ts │ ├── DFAStateEqualityComparator.ts │ ├── LexerDFASerializer.ts │ ├── PredPrediction.ts │ └── index.ts ├── index.ts ├── misc │ ├── BitSet.ts │ ├── DefaultEqualityComparator.ts │ ├── EqualityComparator.ts │ ├── HashMap.ts │ ├── HashSet.ts │ ├── InterpreterDataReader.ts │ ├── Interval.ts │ ├── IntervalSet.ts │ ├── MapKeyEqualityOperator.ts │ ├── MultiMap.ts │ ├── ObjectEqualityComparator.ts │ ├── OrderedHashMap.ts │ ├── OrderedHashSet.ts │ ├── ParseCancellationException.ts │ └── index.ts ├── tree │ ├── AbstractParseTreeVisitor.ts │ ├── ErrorNode.ts │ ├── ParseTree.ts │ ├── ParseTreeListener.ts │ ├── ParseTreeVisitor.ts │ ├── ParseTreeWalker.ts │ ├── TerminalNode.ts │ ├── Trees.ts │ ├── index.ts │ ├── pattern │ │ ├── CannotInvokeStartRuleError.ts │ │ ├── Chunk.ts │ │ ├── ParseTreeMatch.ts │ │ ├── ParseTreePattern.ts │ │ ├── ParseTreePatternMatcher.ts │ │ ├── RuleTagToken.ts │ │ ├── StartRuleDoesNotConsumeFullPatternError.ts │ │ ├── TagChunk.ts │ │ ├── TextChunk.ts │ │ ├── TokenTagToken.ts │ │ └── index.ts │ └── xpath │ │ ├── XPath.ts │ │ ├── XPathElement.ts │ │ ├── XPathLexer.g4 │ │ ├── XPathLexer.ts │ │ ├── XPathLexerErrorListener.ts │ │ ├── XPathRuleAnywhereElement.ts │ │ ├── XPathRuleElement.ts │ │ ├── XPathTokenAnywhereElement.ts │ │ ├── XPathTokenElement.ts │ │ ├── XPathWildcardAnywhereElement.ts │ │ ├── XPathWildcardElement.ts │ │ └── index.ts └── utils │ ├── DoubleDict.ts │ ├── MurmurHash.ts │ ├── helpers.ts │ └── index.ts ├── tests ├── api │ ├── BitSet.spec.ts │ ├── CharStream.spec.ts │ ├── HashSet.spec.ts │ ├── IntervalSet.spec.ts │ ├── OrderedHashSet.spec.ts │ ├── Serialization.spec.ts │ ├── TestCodePointCharStream.spec.ts │ ├── TestInterpreterDataReader.spec.ts │ ├── TestTokenStream.spec.ts │ ├── TestTokenStreamRewriter.spec.ts │ ├── TestVisitors.spec.ts │ ├── XPath.spec.ts │ └── perf │ │ ├── Parser.java │ │ ├── RuleContext.java │ │ ├── TimeLexerSpeed.ts │ │ ├── emoji.txt │ │ ├── udhr_hin.txt │ │ └── udhr_kor.txt ├── benchmarks │ ├── MySQLLexer.g4 │ ├── MySQLParser.g4 │ ├── ParseService.ts │ ├── data │ │ ├── bitrix_queries_cut.sql │ │ ├── rdbms-info.json │ │ ├── sakila-db │ │ │ ├── sakila-data.sql │ │ │ ├── sakila-schema.sql │ │ │ └── single_statement.sql │ │ └── statements.txt │ ├── predefined.tokens │ ├── run-benchmarks.ts │ └── support │ │ ├── MySQLBaseLexer.ts │ │ ├── MySQLBaseRecognizer.ts │ │ ├── MySQLErrorListener.ts │ │ ├── MySQLParser.interp │ │ ├── MySQLRecognizerCommon.ts │ │ └── helpers.ts ├── bugs │ ├── bug70.spec.ts │ ├── bug97.spec.ts │ └── grammars │ │ └── Expression.g4 ├── fixtures │ ├── config.json │ ├── grammars │ │ ├── Expr.g4 │ │ ├── Java.g4 │ │ ├── T1.g4 │ │ ├── T2.g4 │ │ ├── T3.g4 │ │ ├── VisitorBasic.g4 │ │ ├── VisitorCalc.g4 │ │ └── graphemes.g4 │ ├── helpers │ │ └── Character.ts │ ├── templates │ │ ├── TypeScript.grammar.stg │ │ └── TypeScript.spec.stg │ └── tsconfig.json ├── global.d.ts ├── parser │ └── ProfilingATNSimulator.spec.ts ├── recognizer │ └── Recognizer.spec.ts ├── rewriter │ ├── TokenStreamRewriter.spec.ts │ ├── abc.g4 │ ├── calc.g4 │ └── generatedCode │ │ ├── abc.ts │ │ └── calc.ts └── tsconfig.json ├── tsconfig.json └── vitest.config.ts /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Build & Test 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [20.x, 22.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm ci 29 | - run: npm run generate-test-parsers 30 | - run: npm run generate-runtime-tests 31 | - run: npm run build 32 | - run: npm run test 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | package 4 | generated 5 | coverage 6 | *.tgz 7 | .vscode/numbered-bookmarks.json 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | @@ -0,0 +1,10 @@ 2 | .github 3 | .vscode 4 | cli/ 5 | tests/ 6 | coverage/ 7 | dist/*.map 8 | src/ 9 | build/ 10 | 11 | .eslintignore 12 | .eslintrc.json 13 | .project 14 | tsconfig.json 15 | cspell.json 16 | vitest.config.ts 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "antlr-debug", 9 | "request": "launch", 10 | "name": "Debug Current Grammar", 11 | "input": "tests/input.txt", 12 | "grammar": "${file}", 13 | "visualParseTree": true 14 | }, 15 | { 16 | "type": "node", 17 | "request": "launch", 18 | "name": "Debug Current Test File", 19 | "program": "${workspaceFolder}/node_modules/vitest/vitest.mjs", 20 | "args": [ 21 | "run", 22 | "${relativeFile}" 23 | ], 24 | "autoAttachChildProcesses": true, 25 | "skipFiles": [ 26 | "/**", 27 | "**/node_modules/**" 28 | ], 29 | "console": "integratedTerminal", 30 | "smartStep": true 31 | }, 32 | { 33 | "type": "node", 34 | "request": "launch", 35 | "name": "Launch Parser Benchmarks", 36 | "skipFiles": [ 37 | "/**" 38 | ], 39 | "runtimeExecutable": "npx", 40 | "runtimeArgs": [ 41 | "tsx", 42 | "tests/benchmarks/run-benchmarks.ts", 43 | ], 44 | "sourceMaps": true, 45 | "resolveSourceMapLocations": [ 46 | "${workspaceFolder}/**", 47 | "!**/node_modules/**" 48 | ], 49 | }, 50 | { 51 | "type": "node", 52 | "request": "launch", 53 | "name": "Launch Lexer Benchmark", 54 | "skipFiles": [ 55 | "/**" 56 | ], 57 | "runtimeExecutable": "npx", 58 | "runtimeArgs": [ 59 | "tsx", 60 | "tests/api/perf/TimeLexerSpeed.ts", 61 | ], 62 | "sourceMaps": true, 63 | "outputCapture": "std" 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "jest.jestCommandLine": "npm run test --", 3 | "jest.rootPath": "tests" 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | [The "BSD license"] 2 | Copyright (c) 2012-2023 The ANTLR Project. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | 3. Neither name of copyright holders nor the names of its contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /build/generate-test-parsers.sh: -------------------------------------------------------------------------------- 1 | 2 | printf "\x1b[1m\x1b[34mGenerating test parsers...\x1b[0m\n\n" 3 | 4 | antlr-ng -Dlanguage=TypeScript -v --exact-output-dir -o ./tests/generated ./tests/fixtures/grammars/*.g4 5 | antlr-ng -Dlanguage=TypeScript -v --exact-output-dir -o ./tests/bugs/generated ./tests/bugs/grammars/*.g4 6 | antlr-ng -Dlanguage=TypeScript -v -l --exact-output-dir -o tests/benchmarks/generated tests/benchmarks/MySQLLexer.g4 tests/benchmarks/MySQLParser.g4 7 | 8 | printf "done\n\n" 9 | -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "ignorePaths": [ 4 | ".eslintrc.json", 5 | "generated/" 6 | ], 7 | "dictionaryDefinitions": [], 8 | "dictionaries": [], 9 | "words": [ 10 | "ANTLR", 11 | "ANTLRng", 12 | "Approximator", 13 | "Bitsets", 14 | "rdbms", 15 | "runtimes", 16 | "sakila", 17 | "Sonoma", 18 | "Unbuffered", 19 | "unpredicated", 20 | "whitespaces" 21 | ], 22 | "ignoreWords": [ 23 | "AMBIG", 24 | "Dlanguage", 25 | "Evals", 26 | "Grosch", 27 | "Gulik", 28 | "Harwell", 29 | "Hashable", 30 | "IATN", 31 | "LLATN", 32 | "Nondisjoint", 33 | "Preds", 34 | "SLLATN", 35 | "Sethi", 36 | "Ullman", 37 | "Wirth", 38 | "Xexact", 39 | "bitrix", 40 | "instanceof", 41 | "interp", 42 | "localctx", 43 | "longlong", 44 | "nbits", 45 | "opnds", 46 | "outfile", 47 | "parentctx", 48 | "prec", 49 | "precpred", 50 | "recog", 51 | "semctx", 52 | "sempred", 53 | "tgen", 54 | "ttype", 55 | "udhr" 56 | ], 57 | "import": [] 58 | } 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antlr4ng", 3 | "version": "3.0.16", 4 | "type": "module", 5 | "description": "Alternative JavaScript/TypeScript runtime for ANTLR4", 6 | "main": "dist/index.cjs", 7 | "module": "dist/index.mjs", 8 | "types": "dist/index.d.ts", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/mike-lischke/antlr4ng.git" 12 | }, 13 | "keywords": [ 14 | "lexer", 15 | "parser", 16 | "antlr", 17 | "antlr4", 18 | "grammar", 19 | "TypeScript" 20 | ], 21 | "license": "BSD-3-Clause", 22 | "bugs": { 23 | "url": "https://github.com/mike-lischke/antlr4ng/issues" 24 | }, 25 | "homepage": "https://github.com/mike-lischke/antlr4ng", 26 | "sideEffects": false, 27 | "devDependencies": { 28 | "@mike-lischke/antlr-tgen": "1.0.12", 29 | "@eslint/js": "9.21.0", 30 | "@stylistic/eslint-plugin": "4.2.0", 31 | "@stylistic/eslint-plugin-ts": "4.2.0", 32 | "@types/node": "22.13.10", 33 | "@types/unicode-properties": "1.3.2", 34 | "@typescript-eslint/eslint-plugin": "8.26.0", 35 | "@typescript-eslint/parser": "8.26.0", 36 | "@vitest/coverage-v8": "3.0.8", 37 | "antlr-ng": "1.0.8", 38 | "esbuild": "0.25.0", 39 | "eslint": "9.22.0", 40 | "eslint-plugin-import": "2.31.0", 41 | "eslint-plugin-jsdoc": "50.6.3", 42 | "eslint-plugin-prefer-arrow": "1.2.3", 43 | "tsx": "4.19.3", 44 | "typescript": "5.8.2", 45 | "typescript-eslint": "8.26.0", 46 | "unicode-properties": "1.4.1", 47 | "vitest": "3.0.8" 48 | }, 49 | "scripts": { 50 | "prepublishOnly": "npm run build && npm run test", 51 | "build": "tsc && npm run build-cjs && npm run build-mjs", 52 | "build-with-maps": "tsc && npm run build-mjs -- --sourcemap", 53 | "build-and-test": "npm run build && npm run test", 54 | "test": "vitest run --no-watch --no-coverage", 55 | "full-test": "npm run test && npm run run-benchmarks && npm run time-lexer-speed", 56 | "run-benchmarks": "tsx tests/benchmarks/run-benchmarks.ts", 57 | "time-lexer-speed": "tsx tests/api/perf/TimeLexerSpeed.ts", 58 | "generate-test-parsers": "./build/generate-test-parsers.sh", 59 | "generate-runtime-tests": "antlr-tgen --config tests/fixtures/config.json", 60 | "build-bundle": "esbuild ./src/index.js --main-fields=module,main --bundle --target=esnext --keep-names", 61 | "build-mjs": "npm run build-bundle -- --outfile=dist/index.mjs --format=esm", 62 | "build-cjs": "npm run build-bundle -- --outfile=dist/index.cjs --format=cjs", 63 | "generate-xpath-lexer": "antlr-ng -Dlanguage=TypeScript --exact-output-dir -o src/tree/xpath/generated src/tree/xpath/XPathLexer.g4" 64 | }, 65 | "exports": { 66 | "types": "./dist/index.d.ts", 67 | "require": "./dist/index.cjs", 68 | "import": "./dist/index.mjs" 69 | }, 70 | "browserslist": [ 71 | "defaults and fully supports es6-module", 72 | "maintained node versions" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /src/BailErrorStrategy.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { InputMismatchException } from "./InputMismatchException.js"; 10 | import { ParseCancellationException } from "./misc/ParseCancellationException.js"; 11 | import { DefaultErrorStrategy } from "./DefaultErrorStrategy.js"; 12 | import { Parser } from "./Parser.js"; 13 | import { RecognitionException } from "./RecognitionException.js"; 14 | 15 | /** 16 | * This implementation of {@link ANTLRErrorStrategy} responds to syntax errors 17 | * by immediately canceling the parse operation with a 18 | * {@link ParseCancellationException}. The implementation ensures that the 19 | * {@link ParserRuleContext.exception} field is set for all parse tree nodes 20 | * that were not completed prior to encountering the error. 21 | * 22 | * This error strategy is useful in the following scenarios. 23 | * 24 | * - **Two-stage parsing:** This error strategy allows the first 25 | * stage of two-stage parsing to immediately terminate if an error is 26 | * encountered, and immediately fall back to the second stage. In addition to 27 | * avoiding wasted work by attempting to recover from errors here, the empty 28 | * implementation of {@link BailErrorStrategy.sync} improves the performance of the first stage. 29 | * - **Silent validation:** When syntax errors are not being 30 | * reported or logged, and the parse result is simply ignored if errors occur, 31 | * the {@link BailErrorStrategy} avoids wasting work on recovering from errors 32 | * when the result will be ignored either way. 33 | * 34 | * `myParser.setErrorHandler(new BailErrorStrategy());` 35 | * 36 | * @see Parser#setErrorHandler(ANTLRErrorStrategy) 37 | */ 38 | export class BailErrorStrategy extends DefaultErrorStrategy { 39 | 40 | /** 41 | * Instead of recovering from exception `e`, re-throw it wrapped 42 | * in a {@link ParseCancellationException} so it is not caught by the 43 | * rule function catches. Use {@link Exception//getCause()} to get the 44 | * original {@link RecognitionException}. 45 | */ 46 | public override recover(recognizer: Parser, e: RecognitionException): void { 47 | throw new ParseCancellationException(e); 48 | } 49 | 50 | /** 51 | * Make sure we don't attempt to recover inline; if the parser 52 | * successfully recovers, it won't throw an exception. 53 | */ 54 | public override recoverInline(recognizer: Parser): never { 55 | const exception = new InputMismatchException(recognizer); 56 | 57 | throw new ParseCancellationException(exception); 58 | } 59 | 60 | // Make sure we don't attempt to recover from problems in subrules. 61 | public override sync(_recognizer: Parser): void { 62 | // pass 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/BaseErrorListener.ts: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | 6 | /* eslint-disable @typescript-eslint/no-unused-vars */ 7 | 8 | import { ANTLRErrorListener } from "./ANTLRErrorListener.js"; 9 | 10 | import { Parser } from "./Parser.js"; 11 | import { RecognitionException } from "./RecognitionException.js"; 12 | import { Recognizer } from "./Recognizer.js"; 13 | import { Token } from "./Token.js"; 14 | import { ATNConfigSet } from "./atn/ATNConfigSet.js"; 15 | import { ATNSimulator } from "./atn/ATNSimulator.js"; 16 | import { DFA } from "./dfa/DFA.js"; 17 | import { BitSet } from "./misc/BitSet.js"; 18 | 19 | /** 20 | * Provides an empty default implementation of {@link ANTLRErrorListener}. The 21 | * default implementation of each method does nothing, but can be overridden as 22 | * necessary. 23 | */ 24 | export class BaseErrorListener implements ANTLRErrorListener { 25 | public syntaxError(recognizer: Recognizer, offendingSymbol: S | null, 26 | line: number, column: number, msg: string, e: RecognitionException | null): void { 27 | } 28 | 29 | public reportAmbiguity(recognizer: Parser, dfa: DFA, startIndex: number, stopIndex: number, exact: boolean, 30 | ambigAlts: BitSet | undefined, configs: ATNConfigSet): void { 31 | } 32 | 33 | public reportAttemptingFullContext(recognizer: Parser, dfa: DFA, startIndex: number, stopIndex: number, 34 | conflictingAlts: BitSet | undefined, configs: ATNConfigSet): void { 35 | } 36 | 37 | public reportContextSensitivity(recognizer: Parser, dfa: DFA, startIndex: number, stopIndex: number, 38 | prediction: number, configs: ATNConfigSet): void { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/CommonTokenFactory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { CharStream } from "./CharStream.js"; 8 | import { CommonToken } from "./CommonToken.js"; 9 | import { TokenFactory } from "./TokenFactory.js"; 10 | import { TokenSource } from "./TokenSource.js"; 11 | 12 | /** 13 | * This default implementation of {@link TokenFactory} creates {@link CommonToken} objects. 14 | */ 15 | export class CommonTokenFactory implements TokenFactory { 16 | /** 17 | * The default {@link CommonTokenFactory} instance. 18 | * 19 | * 20 | * This token factory does not explicitly copy token text when constructing 21 | * tokens. 22 | */ 23 | public static readonly DEFAULT = new CommonTokenFactory(); 24 | 25 | /** 26 | * Indicates whether {@link CommonToken.setText} should be called after 27 | * constructing tokens to explicitly set the text. This is useful for cases 28 | * where the input stream might not be able to provide arbitrary substrings 29 | * of text from the input after the lexer creates a token (e.g. the 30 | * implementation of {@link CharStream.getText} in 31 | * {@link UnbufferedCharStream} throws an 32 | * {@link UnsupportedOperationException}). Explicitly setting the token text 33 | * allows {@link Token.getText} to be called at any time regardless of the 34 | * input stream implementation. 35 | * 36 | * 37 | * The default value is `false` to avoid the performance and memory 38 | * overhead of copying text for every token unless explicitly requested. 39 | */ 40 | protected readonly copyText: boolean = false; 41 | 42 | public constructor(copyText?: boolean) { 43 | /** 44 | * Indicates whether {@link CommonToken.setText} should be called after 45 | * constructing tokens to explicitly set the text. This is useful for cases 46 | * where the input stream might not be able to provide arbitrary substrings 47 | * of text from the input after the lexer creates a token (e.g. the 48 | * implementation of {@link CharStream.getText} in 49 | * {@link UnbufferedCharStream} throws an 50 | * {@link UnsupportedOperationException}). Explicitly setting the token text 51 | * allows {@link Token.getText} to be called at any time regardless of the 52 | * input stream implementation. 53 | * 54 | * 55 | * The default value is `false` to avoid the performance and memory 56 | * overhead of copying text for every token unless explicitly requested. 57 | */ 58 | this.copyText = copyText ?? false; 59 | } 60 | 61 | public create(source: [TokenSource | null, CharStream | null], type: number, text: string | undefined, 62 | channel: number, start: number, stop: number, line: number, column: number): CommonToken { 63 | const t = CommonToken.fromSource(source, type, channel, start, stop); 64 | 65 | t.line = line; 66 | t.column = column; 67 | if (text) { 68 | t.text = text; 69 | } else if (this.copyText && source[1] !== null) { 70 | t.text = source[1].getTextFromRange(start, stop); 71 | } 72 | 73 | return t; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/ConsoleErrorListener.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { BaseErrorListener } from "./BaseErrorListener.js"; 8 | import { RecognitionException } from "./RecognitionException.js"; 9 | import { Recognizer } from "./Recognizer.js"; 10 | import { ATNSimulator } from "./atn/ATNSimulator.js"; 11 | 12 | /** 13 | * This implementation prints messages to the console containing the values of `line`, `charPositionInLine`, 14 | * and `msg` using the following format. 15 | * 16 | * ``` 17 | * line *line*:*charPositionInLine* *msg* 18 | * ``` 19 | */ 20 | export class ConsoleErrorListener extends BaseErrorListener { 21 | /** 22 | * Provides a default instance of {@link ConsoleErrorListener}. 23 | */ 24 | public static readonly instance = new ConsoleErrorListener(); 25 | 26 | public override syntaxError(recognizer: Recognizer | null, 27 | offendingSymbol: unknown, 28 | line: number, 29 | charPositionInLine: number, 30 | msg: string | null, 31 | _e: RecognitionException | null): void { 32 | console.error("line " + line + ":" + charPositionInLine + " " + msg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/FailedPredicateException.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { PredicateTransition } from "./atn/PredicateTransition.js"; 8 | import { Parser } from "./Parser.js"; 9 | import { RecognitionException } from "./RecognitionException.js"; 10 | 11 | /** 12 | * A semantic predicate failed during validation. Validation of predicates 13 | * occurs when normally parsing the alternative just like matching a token. 14 | * Disambiguating predicate evaluation occurs when we test a predicate during 15 | * prediction. 16 | */ 17 | export class FailedPredicateException extends RecognitionException { 18 | public readonly ruleIndex: number = 0; 19 | public readonly predicateIndex: number = 0; 20 | public readonly predicate?: string; 21 | 22 | public constructor(recognizer: Parser, predicate?: string, message: string | null = null) { 23 | super({ 24 | message: formatMessage(predicate ?? "no predicate", message ?? null), 25 | recognizer, 26 | input: recognizer.inputStream, ctx: recognizer.context, 27 | }); 28 | 29 | const s = recognizer.atn.states[recognizer.state]!; 30 | const trans = s.transitions[0]; 31 | if (trans instanceof PredicateTransition) { 32 | this.ruleIndex = trans.ruleIndex; 33 | this.predicateIndex = trans.predIndex; 34 | } else { 35 | this.ruleIndex = 0; 36 | this.predicateIndex = 0; 37 | } 38 | this.predicate = predicate; 39 | this.offendingToken = recognizer.getCurrentToken(); 40 | } 41 | } 42 | 43 | const formatMessage = (predicate: string | null, message: string | null) => { 44 | if (message !== null) { 45 | return message; 46 | } 47 | 48 | return "failed predicate: {" + predicate + "}?"; 49 | }; 50 | -------------------------------------------------------------------------------- /src/InputMismatchException.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { RecognitionException } from "./RecognitionException.js"; 8 | import { Parser } from "./Parser.js"; 9 | 10 | /** 11 | * This signifies any kind of mismatched input exceptions such as 12 | * when the current input does not match the expected token. 13 | */ 14 | export class InputMismatchException extends RecognitionException { 15 | public constructor(recognizer: Parser) { 16 | super({ message: "", recognizer, input: recognizer.inputStream, ctx: recognizer.context }); 17 | this.offendingToken = recognizer.getCurrentToken(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/InterpreterRuleContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParserRuleContext } from "./ParserRuleContext.js"; 8 | 9 | /** 10 | * This class extends {@link ParserRuleContext} by allowing the value of 11 | * {@link getRuleIndex} to be explicitly set for the context. 12 | * 13 | * {@link ParserRuleContext} does not include field storage for the rule index 14 | * since the context classes created by the code generator override the 15 | * {@link getRuleIndex} method to return the correct value for that context. 16 | * Since the parser interpreter does not use the context classes generated for a 17 | * parser, this class (with slightly more memory overhead per node) is used to 18 | * provide equivalent functionality. 19 | */ 20 | export class InterpreterRuleContext extends ParserRuleContext { 21 | /** This is the backing field for {@link #getRuleIndex}. */ 22 | #ruleIndex: number; 23 | 24 | public constructor(ruleIndex: number, parent: ParserRuleContext | null, invokingStateNumber?: number) { 25 | super(parent, invokingStateNumber); 26 | 27 | this.#ruleIndex = ruleIndex; 28 | } 29 | 30 | public override get ruleIndex(): number { 31 | return this.#ruleIndex; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/LexerInterpreter.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Lexer } from "./Lexer.js"; 8 | import { LexerATNSimulator } from "./atn/LexerATNSimulator.js"; 9 | import { PredictionContextCache } from "./atn/PredictionContextCache.js"; 10 | import { DFA } from "./dfa/DFA.js"; 11 | import { ATN } from "./atn/ATN.js"; 12 | import { Vocabulary } from "./Vocabulary.js"; 13 | import { CharStream } from "./CharStream.js"; 14 | 15 | export class LexerInterpreter extends Lexer { 16 | private decisionToDFA: DFA[]; 17 | private sharedContextCache = new PredictionContextCache(); 18 | 19 | #grammarFileName: string; 20 | #atn: ATN; 21 | 22 | #ruleNames: string[]; 23 | #channelNames: string[]; 24 | #modeNames: string[]; 25 | 26 | #vocabulary: Vocabulary; 27 | 28 | public constructor(grammarFileName: string, vocabulary: Vocabulary, ruleNames: string[], channelNames: string[], 29 | modeNames: string[], atn: ATN, input: CharStream) { 30 | super(input); 31 | 32 | if (atn.grammarType !== ATN.LEXER) { 33 | throw new Error("IllegalArgumentException: The ATN must be a lexer ATN."); 34 | } 35 | 36 | this.#grammarFileName = grammarFileName; 37 | this.#atn = atn; 38 | 39 | this.#ruleNames = ruleNames.slice(0); 40 | this.#channelNames = channelNames.slice(0); 41 | this.#modeNames = modeNames.slice(0); 42 | this.#vocabulary = vocabulary; 43 | 44 | this.decisionToDFA = atn.decisionToState.map((ds, i) => { 45 | return new DFA(ds, i); 46 | }); 47 | 48 | this.interpreter = new LexerATNSimulator(this, atn, this.decisionToDFA, this.sharedContextCache); 49 | } 50 | 51 | public override get atn(): ATN { 52 | return this.#atn; 53 | } 54 | 55 | public get grammarFileName(): string { 56 | return this.#grammarFileName; 57 | } 58 | 59 | public get ruleNames(): string[] { 60 | return this.#ruleNames; 61 | } 62 | 63 | public get channelNames(): string[] { 64 | return this.#channelNames; 65 | } 66 | 67 | public get modeNames(): string[] { 68 | return this.#modeNames; 69 | } 70 | 71 | public get vocabulary(): Vocabulary { 72 | return this.#vocabulary; 73 | } 74 | 75 | public get serializedATN(): number[] { 76 | throw new Error("The LexerInterpreter does not support the serializedATN property."); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/LexerNoViableAltException.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNConfigSet } from "./atn/ATNConfigSet.js"; 8 | import { CharStream } from "./CharStream.js"; 9 | import { Lexer } from "./Lexer.js"; 10 | import { RecognitionException } from "./RecognitionException.js"; 11 | import { TokenStream } from "./TokenStream.js"; 12 | 13 | export class LexerNoViableAltException extends RecognitionException { 14 | public startIndex: number; 15 | public deadEndConfigs: ATNConfigSet; 16 | 17 | protected declare input: CharStream | null; 18 | 19 | public constructor(lexer: Lexer | null, input: CharStream | TokenStream, startIndex: number, 20 | deadEndConfigs: ATNConfigSet) { 21 | super({ message: "", recognizer: lexer, input, ctx: null }); 22 | this.startIndex = startIndex; 23 | this.deadEndConfigs = deadEndConfigs; 24 | } 25 | 26 | public override toString(): string { 27 | let symbol = ""; 28 | if (this.input && this.startIndex >= 0 && this.startIndex < this.input.size) { 29 | symbol = this.input.getTextFromRange(this.startIndex, this.startIndex); 30 | } 31 | 32 | return `LexerNoViableAltException(${symbol})`; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/NoViableAltException.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Parser } from "./Parser.js"; 8 | import { ParserRuleContext } from "./ParserRuleContext.js"; 9 | import { RecognitionException } from "./RecognitionException.js"; 10 | import { Token } from "./Token.js"; 11 | import { TokenStream } from "./TokenStream.js"; 12 | import { ATNConfigSet } from "./atn/ATNConfigSet.js"; 13 | 14 | /** 15 | * Indicates that the parser could not decide which of two or more paths 16 | * to take based upon the remaining input. It tracks the starting token 17 | * of the offending input and also knows where the parser was 18 | * in the various paths when the error. Reported by reportNoViableAlternative() 19 | */ 20 | export class NoViableAltException extends RecognitionException { 21 | /** Which configurations did we try at input.index() that couldn't match input.LT(1)? */ 22 | public readonly deadEndConfigs: ATNConfigSet | null = null; 23 | 24 | /** 25 | * The token object at the start index; the input stream might 26 | * not be buffering tokens so get a reference to it. (At the 27 | * time the error occurred, of course the stream needs to keep a 28 | * buffer all of the tokens but later we might not have access to those.) 29 | */ 30 | public readonly startToken: Token | null; 31 | 32 | public constructor(recognizer: Parser, input: TokenStream | null = null, startToken: Token | null = null, 33 | offendingToken: Token | null = null, deadEndConfigs: ATNConfigSet | null = null, 34 | ctx: ParserRuleContext | null = null) { 35 | ctx = ctx ?? recognizer.context; 36 | offendingToken = offendingToken ?? recognizer.getCurrentToken(); 37 | startToken = startToken ?? recognizer.getCurrentToken(); 38 | input = input ?? recognizer.inputStream; 39 | 40 | super({ message: "", recognizer, input, ctx }); 41 | 42 | // Which configurations did we try at input.index() that couldn't match 43 | // input.LT(1)? 44 | this.deadEndConfigs = deadEndConfigs; 45 | 46 | // The token object at the start index; the input stream might 47 | // not be buffering tokens so get a reference to it. (At the 48 | // time the error occurred, of course the stream needs to keep a 49 | // buffer all of the tokens but later we might not have access to those.) 50 | this.startToken = startToken; 51 | this.offendingToken = offendingToken; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/ProxyErrorListener.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { BaseErrorListener } from "./BaseErrorListener.js"; 8 | import { Parser } from "./Parser.js"; 9 | import { RecognitionException } from "./RecognitionException.js"; 10 | import { Recognizer } from "./Recognizer.js"; 11 | import { Token } from "./Token.js"; 12 | import { ATNConfigSet } from "./atn/ATNConfigSet.js"; 13 | import { ATNSimulator } from "./atn/ATNSimulator.js"; 14 | import { DFA } from "./dfa/DFA.js"; 15 | import { BitSet } from "./misc/BitSet.js"; 16 | 17 | export class ProxyErrorListener extends BaseErrorListener { 18 | public constructor(private delegates: BaseErrorListener[]) { 19 | super(); 20 | 21 | return this; 22 | } 23 | 24 | public override syntaxError(recognizer: Recognizer, 25 | offendingSymbol: S | null, line: number, column: number, msg: string, e: RecognitionException | null): void { 26 | this.delegates.forEach((d) => { 27 | d.syntaxError(recognizer, offendingSymbol, line, column, msg, e); 28 | }); 29 | } 30 | 31 | public override reportAmbiguity(recognizer: Parser, dfa: DFA, startIndex: number, stopIndex: number, exact: boolean, 32 | ambigAlts: BitSet | undefined, configs: ATNConfigSet): void { 33 | this.delegates.forEach((d) => { 34 | d.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs); 35 | }); 36 | } 37 | 38 | public override reportAttemptingFullContext(recognizer: Parser, dfa: DFA, startIndex: number, stopIndex: number, 39 | conflictingAlts: BitSet | undefined, configs: ATNConfigSet): void { 40 | this.delegates.forEach((d) => { 41 | d.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs); 42 | }); 43 | } 44 | 45 | public override reportContextSensitivity(recognizer: Parser, dfa: DFA, startIndex: number, stopIndex: number, 46 | prediction: number, configs: ATNConfigSet): void { 47 | this.delegates.forEach((d) => { 48 | d.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/RecognitionException.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { CharStream } from "./CharStream.js"; 8 | import { ParserRuleContext } from "./ParserRuleContext.js"; 9 | import { Recognizer } from "./Recognizer.js"; 10 | import { Token } from "./Token.js"; 11 | import { TokenStream } from "./TokenStream.js"; 12 | import { ATNSimulator } from "./atn/ATNSimulator.js"; 13 | import { IntervalSet } from "./misc/IntervalSet.js"; 14 | 15 | export interface IExceptionParams { 16 | message: string; 17 | recognizer: Recognizer | null; 18 | input: CharStream | TokenStream | null; 19 | ctx: ParserRuleContext | null; 20 | } 21 | 22 | /** 23 | * The root of the ANTLR exception hierarchy. In general, ANTLR tracks just 24 | * 3 kinds of errors: prediction errors, failed predicate errors, and 25 | * mismatched input errors. In each case, the parser knows where it is 26 | * in the input, where it is in the ATN, the rule invocation stack, 27 | * and what kind of problem occurred. 28 | */ 29 | export class RecognitionException extends Error { 30 | public ctx: ParserRuleContext | null; 31 | 32 | /** 33 | * The current {@link Token} when an error occurred. Since not all streams 34 | * support accessing symbols by index, we have to track the {@link Token} 35 | * instance itself 36 | */ 37 | public offendingToken: Token | null = null; 38 | 39 | /** 40 | * Get the ATN state number the parser was in at the time the error 41 | * occurred. For {@link NoViableAltException} and 42 | * {@link LexerNoViableAltException} exceptions, this is the 43 | * {@link DecisionState} number. For others, it is the state whose outgoing 44 | * edge we couldn't match. 45 | */ 46 | public offendingState = -1; 47 | 48 | protected recognizer: Recognizer | null; 49 | protected input: CharStream | TokenStream | null; 50 | 51 | public constructor(params: IExceptionParams) { 52 | super(params.message); 53 | if (Error.captureStackTrace) { 54 | Error.captureStackTrace(this, RecognitionException); 55 | } 56 | 57 | this.message = params.message; 58 | this.recognizer = params.recognizer; 59 | this.input = params.input; 60 | this.ctx = params.ctx; 61 | 62 | if (this.recognizer !== null) { 63 | this.offendingState = this.recognizer.state; 64 | } 65 | } 66 | 67 | /** 68 | * Gets the set of input symbols which could potentially follow the 69 | * previously matched symbol at the time this exception was thrown. 70 | * 71 | * If the set of expected tokens is not known and could not be computed, 72 | * this method returns `null`. 73 | * 74 | * @returns The set of token types that could potentially follow the current 75 | * state in the ATN, or `null` if the information is not available. 76 | */ 77 | public getExpectedTokens(): IntervalSet | null { 78 | if (this.recognizer !== null && this.ctx !== null) { 79 | return this.recognizer.atn.getExpectedTokens(this.offendingState, this.ctx); 80 | } else { 81 | return null; 82 | } 83 | } 84 | 85 | // If the state number is not known, this method returns -1. 86 | public override toString(): string { 87 | return this.message; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/TokenFactory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { type CharStream } from "./CharStream.js"; 8 | import { type Token } from "./Token.js"; 9 | import { type TokenSource } from "./TokenSource.js"; 10 | 11 | export interface TokenFactory { 12 | /** 13 | * This is the method used to create tokens in the lexer and in the 14 | * error handling strategy. If text!=null, than the start and stop positions 15 | * are wiped to -1 in the text override is set in the CommonToken. 16 | */ 17 | create(source: [TokenSource | null, CharStream | null], type: number, text: string | undefined, channel: number, 18 | start: number, stop: number, line: number, charPositionInLine: number): Symbol; 19 | } 20 | -------------------------------------------------------------------------------- /src/TokenSource.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { type CharStream } from "./CharStream.js"; 8 | import { type Token } from "./Token.js"; 9 | import { type TokenFactory } from "./TokenFactory.js"; 10 | 11 | /** 12 | * A source of tokens must provide a sequence of tokens via {@link nextToken()} 13 | * and also must reveal it's source of characters; {@link CommonToken}'s text is 14 | * computed from a {@link CharStream}; it only store indices into the char 15 | * stream. 16 | * 17 | * Errors from the lexer are never passed to the parser. Either you want to keep 18 | * going or you do not upon token recognition error. If you do not want to 19 | * continue lexing then you do not want to continue parsing. Just throw an 20 | * exception not under {@link RecognitionException} and Java will naturally toss 21 | * you all the way out of the recognizers. If you want to continue lexing then 22 | * you should not throw an exception to the parser--it has already requested a 23 | * token. Keep lexing until you get a valid one. Just report errors and keep 24 | * going, looking for a valid token. 25 | */ 26 | export interface TokenSource { 27 | /** 28 | * Return a {@link Token} object from your input stream (usually a 29 | * {@link CharStream}). Do not fail/return upon lexing error; keep chewing 30 | * on the characters until you get a good one; errors are not passed through 31 | * to the parser. 32 | */ 33 | nextToken(): Token; 34 | 35 | /** 36 | * Get the line number for the current position in the input stream. The 37 | * first line in the input is line 1. 38 | * 39 | @returns The line number for the current position in the input stream, or 40 | * 0 if the current token source does not track line numbers. 41 | */ 42 | line: number; 43 | 44 | /** 45 | * Get the index into the current line for the current position in the input 46 | * stream. The first character on a line has position 0. 47 | * 48 | @returns The line number for the current position in the input stream, or 49 | * -1 if the current token source does not track character positions. 50 | */ 51 | column: number; 52 | 53 | /** 54 | * Get the {@link CharStream} from which this token source is currently 55 | * providing tokens. 56 | * 57 | @returns The {@link CharStream} associated with the current position in 58 | * the input, or `null` if no input stream is available for the token 59 | * source. 60 | */ 61 | inputStream: CharStream | null; 62 | 63 | /** 64 | * Gets the name of the underlying input source. This method returns a 65 | * non-null, non-empty string. If such a name is not known, this method 66 | * returns {@link IntStream.UNKNOWN_SOURCE_NAME}. 67 | */ 68 | sourceName: string; 69 | 70 | /** 71 | * Set the {@link TokenFactory} this token source should use for creating 72 | * {@link Token} objects from the input. 73 | * 74 | * @param factory The {@link TokenFactory} to use for creating tokens. 75 | */ 76 | set tokenFactory(factory: TokenFactory); 77 | 78 | /** 79 | * Gets the {@link TokenFactory} this token source is currently using for 80 | * creating {@link Token} objects from the input. 81 | * 82 | @returns The {@link TokenFactory} currently used by this token source. 83 | */ 84 | get tokenFactory(): TokenFactory; 85 | } 86 | -------------------------------------------------------------------------------- /src/TraceListener.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable no-underscore-dangle */ 8 | 9 | import { Parser } from "./Parser.js"; 10 | import { ParserRuleContext } from "./ParserRuleContext.js"; 11 | import { ParseTreeListener } from "./tree/ParseTreeListener.js"; 12 | import { TerminalNode } from "./tree/TerminalNode.js"; 13 | 14 | export class TraceListener implements ParseTreeListener { 15 | private parser: Parser; 16 | 17 | public constructor(parser: Parser) { 18 | this.parser = parser; 19 | } 20 | 21 | public enterEveryRule(ctx: ParserRuleContext): void { 22 | console.log("enter " + this.parser.ruleNames[ctx.ruleIndex] + ", LT(1)=" + 23 | this.parser.inputStream?.LT(1)?.text); 24 | } 25 | 26 | public visitTerminal(node: TerminalNode): void { 27 | console.log("consume " + node.getSymbol() + " rule " + this.parser.ruleNames[this.parser.context!.ruleIndex]); 28 | } 29 | 30 | public exitEveryRule(ctx: ParserRuleContext): void { 31 | console.log("exit " + this.parser.ruleNames[ctx.ruleIndex] + ", LT(1)=" + 32 | this.parser.inputStream?.LT(1)?.text); 33 | } 34 | 35 | public visitErrorNode(_node: TerminalNode): void { 36 | // intentionally empty 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/WritableToken.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { type Token } from "./Token.js"; 8 | 9 | /** 10 | * This interface provides access to all of the information about a token by 11 | * exposing the properties of the token as getter methods. 12 | */ 13 | export interface WritableToken extends Token { 14 | setText(text: string | null): void; 15 | 16 | setType(ttype: number): void; 17 | 18 | setLine(line: number): void; 19 | 20 | setCharPositionInLine(pos: number): void; 21 | 22 | setChannel(channel: number): void; 23 | 24 | setTokenIndex(index: number): void; 25 | 26 | toString(): string; 27 | } 28 | 29 | export const isWritableToken = (candidate: unknown): candidate is WritableToken => { 30 | return (candidate as WritableToken).setText !== undefined; 31 | }; 32 | -------------------------------------------------------------------------------- /src/atn/ATNDeserializationOptions.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export interface ATNDeserializationOptions { 8 | readOnly: boolean; 9 | verifyATN: boolean; 10 | generateRuleBypassTransitions: boolean; 11 | } 12 | -------------------------------------------------------------------------------- /src/atn/ATNSimulator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { DFAState } from "../dfa/DFAState.js"; 8 | import { getCachedPredictionContext } from "./PredictionContextUtils.js"; 9 | import { ATN } from "./ATN.js"; 10 | import { PredictionContextCache } from "./PredictionContextCache.js"; 11 | import { PredictionContext } from "./PredictionContext.js"; 12 | import { HashMap } from "../misc/HashMap.js"; 13 | import { ObjectEqualityComparator } from "../misc/ObjectEqualityComparator.js"; 14 | 15 | export abstract class ATNSimulator { 16 | /** Must distinguish between missing edge and edge we know leads nowhere */ 17 | public static readonly ERROR = DFAState.fromState(0x7FFFFFFF); 18 | 19 | public readonly atn: ATN; 20 | 21 | /** 22 | * The context cache maps all PredictionContext objects that are == 23 | * to a single cached copy. This cache is shared across all contexts 24 | * in all ATNConfigs in all DFA states. We rebuild each ATNConfigSet 25 | * to use only cached nodes/graphs in addDFAState(). We don't want to 26 | * fill this during closure() since there are lots of contexts that 27 | * pop up but are not used ever again. It also greatly slows down closure(). 28 | * 29 | * This cache makes a huge difference in memory and a little bit in speed. 30 | * For the Java grammar on java.*, it dropped the memory requirements 31 | * at the end from 25M to 16M. We don't store any of the full context 32 | * graphs in the DFA because they are limited to local context only, 33 | * but apparently there's a lot of repetition there as well. We optimize 34 | * the config contexts before storing the config set in the DFA states 35 | * by literally rebuilding them with cached subgraphs only. 36 | * 37 | * I tried a cache for use during closure operations, that was 38 | * whacked after each adaptivePredict(). It cost a little bit 39 | * more time I think and doesn't save on the overall footprint 40 | * so it's not worth the complexity. 41 | */ 42 | public readonly sharedContextCache?: PredictionContextCache; 43 | 44 | public constructor(atn: ATN, sharedContextCache?: PredictionContextCache) { 45 | this.atn = atn; 46 | this.sharedContextCache = sharedContextCache; 47 | 48 | return this; 49 | } 50 | 51 | public getCachedContext(context: PredictionContext): PredictionContext { 52 | if (!this.sharedContextCache) { 53 | return context; 54 | } 55 | const visited = new HashMap(ObjectEqualityComparator.instance); 56 | 57 | return getCachedPredictionContext(context, this.sharedContextCache, visited); 58 | } 59 | 60 | /** 61 | * Clear the DFA cache used by the current instance. Since the DFA cache may 62 | * be shared by multiple ATN simulators, this method may affect the 63 | * performance (but not accuracy) of other parsers which are being used 64 | * concurrently. 65 | * 66 | * @throws UnsupportedOperationException if the current instance does not 67 | * support clearing the DFA. 68 | */ 69 | public abstract clearDFA(): void; 70 | 71 | public abstract reset(): void; 72 | } 73 | -------------------------------------------------------------------------------- /src/atn/ATNState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { IntervalSet } from "../misc/IntervalSet.js"; 8 | import { IComparable } from "../utils/helpers.js"; 9 | import { Transition } from "./Transition.js"; 10 | 11 | export class ATNState implements IComparable { 12 | public static readonly INVALID_STATE_NUMBER = -1; 13 | public static readonly INVALID_TYPE = 0; 14 | public static readonly BASIC = 1; 15 | public static readonly RULE_START = 2; 16 | public static readonly BLOCK_START = 3; 17 | public static readonly PLUS_BLOCK_START = 4; 18 | public static readonly STAR_BLOCK_START = 5; 19 | public static readonly TOKEN_START = 6; 20 | public static readonly RULE_STOP = 7; 21 | public static readonly BLOCK_END = 8; 22 | public static readonly STAR_LOOP_BACK = 9; 23 | public static readonly STAR_LOOP_ENTRY = 10; 24 | public static readonly PLUS_LOOP_BACK = 11; 25 | public static readonly LOOP_END = 12; 26 | 27 | public static readonly stateType: number = ATNState.INVALID_STATE_NUMBER; 28 | 29 | public stateNumber: number = 0; 30 | public ruleIndex: number = 0; // at runtime, we don't have Rule objects 31 | public epsilonOnlyTransitions: boolean = false; 32 | 33 | /** Used to cache lookahead during parsing, not used during construction */ 34 | public nextTokenWithinRule?: IntervalSet; 35 | 36 | /** Track the transitions emanating from this ATN state. */ 37 | public transitions: Transition[] = []; 38 | 39 | public hashCode(): number { 40 | return this.stateNumber; 41 | } 42 | 43 | public equals(other: ATNState): boolean { 44 | return this.stateNumber === other.stateNumber; 45 | } 46 | 47 | public toString(): string { 48 | return `${this.stateNumber}`; 49 | } 50 | 51 | public addTransitionAtIndex(index: number, transition: Transition): void { 52 | if (this.transitions.length === 0) { 53 | this.epsilonOnlyTransitions = transition.isEpsilon; 54 | } else if (this.epsilonOnlyTransitions !== transition.isEpsilon) { 55 | this.epsilonOnlyTransitions = false; 56 | } 57 | 58 | this.transitions.splice(index, 0, transition); 59 | } 60 | 61 | public addTransition(transition: Transition): void { 62 | if (this.transitions.length === 0) { 63 | this.epsilonOnlyTransitions = transition.isEpsilon; 64 | } else if (this.epsilonOnlyTransitions !== transition.isEpsilon) { 65 | this.epsilonOnlyTransitions = false; 66 | } 67 | 68 | this.transitions.push(transition); 69 | } 70 | 71 | public setTransition(i: number, e: Transition): void { 72 | this.transitions.splice(i, 1, e); 73 | } 74 | 75 | public removeTransition(index: number): Transition { 76 | const t = this.transitions.splice(index, 1); 77 | 78 | return t[0]; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/atn/AbstractPredicateTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { Transition } from "./Transition.js"; 9 | 10 | /** Used as base for PredicateTransition and PrecedencePredicateTransition, without adding their individual fields. */ 11 | export abstract class AbstractPredicateTransition extends Transition { 12 | public constructor(target: ATNState) { 13 | super(target); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/atn/ActionTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { Transition } from "./Transition.js"; 9 | 10 | export class ActionTransition extends Transition { 11 | public ruleIndex: number; 12 | public actionIndex: number; 13 | public isCtxDependent: boolean; 14 | 15 | public constructor(target: ATNState, ruleIndex: number, actionIndex?: number, isCtxDependent?: boolean) { 16 | super(target); 17 | this.ruleIndex = ruleIndex; 18 | this.actionIndex = actionIndex ?? -1; 19 | this.isCtxDependent = isCtxDependent ?? false; // e.g., $i ref in pred 20 | } 21 | 22 | public override get isEpsilon(): boolean { 23 | return true; 24 | } 25 | 26 | public get transitionType(): number { 27 | return Transition.ACTION; 28 | } 29 | 30 | public matches(_symbol: number, _minVocabSymbol: number, _maxVocabSymbol: number): boolean { 31 | return false; 32 | } 33 | 34 | public override toString(): string { 35 | return "action_" + this.ruleIndex + ":" + this.actionIndex; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/atn/AmbiguityInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { BitSet } from "../index.js"; 8 | import { DecisionEventInfo } from "./DecisionEventInfo.js"; 9 | 10 | /** 11 | * This class represents profiling event information for an ambiguity. 12 | * Ambiguities are decisions where a particular input resulted in an SLL 13 | * conflict, followed by LL prediction also reaching a conflict state 14 | * (indicating a true ambiguity in the grammar). 15 | * 16 | * This event may be reported during SLL prediction in cases where the 17 | * conflicting SLL configuration set provides sufficient information to 18 | * determine that the SLL conflict is truly an ambiguity. For example, if none 19 | * of the ATN configurations in the conflicting SLL configuration set have 20 | * traversed a global follow transition (i.e. 21 | * {@link ATNConfig.reachesIntoOuterContext} is 0 for all configurations), then 22 | * the result of SLL prediction for that input is known to be equivalent to the 23 | * result of LL prediction for that input. 24 | * 25 | * In some cases, the minimum represented alternative in the conflicting LL 26 | * configuration set is not equal to the minimum represented alternative in the 27 | * conflicting SLL configuration set. Grammars and inputs which result in this 28 | * scenario are unable to use {@link PredictionMode#SLL}, which in turn means 29 | * they cannot use the two-stage parsing strategy to improve parsing performance 30 | * for that input. 31 | * 32 | * @see ParserATNSimulator#reportAmbiguity 33 | * @see ANTLRErrorListener#reportAmbiguity 34 | */ 35 | export interface AmbiguityInfo extends DecisionEventInfo { 36 | /** The set of alternative numbers for this decision event that lead to a valid parse. */ 37 | ambigAlts?: BitSet; 38 | } 39 | -------------------------------------------------------------------------------- /src/atn/ArrayPredictionContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { PredictionContext } from "./PredictionContext.js"; 8 | 9 | import { equalArrays, equalNumberArrays } from "../utils/helpers.js"; 10 | 11 | export class ArrayPredictionContext extends PredictionContext { 12 | public readonly parents: Array = []; 13 | public readonly returnStates: number[] = []; 14 | 15 | public constructor(parents: Array, returnStates: number[]) { 16 | /** 17 | * Parent can be null only if full ctx mode and we make an array 18 | * from {@link EMPTY} and non-empty. We merge {@link EMPTY} by using 19 | * null parent and 20 | * returnState == {@link EMPTY_RETURN_STATE}. 21 | */ 22 | super(PredictionContext.calculateHashCodeList(parents, returnStates)); 23 | 24 | this.parents = parents; 25 | this.returnStates = returnStates; 26 | 27 | return this; 28 | } 29 | 30 | public override isEmpty(): boolean { 31 | // since EMPTY_RETURN_STATE can only appear in the last position, we 32 | // don't need to verify that size==1 33 | return this.returnStates[0] === PredictionContext.EMPTY_RETURN_STATE; 34 | } 35 | 36 | public get length(): number { 37 | return this.returnStates.length; 38 | } 39 | 40 | public getParent(index: number): PredictionContext | null { 41 | return this.parents[index]; 42 | } 43 | 44 | public getReturnState(index: number): number { 45 | return this.returnStates[index]; 46 | } 47 | 48 | public equals(other: unknown): boolean { 49 | if (this === other) { 50 | return true; 51 | } 52 | 53 | if (!(other instanceof ArrayPredictionContext) || this.hashCode() !== other.hashCode()) { 54 | return false; // can't be same if hash is different 55 | } 56 | 57 | return equalNumberArrays(this.returnStates, other.returnStates) && 58 | equalArrays(this.parents, other.parents); 59 | 60 | } 61 | 62 | public override toString(): string { 63 | if (this.isEmpty()) { 64 | return "[]"; 65 | } 66 | 67 | const entries: string[] = []; 68 | for (let i = 0; i < this.returnStates.length; i++) { 69 | if (this.returnStates[i] === PredictionContext.EMPTY_RETURN_STATE) { 70 | entries.push("$"); 71 | continue; 72 | } 73 | 74 | entries.push(this.returnStates[i].toString()); 75 | if (this.parents[i]) { 76 | entries.push(this.parents[i]!.toString()); 77 | } else { 78 | entries.push("null"); 79 | } 80 | } 81 | 82 | return `[${entries.join(", ")}]`; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/atn/AtomTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { IntervalSet } from "../misc/IntervalSet.js"; 8 | import { Transition } from "./Transition.js"; 9 | import { ATNState } from "./ATNState.js"; 10 | 11 | export class AtomTransition extends Transition { 12 | /** The token type or character value; or, signifies special label. */ 13 | public labelValue: number; 14 | 15 | #label: IntervalSet; 16 | 17 | public constructor(target: ATNState, label: number) { 18 | super(target); 19 | this.labelValue = label; 20 | this.#label = IntervalSet.of(label, label); 21 | } 22 | 23 | public override get label(): IntervalSet { 24 | return this.#label; 25 | } 26 | 27 | public override get transitionType(): number { 28 | return Transition.ATOM; 29 | } 30 | 31 | public override matches(symbol: number): boolean { 32 | return this.labelValue === symbol; 33 | } 34 | 35 | public override toString(): string { 36 | return this.labelValue.toString(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/atn/BasicBlockStartState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { BlockStartState } from "./BlockStartState.js"; 9 | 10 | export class BasicBlockStartState extends BlockStartState { 11 | public static override readonly stateType = ATNState.BLOCK_START; 12 | } 13 | -------------------------------------------------------------------------------- /src/atn/BasicState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | 9 | export class BasicState extends ATNState { 10 | public static override readonly stateType = ATNState.BASIC; 11 | } 12 | -------------------------------------------------------------------------------- /src/atn/BlockEndState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { BlockStartState } from "./BlockStartState.js"; 9 | 10 | /** 11 | * Terminal node of a simple `(a|b|c)` block. 12 | */ 13 | export class BlockEndState extends ATNState { 14 | public static override readonly stateType = ATNState.BLOCK_END; 15 | 16 | public startState?: BlockStartState; 17 | } 18 | -------------------------------------------------------------------------------- /src/atn/BlockStartState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { BlockEndState } from "./BlockEndState.js"; 8 | import { DecisionState } from "./DecisionState.js"; 9 | 10 | /** 11 | * The start of a regular `(...)` block 12 | */ 13 | export class BlockStartState extends DecisionState { 14 | public endState?: BlockEndState; 15 | } 16 | -------------------------------------------------------------------------------- /src/atn/CodePointTransitions.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import type { ATNState } from "./ATNState.js"; 8 | import { AtomTransition } from "./AtomTransition.js"; 9 | import { RangeTransition } from "./RangeTransition.js"; 10 | import type { Transition } from "./Transition.js"; 11 | 12 | /* eslint-disable jsdoc/require-param */ 13 | 14 | /** 15 | * Utility class to create {@link AtomTransition}, {@link RangeTransition}, 16 | * and {@link SetTransition} appropriately based on the range of the input. 17 | * 18 | * Previously, we distinguished between atom and range transitions for 19 | * Unicode code points <= U+FFFF and those above. We used a set 20 | * transition for a Unicode code point > U+FFFF. Now that we can serialize 21 | * 32-bit int/chars in the ATN serialization, this is no longer necessary. 22 | */ 23 | export abstract class CodePointTransitions { 24 | /** @returns new {@link AtomTransition} */ 25 | public static createWithCodePoint(target: ATNState, codePoint: number): Transition { 26 | return CodePointTransitions.createWithCodePointRange(target, codePoint, codePoint); 27 | } 28 | 29 | /** @returns new {@link AtomTransition} if range represents one atom else {@link SetTransition}. */ 30 | public static createWithCodePointRange(target: ATNState, codePointFrom: number, codePointTo: number): Transition { 31 | return codePointFrom === codePointTo 32 | ? new AtomTransition(target, codePointFrom) 33 | : new RangeTransition(target, codePointFrom, codePointTo); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/atn/ContextSensitivityInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { DecisionEventInfo } from "./DecisionEventInfo.js"; 8 | 9 | /** 10 | * This class represents profiling event information for a context sensitivity. 11 | * Context sensitivities are decisions where a particular input resulted in an 12 | * SLL conflict, but LL prediction produced a single unique alternative. 13 | * 14 | * In some cases, the unique alternative identified by LL prediction is not 15 | * equal to the minimum represented alternative in the conflicting SLL 16 | * configuration set. Grammars and inputs which result in this scenario are 17 | * unable to use {@link PredictionMode#SLL}, which in turn means they cannot use 18 | * the two-stage parsing strategy to improve parsing performance for that 19 | * input. 20 | * 21 | * @see ParserATNSimulator#reportContextSensitivity 22 | * @see ANTLRErrorListener#reportContextSensitivity 23 | */ 24 | export interface ContextSensitivityInfo extends DecisionEventInfo { 25 | fullCtx: true; 26 | } 27 | -------------------------------------------------------------------------------- /src/atn/DecisionEventInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { TokenStream } from "../TokenStream.js"; 8 | import { ATNConfigSet } from "./ATNConfigSet.js"; 9 | 10 | /** 11 | * This is the base class for gathering detailed information about prediction 12 | * events which occur during parsing. 13 | * 14 | * Note that we could record the parser call stack at the time this event 15 | * occurred but in the presence of left recursive rules, the stack is kind of 16 | * meaningless. It's better to look at the individual configurations for their 17 | * individual stacks. Of course that is a {@link PredictionContext} object 18 | * not a parse tree node and so it does not have information about the extent 19 | * (start...stop) of the various subtrees. Examining the stack tops of all 20 | * configurations provide the return states for the rule invocations. 21 | * From there you can get the enclosing rule. 22 | */ 23 | export interface DecisionEventInfo { 24 | /** 25 | * The invoked decision number which this event is related to. 26 | * 27 | * @see ATN#decisionToState 28 | */ 29 | decision: number; 30 | 31 | /** 32 | * The configuration set containing additional information relevant to the 33 | * prediction state when the current event occurred, or `null` if no 34 | * additional information is relevant or available. 35 | */ 36 | configs: ATNConfigSet | null; 37 | 38 | /** 39 | * The input token stream which is being parsed. 40 | */ 41 | input: TokenStream; 42 | 43 | /** 44 | * The token index in the input stream at which the current prediction was 45 | * originally invoked. 46 | */ 47 | startIndex: number; 48 | 49 | /** 50 | * The token index in the input stream at which the current event occurred. 51 | */ 52 | stopIndex: number; 53 | 54 | /** 55 | * `true` if the current event occurred during LL prediction; 56 | * otherwise, `false` if the input occurred during SLL prediction. 57 | */ 58 | fullCtx: boolean; 59 | } 60 | -------------------------------------------------------------------------------- /src/atn/DecisionState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | 9 | export class DecisionState extends ATNState { 10 | public decision = -1; 11 | public nonGreedy = false; 12 | } 13 | -------------------------------------------------------------------------------- /src/atn/EmptyPredictionContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { PredictionContext } from "./PredictionContext.js"; 8 | import { SingletonPredictionContext } from "./SingletonPredictionContext.js"; 9 | 10 | export class EmptyPredictionContext extends SingletonPredictionContext { 11 | /** 12 | * Represents `$` in local context prediction, which means wildcard. 13 | * `*+x = *`. 14 | */ 15 | public static readonly instance = new EmptyPredictionContext(); 16 | 17 | public constructor() { 18 | super(undefined, PredictionContext.EMPTY_RETURN_STATE); 19 | } 20 | 21 | public override isEmpty(): boolean { 22 | return true; 23 | } 24 | 25 | public override getParent(): PredictionContext | null { 26 | return null; 27 | } 28 | 29 | public override getReturnState(): number { 30 | return this.returnState; 31 | } 32 | 33 | public override equals(other: unknown): boolean { 34 | return this === other; 35 | } 36 | 37 | public override toString(): string { 38 | return "$"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/atn/EpsilonTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Transition } from "./Transition.js"; 8 | import { ATNState } from "./ATNState.js"; 9 | 10 | export class EpsilonTransition extends Transition { 11 | readonly #outermostPrecedenceReturn: number; 12 | 13 | public constructor(target: ATNState, outermostPrecedenceReturn = -1) { 14 | super(target); 15 | this.#outermostPrecedenceReturn = outermostPrecedenceReturn; 16 | } 17 | 18 | /** 19 | * @returns the rule index of a precedence rule for which this transition is 20 | * returning from, where the precedence value is 0; otherwise, -1. 21 | * 22 | * @see ATNConfig.isPrecedenceFilterSuppressed() 23 | * @see ParserATNSimulator.applyPrecedenceFilter(ATNConfigSet) 24 | * @since 4.4.1 25 | */ 26 | public get outermostPrecedenceReturn(): number { 27 | return this.#outermostPrecedenceReturn; 28 | } 29 | 30 | public override get isEpsilon(): boolean { 31 | return true; 32 | } 33 | 34 | public get transitionType(): number { 35 | return Transition.EPSILON; 36 | } 37 | 38 | public override matches(): boolean { 39 | return false; 40 | } 41 | 42 | public override toString(): string { 43 | return "epsilon"; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/atn/LexerATNConfig.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { DecisionState } from "./DecisionState.js"; 8 | import { ATNConfig } from "./ATNConfig.js"; 9 | import { LexerActionExecutor } from "./LexerActionExecutor.js"; 10 | import { ATNState } from "./ATNState.js"; 11 | import { PredictionContext } from "./PredictionContext.js"; 12 | import { MurmurHash } from "../utils/MurmurHash.js"; 13 | import { SemanticContext } from "./index.js"; 14 | 15 | export class LexerATNConfig extends ATNConfig { 16 | /** 17 | * This is the backing field for {@link #getLexerActionExecutor}. 18 | */ 19 | public readonly lexerActionExecutor: LexerActionExecutor | null; 20 | 21 | public readonly passedThroughNonGreedyDecision: boolean; 22 | 23 | public constructor(config: Partial, state: ATNState, context: PredictionContext | null, 24 | lexerActionExecutor: LexerActionExecutor | null) { 25 | super(config, state, context ?? config.context!, context ? SemanticContext.NONE : config.semanticContext); 26 | 27 | this.lexerActionExecutor = context ? lexerActionExecutor : config.lexerActionExecutor ?? null; 28 | this.passedThroughNonGreedyDecision = LexerATNConfig.checkNonGreedyDecision(config, this.state); 29 | 30 | return this; 31 | } 32 | 33 | public static createWithExecutor(config: LexerATNConfig, state: ATNState, 34 | lexerActionExecutor: LexerActionExecutor | null): LexerATNConfig { 35 | return new LexerATNConfig(config, state, config.context, lexerActionExecutor); 36 | } 37 | 38 | public static override createWithConfig(state: ATNState, config: LexerATNConfig, 39 | context?: PredictionContext): LexerATNConfig { 40 | 41 | return new LexerATNConfig(config, state, context ?? null, config.lexerActionExecutor); 42 | } 43 | 44 | public static override createWithContext(state: ATNState, alt: number, context: PredictionContext): LexerATNConfig { 45 | return new LexerATNConfig({ alt }, state, context, null); 46 | } 47 | 48 | private static checkNonGreedyDecision(source: Partial, target: ATNState): boolean { 49 | return source.passedThroughNonGreedyDecision || 50 | (("nonGreedy" in target) && (target as DecisionState).nonGreedy); 51 | } 52 | 53 | public override hashCode(): number { 54 | if (this.cachedHashCode === undefined) { 55 | let hashCode = MurmurHash.initialize(7); 56 | hashCode = MurmurHash.update(hashCode, this.state.stateNumber); 57 | hashCode = MurmurHash.update(hashCode, this.alt); 58 | hashCode = MurmurHash.updateFromComparable(hashCode, this.context); 59 | hashCode = MurmurHash.updateFromComparable(hashCode, this.semanticContext); 60 | hashCode = MurmurHash.update(hashCode, this.passedThroughNonGreedyDecision ? 1 : 0); 61 | hashCode = MurmurHash.updateFromComparable(hashCode, this.lexerActionExecutor); 62 | hashCode = MurmurHash.finish(hashCode, 6); 63 | this.cachedHashCode = hashCode; 64 | } 65 | 66 | return this.cachedHashCode; 67 | } 68 | 69 | public override equals(other: LexerATNConfig): boolean { 70 | if (this === other) { 71 | return true; 72 | } 73 | 74 | return this.passedThroughNonGreedyDecision === other.passedThroughNonGreedyDecision && 75 | (this.lexerActionExecutor && other.lexerActionExecutor 76 | ? this.lexerActionExecutor.equals(other.lexerActionExecutor) 77 | : !other.lexerActionExecutor 78 | ) && super.equals(other); 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/atn/LexerAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Lexer } from "../Lexer.js"; 8 | import type { IComparable } from "../index.js"; 9 | 10 | /** 11 | * Represents a single action which can be executed following the successful 12 | * match of a lexer rule. Lexer actions are used for both embedded action syntax 13 | * and ANTLR 4's new lexer command syntax. 14 | */ 15 | export interface LexerAction extends IComparable { 16 | readonly actionType: number; 17 | isPositionDependent: boolean; 18 | 19 | execute(lexer: Lexer): void; 20 | toString(): string; 21 | } 22 | -------------------------------------------------------------------------------- /src/atn/LexerActionType.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable @typescript-eslint/naming-convention */ 8 | 9 | export const LexerActionType = { 10 | /** The type of a {@link LexerChannelAction} action. */ 11 | CHANNEL: 0, 12 | /** The type of a {@link LexerCustomAction} action */ 13 | CUSTOM: 1, 14 | /** The type of a {@link LexerModeAction} action. */ 15 | MODE: 2, 16 | /** The type of a {@link LexerMoreAction} action. */ 17 | MORE: 3, 18 | /** The type of a {@link LexerPopModeAction} action. */ 19 | POP_MODE: 4, 20 | /** The type of a {@link LexerPushModeAction} action. */ 21 | PUSH_MODE: 5, 22 | /** The type of a {@link LexerSkipAction} action. */ 23 | SKIP: 6, 24 | /** The type of a {@link LexerTypeAction} action. */ 25 | TYPE: 7, 26 | } as const; 27 | -------------------------------------------------------------------------------- /src/atn/LexerChannelAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { LexerActionType } from "./LexerActionType.js"; 10 | import { LexerAction } from "./LexerAction.js"; 11 | import { Lexer } from "../Lexer.js"; 12 | import { MurmurHash } from "../utils/MurmurHash.js"; 13 | 14 | /** 15 | * Implements the `channel` lexer action by calling 16 | * {@link Lexer.setChannel} with the assigned channel. 17 | * Constructs a new `channel` action with the specified channel value. 18 | * 19 | * @param channel The channel value to pass to {@link Lexer.setChannel} 20 | */ 21 | export class LexerChannelAction implements LexerAction { 22 | public readonly channel: number; 23 | public readonly actionType: number; 24 | public isPositionDependent: boolean = false; 25 | 26 | private cachedHashCode: number | undefined; 27 | 28 | public constructor(channel: number) { 29 | this.actionType = LexerActionType.CHANNEL; 30 | this.channel = channel; 31 | } 32 | 33 | /** 34 | * This action is implemented by calling {@link Lexer.setChannel} with the 35 | * value provided by {@link getChannel}. 36 | */ 37 | public execute(lexer: Lexer): void { 38 | // eslint-disable-next-line no-underscore-dangle 39 | lexer.channel = this.channel; 40 | } 41 | 42 | public hashCode(): number { 43 | if (this.cachedHashCode === undefined) { 44 | let hash = MurmurHash.initialize(); 45 | hash = MurmurHash.update(hash, this.actionType); 46 | hash = MurmurHash.update(hash, this.channel); 47 | this.cachedHashCode = MurmurHash.finish(hash, 2); 48 | } 49 | 50 | return this.cachedHashCode; 51 | } 52 | 53 | public equals(other: unknown): boolean { 54 | if (this === other) { 55 | return true; 56 | } 57 | 58 | if (!(other instanceof LexerChannelAction)) { 59 | return false; 60 | } 61 | 62 | return this.channel === other.channel; 63 | } 64 | 65 | public toString(): string { 66 | return "channel(" + this.channel + ")"; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/atn/LexerCustomAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { LexerActionType } from "./LexerActionType.js"; 10 | import { LexerAction } from "./LexerAction.js"; 11 | import { Lexer } from "../Lexer.js"; 12 | import { MurmurHash } from "../utils/MurmurHash.js"; 13 | 14 | /** 15 | * Executes a custom lexer action by calling {@link Recognizer.action} with the 16 | * rule and action indexes assigned to the custom action. The implementation of 17 | * a custom action is added to the generated code for the lexer in an override 18 | * of {@link Recognizer//action} when the grammar is compiled. 19 | * 20 | * This class may represent embedded actions created with the `{...}` 21 | * syntax in ANTLR 4, as well as actions created for lexer commands where the 22 | * command argument could not be evaluated when the grammar was compiled. 23 | */ 24 | export class LexerCustomAction implements LexerAction { 25 | public readonly ruleIndex: number; 26 | public readonly actionIndex: number; 27 | public readonly actionType: number; 28 | public isPositionDependent: boolean = true; 29 | 30 | private cachedHashCode: number | undefined; 31 | 32 | /** 33 | * Constructs a custom lexer action with the specified rule and action indexes. 34 | * 35 | * @param ruleIndex The rule index to use for calls to {@link Recognizer.action}. 36 | * @param actionIndex The action index to use for calls to {@link Recognizer.action}. 37 | */ 38 | public constructor(ruleIndex: number, actionIndex: number) { 39 | this.actionType = LexerActionType.CUSTOM; 40 | this.ruleIndex = ruleIndex; 41 | this.actionIndex = actionIndex; 42 | } 43 | 44 | /** 45 | * Custom actions are implemented by calling {@link Lexer.action} with the 46 | * appropriate rule and action indexes. 47 | */ 48 | public execute(lexer: Lexer): void { 49 | lexer.action(null, this.ruleIndex, this.actionIndex); 50 | } 51 | 52 | public hashCode(): number { 53 | if (this.cachedHashCode === undefined) { 54 | let hash = MurmurHash.initialize(); 55 | hash = MurmurHash.update(hash, this.actionType); 56 | hash = MurmurHash.update(hash, this.ruleIndex); 57 | hash = MurmurHash.update(hash, this.actionIndex); 58 | this.cachedHashCode = MurmurHash.finish(hash, 3); 59 | } 60 | 61 | return this.cachedHashCode; 62 | } 63 | 64 | public equals(other: unknown): boolean { 65 | if (this === other) { 66 | return true; 67 | } 68 | 69 | if (!(other instanceof LexerCustomAction)) { 70 | return false; 71 | } 72 | 73 | return this.ruleIndex === other.ruleIndex && this.actionIndex === other.actionIndex; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/atn/LexerIndexedCustomAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { LexerAction } from "./LexerAction.js"; 10 | import { Lexer } from "../Lexer.js"; 11 | import { MurmurHash } from "../utils/MurmurHash.js"; 12 | 13 | /** 14 | * This implementation of {@link LexerAction} is used for tracking input offsets 15 | * for position-dependent actions within a {@link LexerActionExecutor}. 16 | * 17 | * This action is not serialized as part of the ATN, and is only required for 18 | * position-dependent lexer actions which appear at a location other than the 19 | * end of a rule. For more information about DFA optimizations employed for 20 | * lexer actions, see {@link LexerActionExecutor//append} and 21 | * {@link LexerActionExecutor//fixOffsetBeforeMatch}. 22 | * 23 | * Constructs a new indexed custom action by associating a character offset 24 | * with a {@link LexerAction}. 25 | * 26 | * Note: This class is only required for lexer actions for which 27 | * {@link LexerAction//isPositionDependent} returns `true`. 28 | * 29 | * @param offset The offset into the input {@link CharStream}, relative to 30 | * the token start index, at which the specified lexer action should be 31 | * executed. 32 | * @param action The lexer action to execute at a particular offset in the 33 | * input {@link CharStream}. 34 | */ 35 | 36 | export class LexerIndexedCustomAction implements LexerAction { 37 | public readonly offset: number; 38 | public readonly action: LexerAction; 39 | public readonly actionType: number; 40 | public isPositionDependent: boolean = true; 41 | 42 | private cachedHashCode: number | undefined; 43 | 44 | public constructor(offset: number, action: LexerAction) { 45 | this.actionType = action.actionType; 46 | this.offset = offset; 47 | this.action = action; 48 | } 49 | 50 | /** 51 | * This method calls {@link execute} on the result of {@link getAction} 52 | * using the provided `lexer`. 53 | */ 54 | public execute(lexer: Lexer): void { 55 | // Assume the input stream position was properly set by the calling code 56 | this.action.execute(lexer); 57 | } 58 | 59 | public hashCode(): number { 60 | if (this.cachedHashCode === undefined) { 61 | let hash = MurmurHash.initialize(); 62 | hash = MurmurHash.update(hash, this.offset); 63 | hash = MurmurHash.updateFromComparable(hash, this.action); 64 | 65 | this.cachedHashCode = MurmurHash.finish(hash, 2); 66 | } 67 | 68 | return this.cachedHashCode; 69 | } 70 | 71 | public equals(other: unknown): boolean { 72 | if (this === other) { 73 | return true; 74 | } 75 | 76 | if (!(other instanceof LexerIndexedCustomAction)) { 77 | return false; 78 | } 79 | 80 | return this.offset === other.offset && this.action === other.action; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/atn/LexerModeAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { LexerActionType } from "./LexerActionType.js"; 10 | import { LexerAction } from "./LexerAction.js"; 11 | import { Lexer } from "../Lexer.js"; 12 | import { MurmurHash } from "../utils/MurmurHash.js"; 13 | 14 | /** 15 | * Implements the `mode` lexer action by calling {@link Lexer//mode} with 16 | * the assigned mode 17 | */ 18 | export class LexerModeAction implements LexerAction { 19 | public readonly mode: number; 20 | public readonly actionType: number; 21 | public isPositionDependent: boolean = false; 22 | 23 | private cachedHashCode: number | undefined; 24 | 25 | public constructor(mode: number) { 26 | this.actionType = LexerActionType.MODE; 27 | this.mode = mode; 28 | } 29 | 30 | /** 31 | * This action is implemented by calling {@link Lexer.mode} with the 32 | * value provided by {@link getMode}. 33 | */ 34 | public execute(lexer: Lexer): void { 35 | lexer.mode = this.mode; 36 | } 37 | 38 | public hashCode(): number { 39 | if (this.cachedHashCode === undefined) { 40 | let hash = MurmurHash.initialize(); 41 | hash = MurmurHash.update(hash, this.actionType); 42 | hash = MurmurHash.update(hash, this.mode); 43 | this.cachedHashCode = MurmurHash.finish(hash, 2); 44 | } 45 | 46 | return this.cachedHashCode; 47 | } 48 | 49 | public equals(other: unknown): boolean { 50 | if (this === other) { 51 | return true; 52 | } 53 | 54 | if (!(other instanceof LexerModeAction)) { 55 | return false; 56 | } 57 | 58 | return this.mode === other.mode; 59 | } 60 | 61 | public toString(): string { 62 | return "mode(" + this.mode + ")"; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/atn/LexerMoreAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { LexerActionType } from "./LexerActionType.js"; 10 | import { LexerAction } from "./LexerAction.js"; 11 | import { Lexer } from "../Lexer.js"; 12 | 13 | /** 14 | * Implements the `more` lexer action by calling {@link Lexer//more}. 15 | * 16 | * The `more` command does not have any parameters, so this action is 17 | * implemented as a singleton instance exposed by {@link instance}. 18 | */ 19 | export class LexerMoreAction implements LexerAction { 20 | public static readonly instance = new LexerMoreAction(); 21 | public readonly actionType: number; 22 | public isPositionDependent: boolean = false; 23 | 24 | public constructor() { 25 | this.actionType = LexerActionType.MORE; 26 | } 27 | 28 | public equals(obj: unknown): boolean { 29 | return obj === this; 30 | } 31 | 32 | public hashCode(): number { 33 | return LexerActionType.MORE; 34 | } 35 | 36 | /** 37 | * This action is implemented by calling {@link Lexer.popMode}. 38 | */ 39 | public execute(lexer: Lexer): void { 40 | lexer.more(); 41 | } 42 | 43 | public toString(): string { 44 | return "more"; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/atn/LexerPopModeAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { LexerActionType } from "./LexerActionType.js"; 10 | import { LexerAction } from "./LexerAction.js"; 11 | import { Lexer } from "../Lexer.js"; 12 | 13 | /** 14 | * Implements the `popMode` lexer action by calling {@link Lexer.popMode}. 15 | * 16 | * The `popMode` command does not have any parameters, so this action is 17 | * implemented as a singleton instance exposed by {@link instance}. 18 | */ 19 | export class LexerPopModeAction implements LexerAction { 20 | public static readonly instance = new LexerPopModeAction(); 21 | public readonly actionType: number; 22 | public isPositionDependent: boolean = false; 23 | 24 | public constructor() { 25 | this.actionType = LexerActionType.POP_MODE; 26 | } 27 | 28 | public equals(obj: unknown): boolean { 29 | return obj === this; 30 | } 31 | 32 | public hashCode(): number { 33 | return LexerActionType.POP_MODE; 34 | } 35 | 36 | /** 37 | * This action is implemented by calling {@link Lexer//popMode}. 38 | */ 39 | public execute(lexer: Lexer): void { 40 | lexer.popMode(); 41 | } 42 | 43 | public toString(): string { 44 | return "popMode"; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/atn/LexerPushModeAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* eslint-disable jsdoc/require-param */ 8 | 9 | import { LexerActionType } from "./LexerActionType.js"; 10 | import { LexerAction } from "./LexerAction.js"; 11 | import { Lexer } from "../Lexer.js"; 12 | import { MurmurHash } from "../utils/MurmurHash.js"; 13 | 14 | /** 15 | * Implements the `pushMode` lexer action by calling 16 | * {@link Lexer//pushMode} with the assigned mode 17 | */ 18 | export class LexerPushModeAction implements LexerAction { 19 | public readonly mode: number; 20 | public readonly actionType: number; 21 | public isPositionDependent: boolean = false; 22 | 23 | private cachedHashCode: number | undefined; 24 | 25 | public constructor(mode: number) { 26 | this.actionType = LexerActionType.PUSH_MODE; 27 | this.mode = mode; 28 | } 29 | 30 | /** 31 | * This action is implemented by calling {@link Lexer.pushMode} with the 32 | * value provided by {@link getMode}. 33 | */ 34 | public execute(lexer: Lexer): void { 35 | lexer.pushMode(this.mode); 36 | } 37 | 38 | public hashCode(): number { 39 | if (this.cachedHashCode === undefined) { 40 | let hash = MurmurHash.initialize(); 41 | hash = MurmurHash.update(hash, this.actionType); 42 | hash = MurmurHash.update(hash, this.mode); 43 | this.cachedHashCode = MurmurHash.finish(hash, 2); 44 | } 45 | 46 | return this.cachedHashCode; 47 | } 48 | 49 | public equals(other: unknown): boolean { 50 | if (this === other) { 51 | return true; 52 | } 53 | 54 | if (!(other instanceof LexerPushModeAction)) { 55 | return false; 56 | } 57 | 58 | return this.mode === other.mode; 59 | } 60 | 61 | public toString(): string { 62 | return "pushMode(" + this.mode + ")"; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/atn/LexerSkipAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { LexerActionType } from "./LexerActionType.js"; 8 | import { LexerAction } from "./LexerAction.js"; 9 | import { Lexer } from "../Lexer.js"; 10 | 11 | /** 12 | * Implements the `skip` lexer action by calling {@link Lexer//skip}. 13 | * 14 | * The `skip` command does not have any parameters, so this action is 15 | * implemented as a singleton instance exposed by {@link instance}. 16 | */ 17 | export class LexerSkipAction implements LexerAction { 18 | /** Provides a singleton instance of this parameter-less lexer action. */ 19 | public static readonly instance = new LexerSkipAction(); 20 | public readonly actionType: number; 21 | public isPositionDependent: boolean = false; 22 | 23 | public constructor() { 24 | this.actionType = LexerActionType.SKIP; 25 | } 26 | 27 | public equals(obj: unknown): boolean { 28 | return obj === this; 29 | } 30 | 31 | public hashCode(): number { 32 | return LexerActionType.SKIP; 33 | } 34 | 35 | public execute(lexer: Lexer): void { 36 | lexer.skip(); 37 | } 38 | 39 | public toString(): string { 40 | return "skip"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/atn/LexerTypeAction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { LexerActionType } from "./LexerActionType.js"; 8 | import { LexerAction } from "./LexerAction.js"; 9 | import { Lexer } from "../Lexer.js"; 10 | import { MurmurHash } from "../utils/MurmurHash.js"; 11 | 12 | /** 13 | * Implements the `type` lexer action by calling {@link Lexer.setType} with the assigned type. 14 | */ 15 | export class LexerTypeAction implements LexerAction { 16 | public readonly type: number; 17 | public readonly actionType: number; 18 | public isPositionDependent: boolean = false; 19 | 20 | private cachedHashCode: number | undefined; 21 | 22 | public constructor(type: number) { 23 | this.actionType = LexerActionType.TYPE; 24 | this.type = type; 25 | } 26 | 27 | public execute(lexer: Lexer): void { 28 | lexer.type = this.type; 29 | } 30 | 31 | public hashCode(): number { 32 | if (this.cachedHashCode === undefined) { 33 | let hash = MurmurHash.initialize(); 34 | hash = MurmurHash.update(hash, this.actionType); 35 | hash = MurmurHash.update(hash, this.type); 36 | this.cachedHashCode = MurmurHash.finish(hash, 2); 37 | } 38 | 39 | return this.cachedHashCode; 40 | } 41 | 42 | public equals(other: unknown): boolean { 43 | if (this === other) { 44 | return true; 45 | } 46 | 47 | if (!(other instanceof LexerTypeAction)) { 48 | return false; 49 | } 50 | 51 | return this.type === other.type; 52 | } 53 | 54 | public toString(): string { 55 | return "type(" + this.type + ")"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/atn/LookaheadEventInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { DecisionEventInfo } from "./DecisionEventInfo.js"; 8 | 9 | export interface LookaheadEventInfo extends DecisionEventInfo { 10 | /** 11 | * The alternative chosen by adaptivePredict(), not necessarily 12 | * the outermost alt shown for a rule; left-recursive rules have 13 | * user-level alts that differ from the rewritten rule with a (...) block 14 | * and a (..)* loop. 15 | */ 16 | predictedAlt: number; 17 | } 18 | -------------------------------------------------------------------------------- /src/atn/LoopEndState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | 9 | /** 10 | * Mark the end of a * or + loop 11 | */ 12 | export class LoopEndState extends ATNState { 13 | public static override readonly stateType = ATNState.LOOP_END; 14 | 15 | public loopBackState?: ATNState; 16 | } 17 | -------------------------------------------------------------------------------- /src/atn/NotSetTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { SetTransition } from "./SetTransition.js"; 8 | import { Transition } from "./Transition.js"; 9 | 10 | export class NotSetTransition extends SetTransition { 11 | public override get transitionType(): number { 12 | return Transition.NOT_SET; 13 | } 14 | 15 | public override matches(symbol: number, minVocabSymbol: number, maxVocabSymbol: number): boolean { 16 | return symbol >= minVocabSymbol && symbol <= maxVocabSymbol && 17 | !super.matches(symbol, minVocabSymbol, maxVocabSymbol); 18 | } 19 | 20 | public override toString(): string { 21 | return "~" + super.toString(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/atn/OrderedATNConfigSet.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNConfigSet } from "./ATNConfigSet.js"; 8 | import { OrderedHashSet } from "../misc/OrderedHashSet.js"; 9 | 10 | export class OrderedATNConfigSet extends ATNConfigSet { 11 | public constructor() { 12 | super(); 13 | this.configLookup = new OrderedHashSet(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/atn/PlusBlockStartState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { BlockStartState } from "./BlockStartState.js"; 9 | import { PlusLoopbackState } from "./PlusLoopbackState.js"; 10 | 11 | /** 12 | * Start of `(A|B|...)+` loop. Technically a decision state, but 13 | * we don't use for code generation; somebody might need it, so I'm defining 14 | * it for completeness. In reality, the {@link PlusLoopbackState} node is the 15 | * real decision-making note for `A+` 16 | */ 17 | export class PlusBlockStartState extends BlockStartState { 18 | public static override readonly stateType = ATNState.PLUS_BLOCK_START; 19 | 20 | public loopBackState: PlusLoopbackState; 21 | } 22 | -------------------------------------------------------------------------------- /src/atn/PlusLoopbackState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { DecisionState } from "./DecisionState.js"; 9 | 10 | /** 11 | * Decision state for `A+` and `(A|B)+`. It has two transitions: 12 | * one to the loop back to start of the block and one to exit. 13 | */ 14 | export class PlusLoopbackState extends DecisionState { 15 | public static override readonly stateType = ATNState.PLUS_LOOP_BACK; 16 | } 17 | -------------------------------------------------------------------------------- /src/atn/PrecedencePredicateTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { SemanticContext } from "./SemanticContext.js"; 8 | import { AbstractPredicateTransition } from "./AbstractPredicateTransition.js"; 9 | import { ATNState } from "./ATNState.js"; 10 | import { Transition } from "./Transition.js"; 11 | 12 | export class PrecedencePredicateTransition extends AbstractPredicateTransition { 13 | public readonly precedence: number; 14 | 15 | public constructor(target: ATNState, precedence: number) { 16 | super(target); 17 | this.precedence = precedence; 18 | } 19 | 20 | public override get isEpsilon(): boolean { 21 | return true; 22 | } 23 | 24 | public matches(_symbol: number, _minVocabSymbol: number, _maxVocabSymbol: number): boolean { 25 | return false; 26 | } 27 | 28 | public getPredicate(): SemanticContext.PrecedencePredicate { 29 | return new SemanticContext.PrecedencePredicate(this.precedence); 30 | } 31 | 32 | public get transitionType(): number { 33 | return Transition.PRECEDENCE; 34 | } 35 | 36 | public override toString(): string { 37 | return this.precedence + " >= _p"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/atn/PredicateEvalInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { DecisionEventInfo } from "./DecisionEventInfo.js"; 8 | import { SemanticContext } from "./SemanticContext.js"; 9 | 10 | /** 11 | * This interface represents profiling event information for semantic predicate 12 | * evaluations which occur during prediction. 13 | */ 14 | export interface PredicateEvalInfo extends DecisionEventInfo { 15 | /** The semantic context which was evaluated. */ 16 | semctx: SemanticContext; 17 | 18 | /** 19 | * The alternative number for the decision which is guarded by the semantic 20 | * context {@link #semctx}. Note that other ATN 21 | * configurations may predict the same alternative which are guarded by 22 | * other semantic contexts and/or {@link SemanticContext#NONE}. 23 | */ 24 | predictedAlt: number; 25 | 26 | /** The result of evaluating the semantic context {@link #semctx}. */ 27 | evalResult: boolean; 28 | } 29 | -------------------------------------------------------------------------------- /src/atn/PredicateTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { SemanticContext } from "./SemanticContext.js"; 8 | import { AbstractPredicateTransition } from "./AbstractPredicateTransition.js"; 9 | import { ATNState } from "./ATNState.js"; 10 | import { Transition } from "./Transition.js"; 11 | 12 | export class PredicateTransition extends AbstractPredicateTransition { 13 | public readonly ruleIndex: number; 14 | public readonly predIndex: number; 15 | public readonly isCtxDependent: boolean; // e.g., $i ref in pred 16 | 17 | public constructor(target: ATNState, ruleIndex: number, predIndex: number, isCtxDependent: boolean) { 18 | super(target); 19 | this.ruleIndex = ruleIndex; 20 | this.predIndex = predIndex; 21 | this.isCtxDependent = isCtxDependent; // e.g., $i ref in pred 22 | } 23 | 24 | public override get isEpsilon(): boolean { 25 | return true; 26 | } 27 | 28 | public matches(_symbol: number, _minVocabSymbol: number, _maxVocabSymbol: number): boolean { 29 | return false; 30 | } 31 | 32 | public get transitionType(): number { 33 | return Transition.PREDICATE; 34 | } 35 | 36 | public getPredicate(): SemanticContext.Predicate { 37 | return new SemanticContext.Predicate(this.ruleIndex, this.predIndex, this.isCtxDependent); 38 | } 39 | 40 | public override toString(): string { 41 | return "pred_" + this.ruleIndex + ":" + this.predIndex; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/atn/PredictionContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Recognizer } from "../Recognizer.js"; 8 | import { MurmurHash } from "../utils/MurmurHash.js"; 9 | import { ATNSimulator } from "./ATNSimulator.js"; 10 | 11 | // Most of the implementation is located in PredictionContextUtils.ts, to avoid circular dependencies. 12 | 13 | export abstract class PredictionContext { 14 | /** 15 | * Represents `$` in an array in full context mode, when `$` 16 | * doesn't mean wildcard: `$ + x = [$,x]`. Here, 17 | * `$` = {@link EMPTY_RETURN_STATE}. 18 | */ 19 | public static readonly EMPTY_RETURN_STATE = 0x7FFFFFFF; 20 | 21 | public static traceATNSimulator = false; 22 | 23 | private cachedHashCode: number; 24 | 25 | public constructor(cachedHashCode: number) { 26 | this.cachedHashCode = cachedHashCode; 27 | } 28 | 29 | protected static calculateEmptyHashCode(): number { 30 | let hash = MurmurHash.initialize(31); 31 | hash = MurmurHash.finish(hash, 0); 32 | 33 | return hash; 34 | } 35 | 36 | protected static calculateHashCodeSingle(parent: PredictionContext, returnState: number): number { 37 | let hash = MurmurHash.initialize(31); 38 | hash = MurmurHash.updateFromComparable(hash, parent); 39 | hash = MurmurHash.update(hash, returnState); 40 | hash = MurmurHash.finish(hash, 2); 41 | 42 | return hash; 43 | } 44 | 45 | protected static calculateHashCodeList(parents: Array, returnStates: number[]): number { 46 | let hash = MurmurHash.initialize(31); 47 | 48 | for (const parent of parents) { 49 | hash = MurmurHash.updateFromComparable(hash, parent); 50 | } 51 | 52 | for (const returnState of returnStates) { 53 | hash = MurmurHash.update(hash, returnState); 54 | } 55 | 56 | hash = MurmurHash.finish(hash, 2 * parents.length); 57 | 58 | return hash; 59 | } 60 | 61 | public isEmpty(): boolean { 62 | return false; 63 | } 64 | 65 | public hasEmptyPath(): boolean { 66 | return this.getReturnState(this.length - 1) === PredictionContext.EMPTY_RETURN_STATE; 67 | } 68 | 69 | public hashCode(): number { 70 | return this.cachedHashCode; 71 | } 72 | 73 | public toString(_recog?: Recognizer): string { 74 | return ""; 75 | } 76 | 77 | public abstract getParent(index: number): PredictionContext | null; 78 | public abstract getReturnState(index: number): number; 79 | public abstract get length(): number; 80 | public abstract equals(obj: unknown): boolean; 81 | } 82 | -------------------------------------------------------------------------------- /src/atn/PredictionContextCache.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { HashMap } from "../misc/HashMap.js"; 8 | import { ObjectEqualityComparator } from "../misc/ObjectEqualityComparator.js"; 9 | import { EmptyPredictionContext } from "./EmptyPredictionContext.js"; 10 | import { PredictionContext } from "./PredictionContext.js"; 11 | 12 | /** 13 | * Used to cache {@link PredictionContext} objects. Its used for the shared 14 | * context cache associated with contexts in DFA states. This cache 15 | * can be used for both lexers and parsers. 16 | */ 17 | export class PredictionContextCache { 18 | private cache = new HashMap(ObjectEqualityComparator.instance); 19 | 20 | /** 21 | * Add a context to the cache and return it. If the context already exists, 22 | * return that one instead and do not add a new context to the cache. 23 | * Protect shared cache from unsafe thread access. 24 | * 25 | * @param ctx tbd 26 | * @returns tbd 27 | */ 28 | public add(ctx: PredictionContext): PredictionContext { 29 | if (ctx === EmptyPredictionContext.instance) { 30 | return ctx; 31 | } 32 | const existing = this.cache.get(ctx); 33 | if (existing) { 34 | return existing; 35 | } 36 | this.cache.set(ctx, ctx); 37 | 38 | return ctx; 39 | } 40 | 41 | public get(ctx: PredictionContext): PredictionContext | undefined { 42 | return this.cache.get(ctx); 43 | } 44 | 45 | public get length(): number { 46 | return this.cache.size; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/atn/RangeTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { IntervalSet } from "../misc/IntervalSet.js"; 8 | import { ATNState } from "./ATNState.js"; 9 | import { Transition } from "./Transition.js"; 10 | 11 | export class RangeTransition extends Transition { 12 | public readonly start: number; 13 | public readonly stop: number; 14 | 15 | readonly #label = new IntervalSet(); 16 | 17 | public constructor(target: ATNState, start: number, stop: number) { 18 | super(target); 19 | this.start = start; 20 | this.stop = stop; 21 | this.#label.addRange(start, stop); 22 | } 23 | 24 | public override get label(): IntervalSet { 25 | return this.#label; 26 | } 27 | 28 | public get transitionType(): number { 29 | return Transition.RANGE; 30 | } 31 | 32 | public matches(symbol: number, _minVocabSymbol: number, _maxVocabSymbol: number): boolean { 33 | return symbol >= this.start && symbol <= this.stop; 34 | } 35 | 36 | public override toString(): string { 37 | return "'" + String.fromCharCode(this.start) + "'..'" + String.fromCharCode(this.stop) + "'"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/atn/RuleStartState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { RuleStopState } from "./RuleStopState.js"; 9 | 10 | export class RuleStartState extends ATNState { 11 | public static override readonly stateType = ATNState.RULE_START; 12 | 13 | public stopState?: RuleStopState; 14 | public isLeftRecursiveRule: boolean = false; 15 | } 16 | -------------------------------------------------------------------------------- /src/atn/RuleStopState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | 9 | /** 10 | * The last node in the ATN for a rule, unless that rule is the start symbol. 11 | * In that case, there is one transition to EOF. Later, we might encode 12 | * references to all calls to this rule to compute FOLLOW sets for 13 | * error handling 14 | */ 15 | export class RuleStopState extends ATNState { 16 | public static override readonly stateType = ATNState.RULE_STOP; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/atn/RuleTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { Transition } from "./Transition.js"; 9 | 10 | export class RuleTransition extends Transition { 11 | public ruleIndex: number; 12 | public precedence: number; 13 | public followState: ATNState; 14 | 15 | public constructor(ruleStart: ATNState, ruleIndex: number, precedence: number, followState: ATNState) { 16 | super(ruleStart); 17 | 18 | // Ptr to the rule definition object for this rule ref. 19 | this.ruleIndex = ruleIndex; 20 | this.precedence = precedence; 21 | 22 | // What node to begin computations following ref to rule. 23 | this.followState = followState; 24 | } 25 | 26 | public override get isEpsilon(): boolean { 27 | return true; 28 | } 29 | 30 | public override get transitionType(): number { 31 | return Transition.RULE; 32 | } 33 | 34 | public override matches(_symbol: number, _minVocabSymbol: number, _maxVocabSymbol: number): boolean { 35 | return false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/atn/SetTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { IntervalSet } from "../misc/IntervalSet.js"; 8 | import { Token } from "../Token.js"; 9 | import { ATNState } from "./ATNState.js"; 10 | import { Transition } from "./Transition.js"; 11 | 12 | /** A transition containing a set of values. */ 13 | export class SetTransition extends Transition { 14 | public set: IntervalSet; 15 | 16 | public constructor(target: ATNState, set: IntervalSet) { 17 | super(target); 18 | if (set) { 19 | this.set = set; 20 | } else { 21 | this.set = IntervalSet.of(Token.INVALID_TYPE, Token.INVALID_TYPE); 22 | } 23 | } 24 | 25 | public get transitionType(): number { 26 | return Transition.SET; 27 | } 28 | 29 | public override get label(): IntervalSet { 30 | return this.set; 31 | } 32 | 33 | public override matches(symbol: number, _minVocabSymbol: number, _maxVocabSymbol: number): boolean { 34 | return this.set.contains(symbol); 35 | } 36 | 37 | public override toString(): string { 38 | return this.set.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/atn/SingletonPredictionContext.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { PredictionContext } from "./PredictionContext.js"; 8 | 9 | export class SingletonPredictionContext extends PredictionContext { 10 | public readonly parent: PredictionContext | null; 11 | public readonly returnState: number; 12 | 13 | public constructor(parent: PredictionContext | undefined, returnState: number) { 14 | super(parent 15 | ? PredictionContext.calculateHashCodeSingle(parent, returnState) 16 | : PredictionContext.calculateEmptyHashCode(), 17 | ); 18 | 19 | this.parent = parent ?? null; 20 | this.returnState = returnState; 21 | } 22 | 23 | public getParent(_index: number): PredictionContext | null { 24 | return this.parent; 25 | } 26 | 27 | public getReturnState(_index: number): number { 28 | return this.returnState; 29 | } 30 | 31 | public equals(other: unknown): boolean { 32 | if (this === other) { 33 | return true; 34 | } 35 | 36 | if (!(other instanceof SingletonPredictionContext)) { 37 | return false; 38 | } 39 | 40 | if (this.hashCode() !== other.hashCode()) { 41 | return false; // can't be same if hash is different 42 | } 43 | 44 | if (this.returnState !== other.returnState) { 45 | return false; 46 | } 47 | 48 | if (this.parent == null) { 49 | return other.parent == null; 50 | } 51 | 52 | return this.parent.equals(other.parent); 53 | } 54 | 55 | public override toString(): string { 56 | const up = this.parent === null ? "" : this.parent.toString(); 57 | if (up.length === 0) { 58 | if (this.returnState === PredictionContext.EMPTY_RETURN_STATE) { 59 | return "$"; 60 | } 61 | 62 | return "" + this.returnState; 63 | } else { 64 | return "" + this.returnState + " " + up; 65 | } 66 | } 67 | 68 | public get length(): number { 69 | return 1; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/atn/StarBlockStartState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { BlockStartState } from "./BlockStartState.js"; 9 | 10 | /** 11 | * The block that begins a closure loop 12 | */ 13 | export class StarBlockStartState extends BlockStartState { 14 | public static override readonly stateType = ATNState.STAR_BLOCK_START; 15 | } 16 | -------------------------------------------------------------------------------- /src/atn/StarLoopEntryState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { DecisionState } from "./DecisionState.js"; 9 | import { StarLoopbackState } from "./StarLoopbackState.js"; 10 | 11 | export class StarLoopEntryState extends DecisionState { 12 | public static override readonly stateType = ATNState.STAR_LOOP_ENTRY; 13 | 14 | // This is always set during ATN deserialization 15 | public loopBackState!: StarLoopbackState; 16 | 17 | /** 18 | * Indicates whether this state can benefit from a precedence DFA during SLL 19 | * decision making. 20 | * 21 | * This is a computed property that is calculated during ATN deserialization 22 | * and stored for use in {@link ParserATNSimulator} and 23 | * {@link ParserInterpreter}. 24 | * 25 | * @see `DFA.isPrecedenceDfa` 26 | */ 27 | public precedenceRuleDecision: boolean = false; 28 | } 29 | -------------------------------------------------------------------------------- /src/atn/StarLoopbackState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | 9 | export class StarLoopbackState extends ATNState { 10 | public static override readonly stateType = ATNState.STAR_LOOP_BACK; 11 | } 12 | -------------------------------------------------------------------------------- /src/atn/TokensStartState.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ATNState } from "./ATNState.js"; 8 | import { DecisionState } from "./DecisionState.js"; 9 | 10 | /** 11 | * The Tokens rule start state linking to each lexer rule start state 12 | */ 13 | export class TokensStartState extends DecisionState { 14 | public static override readonly stateType = ATNState.TOKEN_START; 15 | } 16 | -------------------------------------------------------------------------------- /src/atn/Transition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { IntervalSet } from "../misc/IntervalSet.js"; 8 | import { ATNState } from "./ATNState.js"; 9 | 10 | /** 11 | * An ATN transition between any two ATN states. Subclasses define 12 | * atom, set, epsilon, action, predicate, rule transitions. 13 | * 14 | * This is a one way link. It emanates from a state (usually via a list of 15 | * transitions) and has a target state. 16 | * 17 | * Since we never have to change the ATN transitions once we construct it, 18 | * we can fix these transitions as specific classes. The DFA transitions 19 | * on the other hand need to update the labels as it adds transitions to 20 | * the states. We'll use the term Edge for the DFA to distinguish them from 21 | * ATN transitions. 22 | */ 23 | export abstract class Transition { 24 | public static readonly INVALID = 0; 25 | public static readonly EPSILON = 1; 26 | public static readonly RANGE = 2; 27 | public static readonly RULE = 3; 28 | public static readonly PREDICATE = 4; // e.g., {isType(input.LT(1))} 29 | public static readonly ATOM = 5; 30 | public static readonly ACTION = 6; 31 | public static readonly SET = 7; // ~(A|B) or ~atom, wildcard, which convert to next 32 | public static readonly NOT_SET = 8; 33 | public static readonly WILDCARD = 9; 34 | public static readonly PRECEDENCE = 10; 35 | 36 | /** The target of this transition. */ 37 | public target: ATNState; 38 | 39 | public constructor(target: ATNState) { 40 | this.target = target; 41 | } 42 | 43 | /** 44 | * Determines if the transition is an "epsilon" transition. 45 | * 46 | * The default implementation returns `false`. 47 | * 48 | * @returns `true` if traversing this transition in the ATN does not 49 | * consume an input symbol; otherwise, `false` if traversing this 50 | * transition consumes (matches) an input symbol. 51 | */ 52 | public get isEpsilon(): boolean { 53 | return false; 54 | } 55 | 56 | public get label(): IntervalSet | null { 57 | return null; 58 | } 59 | 60 | public toString(): string { 61 | return ""; 62 | } 63 | 64 | public abstract get transitionType(): number; 65 | public abstract matches(symbol: number, minVocabSymbol: number, maxVocabSymbol: number): boolean; 66 | } 67 | -------------------------------------------------------------------------------- /src/atn/WildcardTransition.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Transition } from "./Transition.js"; 8 | 9 | export class WildcardTransition extends Transition { 10 | public override get transitionType(): number { 11 | return Transition.WILDCARD; 12 | } 13 | 14 | public override matches(symbol: number, minVocabSymbol: number, maxVocabSymbol: number): boolean { 15 | return symbol >= minVocabSymbol && symbol <= maxVocabSymbol; 16 | } 17 | 18 | public override toString(): string { 19 | return "."; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/atn/helpers.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { EmptyPredictionContext } from "./EmptyPredictionContext.js"; 8 | import { PredictionContext } from "./PredictionContext.js"; 9 | import { SingletonPredictionContext } from "./SingletonPredictionContext.js"; 10 | 11 | export const createSingletonPredictionContext = (parent: PredictionContext | undefined, 12 | returnState: number): SingletonPredictionContext => { 13 | if (returnState === PredictionContext.EMPTY_RETURN_STATE && parent === null) { 14 | // someone can pass in the bits of an array ctx that mean $ 15 | return EmptyPredictionContext.instance as SingletonPredictionContext; 16 | } else { 17 | return new SingletonPredictionContext(parent, returnState); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/atn/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./ATN.js"; 8 | export * from "./ATNConfig.js"; 9 | export * from "./ATNConfigSet.js"; 10 | export * from "./ATNDeserializationOptions.js"; 11 | export * from "./ATNDeserializer.js"; 12 | export * from "./ATNSerializer.js"; 13 | export * from "./ATNSimulator.js"; 14 | export * from "./ATNState.js"; 15 | export * from "./AbstractPredicateTransition.js"; 16 | export * from "./ActionTransition.js"; 17 | export * from "./AmbiguityInfo.js"; 18 | export * from "./ArrayPredictionContext.js"; 19 | export * from "./AtomTransition.js"; 20 | export * from "./BasicBlockStartState.js"; 21 | export * from "./BasicState.js"; 22 | export * from "./BlockEndState.js"; 23 | export * from "./BlockStartState.js"; 24 | export * from "./CodePointTransitions.js"; 25 | export * from "./ContextSensitivityInfo.js"; 26 | export * from "./DecisionEventInfo.js"; 27 | export * from "./DecisionInfo.js"; 28 | export * from "./DecisionState.js"; 29 | export * from "./EmptyPredictionContext.js"; 30 | export * from "./EpsilonTransition.js"; 31 | export * from "./LL1Analyzer.js"; 32 | export * from "./LexerATNConfig.js"; 33 | export * from "./LexerATNSimulator.js"; 34 | export * from "./LexerAction.js"; 35 | export * from "./LexerActionExecutor.js"; 36 | export * from "./LexerActionType.js"; 37 | export * from "./LexerChannelAction.js"; 38 | export * from "./LexerCustomAction.js"; 39 | export * from "./LexerIndexedCustomAction.js"; 40 | export * from "./LexerModeAction.js"; 41 | export * from "./LexerMoreAction.js"; 42 | export * from "./LexerPopModeAction.js"; 43 | export * from "./LexerPushModeAction.js"; 44 | export * from "./LexerSkipAction.js"; 45 | export * from "./LexerTypeAction.js"; 46 | export * from "./LookaheadEventInfo.js"; 47 | export * from "./LoopEndState.js"; 48 | export * from "./NotSetTransition.js"; 49 | export * from "./OrderedATNConfigSet.js"; 50 | export * from "./ParseInfo.js"; 51 | export * from "./ParserATNSimulator.js"; 52 | export * from "./PlusBlockStartState.js"; 53 | export * from "./PlusLoopbackState.js"; 54 | export * from "./PrecedencePredicateTransition.js"; 55 | export * from "./PredicateEvalInfo.js"; 56 | export * from "./PredicateTransition.js"; 57 | export * from "./PredictionContext.js"; 58 | export * from "./PredictionContextCache.js"; 59 | export * from "./PredictionContextUtils.js"; 60 | export * from "./PredictionMode.js"; 61 | export * from "./ProfilingATNSimulator.js"; 62 | export * from "./RangeTransition.js"; 63 | export * from "./RuleStartState.js"; 64 | export * from "./RuleStopState.js"; 65 | export * from "./RuleTransition.js"; 66 | export * from "./SemanticContext.js"; 67 | export * from "./SetTransition.js"; 68 | export * from "./SingletonPredictionContext.js"; 69 | export * from "./StarBlockStartState.js"; 70 | export * from "./StarLoopEntryState.js"; 71 | export * from "./StarLoopbackState.js"; 72 | export * from "./TokensStartState.js"; 73 | export * from "./Transition.js"; 74 | export * from "./WildcardTransition.js"; 75 | 76 | export * from "./helpers.js"; 77 | -------------------------------------------------------------------------------- /src/dfa/DFASerializer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { DFA } from "./DFA.js"; 8 | import { DFAState } from "./DFAState.js"; 9 | import { Vocabulary } from "../Vocabulary.js"; 10 | 11 | /** A DFA walker that knows how to dump them to serialized strings. */ 12 | export class DFASerializer { 13 | 14 | private readonly dfa: DFA; 15 | private readonly vocabulary: Vocabulary; 16 | 17 | public constructor(dfa: DFA, vocabulary: Vocabulary) { 18 | this.dfa = dfa; 19 | this.vocabulary = vocabulary; 20 | } 21 | 22 | public toString(): string { 23 | if (!this.dfa.s0) { 24 | return ""; 25 | } 26 | 27 | let buf = ""; 28 | const states = this.dfa.getStates(); 29 | for (const s of states) { 30 | let n = 0; 31 | n = s.edges.length; 32 | 33 | for (let i = 0; i < n; i++) { 34 | const t = s.edges[i]; 35 | if (t && t.stateNumber !== 0x7FFFFFFF) { 36 | buf += this.getStateString(s); 37 | const label = this.getEdgeLabel(i); 38 | buf += "-"; 39 | buf += label; 40 | buf += "->"; 41 | buf += this.getStateString(t); 42 | buf += "\n"; 43 | } 44 | } 45 | } 46 | 47 | return buf; 48 | }; 49 | 50 | protected getEdgeLabel(i: number): string { 51 | const name = this.vocabulary.getDisplayName(i - 1); 52 | 53 | return `${name}`; 54 | }; 55 | 56 | protected getStateString(s: DFAState): string { 57 | const n = s.stateNumber; 58 | const baseStateStr = (s.isAcceptState ? ":" : "") + "s" + n + (s.requiresFullContext ? "^" : ""); 59 | if (s.isAcceptState) { 60 | if (s.predicates !== null) { 61 | return `${baseStateStr}=>${s.predicates.toString()}`; 62 | } 63 | 64 | return `${baseStateStr}=>${s.prediction}`; 65 | } else { 66 | return `${baseStateStr}`; 67 | } 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /src/dfa/DFAStateEqualityComparator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { EqualityComparator } from "../misc/EqualityComparator.js"; 8 | import { DFAState } from "./index.js"; 9 | 10 | /** 11 | * The comparator for DFA states. 12 | */ 13 | export class DFAStateEqualityComparator implements EqualityComparator> { 14 | public static readonly instance = new DFAStateEqualityComparator(); 15 | 16 | public hashCode(state: Partial): number { 17 | return DFAState.hashCode(state); 18 | } 19 | 20 | public equals(a: Partial, b: Partial): boolean { 21 | return DFAState.equals(a, b); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/dfa/LexerDFASerializer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Vocabulary } from "../Vocabulary.js"; 8 | import { DFA } from "./DFA.js"; 9 | import { DFASerializer } from "./DFASerializer.js"; 10 | 11 | export class LexerDFASerializer extends DFASerializer { 12 | public constructor(dfa: DFA) { 13 | super(dfa, Vocabulary.EMPTY_VOCABULARY); 14 | } 15 | 16 | public override getEdgeLabel = (i: number): string => { 17 | return "'" + String.fromCharCode(i) + "'"; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/dfa/PredPrediction.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import type { SemanticContext } from "../atn/SemanticContext.js"; 8 | 9 | /** Map a predicate to a predicted alternative. */ 10 | export interface PredPrediction { 11 | pred: SemanticContext; // never null; at least SemanticContext.NONE 12 | alt: number; 13 | }; 14 | 15 | export namespace PredPrediction { 16 | export const toString = (prediction: PredPrediction): string => { 17 | return `(${prediction.pred}, ${prediction.alt})`; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/dfa/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./DFA.js"; 8 | export * from "./DFASerializer.js"; 9 | export * from "./DFAState.js"; 10 | export * from "./LexerDFASerializer.js"; 11 | export * from "./PredPrediction.js"; 12 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./atn/index.js"; 8 | export * from "./dfa/index.js"; 9 | export * from "./misc/index.js"; 10 | export * from "./tree/index.js"; 11 | export * from "./utils/index.js"; 12 | 13 | export * from "./ANTLRErrorListener.js"; 14 | export * from "./ANTLRErrorStrategy.js"; 15 | export * from "./BailErrorStrategy.js"; 16 | export * from "./BaseErrorListener.js"; 17 | export * from "./BufferedTokenStream.js"; 18 | export * from "./CharStream.js"; 19 | export * from "./CommonToken.js"; 20 | export * from "./CommonTokenFactory.js"; 21 | export * from "./CommonTokenStream.js"; 22 | export * from "./ConsoleErrorListener.js"; 23 | export * from "./DefaultErrorStrategy.js"; 24 | export * from "./DiagnosticErrorListener.js"; 25 | export * from "./FailedPredicateException.js"; 26 | export * from "./InputMismatchException.js"; 27 | export * from "./InterpreterRuleContext.js"; 28 | export * from "./IntStream.js"; 29 | export * from "./Lexer.js"; 30 | export * from "./LexerInterpreter.js"; 31 | export * from "./LexerNoViableAltException.js"; 32 | export * from "./ListTokenSource.js"; 33 | export * from "./NoViableAltException.js"; 34 | export * from "./Parser.js"; 35 | export * from "./ParserInterpreter.js"; 36 | export * from "./ParserRuleContext.js"; 37 | export * from "./ProxyErrorListener.js"; 38 | export * from "./RecognitionException.js"; 39 | export * from "./Recognizer.js"; 40 | export * from "./RuntimeMetaData.js"; 41 | export * from "./Token.js"; 42 | export * from "./TokenFactory.js"; 43 | export * from "./TokenSource.js"; 44 | export * from "./TokenStream.js"; 45 | export * from "./TokenStreamRewriter.js"; 46 | export * from "./TraceListener.js"; 47 | export * from "./UnbufferedTokenStream.js"; 48 | export * from "./Vocabulary.js"; 49 | export * from "./WritableToken.js"; 50 | -------------------------------------------------------------------------------- /src/misc/DefaultEqualityComparator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import type { IComparable } from "../index.js"; 8 | import { EqualityComparator } from "./EqualityComparator.js"; 9 | import { ObjectEqualityComparator } from "./ObjectEqualityComparator.js"; 10 | 11 | /** 12 | * This default implementation of {@link EqualityComparator} uses object equality 13 | * for comparisons, but is optimized for null/undefined, string, and number values. 14 | */ 15 | export class DefaultEqualityComparator implements EqualityComparator { 16 | public static readonly instance = new DefaultEqualityComparator(); 17 | 18 | public hashCode(obj: unknown): number { 19 | if (obj == null) { 20 | return 0; 21 | } 22 | 23 | return ObjectEqualityComparator.instance.hashCode(obj as IComparable); 24 | } 25 | 26 | public equals(a: unknown, b: unknown): boolean { 27 | if (a == null) { 28 | return b == null; 29 | } 30 | 31 | if (typeof a === "string" || typeof a === "number") { 32 | return a === b; 33 | } 34 | 35 | return ObjectEqualityComparator.instance.equals(a as IComparable, b as IComparable); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/misc/EqualityComparator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /** 8 | * This interface provides an abstract concept of object equality independent of 9 | * {@link Object#equals} (object equality) and the `==` operator 10 | * (reference equality). It can be used to provide algorithm-specific unordered 11 | * comparisons without requiring changes to the object itself. 12 | */ 13 | export interface EqualityComparator { 14 | /** 15 | * This method returns a hash code for the specified object. 16 | * 17 | * @param obj The object. 18 | * @returns The hash code for `obj`. 19 | */ 20 | hashCode(obj: T): number; 21 | 22 | /** 23 | * This method tests if two objects are equal. 24 | * 25 | * @param a The first object to compare. 26 | * @param b The second object to compare. 27 | * @returns `true` if `a` equals `b`, otherwise `false`. 28 | */ 29 | equals(a: T, b: T): boolean; 30 | } 31 | -------------------------------------------------------------------------------- /src/misc/MapKeyEqualityOperator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import type { IComparable } from "../utils/helpers.js"; 8 | import type { EqualityComparator } from "./EqualityComparator.js"; 9 | 10 | /** 11 | * Since `HashMap` is implemented on top of `HashSet`, we defined a bucket type which can store a 12 | * key-value pair. The value is optional since looking up values in the map by a key only needs to include the key. 13 | */ 14 | export interface Bucket { key: K; value?: V; } 15 | 16 | export class MapKeyEqualityComparator implements EqualityComparator> { 17 | private readonly keyComparator: EqualityComparator; 18 | 19 | public constructor(keyComparator: EqualityComparator) { 20 | this.keyComparator = keyComparator; 21 | } 22 | 23 | public hashCode(obj: Bucket): number { 24 | return this.keyComparator.hashCode(obj.key); 25 | } 26 | 27 | public equals(a: Bucket, b: Bucket): boolean { 28 | return this.keyComparator.equals(a.key, b.key); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/misc/MultiMap.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export class MultiMap extends Map { 8 | public map(key: K, value: V): void { 9 | let elementsForKey = this.get(key); 10 | if (!elementsForKey) { 11 | elementsForKey = new Array(); 12 | this.set(key, elementsForKey); 13 | } 14 | 15 | elementsForKey.push(value); 16 | } 17 | 18 | public getPairs(): Array<[K, V]> { 19 | const pairs = new Array<[K, V]>(); 20 | for (const key of this.keys()) { 21 | const keys = this.get(key) ?? []; 22 | for (const value of keys) { 23 | pairs.push([key, value]); 24 | } 25 | } 26 | 27 | return pairs; 28 | } 29 | 30 | public override toString(): string { 31 | const entries: string[] = []; 32 | this.forEach((value, key) => { 33 | entries.push(`${key}=[${value.join(", ")}]`); 34 | }); 35 | 36 | return `{${entries.join(", ")}}`; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/misc/ObjectEqualityComparator.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import type { IComparable } from "../utils/helpers.js"; 8 | import { EqualityComparator } from "./EqualityComparator.js"; 9 | 10 | /** 11 | * This default implementation of {@link EqualityComparator} uses object equality 12 | * for comparisons by calling {@link Object.hashCode} and {@link Object.equals}. 13 | */ 14 | export class ObjectEqualityComparator implements EqualityComparator { 15 | public static readonly instance: ObjectEqualityComparator = new ObjectEqualityComparator(); 16 | 17 | public hashCode(obj: IComparable): number { 18 | if (obj == null) { 19 | return 0; 20 | } 21 | 22 | return obj.hashCode(); 23 | } 24 | 25 | public equals(a: IComparable, b: IComparable): boolean { 26 | if (a == null) { 27 | return b == null; 28 | } 29 | 30 | return a.equals(b); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/misc/OrderedHashMap.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import type { IComparable } from "../utils/helpers.js"; 8 | import { HashMap } from "./HashMap.js"; 9 | 10 | /** 11 | * This class extends `HashMap` to maintain the insertion order of the keys. 12 | */ 13 | export class OrderedHashMap extends HashMap { 14 | #keys: K[] = []; 15 | 16 | public override clear(): void { 17 | super.clear(); 18 | this.#keys = []; 19 | } 20 | 21 | public override get(key: K): V | undefined { 22 | return super.get(key); 23 | } 24 | 25 | public override set(key: K, value: V): V | undefined { 26 | const result = super.set(key, value); 27 | if (result === undefined) { // The key did not exist yet. 28 | this.#keys.push(key); 29 | } 30 | 31 | return result; 32 | } 33 | 34 | public override setIfAbsent(key: K, value: V): V | undefined { 35 | const result = super.setIfAbsent(key, value); 36 | if (result === undefined) { // The key did not exist yet. 37 | this.#keys.push(key); 38 | } 39 | 40 | return result; 41 | } 42 | 43 | /** 44 | * @returns an iterable of the values in the map, in the order they were inserted. 45 | */ 46 | public override values(): Iterable { 47 | return { 48 | [Symbol.iterator]: () => { 49 | let index = 0; 50 | 51 | return { 52 | next: (): IteratorResult => { 53 | if (index < this.#keys.length) { 54 | return { 55 | done: false, 56 | value: super.get(this.#keys[index++])!, 57 | }; 58 | } 59 | 60 | return { 61 | done: true, 62 | value: undefined, 63 | }; 64 | }, 65 | }; 66 | }, 67 | }; 68 | } 69 | 70 | /** 71 | * @returns an iterable of the keys in the map, in the order they were inserted. 72 | */ 73 | public override keys(): IterableIterator { 74 | return this.#keys[Symbol.iterator](); 75 | } 76 | 77 | public override equals(o: unknown): boolean { 78 | if (!(o instanceof OrderedHashMap)) { 79 | return false; 80 | } 81 | 82 | return super.equals(o); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/misc/OrderedHashSet.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import type { IComparable } from "../utils/helpers.js"; 8 | import { HashSet } from "./HashSet.js"; 9 | 10 | export class OrderedHashSet extends HashSet { 11 | private elements: T[] = []; 12 | 13 | public override getOrAdd(o: T): T { 14 | const oldSize = this.size; 15 | const result = super.getOrAdd(o); 16 | if (this.size > oldSize) { 17 | this.elements.push(o); 18 | } 19 | 20 | return result; 21 | } 22 | 23 | public override equals(o: unknown): boolean { 24 | if (!(o instanceof OrderedHashSet)) { 25 | return false; 26 | } 27 | 28 | return super.equals(o); 29 | } 30 | 31 | public override clear(): void { 32 | super.clear(); 33 | this.elements = []; 34 | } 35 | 36 | public override *[Symbol.iterator](): IterableIterator { 37 | yield* this.elements; 38 | } 39 | 40 | public override toArray(): T[] { 41 | return this.elements.slice(0); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/misc/ParseCancellationException.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /** 8 | * This exception is thrown to cancel a parsing operation. This exception does 9 | * not extend {@link RecognitionException}, allowing it to bypass the standard 10 | * error recovery mechanisms. {@link BailErrorStrategy} throws this exception in 11 | * response to a parse error. 12 | */ 13 | export class ParseCancellationException extends Error { 14 | 15 | public constructor(e: Error) { 16 | super(); 17 | this.cause = e; 18 | //Error.captureStackTrace(this, ParseCancellationException); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/misc/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./BitSet.js"; 8 | export * from "./HashMap.js"; 9 | export * from "./HashSet.js"; 10 | export * from "./Interval.js"; 11 | export * from "./IntervalSet.js"; 12 | export * from "./ParseCancellationException.js"; 13 | export * from "./InterpreterDataReader.js"; 14 | export * from "./OrderedHashMap.js"; 15 | export * from "./OrderedHashSet.js"; 16 | -------------------------------------------------------------------------------- /src/tree/AbstractParseTreeVisitor.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ErrorNode } from "./ErrorNode.js"; 8 | import { ParseTree } from "./ParseTree.js"; 9 | import { ParseTreeVisitor } from "./ParseTreeVisitor.js"; 10 | import { TerminalNode } from "./TerminalNode.js"; 11 | 12 | export abstract class AbstractParseTreeVisitor implements ParseTreeVisitor { 13 | public visit(tree: ParseTree): T | null { 14 | return tree.accept(this); 15 | } 16 | 17 | public visitChildren(node: ParseTree): T | null { 18 | let result = this.defaultResult(); 19 | const n = node.getChildCount(); 20 | for (let i = 0; i < n; i++) { 21 | if (!this.shouldVisitNextChild(node, result)) { 22 | break; 23 | } 24 | 25 | const c = node.getChild(i); 26 | if (c) { 27 | const childResult = c.accept(this); 28 | result = this.aggregateResult(result, childResult); 29 | } 30 | } 31 | 32 | return result; 33 | }; 34 | 35 | public visitTerminal(_node: TerminalNode): T | null { 36 | return this.defaultResult(); 37 | } 38 | 39 | public visitErrorNode(_node: ErrorNode): T | null { 40 | return this.defaultResult(); 41 | } 42 | 43 | protected defaultResult(): T | null { 44 | return null; 45 | } 46 | 47 | protected shouldVisitNextChild(_node: ParseTree, _currentResult: T | null): boolean { 48 | return true; 49 | } 50 | 51 | protected aggregateResult(aggregate: T | null, nextResult: T | null): T | null { 52 | return nextResult; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/tree/ErrorNode.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { type ParseTreeVisitor } from "./ParseTreeVisitor.js"; 8 | import { TerminalNode } from "./TerminalNode.js"; 9 | 10 | /** 11 | * Represents a token that was consumed during resynchronization 12 | * rather than during a valid match operation. For example, 13 | * we will create this kind of a node during single token insertion 14 | * and deletion as well as during "consume until error recovery set" 15 | * upon no viable alternative exceptions. 16 | */ 17 | export class ErrorNode extends TerminalNode { 18 | public override accept(visitor: ParseTreeVisitor): T | null { 19 | return visitor.visitErrorNode(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/tree/ParseTree.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { type Interval } from "../misc/Interval.js"; 8 | import { type Parser } from "../Parser.js"; 9 | import { type ParseTreeVisitor } from "./ParseTreeVisitor.js"; 10 | 11 | /** 12 | * The basic notion of a tree has a parent, a payload, and a list of children. 13 | * It is the most abstract interface for all the trees used by antlr-ng. 14 | * 15 | * Note: this interface is a combination of 3 Java interfaces: ParseTree, SyntaxTree and Tree. 16 | */ 17 | export interface ParseTree { 18 | /** 19 | * The parent of this node. If the return value is null, then this 20 | * node is the root of the tree. 21 | */ 22 | parent: ParseTree | null; 23 | 24 | /** 25 | * This method returns whatever object represents the data at this node. For 26 | * example, for parse trees, the payload can be a {@link Token} representing 27 | * a leaf node or a {@link RuleContext} object representing a rule 28 | * invocation. For abstract syntax trees (ASTs), this is a {@link Token} 29 | * object. 30 | */ 31 | getPayload(): unknown; 32 | 33 | /** If there are children, get the `i`th value indexed from 0. */ 34 | getChild(i: number): ParseTree | null; 35 | 36 | /** The {@link ParseTreeVisitor} needs a double dispatch method. */ 37 | accept(visitor: ParseTreeVisitor): T | null; 38 | 39 | /** 40 | * How many children are there? If there is none, then this 41 | * node represents a leaf node. 42 | */ 43 | getChildCount(): number; 44 | 45 | /** 46 | * Return the combined text of all leaf nodes. Does not get any 47 | * off-channel tokens (if any) so won't return whitespace and 48 | * comments if they are sent to parser on hidden channel. 49 | */ 50 | getText(): string; 51 | 52 | /** 53 | * Print out a whole tree, not just a node, in LISP format 54 | * `(root child1 .. childN)`. Print just a node if this is a leaf. 55 | */ 56 | toStringTree(recog?: Parser): string; 57 | 58 | /** 59 | * Return an {@link Interval} indicating the index in the 60 | * {@link TokenStream} of the first and last token associated with this 61 | * subtree. If this node is a leaf, then the interval represents a single 62 | * token and has interval i..i for token index i. 63 | * 64 | * An interval of i..i-1 indicates an empty interval at position 65 | * i in the input stream, where 0 <= i <= the size of the input 66 | * token stream. Currently, the code base can only have i=0..n-1 but 67 | * in concept one could have an empty interval after EOF. 68 | * 69 | * If source interval is unknown, this returns {@link Interval.INVALID}. 70 | * 71 | * As a weird special case, the source interval for rules matched after 72 | * EOF is unspecified. 73 | */ 74 | getSourceInterval(): Interval; 75 | } 76 | -------------------------------------------------------------------------------- /src/tree/ParseTreeListener.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { type ParserRuleContext } from "../ParserRuleContext.js"; 8 | import { type ErrorNode } from "./ErrorNode.js"; 9 | import { type TerminalNode } from "./TerminalNode.js"; 10 | 11 | export interface ParseTreeListener { 12 | visitTerminal(node: TerminalNode): void; 13 | visitErrorNode(node: ErrorNode): void; 14 | enterEveryRule(node: ParserRuleContext): void; 15 | exitEveryRule(node: ParserRuleContext): void; 16 | } 17 | -------------------------------------------------------------------------------- /src/tree/ParseTreeVisitor.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { type ErrorNode } from "./ErrorNode.js"; 8 | import { type ParseTree } from "./ParseTree.js"; 9 | import { type TerminalNode } from "./TerminalNode.js"; 10 | 11 | /** 12 | * This interface defines the basic notion of a parse tree visitor. Generated 13 | * visitors implement this interface and the `XVisitor` interface for 14 | * grammar `X`. 15 | * 16 | * @param T The return type of the visit operation. Use void for 17 | * operations with no return type. 18 | */ 19 | export interface ParseTreeVisitor { 20 | 21 | /** 22 | * Visit a parse tree, and return a user-defined result of the operation. 23 | * 24 | * @param tree The {@link ParseTree} to visit. 25 | * @returns The result of visiting the parse tree. 26 | */ 27 | visit(tree: ParseTree): T | null; 28 | 29 | /** 30 | * Visit the children of a node, and return a user-defined result of the 31 | * operation. 32 | * 33 | * @param node The {@link RuleNode} whose children should be visited. 34 | * @returns The result of visiting the children of the node. 35 | */ 36 | visitChildren(node: ParseTree): T | null; 37 | 38 | /** 39 | * Visit a terminal node, and return a user-defined result of the operation. 40 | * 41 | * @param node The {@link TerminalNode} to visit. 42 | * @returns The result of visiting the node. 43 | */ 44 | visitTerminal(node: TerminalNode): T | null; 45 | 46 | /** 47 | * Visit an error node, and return a user-defined result of the operation. 48 | * 49 | * @param node The {@link ErrorNode} to visit. 50 | * @returns The result of visiting the node. 51 | */ 52 | visitErrorNode(node: ErrorNode): T | null; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/tree/ParseTreeWalker.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParserRuleContext } from "../ParserRuleContext.js"; 8 | import { ErrorNode } from "./ErrorNode.js"; 9 | import { ParseTree } from "./ParseTree.js"; 10 | import { ParseTreeListener } from "./ParseTreeListener.js"; 11 | import { TerminalNode } from "./TerminalNode.js"; 12 | 13 | export class ParseTreeWalker { 14 | public static DEFAULT = new ParseTreeWalker(); 15 | 16 | /** 17 | * Performs a walk on the given parse tree starting at the root and going down recursively 18 | * with depth-first search. On each node, {@link ParseTreeWalker.enterRule} is called before 19 | * recursively walking down into child nodes, then 20 | * {@link ParseTreeWalker.exitRule} is called after the recursive call to wind up. 21 | * 22 | * @param listener The listener used by the walker to process grammar rules 23 | * @param t The parse tree to be walked on 24 | */ 25 | public walk(listener: T, t: ParseTree): void { 26 | const errorNode = t instanceof ErrorNode; 27 | if (errorNode) { 28 | listener.visitErrorNode(t); 29 | } else if (t instanceof TerminalNode) { 30 | listener.visitTerminal(t); 31 | } else { 32 | const r = t as ParserRuleContext; 33 | this.enterRule(listener, r); 34 | for (let i = 0; i < t.getChildCount(); i++) { 35 | this.walk(listener, t.getChild(i)!); 36 | } 37 | this.exitRule(listener, r); 38 | } 39 | } 40 | 41 | /** 42 | * Enters a grammar rule by first triggering the generic event {@link ParseTreeListener.enterEveryRule} 43 | * then by triggering the event specific to the given parse tree node 44 | * 45 | * @param listener The listener responding to the trigger events 46 | * @param r The grammar rule containing the rule context 47 | */ 48 | protected enterRule(listener: ParseTreeListener, r: ParserRuleContext): void { 49 | const ctx = r.ruleContext; 50 | listener.enterEveryRule(ctx); 51 | ctx.enterRule(listener); 52 | } 53 | 54 | /** 55 | * Exits a grammar rule by first triggering the event specific to the given parse tree node 56 | * then by triggering the generic event {@link ParseTreeListener.exitEveryRule} 57 | * 58 | * @param listener The listener responding to the trigger events 59 | * @param r The grammar rule containing the rule context 60 | */ 61 | protected exitRule(listener: ParseTreeListener, r: ParserRuleContext): void { 62 | const ctx = r.ruleContext; 63 | ctx.exitRule(listener); 64 | listener.exitEveryRule(ctx); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/tree/TerminalNode.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Interval } from "../misc/Interval.js"; 8 | import { Token } from "../Token.js"; 9 | import { ParseTree } from "./ParseTree.js"; 10 | import { type ParseTreeVisitor } from "./ParseTreeVisitor.js"; 11 | 12 | export class TerminalNode implements ParseTree { 13 | public parent: ParseTree | null = null; 14 | public symbol: Token; 15 | 16 | public constructor(symbol: Token) { 17 | this.symbol = symbol; 18 | } 19 | 20 | public getChild(_i: number): ParseTree | null { 21 | return null; 22 | } 23 | 24 | public getSymbol(): Token { 25 | return this.symbol; 26 | } 27 | 28 | public getPayload(): Token | null { 29 | return this.symbol; 30 | } 31 | 32 | public getSourceInterval(): Interval { 33 | if (this.symbol === null) { 34 | return Interval.INVALID_INTERVAL; 35 | } 36 | const tokenIndex = this.symbol.tokenIndex; 37 | 38 | return new Interval(tokenIndex, tokenIndex); 39 | } 40 | 41 | public getChildCount(): number { 42 | return 0; 43 | } 44 | 45 | public accept(visitor: ParseTreeVisitor): T | null { 46 | return visitor.visitTerminal(this); 47 | } 48 | 49 | public getText(): string { 50 | return this.symbol?.text ?? ""; 51 | } 52 | 53 | public toString(): string { 54 | if (this.symbol?.type === Token.EOF) { 55 | return ""; 56 | } else { 57 | return this.symbol?.text ?? ""; 58 | } 59 | } 60 | 61 | public toStringTree(): string { 62 | return this.toString(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/tree/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./AbstractParseTreeVisitor.js"; 8 | export * from "./ErrorNode.js"; 9 | export * from "./ParseTree.js"; 10 | export * from "./ParseTreeListener.js"; 11 | export * from "./ParseTreeVisitor.js"; 12 | export * from "./ParseTreeWalker.js"; 13 | export * from "./TerminalNode.js"; 14 | export * from "./Trees.js"; 15 | 16 | export * from "./xpath/index.js"; 17 | export * from "./pattern/index.js"; 18 | -------------------------------------------------------------------------------- /src/tree/pattern/CannotInvokeStartRuleError.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export class CannotInvokeStartRuleError extends Error { 8 | public constructor(e: Error) { 9 | super(); 10 | this.cause = e; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/tree/pattern/Chunk.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /** 8 | * A chunk is either a token tag, a rule tag, or a span of literal text within a tree pattern. 9 | * 10 | * The method {@link ParseTreePatternMatcher.split(String)} returns a list of 11 | * chunks in preparation for creating a token stream by 12 | * {@link ParseTreePatternMatcher.tokenize(String)}. From there, we get a parse 13 | * tree from with {@link ParseTreePatternMatcher.compile(String, int)}. These 14 | * chunks are converted to {@link RuleTagToken}, {@link TokenTagToken}, or the 15 | * regular tokens of the text surrounding the tags. 16 | */ 17 | export abstract class Chunk { 18 | } 19 | -------------------------------------------------------------------------------- /src/tree/pattern/StartRuleDoesNotConsumeFullPatternError.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | // Fixes https://github.com/antlr/antlr4/issues/413 8 | // "Tree pattern compilation doesn't check for a complete parse" 9 | export class StartRuleDoesNotConsumeFullPatternError extends Error { 10 | }; 11 | -------------------------------------------------------------------------------- /src/tree/pattern/TagChunk.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Chunk } from "./Chunk.js"; 8 | 9 | /** 10 | * Represents a placeholder tag in a tree pattern. A tag can have any of the 11 | * following forms. 12 | * 13 | * - `expr`: An unlabeled placeholder for a parser rule `expr`. 14 | * - `ID`: An unlabeled placeholder for a token of type `ID`. 15 | * - `e:expr`: A labeled placeholder for a parser rule `expr`. 16 | * - `id:ID`: A labeled placeholder for a token of type `ID`. 17 | * 18 | * This class does not perform any validation on the tag or label names aside 19 | * from ensuring that the tag is a non-null, non-empty string. 20 | */ 21 | export class TagChunk extends Chunk { 22 | public readonly tag: string; 23 | 24 | public readonly label?: string; 25 | 26 | /** 27 | * Construct a new instance of {@link TagChunk} using the specified tag and 28 | * no label. 29 | * 30 | * @param tag The tag, which should be the name of a parser rule or token 31 | * type. 32 | * 33 | * @throws IllegalArgumentException if `tag` is `null` or 34 | * empty. 35 | */ 36 | public constructor(tag?: string); 37 | /** 38 | * Construct a new instance of {@link TagChunk} using the specified label 39 | * and tag. 40 | * 41 | * @param label The label for the tag. If this is `null`, the 42 | * {@link TagChunk} represents an unlabeled tag. 43 | * @param tag The tag, which should be the name of a parser rule or token 44 | * type. 45 | * 46 | * @throws IllegalArgumentException if `tag` is `null` or 47 | * empty. 48 | */ 49 | public constructor(label: string | undefined, tag: string); 50 | public constructor(...args: unknown[]) { 51 | let label: string | undefined; 52 | let tag: string; 53 | 54 | if (args.length === 1) { 55 | tag = args[0] as string; 56 | } else { 57 | label = args[0] as string | undefined; 58 | tag = args[1] as string; 59 | } 60 | 61 | super(); 62 | if (!tag) { 63 | throw new Error("tag cannot be null or empty"); 64 | } 65 | 66 | this.label = label; 67 | this.tag = tag; 68 | } 69 | 70 | /** 71 | * @returns a text representation of the tag chunk. Labeled tags 72 | * are returned in the form `label:tag`, and unlabeled tags are 73 | * returned as just the tag name. 74 | */ 75 | public override toString(): string { 76 | if (this.label !== undefined) { 77 | return this.label + ":" + this.tag; 78 | } 79 | 80 | return this.tag; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/tree/pattern/TextChunk.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Chunk } from "./Chunk.js"; 8 | 9 | /** 10 | * Represents a span of raw text (concrete syntax) between tags in a tree 11 | * pattern string. 12 | */ 13 | export class TextChunk extends Chunk { 14 | public readonly text: string; 15 | 16 | /** 17 | * Constructs a new instance of {@link TextChunk} with the specified text. 18 | * 19 | * @param text The text of this chunk. 20 | */ 21 | public constructor(text: string) { 22 | super(); 23 | 24 | this.text = text; 25 | } 26 | 27 | /** 28 | * @returns the result of {@link #getText()} in single quotes. 29 | */ 30 | public override toString(): string { 31 | return "'" + this.text + "'"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/tree/pattern/TokenTagToken.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { CommonToken } from "../../CommonToken.js"; 8 | 9 | /** 10 | * A {@link Token} object representing a token of a particular type; e.g., 11 | * ``. These tokens are created for {@link TagChunk} chunks where the 12 | * tag corresponds to a lexer rule or token type. 13 | */ 14 | export class TokenTagToken extends CommonToken { 15 | public readonly tokenName: string; 16 | 17 | /** 18 | * The name of the label associated with the rule tag, or undefined if this is an unlabeled rule tag. 19 | */ 20 | public readonly label?: string; 21 | 22 | /** 23 | * Constructs a new instance of {@link TokenTagToken} for an unlabeled tag 24 | * with the specified token name and type. 25 | * 26 | * @param tokenName The token name. 27 | * @param type The token type. 28 | */ 29 | public constructor(tokenName: string, type: number); 30 | /** 31 | * Constructs a new instance of {@link TokenTagToken} with the specified 32 | * token name, type, and label. 33 | * 34 | * @param tokenName The token name. 35 | * @param type The token type. 36 | * @param label The label associated with the token tag, or `undefined` if 37 | * the token tag is unlabeled. 38 | */ 39 | public constructor(tokenName: string, type: number, label: string | undefined); 40 | public constructor(tokenName: string, type: number, label?: string | undefined) { 41 | super({ type, source: CommonToken.EMPTY_SOURCE }); 42 | this.tokenName = tokenName; 43 | this.label = label; 44 | } 45 | 46 | /** 47 | * 48 | * @returns the token tag formatted with `<` and `>` delimiters. 49 | */ 50 | public override get text(): string { 51 | if (this.label !== undefined) { 52 | return "<" + this.label + ":" + this.tokenName + ">"; 53 | } 54 | 55 | return "<" + this.tokenName + ">"; 56 | } 57 | 58 | /** 59 | * @returns a string of the form `tokenName:type`. 60 | */ 61 | public override toString(): string { 62 | return this.tokenName + ":" + this.type; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/tree/pattern/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./Chunk.js"; 8 | export * from "./ParseTreeMatch.js"; 9 | export * from "./ParseTreePattern.js"; 10 | export * from "./ParseTreePatternMatcher.js"; 11 | export * from "./RuleTagToken.js"; 12 | export * from "./TagChunk.js"; 13 | export * from "./TextChunk.js"; 14 | export * from "./TokenTagToken.js"; 15 | export * from "./CannotInvokeStartRuleError.js"; 16 | export * from "./StartRuleDoesNotConsumeFullPatternError.js"; 17 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathElement.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParseTree } from "../ParseTree.js"; 8 | 9 | export abstract class XPathElement { 10 | public invert: boolean; 11 | protected nodeName?: string; 12 | 13 | /** 14 | * Construct element like `/ID` or `ID` or `/*` etc... `nodeName` is undefined if just node 15 | * 16 | * @param nodeName The name of the node; may be undefined for any node. 17 | */ 18 | public constructor(nodeName?: string) { 19 | this.nodeName = nodeName; 20 | this.invert = false; 21 | } 22 | 23 | public toString(): string { 24 | const inv: string = this.invert ? "!" : ""; 25 | 26 | return "XPathElement[" + inv + this.nodeName + "]"; 27 | } 28 | 29 | /** 30 | * Given tree rooted at `t` return all nodes matched by this path 31 | * element. 32 | */ 33 | public abstract evaluate(t: ParseTree): ParseTree[]; 34 | } 35 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathLexer.g4: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | lexer grammar XPathLexer; 8 | 9 | tokens { 10 | TOKEN_REF, 11 | RULE_REF 12 | } 13 | 14 | /* 15 | path : separator? word (separator word)* EOF ; 16 | 17 | separator 18 | : '/' '!' 19 | | '//' '!' 20 | | '/' 21 | | '//' 22 | ; 23 | 24 | word: TOKEN_REF 25 | | RULE_REF 26 | | STRING 27 | | '*' 28 | ; 29 | */ 30 | 31 | ANYWHERE: '//'; 32 | ROOT: '/'; 33 | WILDCARD: '*'; 34 | BANG: '!'; 35 | 36 | ID: 37 | NameStartChar NameChar* { 38 | let text = this.text; 39 | if (text.charAt(0) === text.charAt(0).toUpperCase()) { 40 | this.type = XPathLexer.TOKEN_REF; 41 | } else { 42 | this.type = XPathLexer.RULE_REF; 43 | } 44 | } 45 | ; 46 | 47 | fragment NameChar: 48 | [\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nl}\p{Pc}\p{Nd}\p{Mc}\p{Mn}\p{Cf}\u0000-\u0008\u000E-\u001B\u007F-\u009F] 49 | ; 50 | 51 | fragment NameStartChar: [\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nl}]; 52 | 53 | STRING: '\'' .*? '\''; 54 | 55 | //WS : [ \t\r\n]+ -> skip ; 56 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathLexerErrorListener.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { Recognizer } from "../../Recognizer.js"; 8 | import { RecognitionException } from "../../RecognitionException.js"; 9 | import { Token } from "../../Token.js"; 10 | import { ATNSimulator } from "../../atn/ATNSimulator.js"; 11 | import { BaseErrorListener } from "../../BaseErrorListener.js"; 12 | 13 | export class XPathLexerErrorListener extends BaseErrorListener { 14 | public override syntaxError(_recognizer: Recognizer, 15 | _offendingSymbol: S | null, 16 | _line: number, 17 | _charPositionInLine: number, 18 | _msg: string, 19 | _e: RecognitionException | null): void { 20 | // intentionally empty 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathRuleAnywhereElement.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParseTree } from "../ParseTree.js"; 8 | import { Trees } from "../Trees.js"; 9 | import { XPathElement } from "./XPathElement.js"; 10 | 11 | /** 12 | * Either `ID` at start of path or `...//ID` in middle of path. 13 | */ 14 | export class XPathRuleAnywhereElement extends XPathElement { 15 | protected ruleIndex: number; 16 | 17 | public constructor(ruleName: string, ruleIndex: number) { 18 | super(ruleName); 19 | this.ruleIndex = ruleIndex; 20 | } 21 | 22 | public evaluate(t: ParseTree): ParseTree[] { 23 | return Trees.findAllRuleNodes(t, this.ruleIndex); 24 | } 25 | 26 | public override toString(): string { 27 | const inv: string = this.invert ? "!" : ""; 28 | 29 | return "XPathRuleAnywhereElement[" + inv + this.nodeName + "]"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathRuleElement.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParserRuleContext } from "../../ParserRuleContext.js"; 8 | import { ParseTree } from "../ParseTree.js"; 9 | import { Trees } from "../Trees.js"; 10 | import { XPathElement } from "./XPathElement.js"; 11 | 12 | export class XPathRuleElement extends XPathElement { 13 | protected ruleIndex: number; 14 | 15 | public constructor(ruleName: string, ruleIndex: number) { 16 | super(ruleName); 17 | this.ruleIndex = ruleIndex; 18 | } 19 | 20 | public evaluate(t: ParseTree): ParseTree[] { 21 | // return all children of t that match nodeName 22 | const nodes: ParseTree[] = []; 23 | for (const c of Trees.getChildren(t)) { 24 | if (c instanceof ParserRuleContext) { 25 | if ((c.ruleIndex === this.ruleIndex && !this.invert) || 26 | (c.ruleIndex !== this.ruleIndex && this.invert)) { 27 | nodes.push(c); 28 | } 29 | } 30 | } 31 | 32 | return nodes; 33 | } 34 | 35 | public override toString(): string { 36 | const inv: string = this.invert ? "!" : ""; 37 | 38 | return "XPathRuleElement[" + inv + this.nodeName + "]"; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathTokenAnywhereElement.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParseTree } from "../ParseTree.js"; 8 | import { Trees } from "../Trees.js"; 9 | import { XPathElement } from "./XPathElement.js"; 10 | 11 | export class XPathTokenAnywhereElement extends XPathElement { 12 | protected tokenType: number; 13 | 14 | public constructor(tokenName: string, tokenType: number) { 15 | super(tokenName); 16 | this.tokenType = tokenType; 17 | } 18 | 19 | public evaluate(t: ParseTree): ParseTree[] { 20 | return Trees.findAllTokenNodes(t, this.tokenType); 21 | } 22 | 23 | public override toString(): string { 24 | const inv: string = this.invert ? "!" : ""; 25 | 26 | return "XPathTokenAnywhereElement[" + inv + this.nodeName + "]"; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathTokenElement.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParseTree } from "../ParseTree.js"; 8 | import { TerminalNode } from "../TerminalNode.js"; 9 | import { Trees } from "../Trees.js"; 10 | import { XPathElement } from "./XPathElement.js"; 11 | 12 | export class XPathTokenElement extends XPathElement { 13 | protected tokenType: number; 14 | 15 | public constructor(tokenName: string, tokenType: number) { 16 | super(tokenName); 17 | this.tokenType = tokenType; 18 | } 19 | 20 | public evaluate(t: ParseTree): ParseTree[] { 21 | // return all children of t that match nodeName 22 | const nodes: ParseTree[] = []; 23 | for (const c of Trees.getChildren(t)) { 24 | if (c instanceof TerminalNode && c.symbol) { 25 | if ((c.symbol.type === this.tokenType && !this.invert) || 26 | (c.symbol.type !== this.tokenType && this.invert)) { 27 | nodes.push(c); 28 | } 29 | } 30 | } 31 | 32 | return nodes; 33 | } 34 | 35 | public override toString(): string { 36 | const inv: string = this.invert ? "!" : ""; 37 | 38 | return "XPathTokenElement[" + inv + this.nodeName + "]"; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathWildcardAnywhereElement.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParseTree } from "../ParseTree.js"; 8 | import { Trees } from "../Trees.js"; 9 | import { XPath } from "./XPath.js"; 10 | import { XPathElement } from "./XPathElement.js"; 11 | 12 | export class XPathWildcardAnywhereElement extends XPathElement { 13 | public constructor() { 14 | super(XPath.WILDCARD); 15 | } 16 | 17 | public evaluate(t: ParseTree): ParseTree[] { 18 | if (this.invert) { 19 | // !* is weird but valid (empty) 20 | return []; 21 | } 22 | 23 | return Trees.descendants(t); 24 | } 25 | 26 | public override toString(): string { 27 | const inv: string = this.invert ? "!" : ""; 28 | 29 | return "XPathWildcardAnywhereElement[" + inv + this.nodeName + "]"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/tree/xpath/XPathWildcardElement.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { ParseTree } from "../ParseTree.js"; 8 | import { Trees } from "../Trees.js"; 9 | import { XPath } from "./XPath.js"; 10 | import { XPathElement } from "./XPathElement.js"; 11 | 12 | export class XPathWildcardElement extends XPathElement { 13 | public constructor() { 14 | super(XPath.WILDCARD); 15 | } 16 | 17 | public evaluate(t: ParseTree): ParseTree[] { 18 | const kids: ParseTree[] = []; 19 | if (this.invert) { 20 | // !* is weird but valid (empty) 21 | return kids; 22 | } 23 | for (const c of Trees.getChildren(t)) { 24 | kids.push(c); 25 | } 26 | 27 | return kids; 28 | } 29 | 30 | public override toString(): string { 31 | const inv: string = this.invert ? "!" : ""; 32 | 33 | return "XPathWildcardElement[" + inv + this.nodeName + "]"; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/tree/xpath/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./XPath.js"; 8 | export * from "./XPathElement.js"; 9 | export * from "./XPathLexer.js"; 10 | export * from "./XPathLexerErrorListener.js"; 11 | export * from "./XPathRuleAnywhereElement.js"; 12 | export * from "./XPathRuleElement.js"; 13 | export * from "./XPathTokenAnywhereElement.js"; 14 | export * from "./XPathTokenElement.js"; 15 | export * from "./XPathWildcardAnywhereElement.js"; 16 | export * from "./XPathWildcardElement.js"; 17 | -------------------------------------------------------------------------------- /src/utils/DoubleDict.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { HashMap } from "../misc/HashMap.js"; 8 | import { IComparable } from "./helpers.js"; 9 | 10 | export class DoubleDict { 11 | private readonly cacheMap: HashMap>; 12 | 13 | public constructor() { 14 | this.cacheMap = new HashMap>(); 15 | } 16 | 17 | public get(a: Key1, b: Key2): Value | null { 18 | const d = this.cacheMap.get(a) ?? null; 19 | 20 | return d === null ? null : (d.get(b) ?? null); 21 | } 22 | 23 | public set(a: Key1, b: Key2, o: Value): void { 24 | let d = this.cacheMap.get(a); 25 | if (!d) { 26 | d = new HashMap(); 27 | this.cacheMap.set(a, d); 28 | } 29 | 30 | d.set(b, o); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/MurmurHash.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /* 8 | * Parts added + modified: copyright (c) Mike Lischke. All rights reserved. 9 | * Licensed under the MIT License. See License.txt in the project root for license information. 10 | */ 11 | 12 | import { type IComparable } from "./helpers.js"; 13 | 14 | const c1 = 0xCC9E2D51; 15 | const c2 = 0x1B873593; 16 | const r1 = 15; 17 | const r2 = 13; 18 | const m = 5; 19 | const n = 0xE6546B64; 20 | 21 | /** A class that implements the Murmur hash algorithm. */ 22 | export class MurmurHash { 23 | private static readonly defaultSeed = 701; 24 | 25 | private constructor() { /**/ } 26 | 27 | /** 28 | * Initialize the hash using the specified {@code seed}. 29 | * 30 | * @param seed the seed 31 | * 32 | * @returns the intermediate hash value 33 | */ 34 | public static initialize(seed = MurmurHash.defaultSeed): number { 35 | return seed; 36 | } 37 | 38 | public static updateFromComparable(hash: number, value?: IComparable | null): number { 39 | return this.update(hash, value?.hashCode() ?? 0); 40 | } 41 | 42 | /** 43 | * Update the intermediate hash value for the next input {@code value}. 44 | * 45 | * @param hash The intermediate hash value. 46 | * @param value the value to add to the current hash. 47 | * 48 | * @returns the updated intermediate hash value 49 | */ 50 | public static update(hash: number, value: number): number { 51 | value = Math.imul(value, c1); 52 | value = (value << r1) | (value >>> (32 - r1)); 53 | value = Math.imul(value, c2); 54 | 55 | hash = hash ^ value; 56 | hash = (hash << r2) | (hash >>> (32 - r2)); 57 | hash = Math.imul(hash, m) + n; 58 | 59 | return hash; 60 | } 61 | 62 | /** 63 | * Apply the final computation steps to the intermediate value {@code hash} 64 | * to form the final result of the MurmurHash 3 hash function. 65 | * 66 | * @param hash The intermediate hash value. 67 | * @param entryCount The number of values added to the hash. 68 | * 69 | * @returns the final hash result 70 | */ 71 | public static finish(hash: number, entryCount: number): number { 72 | hash ^= entryCount * 4; 73 | hash ^= hash >>> 16; 74 | hash = Math.imul(hash, 0x85EBCA6B); 75 | hash ^= hash >>> 13; 76 | hash = Math.imul(hash, 0xC2B2AE35); 77 | hash ^= hash >>> 16; 78 | 79 | return hash; 80 | }; 81 | 82 | /** 83 | * An all-in-one convenience method to compute a hash for a single value. 84 | * 85 | * @param value The value to hash. 86 | * @param seed The seed for the hash value. 87 | * 88 | * @returns The computed hash. 89 | */ 90 | public static hashCode(value: number, seed?: number): number { 91 | return MurmurHash.finish(MurmurHash.update(seed ?? MurmurHash.defaultSeed, value), 1); 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | /** Expresses the Java concept of object equality (equality based on the content of two objects). */ 8 | export interface IComparable { 9 | equals(obj: unknown): boolean; 10 | hashCode(): number; 11 | } 12 | 13 | export const isComparable = (candidate: unknown): candidate is IComparable => { 14 | return typeof (candidate as IComparable).equals === "function"; 15 | }; 16 | 17 | const valueToString = (v: null | string): string => { 18 | return v === null ? "null" : v; 19 | }; 20 | 21 | /** 22 | * @param value The array to stringify. 23 | * 24 | * @returns a human readable string of an array (usually for debugging and testing). 25 | */ 26 | export const arrayToString = (value: unknown[] | null): string => { 27 | return Array.isArray(value) ? ("[" + value.map(valueToString).join(", ") + "]") : "null"; 28 | }; 29 | 30 | /** 31 | * Compares two arrays for equality, using object equality for elements. 32 | * 33 | * @param a The first array to compare. 34 | * @param b The second array to compare. 35 | * 36 | * @returns `true` if `a` and `b` are equal. 37 | */ 38 | export const equalArrays = (a: T[], b: T[]): boolean => { 39 | if (a === b) { 40 | return true; 41 | } 42 | 43 | if (a.length !== b.length) { 44 | return false; 45 | } 46 | 47 | for (let i = 0; i < a.length; i++) { 48 | const left = a[i]; 49 | const right = b[i]; 50 | if (left === right) { 51 | continue; 52 | } 53 | 54 | if (!left || !left.equals(right)) { 55 | return false; 56 | } 57 | } 58 | 59 | return true; 60 | }; 61 | 62 | /** 63 | * Compares two number arrays for equality. 64 | * 65 | * @param a The first array to compare. 66 | * @param b The second array to compare. 67 | * 68 | * @returns `true` if `a` and `b` are equal. 69 | */ 70 | export const equalNumberArrays = (a: number[], b: number[]): boolean => { 71 | if (a === b) { 72 | return true; 73 | } 74 | 75 | if (a.length !== b.length) { 76 | return false; 77 | } 78 | 79 | for (let i = 0; i < a.length; i++) { 80 | if (a[i] !== b[i]) { 81 | return false; 82 | } 83 | } 84 | 85 | return true; 86 | }; 87 | 88 | /** 89 | * Converts all non-visible whitespaces to escaped equivalents. 90 | * 91 | * @param s The string to convert. 92 | * @param escapeSpaces A flag indicating whether to escape spaces too. 93 | * 94 | * @returns The converted string. 95 | */ 96 | export const escapeWhitespace = (s: string, escapeSpaces = false): string => { 97 | s = s.replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r"); 98 | if (escapeSpaces) { 99 | s = s.replace(/ /g, "\u00B7"); 100 | } 101 | 102 | return s; 103 | }; 104 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | export * from "./helpers.js"; 8 | export * from "./MurmurHash.js"; 9 | export * from "./DoubleDict.js"; 10 | -------------------------------------------------------------------------------- /tests/api/OrderedHashSet.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { describe, expect, it } from "vitest"; 8 | 9 | import { OrderedHashSet, type IComparable } from "../../src/index.js"; 10 | 11 | class TestClass implements IComparable { 12 | #value: number; 13 | 14 | public constructor(value: number) { 15 | this.#value = value; 16 | } 17 | 18 | public equals(o: TestClass): boolean { 19 | return this.#value === o.#value; 20 | } 21 | 22 | public hashCode(): number { 23 | return this.#value; 24 | } 25 | }; 26 | 27 | describe("TestOrderedHashSet", () => { 28 | it("general", () => { 29 | const set = new OrderedHashSet(); 30 | expect(set.size).toBe(0); 31 | 32 | const obj1 = new TestClass(1); 33 | const obj2 = new TestClass(2); 34 | const obj3 = new TestClass(3); 35 | const obj4 = new TestClass(4); 36 | 37 | set.add(obj1); 38 | set.add(obj2); 39 | set.add(obj3); 40 | set.add(obj4); 41 | 42 | expect(set.size).toBe(4); 43 | expect(set.toArray()).toEqual([obj1, obj2, obj3, obj4]); 44 | }); 45 | 46 | it("duplicates and order", () => { 47 | const set = new OrderedHashSet(); 48 | expect(set.size).toBe(0); 49 | 50 | const obj1 = new TestClass(1); 51 | const obj2 = new TestClass(2); 52 | const obj3 = new TestClass(3); 53 | const obj4 = new TestClass(4); 54 | set.addAll([obj1, obj2, obj3, obj4, obj1, obj2, obj3, obj4]); 55 | 56 | expect(set.size).toBe(4); 57 | expect(set.toArray()).toEqual([obj1, obj2, obj3, obj4]); 58 | 59 | set.clear(); 60 | expect(set.size).toBe(0); 61 | 62 | set.addAll([obj4, obj3, obj2, obj1, obj1, obj2, obj2, obj1]); 63 | expect(set.size).toBe(4); 64 | expect(set.toArray()).toEqual([obj4, obj3, obj2, obj1]); 65 | }); 66 | 67 | it("iterator", () => { 68 | const set = new OrderedHashSet(); 69 | expect(set.size).toBe(0); 70 | 71 | const obj1 = new TestClass(1); 72 | const obj2 = new TestClass(2); 73 | const obj3 = new TestClass(3); 74 | const obj4 = new TestClass(4); 75 | 76 | set.add(obj1); 77 | set.add(obj2); 78 | set.add(obj3); 79 | set.add(obj4); 80 | 81 | expect(set.size).toBe(4); 82 | const iter = set[Symbol.iterator](); 83 | expect(iter.next().value).toBe(obj1); 84 | expect(iter.next().value).toBe(obj2); 85 | expect(iter.next().value).toBe(obj3); 86 | expect(iter.next().value).toBe(obj4); 87 | expect(iter.next().done).toBe(true); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /tests/api/Serialization.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { describe, expect, it } from "vitest"; 8 | 9 | import { ATNDeserializer, ATNSerializer } from "../../src/index.js"; 10 | import { MySQLLexer } from "../benchmarks/generated/MySQLLexer.js"; 11 | import { MySQLParser } from "../benchmarks/generated/MySQLParser.js"; 12 | 13 | describe("Test Serialization and Deserialization", () => { 14 | it("Lexer", () => { 15 | // eslint-disable-next-line no-underscore-dangle 16 | const serialized = MySQLLexer._serializedATN; 17 | 18 | const deserializer = new ATNDeserializer(); 19 | const atn = deserializer.deserialize(serialized); 20 | 21 | const serializer = new ATNSerializer(atn); 22 | const serialized2 = serializer.serialize(); 23 | 24 | expect(serialized2.length).toBe(serialized.length); 25 | 26 | expect(serialized2).toEqual(serialized); 27 | }); 28 | 29 | it("Parser", () => { 30 | // eslint-disable-next-line no-underscore-dangle 31 | const serialized = MySQLParser._serializedATN; 32 | 33 | const deserializer = new ATNDeserializer(); 34 | const atn = deserializer.deserialize(serialized); 35 | 36 | const serializer = new ATNSerializer(atn); 37 | const serialized2 = serializer.serialize(); 38 | 39 | expect(serialized2.length).toBe(serialized.length); 40 | 41 | expect(serialized2).toEqual(serialized); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /tests/api/TestInterpreterDataReader.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { describe, it, expect } from "vitest"; 8 | 9 | import { readFileSync } from "fs"; 10 | 11 | import { InterpreterDataReader } from "antlr4ng"; 12 | 13 | /** 14 | * This file represents a simple sanity checks on the parsing of the .interp file 15 | * available to the Java runtime for interpreting rather than compiling and executing parsers. 16 | */ 17 | describe("TestInterpreterDataReader", () => { 18 | it("testLexerFile", () => { 19 | const content = readFileSync("tests/generated/VisitorCalcLexer.interp", "utf-8"); 20 | const interpreterData = InterpreterDataReader.parseInterpreterData(content); 21 | 22 | expect(interpreterData.vocabulary.getMaxTokenType()).toBe(6); 23 | expect(interpreterData.ruleNames).toStrictEqual(["INT", "MUL", "DIV", "ADD", "SUB", "WS"]); 24 | expect(interpreterData.vocabulary.getLiteralNames()) 25 | .toStrictEqual([null, null, "'*'", "'/'", "'+'", "'-'", null]); 26 | expect(interpreterData.vocabulary.getSymbolicNames()) 27 | .toStrictEqual([null, "INT", "MUL", "DIV", "ADD", "SUB", "WS"]); 28 | expect(interpreterData.channels).toStrictEqual(["DEFAULT_TOKEN_CHANNEL", "HIDDEN"]); 29 | expect(interpreterData.modes).toStrictEqual(["DEFAULT_MODE"]); 30 | }); 31 | 32 | it("testParserFile", () => { 33 | const content = readFileSync("tests/generated/VisitorCalc.interp", "utf-8"); 34 | const interpreterData = InterpreterDataReader.parseInterpreterData(content); 35 | 36 | expect(interpreterData.vocabulary.getMaxTokenType()).toBe(6); 37 | expect(interpreterData.ruleNames).toStrictEqual(["s", "expr"]); 38 | expect(interpreterData.vocabulary.getLiteralNames()) 39 | .toStrictEqual([null, null, "'*'", "'/'", "'+'", "'-'", null]); 40 | expect(interpreterData.vocabulary.getSymbolicNames()) 41 | .toStrictEqual([null, "INT", "MUL", "DIV", "ADD", "SUB", "WS"]); 42 | }); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /tests/api/TestTokenStream.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { describe, it, expect } from "vitest"; 8 | 9 | import { BufferedTokenStream, CharStream, Token } from "antlr4ng"; 10 | 11 | import { VisitorBasicLexer } from "../generated/VisitorBasicLexer.js"; 12 | 13 | /** 14 | * This class contains tests for specific API functionality in {@link TokenStream} and derived types. 15 | */ 16 | describe("TestTokenStream", () => { 17 | /** 18 | * This is a targeted regression test for antlr/antlr4#1584 ({@link BufferedTokenStream} 19 | * cannot be reused after EOF). 20 | */ 21 | it("testBufferedTokenStreamReuseAfterFill", () => { 22 | const firstInput = CharStream.fromString("A"); 23 | const tokenStream = new BufferedTokenStream(new VisitorBasicLexer(firstInput)); 24 | tokenStream.fill(); 25 | expect(tokenStream.size).toBe(2); 26 | expect(tokenStream.get(0).type).toBe(VisitorBasicLexer.A); 27 | expect(tokenStream.get(1).type).toBe(Token.EOF); 28 | 29 | const secondInput = CharStream.fromString("AA"); 30 | tokenStream.setTokenSource(new VisitorBasicLexer(secondInput)); 31 | tokenStream.fill(); 32 | expect(tokenStream.size).toBe(3); 33 | expect(tokenStream.get(0).type).toBe(VisitorBasicLexer.A); 34 | expect(tokenStream.get(1).type).toBe(VisitorBasicLexer.A); 35 | expect(tokenStream.get(2).type).toBe(Token.EOF); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/api/XPath.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { beforeAll, describe, expect, it } from "vitest"; 8 | 9 | import { CharStream, CommonTokenStream, ParserRuleContext, XPath, type ParseTree, type TerminalNode } from "antlr4ng"; 10 | 11 | import { ExprLexer } from "../generated/ExprLexer.js"; 12 | import { ExprParser } from "../generated/ExprParser.js"; 13 | 14 | describe("XPath", () => { 15 | const input = "def f(x,y) { x = 3+4; y; ; }\ndef g(x) { return 1+2*x; }\n"; 16 | let parser!: ExprParser; 17 | let parseTree: ParseTree; 18 | 19 | const xpath = [ 20 | "/prog/func", // all funcs under prog at root 21 | "/prog/*", // all children of prog at root 22 | "/*/func", // all func kids of any root node 23 | "prog", // prog must be root node 24 | "/prog", // prog must be root node 25 | "/*", // any root 26 | "*", // any root 27 | "//ID", // any ID in tree 28 | "//expr/primary/ID",// any ID child of a primary under any expr 29 | "//body//ID", // any ID under a body 30 | "//'return'", // any 'return' literal in tree, matched by literal name 31 | "//RETURN", // any 'return' literal in tree, matched by symbolic name 32 | "//primary/*", // all kids of any primary 33 | "//func/*/stat", // all stat nodes grand kids of any func node 34 | "/prog/func/'def'", // all def literal kids of func kid of prog 35 | "//stat/';'", // all ';' under any stat node 36 | "//expr/primary/!ID", // anything but ID under primary under any expr node 37 | "//expr/!primary", // anything but primary under any expr node 38 | "//!*", // nothing anywhere 39 | "/!*", // nothing at root 40 | "//expr//ID", // any ID under any expression (tests antlr/antlr4#370) 41 | ]; 42 | const expected = [ 43 | "[func, func]", 44 | "[func, func]", 45 | "[func, func]", 46 | "[prog]", 47 | "[prog]", 48 | "[prog]", 49 | "[prog]", 50 | "[f, x, y, x, y, g, x, x]", 51 | "[y, x]", 52 | "[x, y, x]", 53 | "[return]", 54 | "[return]", 55 | "[3, 4, y, 1, 2, x]", 56 | "[stat, stat, stat, stat]", 57 | "[def, def]", 58 | "[;, ;, ;, ;]", 59 | "[3, 4, 1, 2]", 60 | "[expr, expr, expr, expr, expr, expr]", 61 | "[]", 62 | "[]", 63 | "[y, x]", 64 | ]; 65 | 66 | beforeAll(() => { 67 | const lexer = new ExprLexer(CharStream.fromString(input)); 68 | const tokens = new CommonTokenStream(lexer); 69 | parser = new ExprParser(tokens); 70 | parseTree = parser.prog(); 71 | }); 72 | 73 | it("Successful matches", () => { 74 | for (let i = 0; i < xpath.length; i++) { 75 | const found = XPath.findAll(parseTree, xpath[i], parser); 76 | 77 | const ruleNames: string[] = []; 78 | for (const t of found) { 79 | if (t instanceof ParserRuleContext) { 80 | const r = t; 81 | ruleNames.push(parser.ruleNames[r.ruleIndex]); 82 | } else { 83 | const token = t as TerminalNode; 84 | ruleNames.push(token.getText()); 85 | } 86 | } 87 | 88 | const result = `[${ruleNames.join(", ")}]`; 89 | 90 | expect(result, "path " + xpath[i] + " failed").to.equal(expected[i]); 91 | } 92 | 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /tests/api/perf/emoji.txt: -------------------------------------------------------------------------------- 1 | 😀🖖💩 2 | 👴🏽🖖🏿👦🏼 3 | 👮‍♀️👷‍♀️👯‍♂️ 4 | 🙇🏻‍♀️👼🏽🎅🏿 5 | 01234 6 | 0️⃣1️⃣2️⃣3️⃣4️⃣ 7 | ™©® 8 | ™️©️®️ 9 | 🇨🇨🇧🇬🇯🇲 10 | 🏳️‍🌈 11 | 🏴‍☠️ 12 | 13 | -------------------------------------------------------------------------------- /tests/benchmarks/support/MySQLBaseRecognizer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, 2023, Oracle and/or its affiliates. 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License, version 2.0, 6 | * as published by the Free Software Foundation. 7 | * 8 | * This program is also distributed with certain software (including 9 | * but not limited to OpenSSL) that is licensed under separate terms, as 10 | * designated in a particular file or component or in included license 11 | * documentation. The authors of MySQL hereby grant you an additional 12 | * permission to link the program and your derivative works with the 13 | * separately licensed software that they have included with 14 | * This program is distributed in the hope that it will be useful, but 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 17 | * the GNU General Public License, version 2.0, for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software Foundation, Inc., 21 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | import { Parser } from "antlr4ng"; 25 | 26 | import { IMySQLRecognizerCommon, SqlMode } from "./MySQLRecognizerCommon.js"; 27 | 28 | export abstract class MySQLBaseRecognizer extends Parser implements IMySQLRecognizerCommon { 29 | 30 | // To parameterize the parsing process. 31 | public serverVersion = 0; 32 | public sqlModes = new Set(); 33 | 34 | /** Enable MRS specific language parts. */ 35 | public supportMrs = true; 36 | 37 | /** Enable Multi Language Extension support. */ 38 | public supportMle = true; 39 | 40 | /** 41 | * Determines if the given SQL mode is currently active in the lexer. 42 | * 43 | * @param mode The mode to check. 44 | * 45 | * @returns True if the mode is one of the currently active modes. 46 | */ 47 | public isSqlModeActive(mode: SqlMode): boolean { 48 | return this.sqlModes.has(mode); 49 | } 50 | 51 | public sqlModeFromString(_modes: string): void { 52 | throw new Error("sqlModeFromString not implemented"); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /tests/bugs/bug97.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { describe, expect, it } from "vitest"; 8 | 9 | import { CharStream, CommonTokenStream } from "antlr4ng"; 10 | 11 | import { ExpressionLexer } from "./generated/ExpressionLexer.js"; 12 | import { 13 | ExpressionParser, type AddContext, type MultiplyContext, type SimpleContext 14 | } from "./generated/ExpressionParser.js"; 15 | import { ExpressionVisitor } from "./generated/ExpressionVisitor.js"; 16 | 17 | class MyVisitor extends ExpressionVisitor { 18 | public visitAdd = (ctx: AddContext): number => { 19 | return this.visit(ctx.expression(0)!)! + this.visit(ctx.expression(1)!)!; 20 | }; 21 | 22 | public visitMultiply = (ctx: MultiplyContext): number => { 23 | return this.visit(ctx.expression(0)!)! * this.visit(ctx.expression(1)!)!; 24 | }; 25 | 26 | public visitSimple = (ctx: SimpleContext): number => { 27 | return Number.parseInt(ctx.number().NUMBER().getText(), 10); 28 | }; 29 | } 30 | 31 | describe("Bug 97", () => { 32 | it("readme.md example visitor does not compile", () => { 33 | const input = "1 + 2 * 3"; 34 | const inputStream = CharStream.fromString(input); 35 | const lexer = new ExpressionLexer(inputStream); 36 | const tokenStream = new CommonTokenStream(lexer); 37 | const parser = new ExpressionParser(tokenStream); 38 | const tree = parser.start(); 39 | 40 | const visitor = new MyVisitor(); 41 | const result = visitor.visit(tree); 42 | 43 | expect(result).toBe(7); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /tests/bugs/grammars/Expression.g4: -------------------------------------------------------------------------------- 1 | grammar Expression; 2 | 3 | start: 4 | expression 5 | ; 6 | 7 | expression: 8 | expression '*' expression # multiply 9 | | expression '/' expression # divide 10 | | expression '+' expression # add 11 | | expression '-' expression # subtract 12 | | number # simple 13 | ; 14 | 15 | number: 16 | NUMBER 17 | ; 18 | 19 | NUMBER: 20 | [0-9]+ 21 | ; 22 | 23 | WS: 24 | [ \t\r\n]+ -> skip 25 | ; 26 | -------------------------------------------------------------------------------- /tests/fixtures/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "TypeScript", 3 | "targetPath": "../generated", 4 | "targetExtension": "ts", 5 | "testFileName": "Test.spec", 6 | "testAnnotations": [ 7 | "it", 8 | "it.skip" 9 | ], 10 | "grammarTemplateFile": "./templates/TypeScript.grammar.stg", 11 | "specTemplateFile": "./templates/TypeScript.spec.stg", 12 | "groupIncludes": [], 13 | "testIncludes": [], 14 | "files": [ 15 | { 16 | "sourcePattern": "./tsconfig.json", 17 | "targetPath": "../generated/" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/fixtures/grammars/Expr.g4: -------------------------------------------------------------------------------- 1 | // $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false 2 | // $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging 3 | 4 | grammar Expr; 5 | 6 | prog 7 | : func+ 8 | ; 9 | 10 | func 11 | : 'def' ID '(' arg (',' arg)* ')' body 12 | ; 13 | 14 | body 15 | : '{' stat+ '}' 16 | ; 17 | 18 | arg 19 | : ID 20 | ; 21 | 22 | stat 23 | : expr ';' # printExpr 24 | | ID '=' expr ';' # assign 25 | | 'return' expr ';' # ret 26 | | ';' # blank 27 | ; 28 | 29 | expr 30 | : expr ('*' | '/') expr # MulDiv 31 | | expr ('+' | '-') expr # AddSub 32 | | primary # prim 33 | ; 34 | 35 | primary 36 | : INT # int 37 | | ID # id 38 | | '(' expr ')' # parens 39 | ; 40 | 41 | MUL 42 | : '*' 43 | ; 44 | 45 | DIV 46 | : '/' 47 | ; 48 | 49 | ADD 50 | : '+' 51 | ; 52 | 53 | SUB 54 | : '-' 55 | ; 56 | 57 | RETURN 58 | : 'return' 59 | ; 60 | 61 | ID 62 | : [a-zA-Z]+ 63 | ; 64 | 65 | INT 66 | : [0-9]+ 67 | ; 68 | 69 | NEWLINE 70 | : '\r'? '\n' -> skip 71 | ; 72 | 73 | WS 74 | : [ \t]+ -> skip 75 | ; 76 | -------------------------------------------------------------------------------- /tests/fixtures/grammars/T1.g4: -------------------------------------------------------------------------------- 1 | lexer grammar T1; 2 | 3 | A: 4 | 'a' 5 | ; 6 | 7 | B: 8 | 'b' 9 | ; 10 | 11 | C: 12 | 'c' 13 | ; 14 | -------------------------------------------------------------------------------- /tests/fixtures/grammars/T2.g4: -------------------------------------------------------------------------------- 1 | lexer grammar T2; 2 | 3 | ID: 4 | 'a' ..'z'+ 5 | ; 6 | 7 | INT: 8 | '0' ..'9'+ 9 | ; 10 | 11 | SEMI: 12 | ';' 13 | ; 14 | 15 | MUL: 16 | '*' 17 | ; 18 | 19 | ASSIGN: 20 | '=' 21 | ; 22 | 23 | WS: 24 | ' ' 25 | ; 26 | -------------------------------------------------------------------------------- /tests/fixtures/grammars/T3.g4: -------------------------------------------------------------------------------- 1 | lexer grammar T3; 2 | 3 | ID: 4 | 'a' ..'z'+ 5 | ; 6 | 7 | INT: 8 | '0' ..'9'+ 9 | ; 10 | 11 | SEMI: 12 | ';' 13 | ; 14 | 15 | ASSIGN: 16 | '=' 17 | ; 18 | 19 | PLUS: 20 | '+' 21 | ; 22 | 23 | MULT: 24 | '*' 25 | ; 26 | 27 | WS: 28 | ' '+ 29 | ; 30 | -------------------------------------------------------------------------------- /tests/fixtures/grammars/VisitorBasic.g4: -------------------------------------------------------------------------------- 1 | grammar VisitorBasic; 2 | 3 | s 4 | : 'A' EOF 5 | ; 6 | 7 | A : 'A'; 8 | -------------------------------------------------------------------------------- /tests/fixtures/grammars/VisitorCalc.g4: -------------------------------------------------------------------------------- 1 | grammar VisitorCalc; 2 | 3 | s 4 | : expr EOF 5 | ; 6 | 7 | expr 8 | : INT # number 9 | | expr (MUL | DIV) expr # multiply 10 | | expr (ADD | SUB) expr # add 11 | ; 12 | 13 | INT : [0-9]+; 14 | MUL : '*'; 15 | DIV : '/'; 16 | ADD : '+'; 17 | SUB : '-'; 18 | WS : [ \t]+ -> channel(HIDDEN); 19 | -------------------------------------------------------------------------------- /tests/fixtures/grammars/graphemes.g4: -------------------------------------------------------------------------------- 1 | grammar graphemes; 2 | 3 | Extend: [\p{Grapheme_Cluster_Break=Extend}]; 4 | ZWJ: '\u200D'; 5 | SpacingMark: [\p{Grapheme_Cluster_Break=SpacingMark}]; 6 | fragment VS15: '\uFE0E'; 7 | fragment VS16: '\uFE0F'; 8 | fragment NonspacingMark: [\p{Nonspacing_Mark}]; 9 | fragment TextPresentationCharacter: [\p{EmojiPresentation=TextDefault}]; 10 | fragment EmojiPresentationCharacter: [\p{EmojiPresentation=EmojiDefault}]; 11 | fragment TextPresentationSequence: EmojiPresentationCharacter VS15; 12 | fragment EmojiPresentationSequence: TextPresentationCharacter VS16; 13 | 14 | /* No Longer supported; see https://github.com/antlr/antlr4/pull/3261 15 | fragment EmojiModifierSequence: 16 | [\p{Grapheme_Cluster_Break=E_Base}\p{Grapheme_Cluster_Break=E_Base_GAZ}] [\p{Grapheme_Cluster_Break=E_Modifier}]; 17 | */ 18 | 19 | fragment EmojiFlagSequence: 20 | [\p{Grapheme_Cluster_Break=Regional_Indicator}] [\p{Grapheme_Cluster_Break=Regional_Indicator}]; 21 | fragment ExtendedPictographic: [\p{Extended_Pictographic}]; 22 | fragment EmojiCombiningSequence: 23 | ( EmojiPresentationSequence 24 | | TextPresentationSequence 25 | | EmojiPresentationCharacter ) 26 | NonspacingMark*; 27 | EmojiCoreSequence: 28 | EmojiCombiningSequence 29 | | EmojiFlagSequence; 30 | fragment EmojiZWJElement: 31 | EmojiPresentationSequence 32 | | EmojiPresentationCharacter 33 | | ExtendedPictographic; 34 | EmojiZWJSequence: 35 | EmojiZWJElement (ZWJ EmojiZWJElement)+; 36 | emoji_sequence: 37 | ( EmojiZWJSequence 38 | | EmojiCoreSequence ) 39 | ( Extend | ZWJ | SpacingMark )*; 40 | 41 | Prepend: [\p{Grapheme_Cluster_Break=Prepend}]; 42 | NonControl: [\P{Grapheme_Cluster_Break=Control}]; 43 | CRLF: [\p{Grapheme_Cluster_Break=CR}][\p{Grapheme_Cluster_Break=LF}]; 44 | HangulSyllable: 45 | [\p{Grapheme_Cluster_Break=L}]* [\p{Grapheme_Cluster_Break=V}]+ [\p{Grapheme_Cluster_Break=T}]* 46 | | [\p{Grapheme_Cluster_Break=L}]* [\p{Grapheme_Cluster_Break=LV}] [\p{Grapheme_Cluster_Break=V}]* [\p{Grapheme_Cluster_Break=T}]* 47 | | [\p{Grapheme_Cluster_Break=L}]* [\p{Grapheme_Cluster_Break=LVT}] [\p{Grapheme_Cluster_Break=T}]* 48 | | [\p{Grapheme_Cluster_Break=L}]+ 49 | | [\p{Grapheme_Cluster_Break=T}]+; 50 | 51 | grapheme_cluster: 52 | CRLF 53 | | Prepend* ( emoji_sequence | HangulSyllable | NonControl ) ( Extend | ZWJ | SpacingMark )*; 54 | 55 | graphemes: grapheme_cluster* EOF; 56 | -------------------------------------------------------------------------------- /tests/fixtures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "strictNullChecks": false, 5 | "noImplicitAny": false, 6 | "noImplicitOverride": false, 7 | "strictPropertyInitialization": false, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /tests/global.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var */ 2 | 3 | export declare global { 4 | declare namespace globalThis { 5 | var antlrTestWrite: ((s: unknown) => void) | undefined; 6 | var antlrTestWriteLn: ((s: unknown) => void) | undefined; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/parser/ProfilingATNSimulator.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | 7 | import { describe, it } from "vitest"; 8 | 9 | describe("ProfilingATNSimulator", () => { 10 | it("Simple test, direct use", () => { 11 | // TODO: move java test case to here, it will need lots of work 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/recognizer/Recognizer.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) The ANTLR Project. All rights reserved. 3 | * Use of this file is governed by the BSD 3-clause license that 4 | * can be found in the LICENSE.txt file in the project root. 5 | */ 6 | import { describe, it, expect } from "vitest"; 7 | 8 | import { ATNSimulator, IntStream, Recognizer, Vocabulary, BaseErrorListener } from "../../src/index.js"; 9 | 10 | describe("Recognizer", () => { 11 | it("Recognizer.#listeners test", () => { 12 | class RecognizerMock extends Recognizer { 13 | public get grammarFileName(): string { 14 | throw new Error("Method not implemented."); 15 | } 16 | 17 | public get ruleNames(): string[] { 18 | throw new Error("Method not implemented."); 19 | } 20 | 21 | public get vocabulary(): Vocabulary { 22 | throw new Error("Method not implemented."); 23 | } 24 | 25 | public get inputStream(): IntStream { 26 | throw new Error("Method not implemented."); 27 | } 28 | 29 | public set inputStream(input: IntStream) { 30 | throw new Error("Method not implemented."); 31 | } 32 | } 33 | 34 | const recog = new RecognizerMock(); 35 | const listener1 = new BaseErrorListener(); 36 | recog.removeErrorListeners(); 37 | recog.addErrorListener(listener1); 38 | expect(recog.getErrorListeners().length).toEqual(1); 39 | const listener2 = new BaseErrorListener(); 40 | recog.addErrorListener(listener2); 41 | expect(recog.getErrorListeners().length).toEqual(2); 42 | recog.removeErrorListener(listener1); 43 | expect(recog.getErrorListeners().length).toEqual(1); 44 | recog.removeErrorListeners(); 45 | expect(recog.getErrorListeners().length).toEqual(0); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /tests/rewriter/abc.g4: -------------------------------------------------------------------------------- 1 | lexer grammar abc; 2 | A: 'a'; 3 | B: 'b'; 4 | C: 'c'; -------------------------------------------------------------------------------- /tests/rewriter/calc.g4: -------------------------------------------------------------------------------- 1 | lexer grammar calc; 2 | ID: 'a' ..'z'+; 3 | INT: '0' ..'9'+; 4 | SEMI: ';'; 5 | PLUS: '+'; 6 | MUL: '*'; 7 | ASSIGN: '='; 8 | WS: ' '+; 9 | -------------------------------------------------------------------------------- /tests/rewriter/generatedCode/abc.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | 3 | import * as antlr4 from "../../../src/index.js"; 4 | 5 | const serializedATN = [4, 0, 3, 13, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 1, 0, 1, 0, 1, 1, 6 | 1, 1, 1, 2, 1, 2, 0, 0, 3, 1, 1, 3, 2, 5, 3, 1, 0, 0, 12, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 7 | 0, 1, 7, 1, 0, 0, 0, 3, 9, 1, 0, 0, 0, 5, 11, 1, 0, 0, 0, 7, 8, 5, 97, 0, 0, 8, 2, 1, 0, 0, 0, 9, 10, 5, 98, 8 | 0, 0, 10, 4, 1, 0, 0, 0, 11, 12, 5, 99, 0, 0, 12, 6, 1, 0, 0, 0, 1, 0, 0]; 9 | 10 | const atn = new antlr4.ATNDeserializer().deserialize(serializedATN); 11 | 12 | const decisionsToDFA = atn.decisionToState.map((ds, index) => { return new antlr4.DFA(ds, index); }); 13 | 14 | export class ABC extends antlr4.Lexer { 15 | public static readonly A = 1; 16 | public static readonly B = 2; 17 | public static readonly C = 3; 18 | 19 | public override get grammarFileName(): string { return "abc.g4"; }; 20 | 21 | public get channelNames(): string[] { return ["DEFAULT_TOKEN_CHANNEL", "HIDDEN"]; } 22 | 23 | public get modeNames(): string[] { return ["DEFAULT_MODE"]; } 24 | public get literalNames(): Array { return [null, "'a'", "'b'", "'c'"]; } 25 | public get symbolicNames(): Array { return [null, "A", "B", "C"]; } 26 | public get ruleNames(): string[] { return ["A", "B", "C"]; } 27 | 28 | public constructor(input: antlr4.CharStream) { 29 | super(input); 30 | this.interpreter = new antlr4.LexerATNSimulator(this, atn, decisionsToDFA, new antlr4.PredictionContextCache()); 31 | } 32 | 33 | public override get vocabulary(): antlr4.Vocabulary { 34 | return antlr4.Vocabulary.EMPTY_VOCABULARY; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/rewriter/generatedCode/calc.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | 3 | import * as antlr4 from "../../../src/index.js"; 4 | 5 | const serializedATN = [4, 0, 7, 38, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 6 | 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 4, 0, 17, 8, 0, 11, 0, 12, 0, 18, 1, 1, 4, 1, 22, 8, 1, 11, 1, 12, 1, 7 | 23, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 4, 6, 35, 8, 6, 11, 6, 12, 6, 36, 0, 0, 7, 1, 1, 8 | 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 1, 0, 0, 40, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 9 | 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 1, 16, 1, 0, 0, 0, 3, 21, 1, 0, 0, 10 | 0, 5, 25, 1, 0, 0, 0, 7, 27, 1, 0, 0, 0, 9, 29, 1, 0, 0, 0, 11, 31, 1, 0, 0, 0, 13, 34, 1, 0, 0, 0, 15, 11 | 17, 2, 97, 122, 0, 16, 15, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 16, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 12 | 2, 1, 0, 0, 0, 20, 22, 2, 48, 57, 0, 21, 20, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 21, 1, 0, 0, 0, 23, 24, 13 | 1, 0, 0, 0, 24, 4, 1, 0, 0, 0, 25, 26, 5, 59, 0, 0, 26, 6, 1, 0, 0, 0, 27, 28, 5, 43, 0, 0, 28, 8, 1, 0, 14 | 0, 0, 29, 30, 5, 42, 0, 0, 30, 10, 1, 0, 0, 0, 31, 32, 5, 61, 0, 0, 32, 12, 1, 0, 0, 0, 33, 35, 5, 32, 15 | 0, 0, 34, 33, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 34, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 14, 1, 0, 0, 16 | 0, 4, 0, 18, 23, 36, 0]; 17 | 18 | const atn = new antlr4.ATNDeserializer().deserialize(serializedATN); 19 | 20 | const decisionsToDFA = atn.decisionToState.map((ds, index) => { return new antlr4.DFA(ds, index); }); 21 | 22 | export class Calc extends antlr4.Lexer { 23 | public static readonly ID = 1; 24 | public static readonly INT = 2; 25 | public static readonly SEMI = 3; 26 | public static readonly PLUS = 4; 27 | public static readonly MUL = 5; 28 | public static readonly ASSIGN = 6; 29 | public static readonly WS = 7; 30 | 31 | public override get grammarFileName(): string { return "calc.g4"; } 32 | public get channelNames(): Array { return ["DEFAULT_TOKEN_CHANNEL", "HIDDEN"]; } 33 | public get modeNames(): Array { return ["DEFAULT_MODE"]; } 34 | public get literalNames(): Array { return [null, null, null, "';'", "'+'", "'*'", "'='"]; } 35 | public get symbolicNames(): Array { 36 | return [null, "ID", "INT", "SEMI", "PLUS", "MUL", "ASSIGN", "WS"]; 37 | } 38 | public get ruleNames(): string[] { return ["ID", "INT", "SEMI", "PLUS", "MUL", "ASSIGN", "WS"]; } 39 | 40 | public constructor(input: antlr4.CharStream) { 41 | super(input); 42 | this.interpreter = new antlr4.LexerATNSimulator(this, atn, decisionsToDFA, new antlr4.PredictionContextCache()); 43 | } 44 | 45 | public override get vocabulary(): antlr4.Vocabulary { 46 | return antlr4.Vocabulary.EMPTY_VOCABULARY; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "Node16", 5 | "target": "ESNext", 6 | "moduleResolution": "Node16", 7 | "resolveJsonModule": true, 8 | "noEmit": true, 9 | "removeComments": false, 10 | "noImplicitAny": true, 11 | "noImplicitOverride": false, 12 | "sourceMap": true, 13 | "inlineSources": true, 14 | "isolatedModules": false, 15 | "allowSyntheticDefaultImports": true, 16 | "strictNullChecks": true, 17 | "skipLibCheck": true, 18 | }, 19 | "include": [ 20 | "**/*.ts", 21 | "./global.d.ts", 22 | "../dist/**/*.ts", 23 | "../src/**/*.ts", 24 | ], 25 | "exclude": [], 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "sourceMap": true, 7 | "esModuleInterop": true, 8 | "noEmit": false, 9 | "declaration": true, 10 | "emitDeclarationOnly": true, 11 | "outDir": "dist", 12 | "noImplicitAny": true, 13 | "strictNullChecks": true, 14 | "preserveConstEnums": true, 15 | "noImplicitOverride": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "isolatedModules": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "forceConsistentCasingInFileNames": true, 21 | "skipLibCheck": true, 22 | "rootDir": "src", 23 | }, 24 | "include": [ 25 | "src/**/*.ts", 26 | ], 27 | "exclude": [ 28 | "node_modules", 29 | "tests", 30 | "dist", 31 | ], 32 | "compileOnSave": true, 33 | "ts-node": { 34 | "esm": true 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | logHeapUsage: false, 6 | isolate: false, 7 | environment: "node", 8 | pool: "threads", 9 | }, 10 | }); 11 | --------------------------------------------------------------------------------