UNPKG

@abaplint/transpiler

Version:
198 lines • 8.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Rearranger = void 0; const core_1 = require("@abaplint/core"); // this rearranges the AST to take precedence into account var EventKind; (function (EventKind) { EventKind[EventKind["None"] = 0] = "None"; EventKind[EventKind["Start"] = 1] = "Start"; EventKind[EventKind["End"] = 2] = "End"; EventKind[EventKind["AtLineSelection"] = 3] = "AtLineSelection"; EventKind[EventKind["AtSelectionScreen"] = 4] = "AtSelectionScreen"; })(EventKind || (EventKind = {})); class Rearranger { run(type, node) { if (!node) { return undefined; } if (type === "INTF") { // no arithmethic expressions in global interfaces return node; } const flattened = this.flatten(node); const rebuilt = this.rebuild(flattened); const rearranged = this.rearrangeListEvents(rebuilt); return rearranged; } /** Rearranges list event blocks so START-OF-SELECTION runs before END-OF-SELECTION, * matching ABAP runtime behavior regardless of source order */ rearrangeListEvents(node) { if (!(node.get() instanceof core_1.Structures.Any)) { return node; } const children = node.getChildren(); // Check if there are any list event statements const hasStartOfSelection = children.some(c => c instanceof core_1.Nodes.StatementNode && c.get() instanceof core_1.Statements.StartOfSelection); const hasEndOfSelection = children.some(c => c instanceof core_1.Nodes.StatementNode && c.get() instanceof core_1.Statements.EndOfSelection); const hasAtLineSelection = children.some(c => c instanceof core_1.Nodes.StatementNode && c.get() instanceof core_1.Statements.AtLineSelection); const hasAtSelectionScreen = children.some(c => c instanceof core_1.Nodes.StatementNode && c.get() instanceof core_1.Statements.AtSelectionScreen); if (!hasStartOfSelection && !hasEndOfSelection && !hasAtLineSelection && !hasAtSelectionScreen) { return node; } // Split children into: preamble (before first event), event blocks (keyed by event type) const preamble = []; const startBlocks = []; const endBlocks = []; // AT LINE-SELECTION and AT SELECTION-SCREEN are interactive events, their blocks are discarded let currentEvent = EventKind.None; for (const child of children) { if (child instanceof core_1.Nodes.StatementNode && child.get() instanceof core_1.Statements.StartOfSelection) { currentEvent = EventKind.Start; startBlocks.push(child); } else if (child instanceof core_1.Nodes.StatementNode && child.get() instanceof core_1.Statements.EndOfSelection) { currentEvent = EventKind.End; endBlocks.push(child); } else if (child instanceof core_1.Nodes.StatementNode && child.get() instanceof core_1.Statements.AtLineSelection) { currentEvent = EventKind.AtLineSelection; } else if (child instanceof core_1.Nodes.StatementNode && child.get() instanceof core_1.Statements.AtSelectionScreen) { currentEvent = EventKind.AtSelectionScreen; } else { switch (currentEvent) { case EventKind.None: preamble.push(child); break; case EventKind.Start: startBlocks.push(child); break; case EventKind.End: endBlocks.push(child); break; case EventKind.AtLineSelection: case EventKind.AtSelectionScreen: // discard: these are interactive events not executed in batch break; default: break; } } } // Reassemble: preamble, then START-OF-SELECTION blocks, then END-OF-SELECTION blocks // AT LINE-SELECTION and AT SELECTION-SCREEN blocks are omitted const newChildren = [...preamble, ...startBlocks, ...endBlocks]; node.setChildren(newChildren); return node; } ///////////////// rebuild(node) { if (node instanceof core_1.Nodes.TokenNode) { return node; } const children = node.getChildren(); children.forEach(this.rebuild.bind(this)); if (node instanceof core_1.Nodes.ExpressionNode) { this.precedence(node); } return node; } // this takes a single flattened node, and splits into binary nodes according to precedence and left to right processing precedence(node) { const children = node.getChildren(); const arith = node.findDirectExpressions(core_1.Expressions.ArithOperator); // after flattening it might have multiple operators under the samenode if (arith.length <= 1) { return; } let splitAt; // lowest precedence: +, - (skip **, *, /, MOD, DIV) for (let i = arith.length - 1; i >= 0; i--) { const a = arith[i]; const concat = a.concatTokens().toUpperCase(); if (concat === "*" || concat === "/" || concat === "**" || concat === "MOD" || concat === "DIV") { continue; } splitAt = a; break; } // medium precedence: *, /, MOD, DIV (skip only **) if (splitAt === undefined) { for (let i = arith.length - 1; i >= 0; i--) { const a = arith[i]; const concat = a.concatTokens().toUpperCase(); if (concat === "**") { continue; } splitAt = a; break; } } // fallback: all ** operators if (splitAt === undefined) { splitAt = arith[arith.length - 1]; } const index = children.indexOf(splitAt); let left = []; { const lhs = children.slice(0, index); if (lhs.length > 1) { const temp = new core_1.Nodes.ExpressionNode(node.get()); temp.setChildren(lhs); this.precedence(temp); left.push(temp); } else { left = lhs; } } let right = []; { const rhs = children.slice(index + 1); if (rhs.length > 1) { const temp = new core_1.Nodes.ExpressionNode(node.get()); temp.setChildren(rhs); this.precedence(temp); right.push(temp); } else { right = rhs; } } node.setChildren(left.concat([splitAt]).concat(right)); } // this flattens the arithmethic expressions so all related is under the same node flatten(node) { if (node instanceof core_1.Nodes.TokenNode) { return node; } const children = node.getChildren(); children.forEach(this.flatten.bind(this)); const last = children[children.length - 1]; const secondLast = children[children.length - 2]; if (last === undefined || secondLast === undefined || !(last instanceof core_1.Nodes.ExpressionNode) || !(last.get() instanceof core_1.Expressions.Source) || !(secondLast instanceof core_1.Nodes.ExpressionNode) || !(secondLast.get() instanceof core_1.Expressions.ArithOperator)) { return node; } const nestedArith = last.findDirectExpressions(core_1.Expressions.ArithOperator); if (nestedArith.length === 0) { return node; } const withoutLast = node.getChildren().slice(0, children.length - 1); const flat = withoutLast.concat(last.getChildren()); node.setChildren(flat); return node; } } exports.Rearranger = Rearranger; //# sourceMappingURL=rearranger.js.map