@abaplint/transpiler 2.13.95 → 2.13.97
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/chunk.js +2 -1
- package/build/src/db/schema_generation/pg_database_schema.js +3 -0
- package/build/src/db/schema_generation/snowflake_database_schema.js +3 -0
- package/build/src/db/schema_generation/sqlite_database_schema.js +3 -0
- package/build/src/expressions/reduce_body.js +12 -16
- package/build/src/expressions/source.d.ts +3 -1
- package/build/src/expressions/source.js +29 -5
- package/build/src/expressions/sql_cond.d.ts +1 -0
- package/build/src/expressions/sql_cond.js +15 -3
- package/build/src/expressions/value_body.js +25 -22
- package/build/src/statements/_dataset.d.ts +17 -0
- package/build/src/statements/_dataset.js +82 -0
- package/build/src/statements/_selection.d.ts +18 -0
- package/build/src/statements/_selection.js +82 -0
- package/build/src/statements/assign.js +9 -8
- package/build/src/statements/call.js +3 -3
- package/build/src/statements/call_function.js +10 -3
- package/build/src/statements/close_dataset.d.ts +1 -1
- package/build/src/statements/close_dataset.js +40 -2
- package/build/src/statements/create_data.js +1 -1
- package/build/src/statements/delete_dataset.d.ts +1 -1
- package/build/src/statements/delete_dataset.js +40 -2
- package/build/src/statements/generate_subroutine.d.ts +3 -1
- package/build/src/statements/generate_subroutine.js +72 -2
- package/build/src/statements/get_dataset.d.ts +1 -1
- package/build/src/statements/get_dataset.js +14 -2
- package/build/src/statements/loop.d.ts +1 -0
- package/build/src/statements/loop.js +12 -0
- package/build/src/statements/move.d.ts +2 -0
- package/build/src/statements/move.js +70 -1
- package/build/src/statements/open_dataset.d.ts +1 -1
- package/build/src/statements/open_dataset.js +32 -2
- package/build/src/statements/parameter.d.ts +2 -1
- package/build/src/statements/parameter.js +68 -2
- package/build/src/statements/perform.js +13 -1
- package/build/src/statements/read_dataset.d.ts +1 -1
- package/build/src/statements/read_dataset.js +16 -2
- package/build/src/statements/select.js +78 -13
- package/build/src/statements/select_option.d.ts +1 -1
- package/build/src/statements/select_option.js +63 -2
- package/build/src/statements/set_dataset.d.ts +1 -1
- package/build/src/statements/set_dataset.js +10 -2
- package/build/src/statements/sort.d.ts +2 -0
- package/build/src/statements/sort.js +33 -4
- package/build/src/statements/transfer.d.ts +1 -1
- package/build/src/statements/transfer.js +14 -2
- package/build/src/structures/class_definition.js +10 -9
- package/package.json +2 -2
package/build/src/chunk.js
CHANGED
|
@@ -170,7 +170,8 @@ class Chunk {
|
|
|
170
170
|
}
|
|
171
171
|
const indentationByLine = this.mappings.length > 0 ? [] : undefined;
|
|
172
172
|
for (const l of this.raw.split("\n")) {
|
|
173
|
-
|
|
173
|
+
// trimStart, a pre-indented closing brace must still close its level
|
|
174
|
+
if (l.trimStart().startsWith("}")) {
|
|
174
175
|
i = i - 1;
|
|
175
176
|
}
|
|
176
177
|
// clamp so unbalanced braces never produce a negative indent/shift
|
|
@@ -95,6 +95,9 @@ class PGDatabaseSchema {
|
|
|
95
95
|
else if (type instanceof abaplint.BasicTypes.IntegerType) {
|
|
96
96
|
return `INT`;
|
|
97
97
|
}
|
|
98
|
+
else if (type instanceof abaplint.BasicTypes.Integer8Type) {
|
|
99
|
+
return `BIGINT`;
|
|
100
|
+
}
|
|
98
101
|
else if (type instanceof abaplint.BasicTypes.FloatType
|
|
99
102
|
|| type instanceof abaplint.BasicTypes.FloatingPointType) {
|
|
100
103
|
return `REAL`;
|
|
@@ -94,6 +94,9 @@ class SnowflakeDatabaseSchema {
|
|
|
94
94
|
else if (type instanceof abaplint.BasicTypes.IntegerType) {
|
|
95
95
|
return `INT`;
|
|
96
96
|
}
|
|
97
|
+
else if (type instanceof abaplint.BasicTypes.Integer8Type) {
|
|
98
|
+
return `NUMBER(19)`;
|
|
99
|
+
}
|
|
97
100
|
else if (type instanceof abaplint.BasicTypes.FloatType
|
|
98
101
|
|| type instanceof abaplint.BasicTypes.FloatingPointType) {
|
|
99
102
|
return `REAL`;
|
|
@@ -96,6 +96,9 @@ class SQLiteDatabaseSchema {
|
|
|
96
96
|
else if (type instanceof abaplint.BasicTypes.IntegerType) {
|
|
97
97
|
return `INT`;
|
|
98
98
|
}
|
|
99
|
+
else if (type instanceof abaplint.BasicTypes.Integer8Type) {
|
|
100
|
+
return `INTEGER`;
|
|
101
|
+
}
|
|
99
102
|
else if (type instanceof abaplint.BasicTypes.FloatType
|
|
100
103
|
|| type instanceof abaplint.BasicTypes.FloatingPointType) {
|
|
101
104
|
return `REAL`;
|
|
@@ -31,22 +31,18 @@ class ReduceBodyTranspiler {
|
|
|
31
31
|
for (const declaration of declarations) {
|
|
32
32
|
ret.appendString(declaration + "\n");
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// no manual indentation, Chunk.runIndentationLogic indents by brace depth,
|
|
35
|
+
// pre-indented closing braces would not be recognized and drift the indentation
|
|
36
36
|
for (const descriptor of descriptors) {
|
|
37
|
-
this.appendBlocks(ret, descriptor.beforeLoop
|
|
38
|
-
ret.appendString(
|
|
39
|
-
|
|
40
|
-
levelIndents.push(indent);
|
|
41
|
-
this.appendBlocks(ret, descriptor.preBody, indent);
|
|
37
|
+
this.appendBlocks(ret, descriptor.beforeLoop);
|
|
38
|
+
ret.appendString(descriptor.open + "\n");
|
|
39
|
+
this.appendBlocks(ret, descriptor.preBody);
|
|
42
40
|
}
|
|
43
|
-
this.appendBlock(ret, this.transpileNext(body, traversal)
|
|
41
|
+
this.appendBlock(ret, this.transpileNext(body, traversal));
|
|
44
42
|
for (let i = descriptors.length - 1; i >= 0; i--) {
|
|
45
43
|
const descriptor = descriptors[i];
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
indent = currentIndent.substring(0, Math.max(0, currentIndent.length - 2));
|
|
49
|
-
ret.appendString(indent + descriptor.close + "\n");
|
|
44
|
+
this.appendBlocks(ret, descriptor.postBody);
|
|
45
|
+
ret.appendString(descriptor.close + "\n");
|
|
50
46
|
}
|
|
51
47
|
ret.appendString(`return ${returnField};\n`);
|
|
52
48
|
ret.appendString("})())");
|
|
@@ -266,15 +262,15 @@ class ReduceBodyTranspiler {
|
|
|
266
262
|
}
|
|
267
263
|
return ret;
|
|
268
264
|
}
|
|
269
|
-
appendBlocks(ret, blocks
|
|
265
|
+
appendBlocks(ret, blocks) {
|
|
270
266
|
for (const block of blocks) {
|
|
271
|
-
this.appendBlock(ret, block
|
|
267
|
+
this.appendBlock(ret, block);
|
|
272
268
|
}
|
|
273
269
|
}
|
|
274
|
-
appendBlock(ret, block
|
|
270
|
+
appendBlock(ret, block) {
|
|
275
271
|
for (const line of block.split("\n")) {
|
|
276
272
|
if (line.trim() !== "") {
|
|
277
|
-
ret.appendString(
|
|
273
|
+
ret.appendString(line.replace(/\r/g, "") + "\n");
|
|
278
274
|
}
|
|
279
275
|
}
|
|
280
276
|
}
|
|
@@ -4,7 +4,9 @@ import { IExpressionTranspiler } from "./_expression_transpiler";
|
|
|
4
4
|
import { Traversal } from "../traversal";
|
|
5
5
|
export declare class SourceTranspiler implements IExpressionTranspiler {
|
|
6
6
|
private readonly addGet;
|
|
7
|
-
|
|
7
|
+
private readonly int8;
|
|
8
|
+
/** int8: the calculation type is int8, each operation gets its left operand as an int8 */
|
|
9
|
+
constructor(addGet?: boolean, int8?: boolean);
|
|
8
10
|
private concatOperands;
|
|
9
11
|
transpile(node: Nodes.ExpressionNode, traversal: Traversal, context?: AbstractType): Chunk;
|
|
10
12
|
}
|
|
@@ -9,11 +9,15 @@ const corresponding_body_1 = require("./corresponding_body");
|
|
|
9
9
|
const filter_body_1 = require("./filter_body");
|
|
10
10
|
const reduce_body_1 = require("./reduce_body");
|
|
11
11
|
const transpile_types_1 = require("../transpile_types");
|
|
12
|
+
const let_1 = require("./let");
|
|
12
13
|
const value_body_1 = require("./value_body");
|
|
13
14
|
class SourceTranspiler {
|
|
14
15
|
addGet;
|
|
15
|
-
|
|
16
|
+
int8;
|
|
17
|
+
/** int8: the calculation type is int8, each operation gets its left operand as an int8 */
|
|
18
|
+
constructor(addGet = false, int8 = false) {
|
|
16
19
|
this.addGet = addGet;
|
|
20
|
+
this.int8 = int8;
|
|
17
21
|
}
|
|
18
22
|
// the operands of a chain of & / && (the rearranger leaves them as a binary
|
|
19
23
|
// tree of Source nodes); an empty list when the node is not such a chain
|
|
@@ -81,6 +85,8 @@ class SourceTranspiler {
|
|
|
81
85
|
break;
|
|
82
86
|
}
|
|
83
87
|
}
|
|
88
|
+
// int8: whether the operand left of the next operator is already an int8
|
|
89
|
+
let leftIsInt8 = false;
|
|
84
90
|
for (let i = startIdx; i < children.length; i++) {
|
|
85
91
|
const c = children[i];
|
|
86
92
|
const isLast = i === children.length - 1;
|
|
@@ -98,7 +104,18 @@ class SourceTranspiler {
|
|
|
98
104
|
ret.appendChunk(traversal.traverse(c));
|
|
99
105
|
}
|
|
100
106
|
else if (c.get() instanceof core_1.Expressions.ArithOperator) {
|
|
101
|
-
|
|
107
|
+
// "/" rounds in calculation type int8, the runtime's divide() does not know the target
|
|
108
|
+
const operator = this.int8 === true && c.concatTokens().trim() === "/"
|
|
109
|
+
? new chunk_1.Chunk().append("abap.operators.divideInt8", c, traversal)
|
|
110
|
+
: traversal.traverse(c);
|
|
111
|
+
if (this.int8 === true && leftIsInt8 === false) {
|
|
112
|
+
ret = new chunk_1.Chunk().appendChunk(operator).appendString("(new abap.types.Integer8().set(").appendChunk(ret).appendString("),");
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
ret = new chunk_1.Chunk().appendChunk(operator).appendString("(").appendChunk(ret).appendString(",");
|
|
116
|
+
}
|
|
117
|
+
// the result of an operation with an int8 operand is an int8
|
|
118
|
+
leftIsInt8 = true;
|
|
102
119
|
post.appendString(")");
|
|
103
120
|
if (this.addGet) {
|
|
104
121
|
post.append(".get()", c, traversal);
|
|
@@ -115,7 +132,8 @@ class SourceTranspiler {
|
|
|
115
132
|
}
|
|
116
133
|
}
|
|
117
134
|
else if (c.get() instanceof core_1.Expressions.Source) {
|
|
118
|
-
ret.appendChunk(new SourceTranspiler(this.addGet).transpile(c, traversal));
|
|
135
|
+
ret.appendChunk(new SourceTranspiler(this.addGet, this.int8).transpile(c, traversal));
|
|
136
|
+
leftIsInt8 = c.findDirectExpression(core_1.Expressions.ArithOperator) !== undefined;
|
|
119
137
|
}
|
|
120
138
|
else if (c.get() instanceof core_1.Expressions.Arrow) {
|
|
121
139
|
ret = new chunk_1.Chunk().appendString("(").appendChunk(ret).appendString(").get().");
|
|
@@ -151,13 +169,19 @@ class SourceTranspiler {
|
|
|
151
169
|
}
|
|
152
170
|
ret = new chunk_1.Chunk().appendString(new _1.TypeNameOrInfer().transpile(typ, traversal).getCode());
|
|
153
171
|
ret.appendString(".set(");
|
|
154
|
-
// todo: handle LET
|
|
155
172
|
const context = new _1.TypeNameOrInfer().findType(typ, traversal);
|
|
156
|
-
|
|
173
|
+
const convSource = c.findDirectExpression(core_1.Expressions.Source);
|
|
174
|
+
ret.appendString(new SourceTranspiler().transpile(convSource, traversal, context).getCode());
|
|
157
175
|
ret.appendString(")");
|
|
158
176
|
if (this.addGet) {
|
|
159
177
|
ret.appendString(".get()");
|
|
160
178
|
}
|
|
179
|
+
const convLet = c.findDirectExpression(core_1.Expressions.Let);
|
|
180
|
+
if (convLet) {
|
|
181
|
+
// the LET bindings are declared in a scope of their own, before the value is built
|
|
182
|
+
const letCode = new let_1.LetTranspiler().transpile(convLet, traversal).getCode();
|
|
183
|
+
ret = new chunk_1.Chunk().appendString(`(await (async () => { ${letCode} return ${ret.getCode()}; })())`);
|
|
184
|
+
}
|
|
161
185
|
}
|
|
162
186
|
else if (c.get() instanceof core_1.Expressions.ValueBody) {
|
|
163
187
|
continue;
|
|
@@ -151,6 +151,11 @@ class SQLCondTranspiler {
|
|
|
151
151
|
ret += fieldName + " IS NULL";
|
|
152
152
|
return ret;
|
|
153
153
|
}
|
|
154
|
+
const aggregation = c.findDirectExpression(abaplint.Expressions.SQLAggregation);
|
|
155
|
+
if (aggregation && operator && source) {
|
|
156
|
+
return aggregation.concatTokens() + " " + this.sqlOperator(operator.concatTokens()) + " "
|
|
157
|
+
+ this.sqlSource(source, traversal, filename, table, true);
|
|
158
|
+
}
|
|
154
159
|
if (fieldName === undefined || operator === undefined || source === undefined) {
|
|
155
160
|
throw new Error("SQL Condition, transpiler todo2, " + c.concatTokens());
|
|
156
161
|
}
|
|
@@ -212,17 +217,18 @@ class SQLCondTranspiler {
|
|
|
212
217
|
}
|
|
213
218
|
return ret + ")";
|
|
214
219
|
}
|
|
215
|
-
sqlSource(source, traversal, filename, table) {
|
|
220
|
+
sqlSource(source, traversal, filename, table, numericHost = false) {
|
|
216
221
|
let ret = "";
|
|
217
222
|
const simple = source.findDirectExpression(abaplint.Expressions.SimpleSource3);
|
|
218
223
|
const hostExpression = source.findDirectExpression(abaplint.Expressions.Source);
|
|
219
224
|
const alias = source.findDirectExpression(abaplint.Expressions.SQLAliasField);
|
|
220
225
|
if (source.getFirstToken().getStr() === "@" && hostExpression) {
|
|
221
226
|
const code = new source_1.SourceTranspiler(true).transpile(hostExpression, traversal).getCode();
|
|
222
|
-
ret +=
|
|
227
|
+
ret += this.sqlHost(code, numericHost);
|
|
223
228
|
}
|
|
224
229
|
else if (simple && simple.findDirectExpression(abaplint.Expressions.Constant) === undefined) {
|
|
225
|
-
|
|
230
|
+
const code = new simple_source3_1.SimpleSource3Transpiler(true).transpile(simple, traversal).getCode();
|
|
231
|
+
ret += this.sqlHost(code, numericHost);
|
|
226
232
|
}
|
|
227
233
|
else if (alias) {
|
|
228
234
|
// SQLAliasField might be a SQL reference or value from ABAP interface
|
|
@@ -257,6 +263,12 @@ class SQLCondTranspiler {
|
|
|
257
263
|
}
|
|
258
264
|
return ret;
|
|
259
265
|
}
|
|
266
|
+
sqlHost(code, numericHost) {
|
|
267
|
+
if (numericHost) {
|
|
268
|
+
return `" + ((value) => typeof value === "number" || typeof value === "bigint" ? value : "'" + value + "'")(${code}) + "`;
|
|
269
|
+
}
|
|
270
|
+
return "'\" + " + code + " + \"'";
|
|
271
|
+
}
|
|
260
272
|
basicConditionNew(node, traversal, filename, table) {
|
|
261
273
|
const sourceWithComponents = this.sourceWithComponents(node, traversal);
|
|
262
274
|
if (sourceWithComponents) {
|
|
@@ -30,6 +30,7 @@ class ValueBodyTranspiler {
|
|
|
30
30
|
const outerLetCode = outerLet === undefined
|
|
31
31
|
? undefined
|
|
32
32
|
: new let_1.LetTranspiler().transpile(outerLet, traversal).getCode();
|
|
33
|
+
let hasFor = false;
|
|
33
34
|
const children = body.getChildren();
|
|
34
35
|
for (let i = 0; i < children.length; i++) {
|
|
35
36
|
const child = children[i];
|
|
@@ -72,6 +73,7 @@ class ValueBodyTranspiler {
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
i = idx - 1;
|
|
76
|
+
hasFor = true;
|
|
75
77
|
const result = this.buildForChain(forNodes, typ, traversal, body, baseCode, outerLetCode);
|
|
76
78
|
ret = result.chunk;
|
|
77
79
|
post = result.post;
|
|
@@ -99,14 +101,18 @@ class ValueBodyTranspiler {
|
|
|
99
101
|
throw new Error("ValueBodyTranspiler, unknown " + child.get().constructor.name + " \"" + child.concatTokens()) + "\"";
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
|
-
|
|
104
|
+
ret.appendString(post);
|
|
105
|
+
if (outerLetCode !== undefined && hasFor === false) {
|
|
106
|
+
// without FOR the LET bindings are declared here, in a scope of their own, before the value is built
|
|
107
|
+
ret = new chunk_1.Chunk().appendString(`(await (async () => { ${outerLetCode} return ${ret.getCode()}; })())`);
|
|
108
|
+
}
|
|
109
|
+
return ret;
|
|
103
110
|
}
|
|
104
111
|
buildForChain(forNodes, typ, traversal, body, baseCode, outerLetCode) {
|
|
105
112
|
const val = baseCode ?? new type_name_or_infer_1.TypeNameOrInfer().transpile(typ, traversal).getCode();
|
|
106
113
|
const chunk = new chunk_1.Chunk();
|
|
107
114
|
const preLoopDecls = outerLetCode === undefined ? [] : [outerLetCode];
|
|
108
115
|
const descriptors = [];
|
|
109
|
-
const levelIndents = [];
|
|
110
116
|
let uniqueCounter = 1;
|
|
111
117
|
for (const child of forNodes) {
|
|
112
118
|
const loop = child.findDirectExpression(core_1.Expressions.InlineLoopDefinition);
|
|
@@ -222,11 +228,11 @@ class ValueBodyTranspiler {
|
|
|
222
228
|
letCode = new let_1.LetTranspiler().transpile(llet, traversal).getCode();
|
|
223
229
|
}
|
|
224
230
|
const condCode = traversal.traverse(cond).getCode();
|
|
225
|
-
const preCheck = hasWhile ? `if (!(${condCode})) {\
|
|
231
|
+
const preCheck = hasWhile ? `if (!(${condCode})) {\nbreak;\n}` : "";
|
|
226
232
|
const postLoop = [];
|
|
227
233
|
postLoop.push(incrementLine);
|
|
228
234
|
if (hasUntil) {
|
|
229
|
-
postLoop.push(`if (${condCode}) {\
|
|
235
|
+
postLoop.push(`if (${condCode}) {\nbreak;\n}`);
|
|
230
236
|
}
|
|
231
237
|
descriptors.push({
|
|
232
238
|
beforeLoop: [declare, `${counterName}.set(${start});`],
|
|
@@ -241,34 +247,31 @@ class ValueBodyTranspiler {
|
|
|
241
247
|
throw new Error("ValueBodyTranspiler FOR internal error");
|
|
242
248
|
}
|
|
243
249
|
chunk.appendString("await (async () => {\n");
|
|
244
|
-
this.appendBlocks(chunk, preLoopDecls
|
|
250
|
+
this.appendBlocks(chunk, preLoopDecls);
|
|
245
251
|
chunk.appendString(`const VAL = ${val};\n`);
|
|
246
|
-
|
|
252
|
+
// no manual indentation, Chunk.runIndentationLogic indents by brace depth,
|
|
253
|
+
// pre-indented closing braces would not be recognized and drift the indentation
|
|
247
254
|
for (const desc of descriptors) {
|
|
248
|
-
this.appendBlocks(chunk, desc.beforeLoop
|
|
249
|
-
chunk.appendString(
|
|
250
|
-
|
|
251
|
-
levelIndents.push(indent);
|
|
252
|
-
this.appendBlocks(chunk, desc.preBody, indent);
|
|
255
|
+
this.appendBlocks(chunk, desc.beforeLoop);
|
|
256
|
+
chunk.appendString(desc.open + "\n");
|
|
257
|
+
this.appendBlocks(chunk, desc.preBody);
|
|
253
258
|
}
|
|
254
|
-
chunk.appendString(
|
|
259
|
+
chunk.appendString("VAL");
|
|
255
260
|
let post = ";\n";
|
|
256
261
|
for (let i = descriptors.length - 1; i >= 0; i--) {
|
|
257
262
|
const desc = descriptors[i];
|
|
258
|
-
|
|
259
|
-
post +=
|
|
260
|
-
const parentIndent = currentIndent.substring(0, Math.max(0, currentIndent.length - 2));
|
|
261
|
-
post += parentIndent + desc.close + "\n";
|
|
263
|
+
post += this.blocksToString(desc.postBody);
|
|
264
|
+
post += desc.close + "\n";
|
|
262
265
|
}
|
|
263
266
|
post += "return VAL;\n})()";
|
|
264
267
|
return { chunk, post };
|
|
265
268
|
}
|
|
266
|
-
appendBlocks(chunk, blocks
|
|
269
|
+
appendBlocks(chunk, blocks) {
|
|
267
270
|
for (const block of blocks) {
|
|
268
|
-
this.appendBlock(chunk, block
|
|
271
|
+
this.appendBlock(chunk, block);
|
|
269
272
|
}
|
|
270
273
|
}
|
|
271
|
-
appendBlock(chunk, block
|
|
274
|
+
appendBlock(chunk, block) {
|
|
272
275
|
if (block === "") {
|
|
273
276
|
return;
|
|
274
277
|
}
|
|
@@ -278,10 +281,10 @@ class ValueBodyTranspiler {
|
|
|
278
281
|
if (clean.trim() === "") {
|
|
279
282
|
continue;
|
|
280
283
|
}
|
|
281
|
-
chunk.appendString(
|
|
284
|
+
chunk.appendString(clean + "\n");
|
|
282
285
|
}
|
|
283
286
|
}
|
|
284
|
-
blocksToString(blocks
|
|
287
|
+
blocksToString(blocks) {
|
|
285
288
|
let ret = "";
|
|
286
289
|
for (const block of blocks) {
|
|
287
290
|
if (block === "") {
|
|
@@ -293,7 +296,7 @@ class ValueBodyTranspiler {
|
|
|
293
296
|
if (clean.trim() === "") {
|
|
294
297
|
continue;
|
|
295
298
|
}
|
|
296
|
-
ret +=
|
|
299
|
+
ret += clean + "\n";
|
|
297
300
|
}
|
|
298
301
|
}
|
|
299
302
|
return ret;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as abaplint from "@abaplint/core";
|
|
2
|
+
import { Traversal } from "../traversal";
|
|
3
|
+
/** the Sources and Targets of a DATASET statement, each found by the
|
|
4
|
+
* keywords right in front of it: "MESSAGE" in "FOR INPUT IN BINARY MODE
|
|
5
|
+
* MESSAGE m", "LENGTH" only when not "MAXIMUM LENGTH" or "ACTUAL LENGTH" */
|
|
6
|
+
export declare class DatasetOperands {
|
|
7
|
+
private readonly operands;
|
|
8
|
+
private readonly words;
|
|
9
|
+
constructor(node: abaplint.Nodes.StatementNode, traversal: Traversal);
|
|
10
|
+
/** whether the statement has these keywords in a row: its own tokens only,
|
|
11
|
+
* never the text of an operand, so a variable lv_type or a literal
|
|
12
|
+
* 'no end of line' is not an addition */
|
|
13
|
+
has(words: string): boolean;
|
|
14
|
+
/** the dataset name, or the source of TRANSFER: the first operand */
|
|
15
|
+
first(): string;
|
|
16
|
+
after(words: string, notAfter?: string[]): string | undefined;
|
|
17
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DatasetOperands = void 0;
|
|
37
|
+
const abaplint = __importStar(require("@abaplint/core"));
|
|
38
|
+
/** the Sources and Targets of a DATASET statement, each found by the
|
|
39
|
+
* keywords right in front of it: "MESSAGE" in "FOR INPUT IN BINARY MODE
|
|
40
|
+
* MESSAGE m", "LENGTH" only when not "MAXIMUM LENGTH" or "ACTUAL LENGTH" */
|
|
41
|
+
class DatasetOperands {
|
|
42
|
+
operands = [];
|
|
43
|
+
words = [];
|
|
44
|
+
constructor(node, traversal) {
|
|
45
|
+
let keywords = [];
|
|
46
|
+
for (const c of node.getChildren()) {
|
|
47
|
+
if (c instanceof abaplint.Nodes.TokenNode) {
|
|
48
|
+
keywords.push(c.getFirstToken().getStr().toUpperCase());
|
|
49
|
+
this.words.push(c.getFirstToken().getStr().toUpperCase());
|
|
50
|
+
}
|
|
51
|
+
else if (c instanceof abaplint.Nodes.ExpressionNode) {
|
|
52
|
+
this.operands.push({ keywords, code: traversal.traverse(c).getCode() });
|
|
53
|
+
keywords = [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** whether the statement has these keywords in a row: its own tokens only,
|
|
58
|
+
* never the text of an operand, so a variable lv_type or a literal
|
|
59
|
+
* 'no end of line' is not an addition */
|
|
60
|
+
has(words) {
|
|
61
|
+
// abaplint splits UTF-8, NON-UNICODE and BYTE-ORDER into three tokens
|
|
62
|
+
const joined = this.words.join(" ").replace(/ - /g, "-");
|
|
63
|
+
return (" " + joined + " ").includes(" " + words + " ");
|
|
64
|
+
}
|
|
65
|
+
/** the dataset name, or the source of TRANSFER: the first operand */
|
|
66
|
+
first() {
|
|
67
|
+
return this.operands[0].code;
|
|
68
|
+
}
|
|
69
|
+
after(words, notAfter = []) {
|
|
70
|
+
const want = words.split(" ");
|
|
71
|
+
for (const o of this.operands.slice(1)) {
|
|
72
|
+
const tail = o.keywords.slice(-want.length);
|
|
73
|
+
const before = o.keywords[o.keywords.length - want.length - 1];
|
|
74
|
+
if (tail.join(" ") === words && (before === undefined || notAfter.includes(before) === false)) {
|
|
75
|
+
return o.code;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.DatasetOperands = DatasetOperands;
|
|
82
|
+
//# sourceMappingURL=_dataset.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as abaplint from "@abaplint/core";
|
|
2
|
+
import { Traversal } from "../traversal";
|
|
3
|
+
/** PARAMETERS and SELECT-OPTIONS declare program variables that exist, with their
|
|
4
|
+
* DEFAULT values, before INITIALIZATION runs. Without LOWER CASE a character-like
|
|
5
|
+
* default is upper-cased (measured on a 7.5x system: 'abcdef' into a parameter of
|
|
6
|
+
* length 4 is "ABCD", a string parameter and the LOW of a select-option likewise) */
|
|
7
|
+
export declare class SelectionDefault {
|
|
8
|
+
static variable(node: abaplint.Nodes.StatementNode, traversal: Traversal): {
|
|
9
|
+
name: string;
|
|
10
|
+
type: abaplint.AbstractType;
|
|
11
|
+
} | undefined;
|
|
12
|
+
/** the expression after a keyword of the statement, eg. DEFAULT; only a keyword
|
|
13
|
+
* token counts, an operand named like the keyword does not */
|
|
14
|
+
static after(node: abaplint.Nodes.StatementNode, keyword: string): abaplint.Nodes.ExpressionNode | undefined;
|
|
15
|
+
/** sets the target to a DEFAULT operand, a Constant or a FieldChain, and upper-cases
|
|
16
|
+
* it when the target is character-like and the statement has no LOWER CASE */
|
|
17
|
+
static set(node: abaplint.Nodes.StatementNode, target: string, targetType: abaplint.AbstractType, operand: abaplint.Nodes.ExpressionNode, traversal: Traversal): string;
|
|
18
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SelectionDefault = void 0;
|
|
37
|
+
const abaplint = __importStar(require("@abaplint/core"));
|
|
38
|
+
const traversal_1 = require("../traversal");
|
|
39
|
+
/** PARAMETERS and SELECT-OPTIONS declare program variables that exist, with their
|
|
40
|
+
* DEFAULT values, before INITIALIZATION runs. Without LOWER CASE a character-like
|
|
41
|
+
* default is upper-cased (measured on a 7.5x system: 'abcdef' into a parameter of
|
|
42
|
+
* length 4 is "ABCD", a string parameter and the LOW of a select-option likewise) */
|
|
43
|
+
class SelectionDefault {
|
|
44
|
+
static variable(node, traversal) {
|
|
45
|
+
const token = node.findDirectExpression(abaplint.Expressions.FieldSub)?.getFirstToken();
|
|
46
|
+
if (token === undefined) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const found = traversal.findCurrentScopeByToken(token)?.findVariable(token.getStr());
|
|
50
|
+
if (found === undefined) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
name: traversal_1.Traversal.prefixVariable(traversal_1.Traversal.escapeNamespace(found.getName().toLowerCase())),
|
|
55
|
+
type: found.getType(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** the expression after a keyword of the statement, eg. DEFAULT; only a keyword
|
|
59
|
+
* token counts, an operand named like the keyword does not */
|
|
60
|
+
static after(node, keyword) {
|
|
61
|
+
const children = node.getChildren();
|
|
62
|
+
for (let i = 0; i < children.length - 1; i++) {
|
|
63
|
+
if (children[i] instanceof abaplint.Nodes.TokenNode && children[i].concatTokens().toUpperCase() === keyword) {
|
|
64
|
+
return children[i + 1];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
/** sets the target to a DEFAULT operand, a Constant or a FieldChain, and upper-cases
|
|
70
|
+
* it when the target is character-like and the statement has no LOWER CASE */
|
|
71
|
+
static set(node, target, targetType, operand, traversal) {
|
|
72
|
+
let code = "\n" + target + ".set(" + traversal.traverse(operand).getCode() + ");";
|
|
73
|
+
const characterLike = targetType instanceof abaplint.BasicTypes.CharacterType
|
|
74
|
+
|| targetType instanceof abaplint.BasicTypes.StringType;
|
|
75
|
+
if (node.findDirectTokenByText("LOWER") === undefined && characterLike === true) {
|
|
76
|
+
code += "\n" + target + ".set(abap.builtin.to_upper({val: " + target + "}));";
|
|
77
|
+
}
|
|
78
|
+
return code;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.SelectionDefault = SelectionDefault;
|
|
82
|
+
//# sourceMappingURL=_selection.js.map
|
|
@@ -43,15 +43,16 @@ class AssignTranspiler {
|
|
|
43
43
|
if (node?.findFirstExpression(abaplint.Expressions.TableExpression) === undefined) {
|
|
44
44
|
return code;
|
|
45
45
|
}
|
|
46
|
+
// not indented, Chunk.runIndentationLogic indents by brace depth
|
|
46
47
|
return `(await (async () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
try {
|
|
49
|
+
return ${code};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (abap.isLineNotFound(error)) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
55
56
|
})())`;
|
|
56
57
|
}
|
|
57
58
|
transpile(node, traversal) {
|
|
@@ -145,7 +145,7 @@ class CallTranspiler {
|
|
|
145
145
|
post += `\nabap.builtin.sy.get().subrc.set(0);
|
|
146
146
|
} catch (e) {
|
|
147
147
|
if (e.classic) {
|
|
148
|
-
|
|
148
|
+
switch (e.classic.toUpperCase()) {\n`;
|
|
149
149
|
for (const e of node.findAllExpressions(abaplint.Expressions.ParameterException)) {
|
|
150
150
|
const name = e.getFirstToken().getStr().toUpperCase();
|
|
151
151
|
// todo, also handle SimpleChain,
|
|
@@ -164,9 +164,9 @@ if (e.classic) {
|
|
|
164
164
|
post += `case "${name}": abap.builtin.sy.get().subrc.set(${value}); break;\n`;
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
post += `
|
|
167
|
+
post += `}
|
|
168
168
|
} else {
|
|
169
|
-
|
|
169
|
+
throw e;
|
|
170
170
|
}
|
|
171
171
|
}`;
|
|
172
172
|
return { pre, post };
|
|
@@ -65,12 +65,13 @@ class CallFunctionTranspiler {
|
|
|
65
65
|
}
|
|
66
66
|
const calling = node.findExpressionAfterToken("CALLING");
|
|
67
67
|
const dest = node.findDirectExpression(abaplint.Expressions.Destination)?.findDirectExpression(abaplint.Expressions.Source);
|
|
68
|
-
|
|
68
|
+
const updateTask = node.findDirectTokenByText("UPDATE") !== undefined;
|
|
69
|
+
if (dest && updateTask === false) {
|
|
69
70
|
const s = new expressions_1.SourceTranspiler(true).transpile(dest, traversal);
|
|
70
71
|
param = param.replace("{", ",").replace(/}$/, "");
|
|
71
72
|
ret.appendString(`await abap.statements.callFunction({name:${fmname},destination:${s.getCode()}${param}});`);
|
|
72
73
|
}
|
|
73
|
-
else if (calling) {
|
|
74
|
+
else if (calling && updateTask === false) {
|
|
74
75
|
param = param.replace("{", ",").replace(/}$/, "");
|
|
75
76
|
// typically used in combination with STARTING NEW TASK so dont await,
|
|
76
77
|
ret.appendString(`abap.statements.callFunction({name:${fmname},calling:this.${calling.concatTokens().toLowerCase()}${param}});`);
|
|
@@ -80,7 +81,13 @@ class CallFunctionTranspiler {
|
|
|
80
81
|
const call = `abap.FunctionModules[${fmname}]`;
|
|
81
82
|
// eslint-disable-next-line max-len
|
|
82
83
|
ret.appendString(`if (${call} === undefined) { if (${illegalFunc} === undefined) { throw "CX_SY_DYN_CALL_ILLEGAL_FUNC not found"; } else { throw await new ${illegalFunc}().constructor_({function: new abap.types.String().set(${fmname})});} }\n`);
|
|
83
|
-
|
|
84
|
+
if (updateTask) {
|
|
85
|
+
param = param.replace("{", ",").replace(/}$/, "");
|
|
86
|
+
ret.appendString(`await abap.statements.callFunction({name:${fmname},updateTask:true${param}});`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
ret.appendString(`await ${call}(${param});`);
|
|
90
|
+
}
|
|
84
91
|
}
|
|
85
92
|
if (exceptions) {
|
|
86
93
|
const build = call_1.CallTranspiler.buildExceptions(exceptions);
|
|
@@ -3,5 +3,5 @@ import { IStatementTranspiler } from "./_statement_transpiler";
|
|
|
3
3
|
import { Traversal } from "../traversal";
|
|
4
4
|
import { Chunk } from "../chunk";
|
|
5
5
|
export declare class CloseDatasetTranspiler implements IStatementTranspiler {
|
|
6
|
-
transpile(
|
|
6
|
+
transpile(node: abaplint.Nodes.StatementNode, traversal: Traversal): Chunk;
|
|
7
7
|
}
|