antlr-ng
Version:
Next generation ANTLR Tool
184 lines (183 loc) • 7.06 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { CharStream, CommonToken, CommonTokenStream } from "antlr4ng";
import { Constants } from "../Constants.js";
import { ANTLRv4Lexer } from "../generated/ANTLRv4Lexer.js";
import { ANTLRv4Parser } from "../generated/ANTLRv4Parser.js";
import { OrderedHashMap } from "../misc/OrderedHashMap.js";
import { DictType } from "../misc/types.js";
import { ScopeParser } from "../parse/ScopeParser.js";
import { ToolANTLRParser } from "../parse/ToolANTLRParser.js";
import { BasicSemanticChecks } from "../semantics/BasicSemanticChecks.js";
import { RuleCollector } from "../semantics/RuleCollector.js";
import { ParseTreeToASTConverter } from "../support/ParseTreeToASTConverter.js";
import { isTokenName } from "../support/helpers.js";
import { GrammarTransformPipeline } from "../tool/GrammarTransformPipeline.js";
import { IssueCode } from "../tool/Issues.js";
import { LabelElementPair } from "../tool/LabelElementPair.js";
import { GrammarAST } from "../tool/ast/GrammarAST.js";
import { LeftRecursiveRuleAnalyzer } from "./LeftRecursiveRuleAnalyzer.js";
class LeftRecursiveRuleTransformer {
static {
__name(this, "LeftRecursiveRuleTransformer");
}
ast;
rules;
g;
tool;
constructor(ast, rules, g) {
this.ast = ast;
this.rules = rules;
this.g = g;
this.tool = g.tool;
}
translateLeftRecursiveRules() {
const language = this.g.getLanguage();
const leftRecursiveRuleNames = [];
for (const r of this.rules) {
if (!isTokenName(r.name)) {
if (LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(r.ast, r.name)) {
const fitsPattern = this.translateLeftRecursiveRule(this.ast, r, language);
if (fitsPattern) {
leftRecursiveRuleNames.push(r.name);
} else {
this.g.tool.errorManager.grammarError(
IssueCode.NonconformingLrRule,
this.g.fileName,
r.ast.children[0].token,
r.name
);
}
}
}
}
const ruleRefs = this.ast.getNodesWithType(ANTLRv4Parser.RULE_REF);
for (const r of ruleRefs) {
if (r.parent.getType() === ANTLRv4Parser.RULE) {
continue;
}
const rule = r;
if (rule.getOptionString(Constants.PrecedenceOptionName)) {
continue;
}
if (leftRecursiveRuleNames.includes(rule.getText())) {
const token = CommonToken.fromType(ANTLRv4Parser.INT, "0");
rule.setOption(Constants.PrecedenceOptionName, new GrammarAST(token));
}
}
}
/** @returns true if successful */
translateLeftRecursiveRule(context, r, language) {
const prevRuleAST = r.ast;
const ruleName = prevRuleAST.children[0].getText();
const leftRecursiveRuleWalker = new LeftRecursiveRuleAnalyzer(prevRuleAST, this.tool, ruleName, language);
let isLeftRec;
try {
isLeftRec = leftRecursiveRuleWalker.recursiveRule();
} catch {
isLeftRec = false;
}
if (!isLeftRec) {
return false;
}
const rules = context.getFirstChildWithType(ANTLRv4Parser.RULES);
const newRuleText = leftRecursiveRuleWalker.getArtificialOpPrecRule();
const t = this.parseArtificialRule(prevRuleAST.g, newRuleText);
if (t === void 0) {
return false;
}
t.children[0].token = prevRuleAST.children[0].token;
rules.setChild(prevRuleAST.childIndex, t);
r.ast = t;
const transform = new GrammarTransformPipeline(this.g, this.g.tool);
transform.reduceBlocksToSets(r.ast);
const ruleCollector = new RuleCollector(this.g);
ruleCollector.visit(t, ANTLRv4Parser.RULE_ruleSpec);
const basics = new BasicSemanticChecks(this.g, ruleCollector);
basics.checkAssocElementOption = false;
basics.visit(t, ANTLRv4Parser.RULE_ruleSpec);
r.recPrimaryAlts = new Array();
r.recPrimaryAlts.push(...leftRecursiveRuleWalker.prefixAndOtherAlts);
if (r.recPrimaryAlts.length === 0) {
this.g.tool.errorManager.grammarError(
IssueCode.NoNonLrAlts,
this.g.fileName,
r.ast.children[0].token,
r.name
);
}
r.recOpAlts = new OrderedHashMap();
leftRecursiveRuleWalker.binaryAlts.forEach((value, key) => {
r.recOpAlts.set(key, value);
});
leftRecursiveRuleWalker.ternaryAlts.forEach((value, key) => {
r.recOpAlts.set(key, value);
});
leftRecursiveRuleWalker.suffixAlts.forEach((value, key) => {
r.recOpAlts.set(key, value);
});
this.setAltASTPointers(r, t);
const arg = r.ast.getFirstChildWithType(ANTLRv4Parser.ARG_ACTION);
if (arg !== null) {
r.args = ScopeParser.parseTypedArgList(arg, arg.getText(), this.g);
r.args.type = DictType.Argument;
r.args.ast = arg;
arg.resolver = r.alt[1];
}
for (const [ast, _] of leftRecursiveRuleWalker.leftRecursiveRuleRefLabels) {
const labelOpNode = ast.parent;
const elementNode = labelOpNode.children[1];
const lp = new LabelElementPair(this.g, ast, elementNode, labelOpNode.getType());
r.alt[1].labelDefs.set(ast.getText(), [lp]);
}
r.leftRecursiveRuleRefLabels = leftRecursiveRuleWalker.leftRecursiveRuleRefLabels;
this.tool.logInfo({ component: "grammar", msg: "added: " + t.toStringTree() });
return true;
}
parseArtificialRule(g, ruleText) {
const stream = CharStream.fromString(ruleText);
stream.name = g.fileName;
const lexer = new ANTLRv4Lexer(stream);
const tokens = new CommonTokenStream(lexer);
const p = new ToolANTLRParser(tokens, this.tool);
const ruleStart = null;
try {
const r = p.ruleSpec();
const root = new GrammarAST();
ParseTreeToASTConverter.convertRuleSpecToAST(r, root);
const ruleAST = root.children[0];
GrammarTransformPipeline.setGrammarPtr(g, ruleAST);
GrammarTransformPipeline.augmentTokensWithOriginalPosition(g, ruleAST);
return ruleAST;
} catch (e) {
this.g.tool.errorManager.toolError(
IssueCode.InternalError,
e,
ruleStart,
"error parsing rule created during left-recursion detection: " + ruleText
);
}
return void 0;
}
setAltASTPointers(r, t) {
const ruleBlk = t.getFirstChildWithType(ANTLRv4Parser.BLOCK);
const mainAlt = ruleBlk.children[0];
const primaryBlk = mainAlt.children[0];
const opsBlk = mainAlt.children[1].children[0];
for (let i = 0; i < r.recPrimaryAlts.length; i++) {
const altInfo = r.recPrimaryAlts[i];
altInfo.altAST = primaryBlk.children[i];
altInfo.altAST.leftRecursiveAltInfo = altInfo;
altInfo.originalAltAST.leftRecursiveAltInfo = altInfo;
}
for (let i = 0; i < r.recOpAlts.size; i++) {
const altInfo = r.recOpAlts.getElement(i);
altInfo.altAST = opsBlk.children[i];
altInfo.altAST.leftRecursiveAltInfo = altInfo;
altInfo.originalAltAST.leftRecursiveAltInfo = altInfo;
}
}
}
export {
LeftRecursiveRuleTransformer
};