UNPKG

@abaplint/core

Version:
229 lines • 9.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExpandMacros = void 0; const Statements = require("./statements"); const Expressions = require("./expressions"); const Tokens = require("../1_lexer/tokens"); const _statement_1 = require("./statements/_statement"); const statement_node_1 = require("../nodes/statement_node"); const token_node_1 = require("../nodes/token_node"); const statement_parser_1 = require("./statement_parser"); const memory_file_1 = require("../../files/memory_file"); const lexer_1 = require("../1_lexer/lexer"); const virtual_position_1 = require("../../virtual_position"); class Macros { constructor(globalMacros) { this.macros = {}; for (const m of globalMacros) { this.macros[m.toUpperCase()] = { statements: [], filename: undefined, }; } } addMacro(name, contents, filename) { if (this.isMacro(name)) { return; } this.macros[name.toUpperCase()] = { statements: contents, filename: filename, }; } getContents(name) { return this.macros[name.toUpperCase()].statements; } listMacroNames() { return Object.keys(this.macros); } isMacro(name) { if (this.macros[name.toUpperCase()]) { return true; } return false; } getMacroFilename(name) { return this.macros[name.toUpperCase()].filename; } } class ExpandMacros { // "reg" must be supplied if there are cross object macros via INCLUDE constructor(globalMacros, version, reg) { this.macros = new Macros(globalMacros); this.version = version; this.globalMacros = globalMacros; this.reg = reg; } find(statements, file, clear = true) { var _a, _b, _c; let nameToken = undefined; let start = undefined; let contents = []; const macroReferences = (_a = this.reg) === null || _a === void 0 ? void 0 : _a.getMacroReferences(); if (clear) { macroReferences === null || macroReferences === void 0 ? void 0 : macroReferences.clear(file.getFilename()); } for (let i = 0; i < statements.length; i++) { const statement = statements[i]; const type = statement.get(); if (type instanceof Statements.Define) { // todo, will this break if first token is a pragma? nameToken = statement.getTokens()[1]; start = statement.getFirstToken().getStart(); contents = []; } else if (type instanceof Statements.Include) { const includeName = (_b = statement.findDirectExpression(Expressions.IncludeName)) === null || _b === void 0 ? void 0 : _b.concatTokens(); // todo, this does not take function module includes into account // todo, workaround for cyclic includes? const prog = (_c = this.reg) === null || _c === void 0 ? void 0 : _c.getObject("PROG", includeName); if (prog) { prog.parse(this.version, this.globalMacros, this.reg); const includeMainFile = prog.getMainABAPFile(); if (includeMainFile) { // slow, this copies everything, this.find([...includeMainFile.getStatements()], includeMainFile, false); } } } else if (nameToken) { if (type instanceof Statements.EndOfDefinition) { this.macros.addMacro(nameToken.getStr(), contents, file.getFilename()); macroReferences === null || macroReferences === void 0 ? void 0 : macroReferences.addDefinition({ filename: file.getFilename(), token: nameToken }, start, statement.getLastToken().getEnd()); nameToken = undefined; } else if (!(type instanceof _statement_1.Comment)) { statements[i] = new statement_node_1.StatementNode(new _statement_1.MacroContent()).setChildren(this.tokensToNodes(statement.getTokens())); contents.push(statements[i]); } } } } handleMacros(statements, file) { var _a; const result = []; let containsUnknown = false; const macroReferences = (_a = this.reg) === null || _a === void 0 ? void 0 : _a.getMacroReferences(); for (const statement of statements) { const type = statement.get(); if (type instanceof _statement_1.Unknown || type instanceof _statement_1.MacroCall) { const macroName = this.findName(statement.getTokens()); if (macroName && this.macros.isMacro(macroName)) { const filename = this.macros.getMacroFilename(macroName); if (filename) { macroReferences === null || macroReferences === void 0 ? void 0 : macroReferences.addReference({ filename: filename, token: statement.getFirstToken(), }); } result.push(new statement_node_1.StatementNode(new _statement_1.MacroCall(), statement.getColon()).setChildren(this.tokensToNodes(statement.getTokens()))); const expanded = this.expandContents(macroName, statement); const handled = this.handleMacros(expanded, file); for (const e of handled.statements) { result.push(e); } if (handled.containsUnknown === true) { containsUnknown = true; } continue; } else { containsUnknown = true; } } result.push(statement); } return { statements: result, containsUnknown }; } ////////////// expandContents(name, statement) { const contents = this.macros.getContents(name); if (contents === undefined || contents.length === 0) { return []; } let str = ""; for (const c of contents) { let concat = c.concatTokens(); if (c.getTerminator() === ",") { // workaround for chained statements concat = concat.replace(/,$/, "."); } str += concat + "\n"; } const inputs = this.buildInput(statement); let i = 1; for (const input of inputs) { const search = "&" + i; const reg = new RegExp(search, "g"); str = str.replace(reg, input); i++; } const file = new memory_file_1.MemoryFile("expand_macros.abap.prog", str); const lexerResult = new lexer_1.Lexer().run(file, statement.getFirstToken().getStart()); const result = new statement_parser_1.StatementParser(this.version, this.reg).run([lexerResult], this.macros.listMacroNames()); return result[0].statements; } buildInput(statement) { const result = []; const tokens = statement.getTokens(); let build = ""; for (let i = 1; i < tokens.length - 1; i++) { const now = tokens[i]; let next = tokens[i + 1]; if (i + 2 === tokens.length) { next = undefined; // dont take the punctuation } // argh, macros is a nightmare let end = now.getStart(); if (end instanceof virtual_position_1.VirtualPosition) { end = new virtual_position_1.VirtualPosition(end, end.vrow, end.vcol + now.getStr().length); } else { end = now.getEnd(); } if (next && next.getStart().equals(end)) { build += now.getStr(); } else { build += now.getStr(); result.push(build); build = ""; } } return result; } findName(tokens) { let macroName = undefined; let previous = undefined; for (const i of tokens) { if (previous && (previous === null || previous === void 0 ? void 0 : previous.getEnd().getCol()) !== i.getStart().getCol()) { break; } else if (i instanceof Tokens.Identifier || i.getStr() === "-") { if (macroName === undefined) { macroName = i.getStr(); } else { macroName += i.getStr(); } } else if (i instanceof Tokens.Pragma) { continue; } else { break; } previous = i; } return macroName; } tokensToNodes(tokens) { const ret = []; for (const t of tokens) { ret.push(new token_node_1.TokenNode(t)); } return ret; } } exports.ExpandMacros = ExpandMacros; //# sourceMappingURL=expand_macros.js.map