@abaplint/transpiler
Version:
198 lines • 8.35 kB
JavaScript
;
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