UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

316 lines (315 loc) 8.97 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); import { MurmurHash } from "../support/MurmurHash.js"; import { Alternative } from "./Alternative.js"; import { Grammar } from "./Grammar.js"; import { LabelType } from "./LabelType.js"; class Rule { static { __name(this, "Rule"); } static validLexerCommands = /* @__PURE__ */ new Set([ "mode", "pushMode", "type", "channel", "popMode", "skip", "more" ]); name; modifiers; ast; args; retvals; locals; /** In which grammar does this rule live? */ g; /** If we're in a lexer grammar, we might be in a mode. */ mode; /** If null then use value from global option that is false by default. */ caseInsensitive; /** * Map a name to an action for this rule like @init {...}. The code generator will use this to fill holes in the * rule template. I track the AST node for the action in case I need the line number for errors. */ namedActions = /* @__PURE__ */ new Map(); /** * Track exception handlers; points at "catch" node of (catch exception action) don't track finally action. */ exceptions = new Array(); /** * Track all executable actions other than named actions like @init and catch/finally (not in an alt). Also tracks * predicates, rewrite actions. We need to examine these actions before code generation so that we can detect * refs to $rule.attr etc... * * This tracks per rule. Alternative objs also track per alt. */ actions = new Array(); /** Set by SymbolCollector. */ finallyAction; numberOfAlts; /** Nobody calls us. */ isStartRule = true; /** 1..n alts */ alt = []; /** All rules have unique index 0..n - 1. */ index; /** If lexer; 0..n-1 for n actions in a rule. */ actionIndex = -1; constructor(g, name, ast, numberOfAlts, lexerMode, caseInsensitive) { caseInsensitive ??= false; this.g = g; this.name = name; this.ast = ast; this.numberOfAlts = numberOfAlts; this.alt = new Array(numberOfAlts + 1); for (let i = 1; i <= numberOfAlts; i++) { this.alt[i] = new Alternative(this, i); } this.mode = lexerMode; this.caseInsensitive = caseInsensitive; } hashCode() { let hash = MurmurHash.initialize(); hash = hash * 31 + MurmurHash.update(hash, this.name); hash = hash * 31 + this.numberOfAlts; return hash; } defineActionInAlt(currentAlt, actionAST) { this.actions.push(actionAST); this.alt[currentAlt].actions.push(actionAST); if (this.g.isLexer()) { this.defineLexerAction(actionAST); } } /** * Lexer actions are numbered across rules 0..n - 1. * * @param actionAST The action to define. */ defineLexerAction(actionAST) { this.actionIndex = this.g.lexerActions.size; if (!this.g.lexerActions.has(actionAST)) { this.g.lexerActions.set(actionAST, this.actionIndex); } } definePredicateInAlt(currentAlt, predAST) { this.actions.push(predAST); this.alt[currentAlt].actions.push(predAST); if (!this.g.sempreds.has(predAST)) { this.g.sempreds.set(predAST, this.g.sempreds.size); } } resolveRetvalOrProperty(y) { if (this.retvals) { const a = this.retvals.get(y); if (a) { return a; } } const d = this.getPredefinedScope(LabelType.RuleLabel); return d?.get(y) ?? null; } getTokenRefs() { const refs = /* @__PURE__ */ new Set(); for (let i = 1; i <= this.numberOfAlts; i++) { for (const key of this.alt[i].tokenRefs.keys()) { refs.add(key); } } return refs; } getElementLabelNames() { const refs = /* @__PURE__ */ new Set(); for (let i = 1; i <= this.numberOfAlts; i++) { for (const key of this.alt[i].labelDefs.keys()) { refs.add(key); } } if (refs.size === 0) { return null; } return refs; } getElementLabelDefs() { const defs = /* @__PURE__ */ new Map(); for (let i = 1; i <= this.numberOfAlts; i++) { for (const pairs of this.alt[i].labelDefs.values()) { for (const p of pairs) { const text = p.label.getText(); let list = defs.get(text); if (!list) { list = []; defs.set(text, list); } list.push(p); } } } return defs; } hasAltSpecificContexts() { return this.getAltLabels() !== null; } /** * Used for recursive rules (subclass), which have 1 alt, but many original alts. * * @returns The original alt number for this rule. */ getOriginalNumberOfAlts() { return this.numberOfAlts; } /** * Gets `#` labels. The keys of the map are the labels applied to outer alternatives of a lexer rule, and the * values are collections of pairs (alternative number and {@link AltAST}) identifying the alternatives with * this label. Unlabeled alternatives are not included in the result. * * @returns A map of `#` labels to their AST nodes they are applied to, or `null` if no such labels are present. */ getAltLabels() { const labels = /* @__PURE__ */ new Map(); for (let i = 1; i <= this.numberOfAlts; i++) { const altLabel = this.alt[i].ast.altLabel; if (altLabel) { let list = labels.get(altLabel.getText()); if (!list) { list = []; labels.set(altLabel.getText(), list); } list.push([i, this.alt[i].ast]); } } if (labels.size === 0) { return null; } return labels; } getUnlabeledAltASTs() { const alts = new Array(); for (let i = 1; i <= this.numberOfAlts; i++) { const altLabel = this.alt[i].ast.altLabel; if (!altLabel) { alts.push(this.alt[i].ast); } } if (alts.length === 0) { return null; } return alts; } resolveToAttribute(...args) { if (args.length === 3) { const [x2, y, _node2] = args; const anyLabelDef = this.getAnyLabelDef(x2); if (anyLabelDef !== null) { if (anyLabelDef.type === LabelType.RuleLabel) { return this.g.getRule(anyLabelDef.element.getText())?.resolveRetvalOrProperty(y) ?? null; } else { const scope = this.getPredefinedScope(anyLabelDef.type); if (scope === null) { return null; } return scope.get(y); } } return null; } const [x, _node] = args; if (this.args) { const a = this.args.get(x); if (a) { return a; } } if (this.retvals) { const a = this.retvals.get(x); if (a) { return a; } } if (this.locals) { const a = this.locals.get(x); if (a) { return a; } } const properties = this.getPredefinedScope(LabelType.RuleLabel); return properties?.get(x) ?? null; } resolvesToLabel(x, node) { const anyLabelDef = this.getAnyLabelDef(x); return anyLabelDef !== null && (anyLabelDef.type === LabelType.RuleLabel || anyLabelDef.type === LabelType.TokenLabel); } resolvesToListLabel(x, node) { const anyLabelDef = this.getAnyLabelDef(x); return anyLabelDef !== null && (anyLabelDef.type === LabelType.RuleListLabel || anyLabelDef.type === LabelType.TokenListLabel); } resolvesToToken(x, node) { const anyLabelDef = this.getAnyLabelDef(x); if (anyLabelDef !== null && anyLabelDef.type === LabelType.TokenLabel) { return true; } return false; } resolvesToAttributeDict(x, node) { if (this.resolvesToToken(x, node)) { return true; } return false; } resolveToRule(x) { if (x === this.name) { return this; } const anyLabelDef = this.getAnyLabelDef(x); if (anyLabelDef !== null && anyLabelDef.type === LabelType.RuleLabel) { return this.g.getRule(anyLabelDef.element.getText()) ?? null; } return this.g.getRule(x); } getAnyLabelDef(x) { const labels = this.getElementLabelDefs().get(x); if (labels) { return labels[0] ?? null; } return null; } getPredefinedScope(labelType) { const grammarLabelKey = this.g.getTypeString() + ":" + labelType; return Grammar.grammarAndLabelRefTypeToScope.get(grammarLabelKey) ?? null; } isFragment() { if (!this.modifiers) { return false; } for (const a of this.modifiers) { if (a.getText() === "fragment") { return true; } } return false; } equals(obj) { if (this === obj) { return true; } if (!(obj instanceof Rule)) { return false; } return this.name === obj.name; } toString() { let buf = "Rule{name=" + this.name; if (this.args) { buf += ", args=" + JSON.stringify(this.args, null, 2); } if (this.retvals) { buf += ", retvals=" + JSON.stringify(this.retvals, null, 4); } return buf + "}"; } } export { Rule };