@abaplint/transpiler 2.13.85 → 2.13.87
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.
- package/build/src/expressions/constant.js +4 -2
- package/build/src/expressions/source.d.ts +1 -0
- package/build/src/expressions/source.js +40 -0
- package/build/src/initialization.js +2 -1
- package/build/src/keywords.js +6 -2
- package/build/src/rearranger.js +17 -1
- package/build/src/statements/select.js +12 -6
- package/build/src/structures/function_module.js +7 -7
- package/package.json +3 -3
|
@@ -85,8 +85,10 @@ class ConstantTranspiler {
|
|
|
85
85
|
return new chunk_1.Chunk(`todo, Constant`);
|
|
86
86
|
}
|
|
87
87
|
handleCharacter(res) {
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// length of the value: strip the enclosing quotes first, an escaped
|
|
89
|
+
// quote ('') inside counts as one character and must not merge with them
|
|
90
|
+
const inner = res.substring(1, res.length - 1).replace(/''/g, "'");
|
|
91
|
+
let length = inner.length;
|
|
90
92
|
if (length <= 0) {
|
|
91
93
|
// note: Characters cannot have length = zero, 1 is minimum
|
|
92
94
|
length = 1;
|
|
@@ -5,5 +5,6 @@ import { Traversal } from "../traversal";
|
|
|
5
5
|
export declare class SourceTranspiler implements IExpressionTranspiler {
|
|
6
6
|
private readonly addGet;
|
|
7
7
|
constructor(addGet?: boolean);
|
|
8
|
+
private concatOperands;
|
|
8
9
|
transpile(node: Nodes.ExpressionNode, traversal: Traversal, context?: AbstractType): Chunk;
|
|
9
10
|
}
|
|
@@ -15,10 +15,50 @@ class SourceTranspiler {
|
|
|
15
15
|
constructor(addGet = false) {
|
|
16
16
|
this.addGet = addGet;
|
|
17
17
|
}
|
|
18
|
+
// the operands of a chain of & / && (the rearranger leaves them as a binary
|
|
19
|
+
// tree of Source nodes); an empty list when the node is not such a chain
|
|
20
|
+
concatOperands(node) {
|
|
21
|
+
const children = node.getChildren();
|
|
22
|
+
if (children.length !== 3) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const op = children[1];
|
|
26
|
+
if (!(op instanceof core_1.Nodes.TokenNode) || (op.getFirstToken().getStr() !== "&&" && op.getFirstToken().getStr() !== "&")) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const side = (c) => {
|
|
30
|
+
if (c instanceof core_1.Nodes.ExpressionNode && c.get() instanceof core_1.Expressions.Source) {
|
|
31
|
+
const inner = this.concatOperands(c);
|
|
32
|
+
return inner.length > 0 ? inner : [c];
|
|
33
|
+
}
|
|
34
|
+
const wrapped = new core_1.Nodes.ExpressionNode(new core_1.Expressions.Source());
|
|
35
|
+
wrapped.setChildren([c]);
|
|
36
|
+
return [wrapped];
|
|
37
|
+
};
|
|
38
|
+
return [...side(children[0]), ...side(children[2])];
|
|
39
|
+
}
|
|
18
40
|
transpile(node, traversal, context) {
|
|
19
41
|
let ret = new chunk_1.Chunk();
|
|
20
42
|
const post = new chunk_1.Chunk();
|
|
21
43
|
const children = node.getChildren();
|
|
44
|
+
// a & b & c ... : one flat concat([a, b, c]) instead of one nested
|
|
45
|
+
// concat( ) per operand (a long chain would otherwise exhaust the stack
|
|
46
|
+
// of a parser with a small budget, e.g. a service worker's)
|
|
47
|
+
const operands = this.concatOperands(node);
|
|
48
|
+
if (operands.length > 2) {
|
|
49
|
+
const flat = new chunk_1.Chunk().appendString("abap.operators.concat([");
|
|
50
|
+
for (let i = 0; i < operands.length; i++) {
|
|
51
|
+
if (i > 0) {
|
|
52
|
+
flat.appendString(",");
|
|
53
|
+
}
|
|
54
|
+
flat.appendChunk(new SourceTranspiler(false).transpile(operands[i], traversal, context));
|
|
55
|
+
}
|
|
56
|
+
flat.appendString("])");
|
|
57
|
+
if (this.addGet) {
|
|
58
|
+
flat.appendString(".get()");
|
|
59
|
+
}
|
|
60
|
+
return flat;
|
|
61
|
+
}
|
|
22
62
|
// Pre-scan for unary prefix +/- tokens (WDashW / WPlusW) before any expression
|
|
23
63
|
let prefixNegated = false;
|
|
24
64
|
let startIdx = 0;
|
|
@@ -117,7 +117,8 @@ globalThis.abap = new runtime.ABAP();\n`;
|
|
|
117
117
|
|| obj instanceof abaplint.Objects.Oauth2Profile
|
|
118
118
|
|| obj instanceof abaplint.Objects.WebMIME
|
|
119
119
|
|| obj instanceof abaplint.Objects.TypePool
|
|
120
|
-
|| obj instanceof abaplint.Objects.TableType
|
|
120
|
+
|| obj instanceof abaplint.Objects.TableType
|
|
121
|
+
|| obj instanceof abaplint.Objects.View) {
|
|
121
122
|
list.push(imp(`${escapeNamespaceFilename(obj.getName().toLowerCase())}.${obj.getType().toLowerCase()}`));
|
|
122
123
|
}
|
|
123
124
|
}
|
package/build/src/keywords.js
CHANGED
|
@@ -20,7 +20,11 @@ exports.DEFAULT_KEYWORDS = new Set([
|
|
|
20
20
|
"throw", "throws", "transient", "true",
|
|
21
21
|
"try", "typeof", "var", "void",
|
|
22
22
|
"delete",
|
|
23
|
-
"volatile", "while", "yield"
|
|
23
|
+
"volatile", "while", "yield",
|
|
24
|
+
// both produce a SyntaxError in the emitted module: they are reserved words
|
|
25
|
+
// in JavaScript and legal ABAP names. "super" is NOT in this list on purpose -
|
|
26
|
+
// the emitted code uses the JS `super` for ABAP's `super->method( )`, and
|
|
27
|
+
// escaping it turns that call into `$super.get().method()`
|
|
28
|
+
"case", "with"
|
|
24
29
|
]);
|
|
25
|
-
// "with"
|
|
26
30
|
//# sourceMappingURL=keywords.js.map
|
package/build/src/rearranger.js
CHANGED
|
@@ -189,7 +189,23 @@ class Rearranger {
|
|
|
189
189
|
return node;
|
|
190
190
|
}
|
|
191
191
|
const withoutLast = node.getChildren().slice(0, children.length - 1);
|
|
192
|
-
|
|
192
|
+
// The nested children come up so that precedence can be decided across
|
|
193
|
+
// the whole chain, and everything before the nested operator has to
|
|
194
|
+
// arrive as one operand. For "a * b / c" that is already true, b being a
|
|
195
|
+
// single Source. For "a * CONV f( b ) / c" it is not: CONV is five
|
|
196
|
+
// children, and hoisting them one by one puts the type name and the
|
|
197
|
+
// body beside the arithmetic, where the transpiler reads them as
|
|
198
|
+
// operands and loses the rest of the expression. So the head is wrapped
|
|
199
|
+
// back into one Source before it moves.
|
|
200
|
+
const nested = last.getChildren();
|
|
201
|
+
const firstOperator = nested.findIndex((c) => c instanceof core_1.Nodes.ExpressionNode && c.get() instanceof core_1.Expressions.ArithOperator);
|
|
202
|
+
let head = nested.slice(0, firstOperator);
|
|
203
|
+
if (head.length > 1) {
|
|
204
|
+
const operand = new core_1.Nodes.ExpressionNode(last.get());
|
|
205
|
+
operand.setChildren(head);
|
|
206
|
+
head = [operand];
|
|
207
|
+
}
|
|
208
|
+
const flat = withoutLast.concat(head, nested.slice(firstOperator));
|
|
193
209
|
node.setChildren(flat);
|
|
194
210
|
return node;
|
|
195
211
|
}
|
|
@@ -93,9 +93,11 @@ class SelectTranspiler {
|
|
|
93
93
|
where = sqlCond;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
let whereClause = "";
|
|
96
97
|
if (where) {
|
|
97
|
-
|
|
98
|
+
whereClause = "WHERE " + new expressions_1.SQLCondTranspiler().transpile(where, traversal, table).getCode() + " ";
|
|
98
99
|
}
|
|
100
|
+
select += whereClause;
|
|
99
101
|
const groupBy = node.findFirstExpression(abaplint.Expressions.SQLGroupBy);
|
|
100
102
|
if (groupBy) {
|
|
101
103
|
select += new sql_group_by_1.SQLGroupByTranspiler().transpile(groupBy, traversal).getCode() + " ";
|
|
@@ -136,6 +138,7 @@ class SelectTranspiler {
|
|
|
136
138
|
else {
|
|
137
139
|
const code = new expressions_1.FieldChainTranspiler(true).transpile(chain, traversal).getCode();
|
|
138
140
|
select = select.replace(search, `" + ${code} + "`);
|
|
141
|
+
whereClause = whereClause.replace(search, `" + ${code} + "`);
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
144
|
}
|
|
@@ -163,14 +166,17 @@ class SelectTranspiler {
|
|
|
163
166
|
const unique2 = unique_identifier_1.UniqueIdentifier.get();
|
|
164
167
|
const fn = node.findFirstExpression(abaplint.Expressions.SQLForAllEntries)?.findDirectExpression(abaplint.Expressions.SQLSource);
|
|
165
168
|
const faeTranspiled = new expressions_1.SQLSourceTranspiler().transpile(fn, traversal).getCode();
|
|
169
|
+
// ABAP semantics: with an empty driving table the whole WHERE condition is ignored
|
|
170
|
+
const selectEmpty = select.replace(whereClause, "");
|
|
166
171
|
select = select.replace(new RegExp(" " + escapeRegExp(faeTranspiled), "g"), " " + unique);
|
|
167
172
|
select = select.replace(unique + ".get().table_line.get()", unique + ".get()"); // there can be only one?
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
// FOR ALL ENTRIES removes duplicate rows from the result: duplicates of
|
|
174
|
+
// the selected columns, so de-duplicate by the target's components. The
|
|
175
|
+
// DB key alone is wrong for projections (INTO CORRESPONDING FIELDS
|
|
176
|
+
// without the key fields crashed on the missing component names)
|
|
177
|
+
const by = `Object.keys(${target}.getRowType().get())`;
|
|
172
178
|
const code = `if (${faeTranspiled}.array().length === 0) {
|
|
173
|
-
|
|
179
|
+
await abap.statements.select(${target}, {select: "${selectEmpty.trim()}"${extra}});
|
|
174
180
|
} else {
|
|
175
181
|
const ${unique2} = ${faeTranspiled}.array();
|
|
176
182
|
${target}.clear();
|
|
@@ -87,16 +87,16 @@ class FunctionModuleTranspiler {
|
|
|
87
87
|
direction = "importing";
|
|
88
88
|
}
|
|
89
89
|
// note: all directions are optional
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
ret += `let ${
|
|
90
|
+
const name = p.name.toLowerCase();
|
|
91
|
+
// a parameter named like a JavaScript keyword, RETURN on every BAPI,
|
|
92
|
+
// is declared the way the function body refers to it
|
|
93
|
+
const variable = traversal_1.Traversal.prefixVariable(name);
|
|
94
|
+
ret += `let ${variable} = INPUT.${direction}?.${name};\n`;
|
|
95
95
|
const type = scope?.findVariable(name)?.getType();
|
|
96
96
|
if (type !== undefined && p.optional === true) {
|
|
97
97
|
// todo, set DEFAULT value
|
|
98
|
-
ret += `if (${
|
|
99
|
-
${
|
|
98
|
+
ret += `if (${variable} === undefined) {
|
|
99
|
+
${variable} = ${transpile_types_1.TranspileTypes.toType(type)};
|
|
100
100
|
}\n`;
|
|
101
101
|
}
|
|
102
102
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/transpiler",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.87",
|
|
4
4
|
"description": "Transpiler",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/src/index.d.ts",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"author": "abaplint",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@abaplint/core": "^2.120.
|
|
32
|
+
"@abaplint/core": "^2.120.52",
|
|
33
33
|
"source-map": "^0.8.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/chai": "^5.2.3",
|
|
37
37
|
"@types/mocha": "^10.0.10",
|
|
38
38
|
"chai": "^6.2.2",
|
|
39
|
-
"mocha": "^12.0.
|
|
39
|
+
"mocha": "^12.0.1",
|
|
40
40
|
"source-map-support": "^0.5.21",
|
|
41
41
|
"typescript": "^6.0.3"
|
|
42
42
|
}
|