@abaplint/transpiler 2.13.48 → 2.13.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -3,4 +3,9 @@ import { Traversal } from "../traversal";
|
|
|
3
3
|
import { Chunk } from "../chunk";
|
|
4
4
|
export declare class FilterBodyTranspiler {
|
|
5
5
|
transpile(typ: Nodes.ExpressionNode, body: Nodes.ExpressionNode, traversal: Traversal): Chunk;
|
|
6
|
+
private transpileSingle;
|
|
7
|
+
private transpileIn;
|
|
8
|
+
private transpileInCondition;
|
|
9
|
+
private transpileInCompare;
|
|
10
|
+
private filterOperand;
|
|
6
11
|
}
|
|
@@ -2,39 +2,151 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FilterBodyTranspiler = void 0;
|
|
4
4
|
const core_1 = require("@abaplint/core");
|
|
5
|
+
const traversal_1 = require("../traversal");
|
|
5
6
|
const chunk_1 = require("../chunk");
|
|
6
7
|
const type_name_or_infer_1 = require("./type_name_or_infer");
|
|
7
8
|
const transpile_types_1 = require("../transpile_types");
|
|
8
9
|
const unique_identifier_1 = require("../unique_identifier");
|
|
10
|
+
const component_chain_simple_1 = require("./component_chain_simple");
|
|
9
11
|
class FilterBodyTranspiler {
|
|
10
12
|
transpile(typ, body, traversal) {
|
|
11
13
|
if (!(typ.get() instanceof core_1.Expressions.TypeNameOrInfer)) {
|
|
12
14
|
throw new Error("FilterBodyTranspiler, Expected TypeNameOrInfer");
|
|
13
15
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
const sources = body.findDirectExpressions(core_1.Expressions.Source);
|
|
17
|
+
if (sources.length === 0) {
|
|
18
|
+
throw new Error("FilterBodyTranspiler, source not found");
|
|
16
19
|
}
|
|
17
|
-
const source = traversal.traverse(
|
|
20
|
+
const source = traversal.traverse(sources[0]).getCode();
|
|
18
21
|
const type = new type_name_or_infer_1.TypeNameOrInfer().findType(typ, traversal);
|
|
19
22
|
const target = transpile_types_1.TranspileTypes.toType(type);
|
|
23
|
+
const whereNode = body.findDirectExpression(core_1.Expressions.ComponentCond);
|
|
24
|
+
if (whereNode === undefined) {
|
|
25
|
+
throw new Error("FilterBodyTranspiler, WHERE not found");
|
|
26
|
+
}
|
|
27
|
+
if (sources.length > 1) {
|
|
28
|
+
return this.transpileIn(target, source, traversal.traverse(sources[1]).getCode(), whereNode, body.findDirectTokenByText("EXCEPT") !== undefined, body, traversal);
|
|
29
|
+
}
|
|
30
|
+
return this.transpileSingle(target, source, whereNode, body.findDirectTokenByText("EXCEPT") !== undefined, body, traversal);
|
|
31
|
+
}
|
|
32
|
+
transpileSingle(target, source, whereNode, except, body, traversal) {
|
|
33
|
+
const result = unique_identifier_1.UniqueIdentifier.get();
|
|
34
|
+
const row = unique_identifier_1.UniqueIdentifier.get();
|
|
35
|
+
const where = traversal.traverse(whereNode).getCode();
|
|
36
|
+
const options = [];
|
|
37
|
+
options.push(except ? `where: async (I) => !((${where})(I))` : `where: async ${where}`);
|
|
38
|
+
const key = body.findDirectExpression(core_1.Expressions.SimpleName);
|
|
39
|
+
if (key) {
|
|
40
|
+
options.push(`usingKey: "${key.concatTokens().toLowerCase()}"`);
|
|
41
|
+
}
|
|
20
42
|
const ret = new chunk_1.Chunk();
|
|
21
43
|
ret.appendString("(await (async () => {\n");
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
const id = unique_identifier_1.UniqueIdentifier.get();
|
|
29
|
-
const loop = unique_identifier_1.UniqueIdentifier.get();
|
|
30
|
-
ret.appendString(`const ${id} = ${target};\n`);
|
|
31
|
-
ret.appendString(`for await (const ${loop} of abap.statements.loop(${source}${loopWhere})) {\n`);
|
|
32
|
-
ret.appendString(`abap.statements.insertInternal({"table": ${id}, "data": ${loop}});\n`);
|
|
33
|
-
ret.appendString(`}\n`);
|
|
34
|
-
ret.appendString(`return ${id};\n`);
|
|
44
|
+
ret.appendString(`const ${result} = ${target};\n`);
|
|
45
|
+
ret.appendString(`for await (const ${row} of abap.statements.loop(${source}, {${options.join(", ")}})) {\n`);
|
|
46
|
+
ret.appendString(`abap.statements.insertInternal({"table": ${result}, "data": ${row}});\n`);
|
|
47
|
+
ret.appendString("}\n");
|
|
48
|
+
ret.appendString(`return ${result};\n`);
|
|
35
49
|
ret.appendString("})())");
|
|
36
50
|
return ret;
|
|
37
51
|
}
|
|
52
|
+
transpileIn(target, source, filterSource, whereNode, except, body, traversal) {
|
|
53
|
+
const result = unique_identifier_1.UniqueIdentifier.get();
|
|
54
|
+
const sourceRow = unique_identifier_1.UniqueIdentifier.get();
|
|
55
|
+
const filterRow = unique_identifier_1.UniqueIdentifier.get();
|
|
56
|
+
const matched = unique_identifier_1.UniqueIdentifier.get();
|
|
57
|
+
const condition = this.transpileInCondition(whereNode, traversal, sourceRow, filterRow);
|
|
58
|
+
const key = body.findDirectExpression(core_1.Expressions.SimpleName);
|
|
59
|
+
const filterOptions = key ? `, {usingKey: "${key.concatTokens().toLowerCase()}"}` : "";
|
|
60
|
+
const selection = except ? `!${matched}` : matched;
|
|
61
|
+
const ret = new chunk_1.Chunk();
|
|
62
|
+
ret.appendString("(await (async () => {\n");
|
|
63
|
+
ret.appendString(`const ${result} = ${target};\n`);
|
|
64
|
+
ret.appendString(`for await (const ${sourceRow} of abap.statements.loop(${source})) {\n`);
|
|
65
|
+
ret.appendString(`let ${matched} = false;\n`);
|
|
66
|
+
ret.appendString(`for await (const ${filterRow} of abap.statements.loop(${filterSource}${filterOptions})) {\n`);
|
|
67
|
+
ret.appendString(`if (${condition}) {\n`);
|
|
68
|
+
ret.appendString(`${matched} = true;\n`);
|
|
69
|
+
ret.appendString("break;\n}\n}\n");
|
|
70
|
+
ret.appendString(`if (${selection}) {\n`);
|
|
71
|
+
ret.appendString(`abap.statements.insertInternal({"table": ${result}, "data": ${sourceRow}});\n`);
|
|
72
|
+
ret.appendString("}\n}\n");
|
|
73
|
+
ret.appendString(`return ${result};\n`);
|
|
74
|
+
ret.appendString("})())");
|
|
75
|
+
return ret;
|
|
76
|
+
}
|
|
77
|
+
transpileInCondition(node, traversal, sourceRow, filterRow) {
|
|
78
|
+
if (node.get() instanceof core_1.Expressions.ComponentCompare) {
|
|
79
|
+
return this.transpileInCompare(node, traversal, sourceRow, filterRow);
|
|
80
|
+
}
|
|
81
|
+
let ret = "";
|
|
82
|
+
for (const child of node.getChildren()) {
|
|
83
|
+
if (child instanceof core_1.Nodes.ExpressionNode) {
|
|
84
|
+
ret += this.transpileInCondition(child, traversal, sourceRow, filterRow);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
switch (child.concatTokens().toUpperCase()) {
|
|
88
|
+
case "AND":
|
|
89
|
+
ret += " && ";
|
|
90
|
+
break;
|
|
91
|
+
case "OR":
|
|
92
|
+
ret += " || ";
|
|
93
|
+
break;
|
|
94
|
+
case "NOT":
|
|
95
|
+
ret += "!";
|
|
96
|
+
break;
|
|
97
|
+
case "(":
|
|
98
|
+
ret += "(";
|
|
99
|
+
break;
|
|
100
|
+
case ")":
|
|
101
|
+
ret += ")";
|
|
102
|
+
break;
|
|
103
|
+
default: throw new Error("FilterBodyTranspiler, unexpected condition token " + child.concatTokens());
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return ret;
|
|
108
|
+
}
|
|
109
|
+
transpileInCompare(node, traversal, sourceRow, filterRow) {
|
|
110
|
+
const leftNode = node.findDirectExpression(core_1.Expressions.ComponentChainSimple);
|
|
111
|
+
if (leftNode === undefined) {
|
|
112
|
+
throw new Error("FilterBodyTranspiler, comparison component not found");
|
|
113
|
+
}
|
|
114
|
+
const left = new component_chain_simple_1.ComponentChainSimpleTranspiler(`${sourceRow}.get().`).transpile(leftNode, traversal).getCode();
|
|
115
|
+
const sources = node.findDirectExpressions(core_1.Expressions.Source);
|
|
116
|
+
const concat = node.concatTokens().toUpperCase();
|
|
117
|
+
const negate = concat.startsWith("NOT ") ? "!" : "";
|
|
118
|
+
const operator = node.findDirectExpression(core_1.Expressions.CompareOperator);
|
|
119
|
+
if (operator && sources[0]) {
|
|
120
|
+
const compare = traversal.traverse(operator).getCode();
|
|
121
|
+
return `${negate}abap.compare.${compare}(${left}, ${this.filterOperand(sources[0], traversal, filterRow)})`;
|
|
122
|
+
}
|
|
123
|
+
if (concat.includes(" BETWEEN ") && sources.length === 2) {
|
|
124
|
+
const between = `abap.compare.ge(${left}, ${this.filterOperand(sources[0], traversal, filterRow)}) && `
|
|
125
|
+
+ `abap.compare.le(${left}, ${this.filterOperand(sources[1], traversal, filterRow)})`;
|
|
126
|
+
return concat.includes(" NOT BETWEEN ") ? `!(${between})` : `(${between})`;
|
|
127
|
+
}
|
|
128
|
+
if (concat.endsWith("IS INITIAL")) {
|
|
129
|
+
return `${negate}abap.compare.initial(${left})`;
|
|
130
|
+
}
|
|
131
|
+
else if (concat.endsWith("IS NOT INITIAL")) {
|
|
132
|
+
return `!abap.compare.initial(${left})`;
|
|
133
|
+
}
|
|
134
|
+
throw new Error("FilterBodyTranspiler, unsupported IN comparison " + node.concatTokens());
|
|
135
|
+
}
|
|
136
|
+
filterOperand(source, traversal, filterRow) {
|
|
137
|
+
const code = traversal.traverse(source).getCode();
|
|
138
|
+
const sourceField = source.findFirstExpression(core_1.Expressions.SourceField);
|
|
139
|
+
const name = sourceField?.findDirectExpression(core_1.Expressions.Field)?.concatTokens();
|
|
140
|
+
if (name === undefined) {
|
|
141
|
+
return code;
|
|
142
|
+
}
|
|
143
|
+
const escaped = traversal_1.Traversal.escapeNamespace(name)?.replace("~", "$").toLowerCase();
|
|
144
|
+
const variable = traversal_1.Traversal.prefixVariable(traversal_1.Traversal.escapeNamespace(name));
|
|
145
|
+
if (escaped === undefined || code.startsWith(variable) === false) {
|
|
146
|
+
return code;
|
|
147
|
+
}
|
|
148
|
+
return `${filterRow}.get().${escaped}` + code.substring(variable.length);
|
|
149
|
+
}
|
|
38
150
|
}
|
|
39
151
|
exports.FilterBodyTranspiler = FilterBodyTranspiler;
|
|
40
152
|
//# sourceMappingURL=filter_body.js.map
|
|
@@ -3,7 +3,11 @@ import { Traversal } from "../traversal";
|
|
|
3
3
|
import { Chunk } from "../chunk";
|
|
4
4
|
export declare class ReduceBodyTranspiler {
|
|
5
5
|
transpile(typ: Nodes.ExpressionNode, body: Nodes.ExpressionNode, traversal: Traversal): Chunk;
|
|
6
|
-
private
|
|
6
|
+
private describeFor;
|
|
7
|
+
private describeGroupsFor;
|
|
8
|
+
private describeIndexFor;
|
|
7
9
|
private declareInit;
|
|
8
10
|
private transpileNext;
|
|
11
|
+
private appendBlocks;
|
|
12
|
+
private appendBlock;
|
|
9
13
|
}
|
|
@@ -6,141 +6,278 @@ const traversal_1 = require("../traversal");
|
|
|
6
6
|
const chunk_1 = require("../chunk");
|
|
7
7
|
const transpile_types_1 = require("../transpile_types");
|
|
8
8
|
const target_1 = require("./target");
|
|
9
|
+
const let_1 = require("./let");
|
|
10
|
+
const statements_1 = require("../statements");
|
|
11
|
+
const source_field_symbol_1 = require("./source_field_symbol");
|
|
12
|
+
const unique_identifier_1 = require("../unique_identifier");
|
|
9
13
|
class ReduceBodyTranspiler {
|
|
10
14
|
transpile(typ, body, traversal) {
|
|
11
15
|
if (!(typ.get() instanceof core_1.Expressions.TypeNameOrInfer)) {
|
|
12
16
|
throw new Error("ReduceBodyTranspiler, Expected TypeNameOrInfer");
|
|
13
17
|
}
|
|
14
|
-
else if (body.findDirectExpression(core_1.Expressions.Let) !== undefined) {
|
|
15
|
-
return new chunk_1.Chunk(`(() => { throw new Error("ReduceBodyTranspiler LET, not supported, transpiler"); })()`);
|
|
16
|
-
}
|
|
17
18
|
const forExpressions = body.findDirectExpressions(core_1.Expressions.For);
|
|
18
|
-
const forExpression = forExpressions[0];
|
|
19
19
|
if (forExpressions.length === 0) {
|
|
20
20
|
throw new Error("ReduceBodyTranspiler, expected FOR");
|
|
21
21
|
}
|
|
22
|
-
else if (forExpressions.length > 1) {
|
|
23
|
-
throw new Error("ReduceBodyTranspiler, multiple FOR not supported, " + body.concatTokens());
|
|
24
|
-
}
|
|
25
|
-
const loopExpression = forExpression.findDirectExpression(core_1.Expressions.InlineLoopDefinition);
|
|
26
|
-
if (loopExpression === undefined) {
|
|
27
|
-
// index based FOR, eg. "FOR i = 1 WHILE i <= 5"
|
|
28
|
-
return this.transpileIndex(body, forExpression, traversal);
|
|
29
|
-
}
|
|
30
|
-
else if (["THEN", "UNTIL", "WHILE", "FROM", "TO", "GROUPS"].some(token => forExpression.findDirectTokenByText(token))) {
|
|
31
|
-
throw new Error("ValueBody FOR todo, " + body.concatTokens());
|
|
32
|
-
}
|
|
33
|
-
const loopSource = traversal.traverse(loopExpression?.findDirectExpression(core_1.Expressions.Source)).getCode();
|
|
34
|
-
const loopVariable = traversal.traverse(loopExpression?.findDirectExpression(core_1.Expressions.TargetField)
|
|
35
|
-
|| loopExpression?.findDirectExpression(core_1.Expressions.TargetFieldSymbol)).getCode();
|
|
36
|
-
// const type = new TypeNameOrInfer().findType(typ, traversal);
|
|
37
|
-
// const target = TranspileTypes.toType(type);
|
|
38
22
|
const ret = new chunk_1.Chunk();
|
|
39
23
|
ret.appendString("(await (async () => {\n");
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const where = traversal.traverse(whereNode).getCode();
|
|
44
|
-
loopWhere = `, {"where": async ` + where + `}`;
|
|
24
|
+
const outerLet = body.findDirectExpression(core_1.Expressions.Let);
|
|
25
|
+
if (outerLet) {
|
|
26
|
+
ret.appendString(new let_1.LetTranspiler().transpile(outerLet, traversal).getCode() + "\n");
|
|
45
27
|
}
|
|
46
|
-
/*
|
|
47
|
-
const returnId = UniqueIdentifier.get();
|
|
48
|
-
ret.appendString(`const ${returnId} = ${target};\n`);
|
|
49
|
-
*/
|
|
50
28
|
const returnField = this.declareInit(body, traversal, ret);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
const declarations = [];
|
|
30
|
+
const descriptors = forExpressions.map(forExpression => this.describeFor(forExpression, body, traversal, declarations));
|
|
31
|
+
for (const declaration of declarations) {
|
|
32
|
+
ret.appendString(declaration + "\n");
|
|
33
|
+
}
|
|
34
|
+
let indent = "";
|
|
35
|
+
const levelIndents = [];
|
|
36
|
+
for (const descriptor of descriptors) {
|
|
37
|
+
this.appendBlocks(ret, descriptor.beforeLoop, indent);
|
|
38
|
+
ret.appendString(indent + descriptor.open + "\n");
|
|
39
|
+
indent += " ";
|
|
40
|
+
levelIndents.push(indent);
|
|
41
|
+
this.appendBlocks(ret, descriptor.preBody, indent);
|
|
42
|
+
}
|
|
43
|
+
this.appendBlock(ret, this.transpileNext(body, traversal), indent);
|
|
44
|
+
for (let i = descriptors.length - 1; i >= 0; i--) {
|
|
45
|
+
const descriptor = descriptors[i];
|
|
46
|
+
const currentIndent = levelIndents[i];
|
|
47
|
+
this.appendBlocks(ret, descriptor.postBody, currentIndent);
|
|
48
|
+
indent = currentIndent.substring(0, Math.max(0, currentIndent.length - 2));
|
|
49
|
+
ret.appendString(indent + descriptor.close + "\n");
|
|
50
|
+
}
|
|
54
51
|
ret.appendString(`return ${returnField};\n`);
|
|
55
52
|
ret.appendString("})())");
|
|
56
53
|
return ret;
|
|
57
54
|
}
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
55
|
+
describeFor(forExpression, body, traversal, declarations) {
|
|
56
|
+
if (forExpression.findDirectTokenByText("GROUPS")) {
|
|
57
|
+
return this.describeGroupsFor(forExpression, body, traversal);
|
|
61
58
|
}
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
|
|
59
|
+
const loopExpression = forExpression.findDirectExpression(core_1.Expressions.InlineLoopDefinition);
|
|
60
|
+
if (loopExpression === undefined) {
|
|
61
|
+
return this.describeIndexFor(forExpression, body, traversal);
|
|
62
|
+
}
|
|
63
|
+
const sourceNode = loopExpression.findDirectExpression(core_1.Expressions.Source);
|
|
64
|
+
if (sourceNode === undefined) {
|
|
65
|
+
throw new Error("ReduceBodyTranspiler FOR missing source, " + body.concatTokens());
|
|
66
|
+
}
|
|
67
|
+
const loopSource = traversal.traverse(sourceNode).getCode();
|
|
68
|
+
const options = [];
|
|
69
|
+
const whereNode = forExpression.findDirectExpression(core_1.Expressions.ComponentCond);
|
|
70
|
+
if (whereNode) {
|
|
71
|
+
options.push("where: async " + traversal.traverse(whereNode).getCode());
|
|
72
|
+
}
|
|
73
|
+
const fromNode = forExpression.findExpressionAfterToken("FROM");
|
|
74
|
+
if (fromNode && fromNode instanceof core_1.Nodes.ExpressionNode) {
|
|
75
|
+
options.push("from: " + traversal.traverse(fromNode).getCode());
|
|
76
|
+
}
|
|
77
|
+
const toNode = forExpression.findExpressionAfterToken("TO");
|
|
78
|
+
if (toNode && toNode instanceof core_1.Nodes.ExpressionNode) {
|
|
79
|
+
options.push("to: " + traversal.traverse(toNode).getCode());
|
|
80
|
+
}
|
|
81
|
+
const keyNode = loopExpression.findExpressionAfterToken("KEY");
|
|
82
|
+
if (keyNode) {
|
|
83
|
+
options.push(`usingKey: "${keyNode.concatTokens().toLowerCase()}"`);
|
|
84
|
+
}
|
|
85
|
+
const unique = unique_identifier_1.UniqueIdentifier.get();
|
|
86
|
+
const preBody = [];
|
|
87
|
+
const postBody = [];
|
|
88
|
+
const fieldSymbol = loopExpression.findDirectExpression(core_1.Expressions.TargetFieldSymbol);
|
|
89
|
+
if (fieldSymbol) {
|
|
90
|
+
declarations.push(new statements_1.FieldSymbolTranspiler().transpile(fieldSymbol, traversal).getCode());
|
|
91
|
+
const target = new source_field_symbol_1.SourceFieldSymbolTranspiler().transpile(fieldSymbol, traversal).getCode();
|
|
92
|
+
preBody.push(`${target}.assign(${unique});`);
|
|
93
|
+
postBody.push(`${target}.unassign();`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const field = loopExpression.findDirectExpression(core_1.Expressions.TargetField);
|
|
97
|
+
if (field === undefined) {
|
|
98
|
+
throw new Error("ReduceBodyTranspiler FOR missing target, " + body.concatTokens());
|
|
99
|
+
}
|
|
100
|
+
preBody.push(`const ${traversal.traverse(field).getCode()} = ${unique}.clone();`);
|
|
101
|
+
}
|
|
102
|
+
const indexTarget = loopExpression.findExpressionAfterToken("INTO");
|
|
103
|
+
const beforeLoop = [];
|
|
104
|
+
if (indexTarget && indexTarget instanceof core_1.Nodes.ExpressionNode) {
|
|
105
|
+
const indexName = unique_identifier_1.UniqueIdentifier.get();
|
|
106
|
+
const indexCode = traversal.traverse(indexTarget).getCode();
|
|
107
|
+
beforeLoop.push(`let ${indexName} = 1;`);
|
|
108
|
+
preBody.push(`const ${indexCode} = new abap.types.Integer().set(${indexName});`);
|
|
109
|
+
postBody.push(`${indexName}++;`);
|
|
110
|
+
}
|
|
111
|
+
const letNode = forExpression.findDirectExpression(core_1.Expressions.Let);
|
|
112
|
+
if (letNode) {
|
|
113
|
+
preBody.push(new let_1.LetTranspiler().transpile(letNode, traversal).getCode());
|
|
65
114
|
}
|
|
115
|
+
const opts = options.length === 0 ? "" : `, {${options.join(", ")}}`;
|
|
116
|
+
return {
|
|
117
|
+
beforeLoop,
|
|
118
|
+
open: `for await (const ${unique} of abap.statements.loop(${loopSource}${opts})) {`,
|
|
119
|
+
preBody,
|
|
120
|
+
postBody,
|
|
121
|
+
close: "}",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
describeGroupsFor(forExpression, body, traversal) {
|
|
125
|
+
const targets = forExpression.findDirectExpressions(core_1.Expressions.TargetField);
|
|
126
|
+
const source = forExpression.findDirectExpression(core_1.Expressions.Source);
|
|
127
|
+
const groupBy = forExpression.findDirectExpression(core_1.Expressions.FieldChain);
|
|
128
|
+
if (targets.length !== 2 || source === undefined || groupBy === undefined) {
|
|
129
|
+
throw new Error("ReduceBodyTranspiler invalid GROUPS FOR, " + body.concatTokens());
|
|
130
|
+
}
|
|
131
|
+
const groupTarget = traversal.traverse(targets[0]).getCode();
|
|
132
|
+
const memberTarget = traversal.traverse(targets[1]).getCode();
|
|
133
|
+
const sourceCode = traversal.traverse(source).getCode();
|
|
134
|
+
const groupByCode = traversal.traverse(groupBy).getCode();
|
|
135
|
+
const groups = unique_identifier_1.UniqueIdentifier.get();
|
|
136
|
+
const row = unique_identifier_1.UniqueIdentifier.get();
|
|
137
|
+
const key = unique_identifier_1.UniqueIdentifier.get();
|
|
138
|
+
const rawKey = unique_identifier_1.UniqueIdentifier.get();
|
|
139
|
+
const entry = unique_identifier_1.UniqueIdentifier.get();
|
|
140
|
+
const generator = `(async function*() {\n`
|
|
141
|
+
+ `const ${groups} = new Map();\n`
|
|
142
|
+
+ `for await (const ${row} of abap.statements.loop(${sourceCode})) {\n`
|
|
143
|
+
+ `const ${memberTarget} = ${row}.clone();\n`
|
|
144
|
+
+ `const ${key} = ${groupByCode};\n`
|
|
145
|
+
+ `const ${rawKey} = ${key}.get();\n`
|
|
146
|
+
+ `let ${entry} = ${groups}.get(${rawKey});\n`
|
|
147
|
+
+ `if (${entry} === undefined) {\n`
|
|
148
|
+
+ `${entry} = {key: ${key}.clone(), members: []};\n`
|
|
149
|
+
+ `${groups}.set(${rawKey}, ${entry});\n`
|
|
150
|
+
+ `}\n`
|
|
151
|
+
+ `${entry}.members.push(${row});\n`
|
|
152
|
+
+ `}\n`
|
|
153
|
+
+ `for (const value of ${groups}.values()) { yield value; }\n`
|
|
154
|
+
+ `})()`;
|
|
155
|
+
const loopEntry = unique_identifier_1.UniqueIdentifier.get();
|
|
156
|
+
return {
|
|
157
|
+
beforeLoop: [],
|
|
158
|
+
open: `for await (const ${loopEntry} of ${generator}) {`,
|
|
159
|
+
preBody: [
|
|
160
|
+
`const ${groupTarget} = ${loopEntry}.key.clone();`,
|
|
161
|
+
`const ${memberTarget} = ${loopEntry}.members[0].clone();`,
|
|
162
|
+
],
|
|
163
|
+
postBody: [],
|
|
164
|
+
close: "}",
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
describeIndexFor(forExpression, body, traversal) {
|
|
168
|
+
const counter = forExpression.findDirectExpression(core_1.Expressions.InlineFieldDefinition);
|
|
66
169
|
const cond = forExpression.findDirectExpression(core_1.Expressions.Cond);
|
|
67
|
-
if (cond === undefined) {
|
|
68
|
-
throw new Error("
|
|
170
|
+
if (counter === undefined || cond === undefined) {
|
|
171
|
+
throw new Error("ReduceBodyTranspiler invalid index FOR, " + body.concatTokens());
|
|
69
172
|
}
|
|
70
173
|
const hasUntil = forExpression.findDirectTokenByText("UNTIL") !== undefined;
|
|
71
174
|
const hasWhile = forExpression.findDirectTokenByText("WHILE") !== undefined;
|
|
72
175
|
if ((hasUntil ? 1 : 0) + (hasWhile ? 1 : 0) !== 1) {
|
|
73
|
-
throw new Error("
|
|
176
|
+
throw new Error("ReduceBodyTranspiler index FOR requires WHILE or UNTIL, " + body.concatTokens());
|
|
74
177
|
}
|
|
75
178
|
const fieldName = counter.findDirectExpression(core_1.Expressions.Field)?.concatTokens().toLowerCase();
|
|
76
|
-
|
|
77
|
-
|
|
179
|
+
const source = counter.findDirectExpression(core_1.Expressions.Source);
|
|
180
|
+
if (fieldName === undefined || source === undefined) {
|
|
181
|
+
throw new Error("ReduceBodyTranspiler invalid index definition, " + body.concatTokens());
|
|
78
182
|
}
|
|
79
|
-
const
|
|
80
|
-
const variable = scope?.findVariable(fieldName);
|
|
183
|
+
const variable = traversal.findCurrentScopeByToken(counter.getFirstToken())?.findVariable(fieldName);
|
|
81
184
|
if (variable === undefined) {
|
|
82
|
-
throw new Error(
|
|
185
|
+
throw new Error(`ReduceBodyTranspiler: variable ${fieldName} not found`);
|
|
83
186
|
}
|
|
84
187
|
const counterName = traversal_1.Traversal.prefixVariable(fieldName);
|
|
85
|
-
const startSource = counter.findDirectExpression(core_1.Expressions.Source);
|
|
86
|
-
if (startSource === undefined) {
|
|
87
|
-
throw new Error("ValueBody FOR missing initial value, " + body.concatTokens());
|
|
88
|
-
}
|
|
89
|
-
const start = traversal.traverse(startSource).getCode();
|
|
90
188
|
const thenExpr = forExpression.findExpressionAfterToken("THEN");
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
incrementExpression = `abap.operators.add(${counterName}, new abap.types.Integer().set(1))`;
|
|
97
|
-
}
|
|
189
|
+
const increment = thenExpr && thenExpr instanceof core_1.Nodes.ExpressionNode
|
|
190
|
+
? traversal.traverse(thenExpr).getCode()
|
|
191
|
+
: `abap.operators.add(${counterName}, new abap.types.Integer().set(1))`;
|
|
98
192
|
const condCode = traversal.traverse(cond).getCode();
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
const returnField = this.declareInit(body, traversal, ret);
|
|
102
|
-
ret.appendString(transpile_types_1.TranspileTypes.declare(variable) + `\n`);
|
|
103
|
-
ret.appendString(`${counterName}.set(${start});\n`);
|
|
104
|
-
ret.appendString(`while (true) {\n`);
|
|
193
|
+
const preBody = [];
|
|
194
|
+
const postBody = [`${counterName}.set(${increment});`];
|
|
105
195
|
if (hasWhile) {
|
|
106
|
-
|
|
196
|
+
preBody.push(`if (!(${condCode})) {\nbreak;\n}`);
|
|
197
|
+
}
|
|
198
|
+
const letNode = forExpression.findDirectExpression(core_1.Expressions.Let);
|
|
199
|
+
if (letNode) {
|
|
200
|
+
preBody.push(new let_1.LetTranspiler().transpile(letNode, traversal).getCode());
|
|
107
201
|
}
|
|
108
|
-
ret.appendString(this.transpileNext(body, traversal));
|
|
109
|
-
ret.appendString(`${counterName}.set(${incrementExpression});\n`);
|
|
110
202
|
if (hasUntil) {
|
|
111
|
-
|
|
203
|
+
postBody.push(`if (${condCode}) {\nbreak;\n}`);
|
|
112
204
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
205
|
+
return {
|
|
206
|
+
beforeLoop: [transpile_types_1.TranspileTypes.declare(variable), `${counterName}.set(${traversal.traverse(source).getCode()});`],
|
|
207
|
+
open: "while (true) {",
|
|
208
|
+
preBody,
|
|
209
|
+
postBody,
|
|
210
|
+
close: "}",
|
|
211
|
+
};
|
|
117
212
|
}
|
|
118
213
|
declareInit(body, traversal, ret) {
|
|
119
214
|
let returnField = "";
|
|
120
215
|
for (const init of body.findDirectExpressions(core_1.Expressions.InlineFieldDefinition)) {
|
|
121
|
-
const fieldName = init.findDirectExpression(core_1.Expressions.Field)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
216
|
+
const fieldName = init.findDirectExpression(core_1.Expressions.Field)?.concatTokens().toLowerCase();
|
|
217
|
+
if (fieldName === undefined) {
|
|
218
|
+
throw new Error("ReduceBodyTranspiler INIT missing field");
|
|
219
|
+
}
|
|
220
|
+
if (returnField === "") {
|
|
221
|
+
returnField = traversal_1.Traversal.prefixVariable(fieldName);
|
|
222
|
+
}
|
|
223
|
+
const variable = traversal.findCurrentScopeByToken(init.getFirstToken())?.findVariable(fieldName);
|
|
125
224
|
if (variable === undefined) {
|
|
126
225
|
throw new Error(`ReduceBodyTranspiler: variable ${fieldName} not found`);
|
|
127
226
|
}
|
|
128
|
-
|
|
227
|
+
const target = traversal_1.Traversal.prefixVariable(fieldName);
|
|
228
|
+
ret.appendString(transpile_types_1.TranspileTypes.declare(variable) + "\n");
|
|
229
|
+
const source = init.findDirectExpression(core_1.Expressions.Source);
|
|
230
|
+
if (source) {
|
|
231
|
+
ret.appendString(`${target}.set(${traversal.traverse(source).getCode()});\n`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (returnField === "") {
|
|
235
|
+
throw new Error("ReduceBodyTranspiler INIT missing");
|
|
129
236
|
}
|
|
130
237
|
return returnField;
|
|
131
238
|
}
|
|
132
239
|
transpileNext(body, traversal) {
|
|
133
240
|
let ret = "";
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
241
|
+
const children = body.findDirectExpression(core_1.Expressions.ReduceNext)?.getChildren() || [];
|
|
242
|
+
for (let i = 0; i < children.length; i++) {
|
|
243
|
+
const child = children[i];
|
|
244
|
+
if (!(child instanceof core_1.Nodes.ExpressionNode) || !(child.get() instanceof core_1.Expressions.SimpleTarget)) {
|
|
245
|
+
continue;
|
|
137
246
|
}
|
|
138
|
-
|
|
139
|
-
|
|
247
|
+
const source = children.slice(i + 1).find(candidate => candidate instanceof core_1.Nodes.ExpressionNode && candidate.get() instanceof core_1.Expressions.Source);
|
|
248
|
+
if (!(source instanceof core_1.Nodes.ExpressionNode)) {
|
|
249
|
+
throw new Error("ReduceBodyTranspiler NEXT missing source");
|
|
140
250
|
}
|
|
251
|
+
const target = new target_1.TargetTranspiler().transpile(child, traversal).getCode();
|
|
252
|
+
const value = traversal.traverse(source).getCode();
|
|
253
|
+
const between = children.slice(i + 1, children.indexOf(source)).map(candidate => candidate.concatTokens()).join("");
|
|
254
|
+
const operators = {
|
|
255
|
+
"+=": "add",
|
|
256
|
+
"-=": "minus",
|
|
257
|
+
"*=": "multiply",
|
|
258
|
+
"/=": "divide",
|
|
259
|
+
"&&=": "concat",
|
|
260
|
+
};
|
|
261
|
+
const operator = operators[between];
|
|
262
|
+
ret += operator
|
|
263
|
+
? `${target}.set(abap.operators.${operator}(${target}, ${value}));\n`
|
|
264
|
+
: `${target}.set(${value});\n`;
|
|
265
|
+
i = children.indexOf(source);
|
|
141
266
|
}
|
|
142
267
|
return ret;
|
|
143
268
|
}
|
|
269
|
+
appendBlocks(ret, blocks, indent) {
|
|
270
|
+
for (const block of blocks) {
|
|
271
|
+
this.appendBlock(ret, block, indent);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
appendBlock(ret, block, indent) {
|
|
275
|
+
for (const line of block.split("\n")) {
|
|
276
|
+
if (line.trim() !== "") {
|
|
277
|
+
ret.appendString(indent + line.replace(/\r/g, "") + "\n");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
144
281
|
}
|
|
145
282
|
exports.ReduceBodyTranspiler = ReduceBodyTranspiler;
|
|
146
283
|
//# sourceMappingURL=reduce_body.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/transpiler",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.49",
|
|
4
4
|
"description": "Transpiler",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/src/index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"author": "abaplint",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@abaplint/core": "^2.120.
|
|
32
|
+
"@abaplint/core": "^2.120.18",
|
|
33
33
|
"source-map": "^0.7.6"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|