UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

284 lines (283 loc) 10.6 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); import { ANTLRv4Parser } from "../generated/ANTLRv4Parser.js"; import { CommonTreeNodeStream } from "../tree/CommonTreeNodeStream.js"; import { SourceGenTriggers } from "../tree/walkers/SourceGenTriggers.js"; import { Utils } from "../misc/Utils.js"; import { IssueCode } from "../tool/Issues.js"; import { LeftRecursiveRule } from "../tool/LeftRecursiveRule.js"; import { PredAST } from "../tool/ast/PredAST.js"; import { Action } from "./model/Action.js"; import { AltBlock } from "./model/AltBlock.js"; import { BaseListenerFile } from "./model/BaseListenerFile.js"; import { BaseVisitorFile } from "./model/BaseVisitorFile.js"; import { Choice } from "./model/Choice.js"; import { Lexer } from "./model/Lexer.js"; import { LexerFile } from "./model/LexerFile.js"; import { ListenerFile } from "./model/ListenerFile.js"; import { RuleActionFunction } from "./model/RuleActionFunction.js"; import { RuleSempredFunction } from "./model/RuleSempredFunction.js"; import { VisitorFile } from "./model/VisitorFile.js"; class OutputModelController { static { __name(this, "OutputModelController"); } /** Who does the work? */ factory; currentBlock; currentOuterMostAlt; /** While walking code in rules, this is set to the tree walker that triggers actions. */ walker; currentRuleStack = new Array(); errorManager; constructor(factory) { this.factory = factory; this.errorManager = factory.g.tool.errorManager; } /** * Build a file with a parser containing rule functions. Use the controller as factory in SourceGenTriggers so * it triggers codegen extensions too, not just the factory functions in this factory. */ buildParserOutputModel(header, toolParameters) { const gen = this.factory.getGenerator(); const file = this.parserFile(gen.getRecognizerFileName(header), toolParameters); file.parser = this.parser(file); const g = this.factory.g; for (const r of g.rules.values()) { this.buildRuleFunction(file.parser, r); } return file; } buildLexerOutputModel(header, toolParameters) { const gen = this.factory.getGenerator(); const file = this.lexerFile(gen.getRecognizerFileName(header), toolParameters); file.lexer = this.lexer(file); const g = this.factory.g; for (const r of g.rules.values()) { this.buildLexerRuleActions(file.lexer, r); } return file; } buildListenerOutputModel(header) { const gen = this.factory.getGenerator(); return new ListenerFile(this.factory, gen.getListenerFileName(header)); } buildBaseListenerOutputModel(header) { const gen = this.factory.getGenerator(); return new BaseListenerFile(this.factory, gen.getBaseListenerFileName(header)); } buildVisitorOutputModel(header) { const gen = this.factory.getGenerator(); return new VisitorFile(this.factory, gen.getVisitorFileName(header)); } buildBaseVisitorOutputModel(header) { const gen = this.factory.getGenerator(); return new BaseVisitorFile(this.factory, gen.getBaseVisitorFileName(header)); } parserFile(fileName, toolParameters) { return this.factory.parserFile(fileName, toolParameters); } parser(file) { return this.factory.parser(file); } lexerFile(fileName, toolParameters) { return new LexerFile(this.factory, fileName, toolParameters); } lexer(file) { return new Lexer(this.factory, file); } /** * Create RuleFunction per rule and update sempreds,actions of parser output object with stuff found in r. */ buildRuleFunction(parser, r) { const ruleFunction = this.rule(r); parser.funcs.push(ruleFunction); this.pushCurrentRule(ruleFunction); ruleFunction.fillNamedActions(this.factory, r); if (r instanceof LeftRecursiveRule) { this.buildLeftRecursiveRuleFunction(r, ruleFunction); } else { this.buildNormalRuleFunction(r, ruleFunction); } const g = this.getGrammar(); for (const a of r.actions) { if (a instanceof PredAST) { const p = a; let rsf = parser.sempredFuncs.get(r); if (rsf === void 0) { rsf = new RuleSempredFunction(this.factory, r, ruleFunction.ctxType); parser.sempredFuncs.set(r, rsf); } rsf.actions.set(g.sempreds.get(p), new Action(this.factory, p)); } } this.popCurrentRule(); } buildLeftRecursiveRuleFunction(r, ruleFunction) { this.buildNormalRuleFunction(r, ruleFunction); const gen = this.factory.getGenerator(); const codegenTemplates = gen.templates; const outerAlt = ruleFunction.code[0]; const primaryAltsCode = new Array(); const primaryStuff = outerAlt.ops[0]; if (primaryStuff instanceof Choice) { const primaryAltBlock = primaryStuff; primaryAltsCode.push(...primaryAltBlock.alts); } else { primaryAltsCode.push(primaryStuff); } const opAltStarBlock = outerAlt.ops[1]; const altForOpAltBlock = opAltStarBlock.alts[0]; const opAltsCode = new Array(); const opStuff = altForOpAltBlock.ops[0]; if (opStuff instanceof AltBlock) { const opAltBlock = opStuff; opAltsCode.push(...opAltBlock.alts); } else { opAltsCode.push(opStuff); } for (let i = 0; i < primaryAltsCode.length; i++) { const altInfo = r.recPrimaryAlts[i]; if (altInfo.altLabel === void 0) { continue; } const altActionST = codegenTemplates.getInstanceOf("recRuleReplaceContext"); altActionST.add("ctxName", Utils.capitalize(altInfo.altLabel)); const altAction = new Action(this.factory, ruleFunction.altLabelCtxs.get(altInfo.altLabel), altActionST); const alt = primaryAltsCode[i]; alt.insertOp(0, altAction); } const setStopTokenAST = codegenTemplates.getInstanceOf("recRuleSetStopToken"); const setStopTokenAction = new Action(this.factory, ruleFunction.ruleCtx, setStopTokenAST); outerAlt.insertOp(1, setStopTokenAction); const setPrevCtx = codegenTemplates.getInstanceOf("recRuleSetPrevCtx"); const setPrevCtxAction = new Action(this.factory, ruleFunction.ruleCtx, setPrevCtx); opAltStarBlock.addIterationOp(setPrevCtxAction); for (let i = 0; i < opAltsCode.length; i++) { let altActionST; const altInfo = r.recOpAlts.getElement(i); let templateName; if (altInfo.altLabel !== void 0) { templateName = "recRuleLabeledAltStartAction"; altActionST = codegenTemplates.getInstanceOf(templateName); altActionST.add("currentAltLabel", altInfo.altLabel); } else { templateName = "recRuleAltStartAction"; altActionST = codegenTemplates.getInstanceOf(templateName); altActionST.add("ctxName", Utils.capitalize(r.name)); } altActionST.add("ruleName", r.name); altActionST.add("label", altInfo.leftRecursiveRuleRefLabel); if (altActionST.impl.formalArguments.has("isListLabel")) { altActionST.add("isListLabel", altInfo.isListLabel); } else if (altInfo.isListLabel) { this.errorManager.toolError(IssueCode.CodeTemaplateArgIssue, templateName, "isListLabel"); } const decl = ruleFunction.altLabelCtxs.get(altInfo.altLabel); const altAction = new Action(this.factory, decl, altActionST); opAltsCode[i].insertOp(0, altAction); } } buildNormalRuleFunction(r, ruleFunction) { const gen = this.factory.getGenerator(); const blk = r.ast.getFirstChildWithType(ANTLRv4Parser.BLOCK); const nodes = new CommonTreeNodeStream(blk); this.walker = new SourceGenTriggers(this.errorManager, nodes, this); ruleFunction.code = this.walker.block(null, null); ruleFunction.hasLookaheadBlock = this.walker.hasLookaheadBlock; ruleFunction.ctxType = gen.target.getRuleFunctionContextStructName(ruleFunction); ruleFunction.postamble = this.rulePostamble(ruleFunction, r); } buildLexerRuleActions(lexer, r) { if (r.actions.length === 0) { return; } const gen = this.factory.getGenerator(); const g = this.factory.g; const ctxType = gen.target.getRuleFunctionContextStructName(r); const raf = lexer.actionFuncs.get(r) ?? new RuleActionFunction(this.factory, r, ctxType); for (const a of r.actions) { if (a instanceof PredAST) { const p = a; let rsf = lexer.sempredFuncs.get(r); if (!rsf) { rsf = new RuleSempredFunction(this.factory, r, ctxType); lexer.sempredFuncs.set(r, rsf); } rsf.actions.set(g.sempreds.get(p), new Action(this.factory, p)); } else if (a.getType() === ANTLRv4Parser.ACTION) { raf.actions.set(g.lexerActions.get(a), new Action(this.factory, a)); } } if (raf.actions.size > 0 && !lexer.actionFuncs.has(r)) { lexer.actionFuncs.set(r, raf); } } rule(r) { return this.factory.rule(r); } rulePostamble(ruleFunction, r) { return this.factory.rulePostamble(ruleFunction, r); } getGrammar() { return this.factory.g; } alternative(alt, outerMost) { return this.factory.alternative(alt, outerMost); } finishAlternative(blk, ops, outerMost) { return this.factory.finishAlternative(blk, ops); } ruleRef(id, label, args) { return this.factory.ruleRef(id, label, args); } tokenRef(id, label, args) { return this.factory.tokenRef(id, label, args); } stringRef(id, label) { return this.factory.stringRef(id, label); } /** (A|B|C) possibly with ebnfRoot and label. */ set(setAST, labelAST, invert) { return this.factory.set(setAST, labelAST, invert); } epsilon(alt, outerMost) { return this.factory.epsilon(alt, outerMost); } wildcard(ast, labelAST) { return this.factory.wildcard(ast, labelAST); } action(ast) { return this.factory.action(ast); } sempred(ast) { return this.factory.sempred(ast); } getChoiceBlock(blkAST, alts, label) { return this.factory.getChoiceBlock(blkAST, alts, label); } getEBNFBlock(ebnfRoot, alts) { return this.factory.getEBNFBlock(ebnfRoot, alts); } needsImplicitLabel(id, op) { return this.factory.needsImplicitLabel(id, op); } get currentRuleFunction() { if (this.currentRuleStack.length > 0) { return this.currentRuleStack[this.currentRuleStack.length - 1]; } return void 0; } pushCurrentRule(r) { this.currentRuleStack.push(r); } popCurrentRule() { if (this.currentRuleStack.length > 0) { return this.currentRuleStack.pop(); } return null; } } export { OutputModelController };