UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

95 lines (94 loc) 3.21 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); import { HashSet, RuleStopState, RuleTransition } from "antlr4ng"; import { LeftRecursionCyclesMessage } from "../tool/LeftRecursionCyclesMessage.js"; class LeftRecursionDetector { static { __name(this, "LeftRecursionDetector"); } /** Holds a list of cycles (sets of rule names). */ listOfRecursiveCycles = []; /** Which rule start states have we visited while looking for a single left-recursion check? */ rulesVisitedPerRuleCheck = new HashSet(); g; atn; constructor(g, atn) { this.g = g; this.atn = atn; } check(...args) { if (args.length === 0) { for (const start of this.atn.ruleToStartState) { this.rulesVisitedPerRuleCheck.clear(); this.rulesVisitedPerRuleCheck.add(start); this.check(this.g.getRule(start.ruleIndex), start, /* @__PURE__ */ new Set()); } if (this.listOfRecursiveCycles.length > 0) { this.leftRecursionCycles(this.g.fileName, this.listOfRecursiveCycles); } return; } const [enclosingRule, s, visitedStates] = args; if (s instanceof RuleStopState) { return true; } if (visitedStates.has(s)) { return false; } visitedStates.add(s); const n = s.transitions.length; let stateReachesStopState = false; for (let i = 0; i < n; i++) { const t = s.transitions[i]; if (t instanceof RuleTransition) { const rt = t; const r = this.g.getRule(rt.ruleIndex); if (this.rulesVisitedPerRuleCheck.contains(t.target)) { this.addRulesToCycle(enclosingRule, r); } else { this.rulesVisitedPerRuleCheck.add(t.target); const nullable = this.check(r, t.target, /* @__PURE__ */ new Set()); this.rulesVisitedPerRuleCheck.remove(t.target); if (nullable) { stateReachesStopState ||= this.check(enclosingRule, rt.followState, visitedStates); } } } else { if (t.isEpsilon) { stateReachesStopState ||= this.check(enclosingRule, t.target, visitedStates); } } } return stateReachesStopState; } /** * enclosingRule calls targetRule. Find the cycle containing the target and add the caller. Find the cycle * containing the caller and add the target. If no cycles contain either, then create a new cycle. */ addRulesToCycle(enclosingRule, targetRule) { let foundCycle = false; for (const rulesInCycle of this.listOfRecursiveCycles) { if (rulesInCycle.includes(targetRule)) { rulesInCycle.push(enclosingRule); foundCycle = true; } if (rulesInCycle.includes(enclosingRule)) { rulesInCycle.push(targetRule); foundCycle = true; } } if (!foundCycle) { const cycle = new Array(); cycle.push(targetRule); cycle.push(enclosingRule); this.listOfRecursiveCycles.push(cycle); } } leftRecursionCycles(fileName, cycles) { const msg = new LeftRecursionCyclesMessage(fileName, cycles); this.g.tool.errorManager.error(msg); } } export { LeftRecursionDetector };