antlr-ng
Version:
Next generation ANTLR Tool
316 lines (315 loc) • 8.97 kB
JavaScript
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
};