antlr-ng
Version:
Next generation ANTLR Tool
152 lines (151 loc) • 4.93 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { Token } from "antlr4ng";
import { Constants } from "../Constants.js";
import { IssueCode } from "../tool/Issues.js";
import { CommonTree } from "./CommonTree.js";
import { CommonTreeNodeStream } from "./CommonTreeNodeStream.js";
import { MismatchedTreeNodeException } from "./exceptions/MismatchTreeNodeException.js";
class TreeParser {
static {
__name(this, "TreeParser");
}
input;
errorManager;
/**
* This is true when we see an error and before having successfully matched a token. Prevents generation of more
* than one error message per error.
*/
errorRecovery = false;
/**
* In lieu of a return value, this indicates that a rule or token has failed to match. Reset to false upon valid
* token match.
*/
failed = false;
/** If 0, no backtracking is going on. Safe to exec actions etc... If > 0 then it's the level of backtracking. */
backtracking = 0;
constructor(errorManager, input) {
this.errorManager = errorManager;
this.input = input ?? new CommonTreeNodeStream(new CommonTree());
}
static getAncestor(t, goal) {
while (t !== null) {
if (t.getType() === goal) {
return t;
}
t = t.parent;
}
return null;
}
/**
* Match '.' in tree parser has special meaning. Skip node or entire tree if node has children. If children,
* scan until corresponding UP node.
*/
matchAny() {
this.errorRecovery = false;
this.failed = false;
let lookAhead = this.input.lookaheadType(1);
if (lookAhead && lookAhead.children.length === 0) {
this.input.consume();
return;
}
let level = 0;
if (lookAhead) {
let tokenType = lookAhead.getType();
while (tokenType !== Token.EOF && !(tokenType === Constants.Up && level === 0)) {
this.input.consume();
lookAhead = this.input.lookaheadType(1);
if (lookAhead) {
tokenType = lookAhead.getType();
if (tokenType === Constants.Down) {
++level;
} else {
if (tokenType === Constants.Up) {
--level;
}
}
}
}
}
this.input.consume();
}
/**
* Check if current node in input has a context. Context means sequence of nodes towards root of tree. For
* example, you might say context is "MULT" which means my parent must be MULT. "CLASS VARDEF" says current node
* must be child of a VARDEF and whose parent is a CLASS node. You can use "..." to mean zero-or-more nodes.
* "METHOD ... VARDEF" means my parent is VARDEF and somewhere above that is a METHOD node. The first node in t
* he context is not necessarily the root. The context matcher stops matching and returns true when it runs out
* of context. There is no way to force the first node to be the root.
*/
inContext(nodes) {
let ni = nodes.length - 1;
const t = this.input.lookaheadType(1);
let run = t.parent;
while (ni >= 0 && run !== null) {
if (nodes[ni] === Constants.Up) {
if (ni === 0) {
return true;
}
const goal = nodes[ni - 1];
const ancestor = TreeParser.getAncestor(run, goal);
if (ancestor === null) {
return false;
}
run = ancestor;
ni--;
}
if (run.getType() !== nodes[ni]) {
return false;
}
ni--;
run = run.parent;
}
if (run === null && ni >= 0) {
return false;
}
return true;
}
/**
* Match current input symbol against ttype. Attempt single token insertion or deletion error recovery. If
* that fails, throw MismatchedTokenException.
*/
match(input, ttype) {
this.failed = false;
const matchedSymbol = input.lookaheadType(1);
if (input.lookahead(1) === ttype) {
input.consume();
this.errorRecovery = false;
return matchedSymbol;
}
if (this.backtracking > 0) {
this.failed = true;
return matchedSymbol;
}
throw new MismatchedTreeNodeException(ttype);
}
/**
* Report a recognition problem.
*
* This method sets errorRecovery to indicate the parser is recovering not parsing. Once in recovery mode,
* no errors are generated. To get out of recovery mode, the parser must successfully match
* a token (after a resync). So it will go:
*
* 1. error occurs
* 2. enter recovery mode, report error
* 3. consume until token found in resynch set
* 4. try to resume parsing
* 5. next match() will reset errorRecovery mode
*
* If you override, make sure to update syntaxErrors if you care about that.
*/
reportError(e) {
if (this.errorRecovery) {
return;
}
this.errorRecovery = true;
this.errorManager.toolError(IssueCode.InternalError, e);
}
}
export {
TreeParser
};