@abaplint/core
Version:
abaplint - Core API
229 lines • 9.3 kB
JavaScript
;
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