UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

184 lines (183 loc) 7.06 kB
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 };