UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

152 lines (151 loc) 4.93 kB
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 };