UNPKG

@abaplint/transpiler

Version:
292 lines • 15.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ValueBodyTranspiler = void 0; const core_1 = require("@abaplint/core"); const traversal_1 = require("../traversal"); const chunk_1 = require("../chunk"); const type_name_or_infer_1 = require("./type_name_or_infer"); const value_body_line_1 = require("./value_body_line"); const field_assignment_1 = require("./field_assignment"); const statements_1 = require("../statements"); const source_field_symbol_1 = require("./source_field_symbol"); const transpile_types_1 = require("../transpile_types"); const let_1 = require("./let"); class ValueBodyTranspiler { transpile(typ, body, traversal) { if (!(typ.get() instanceof core_1.Expressions.TypeNameOrInfer)) { throw new Error("ValueBodyTranspiler, Expected TypeNameOrInfer"); } let ret = new chunk_1.Chunk().appendString(new type_name_or_infer_1.TypeNameOrInfer().transpile(typ, traversal).getCode()); const context = new type_name_or_infer_1.TypeNameOrInfer().findType(typ, traversal); if (context instanceof core_1.BasicTypes.VoidType || context instanceof core_1.BasicTypes.UnknownType) { // compile option is runtime error, or it failed during the validation step return new chunk_1.Chunk(transpile_types_1.TranspileTypes.toType(context)); } let post = ""; let extraFields = ""; const hasLines = body.findDirectExpression(core_1.Expressions.ValueBodyLine) !== undefined; const children = body.getChildren(); for (let i = 0; i < children.length; i++) { const child = children[i]; if (child.get() instanceof core_1.Expressions.FieldAssignment && child instanceof core_1.Nodes.ExpressionNode) { const transpiled = new field_assignment_1.FieldAssignmentTranspiler().transpile(child, traversal, context).getCode(); if (hasLines === false) { ret.appendString(transpiled); } else { extraFields += transpiled; } } else if (child.get() instanceof core_1.Expressions.ValueBase && child instanceof core_1.Nodes.ExpressionNode) { const source = traversal.traverse(child.findDirectExpression(core_1.Expressions.Source)); ret = new chunk_1.Chunk().appendString(source.getCode() + ".clone()"); } else if (child.get() instanceof core_1.Expressions.ValueBodyLine && child instanceof core_1.Nodes.ExpressionNode) { if (!(context instanceof core_1.BasicTypes.TableType)) { throw new Error("ValueBodyTranspiler, Expected BasicTypes, " + body.concatTokens()); } const rowType = context.getRowType(); ret.appendString(new value_body_line_1.ValueBodyLineTranspiler().transpile(rowType, child, traversal, extraFields).getCode()); } else if (child.get() instanceof core_1.Expressions.Source && child instanceof core_1.Nodes.ExpressionNode) { const source = traversal.traverse(child); ret.appendString(".set(" + source.getCode() + ".clone())"); } else if (child.get() instanceof core_1.Expressions.For && child instanceof core_1.Nodes.ExpressionNode) { const forNodes = []; let idx = i; while (idx < children.length) { const candidate = children[idx]; if (candidate.get() instanceof core_1.Expressions.For && candidate instanceof core_1.Nodes.ExpressionNode) { forNodes.push(candidate); idx++; } else { break; } } i = idx - 1; const result = this.buildForChain(forNodes, typ, traversal, body); ret = result.chunk; post = result.post; } else if (child instanceof core_1.Nodes.TokenNode && child.getFirstToken().getStr().toUpperCase() === "DEFAULT") { // note: this is last in the body, so its okay to prepend and postpend const sources = body.findDirectExpressions(core_1.Expressions.Source); const deflt = traversal.traverse(sources[1]).getCode(); const pre = `(await (async () => { try { return `; ret = new chunk_1.Chunk().appendString(pre + ret.getCode()); post += `; } catch (error) { if (abap.isLineNotFound(error)) { return ${deflt}; } throw error; } })())`; } else if (child instanceof core_1.Nodes.TokenNode && child.getFirstToken().getStr().toUpperCase() === "OPTIONAL") { // note: this is last in the body, so its okay to prepend and postpend const pre = `(await (async () => { try { return `; ret = new chunk_1.Chunk().appendString(pre + ret.getCode()); post += `; } catch (error) { if (abap.isLineNotFound(error)) { return ${transpile_types_1.TranspileTypes.toType(context)}; } throw error; } })())`; } else { throw new Error("ValueBodyTranspiler, unknown " + child.get().constructor.name + " \"" + child.concatTokens()) + "\""; } } return ret.appendString(post); } buildForChain(forNodes, typ, traversal, body) { const val = new type_name_or_infer_1.TypeNameOrInfer().transpile(typ, traversal).getCode(); const chunk = new chunk_1.Chunk(); const preLoopDecls = []; const descriptors = []; const levelIndents = []; let uniqueCounter = 1; for (const child of forNodes) { const loop = child.findDirectExpression(core_1.Expressions.InlineLoopDefinition); if (loop) { if (["THEN", "UNTIL", "WHILE", "FROM", "TO", "GROUPS"].some(token => child.findDirectTokenByText(token))) { throw new Error("ValueBody FOR todo, " + body.concatTokens()); } let loopWhere = ""; const whereNode = child.findDirectExpression(core_1.Expressions.ComponentCond); if (whereNode) { const where = traversal.traverse(whereNode).getCode(); loopWhere = `, {"where": async ` + where + `}`; } const base = loop.findDirectExpression(core_1.Expressions.ValueBase); if (base) { throw new Error("ValueBody FOR todo, base, " + body.concatTokens()); } let targetDeclare = ""; let targetAction = ""; const fs = loop.findDirectExpression(core_1.Expressions.TargetFieldSymbol); const uniqueName = `unique${uniqueCounter++}`; if (fs) { targetDeclare = new statements_1.FieldSymbolTranspiler().transpile(fs, traversal).getCode(); const targetName = new source_field_symbol_1.SourceFieldSymbolTranspiler().transpile(fs, traversal).getCode(); targetAction = `${targetName}.assign(${uniqueName});`; } else { const field = traversal.traverse(loop.findDirectExpression(core_1.Expressions.TargetField)); if (field === undefined) { throw new Error("ValueBody FOR empty field todo, " + body.concatTokens()); } targetAction = `const ${field.getCode()} = ${uniqueName}.clone();`; } const llet = child.findDirectExpression(core_1.Expressions.Let); if (llet) { targetAction += new let_1.LetTranspiler().transpile(llet, traversal).getCode(); } const preBodyStatements = []; if (targetAction !== "") { preBodyStatements.push(targetAction); } const source = traversal.traverse(loop.findDirectExpression(core_1.Expressions.Source)).getCode(); if (targetDeclare !== "") { preLoopDecls.push(targetDeclare); } const descriptor = { beforeLoop: [], open: `for await (const ${uniqueName} of abap.statements.loop(${source}${loopWhere})) {`, preBody: preBodyStatements, postBody: [], close: "}", }; const loopTokens = loop.concatTokens().toUpperCase(); const indexTarget = loopTokens.includes("INDEX INTO") ? loop.findExpressionAfterToken("INTO") : undefined; if (indexTarget && indexTarget instanceof core_1.Nodes.ExpressionNode) { const indexCode = traversal.traverse(indexTarget).getCode(); const counterName = `unique${uniqueCounter++}`; descriptor.beforeLoop.push(`let ${counterName} = 1;`); descriptor.preBody.push(`const ${indexCode} = new abap.types.Integer().set(${counterName});`); descriptor.postBody.push(`${counterName}++;`); } descriptors.push(descriptor); } else { const counter = child.findDirectExpression(core_1.Expressions.InlineFieldDefinition); if (counter === undefined) { throw new Error("ValueBody FOR todo, " + body.concatTokens()); } if (["GROUPS", "FROM", "TO"].some(token => child.findDirectTokenByText(token))) { throw new Error("ValueBody FOR todo, " + body.concatTokens()); } if (child.findDirectExpression(core_1.Expressions.ComponentCond)) { throw new Error("ValueBody FOR todo, component cond, " + body.concatTokens()); } const cond = child.findDirectExpression(core_1.Expressions.Cond); if (cond === undefined) { throw new Error("ValueBody FOR missing condition, " + body.concatTokens()); } const hasUntil = child.findDirectTokenByText("UNTIL") !== undefined; const hasWhile = child.findDirectTokenByText("WHILE") !== undefined; if ((hasUntil ? 1 : 0) + (hasWhile ? 1 : 0) !== 1) { throw new Error("ValueBody FOR todo, condition, " + body.concatTokens()); } const fieldExpr = counter.findDirectExpression(core_1.Expressions.Field); const fieldName = fieldExpr?.concatTokens().toLowerCase(); if (fieldName === undefined) { throw new Error("ValueBody FOR todo, inline field, " + body.concatTokens()); } const scope = traversal.findCurrentScopeByToken(counter.getFirstToken()); const variable = scope?.findVariable(fieldName); if (variable === undefined) { throw new Error("ValueBody FOR todo, variable, " + body.concatTokens()); } const declare = transpile_types_1.TranspileTypes.declare(variable); const counterName = traversal_1.Traversal.prefixVariable(fieldName); const startSource = counter.findDirectExpression(core_1.Expressions.Source); if (startSource === undefined) { throw new Error("ValueBody FOR missing initial value, " + body.concatTokens()); } const start = traversal.traverse(startSource).getCode(); const thenExpr = child.findExpressionAfterToken("THEN"); let incrementExpression = ""; if (thenExpr && thenExpr instanceof core_1.Nodes.ExpressionNode) { incrementExpression = traversal.traverse(thenExpr).getCode(); } else { incrementExpression = `abap.operators.add(${counterName}, new abap.types.Integer().set(1))`; } const incrementLine = `${counterName}.set(${incrementExpression});`; const llet = child.findDirectExpression(core_1.Expressions.Let); let letCode = ""; if (llet) { letCode = new let_1.LetTranspiler().transpile(llet, traversal).getCode(); } const condCode = traversal.traverse(cond).getCode(); const preCheck = hasWhile ? `if (!(${condCode})) {\n break;\n}` : ""; const postLoop = []; postLoop.push(incrementLine); if (hasUntil) { postLoop.push(`if (${condCode}) {\n break;\n}`); } descriptors.push({ beforeLoop: [declare, `${counterName}.set(${start});`], open: "while (true) {", preBody: [preCheck, letCode].filter(s => s !== ""), postBody: postLoop, close: "}", }); } } if (descriptors.length === 0) { throw new Error("ValueBodyTranspiler FOR internal error"); } chunk.appendString("await (async () => {\n"); this.appendBlocks(chunk, preLoopDecls, ""); chunk.appendString(`const VAL = ${val};\n`); let indent = ""; for (const desc of descriptors) { this.appendBlocks(chunk, desc.beforeLoop, indent); chunk.appendString(indent + desc.open + "\n"); indent += " "; levelIndents.push(indent); this.appendBlocks(chunk, desc.preBody, indent); } chunk.appendString(indent + "VAL"); let post = ";\n"; for (let i = descriptors.length - 1; i >= 0; i--) { const desc = descriptors[i]; const currentIndent = levelIndents[i] ?? ""; post += this.blocksToString(desc.postBody, currentIndent); const parentIndent = currentIndent.substring(0, Math.max(0, currentIndent.length - 2)); post += parentIndent + desc.close + "\n"; } post += "return VAL;\n})()"; return { chunk, post }; } appendBlocks(chunk, blocks, indent) { for (const block of blocks) { this.appendBlock(chunk, block, indent); } } appendBlock(chunk, block, indent) { if (block === "") { return; } const lines = block.split("\n"); for (const line of lines) { const clean = line.replace(/\r/g, ""); if (clean.trim() === "") { continue; } chunk.appendString(indent + clean + "\n"); } } blocksToString(blocks, indent) { let ret = ""; for (const block of blocks) { if (block === "") { continue; } const lines = block.split("\n"); for (const line of lines) { const clean = line.replace(/\r/g, ""); if (clean.trim() === "") { continue; } ret += indent + clean + "\n"; } } return ret; } } exports.ValueBodyTranspiler = ValueBodyTranspiler; //# sourceMappingURL=value_body.js.map