@abaplint/transpiler 2.13.98 → 2.13.100
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.
|
@@ -105,8 +105,18 @@ class SelectTranspiler {
|
|
|
105
105
|
const having = node.findDirectExpression(abaplint.Expressions.Select)
|
|
106
106
|
?.findDirectExpression(abaplint.Expressions.SQLHaving);
|
|
107
107
|
const havingCond = having?.findFirstExpression(abaplint.Expressions.SQLCond);
|
|
108
|
+
let dynamicHaving;
|
|
108
109
|
if (havingCond) {
|
|
109
|
-
|
|
110
|
+
const dynamic = havingCond.findFirstExpression(abaplint.Expressions.Dynamic);
|
|
111
|
+
const chain = dynamic?.findFirstExpression(abaplint.Expressions.FieldChain);
|
|
112
|
+
if (chain && havingCond.concatTokens() === dynamic?.concatTokens()) {
|
|
113
|
+
dynamicHaving = dynamic;
|
|
114
|
+
const code = new expressions_1.FieldChainTranspiler(false).transpile(chain, traversal).getCode();
|
|
115
|
+
select += `" + ${this.dynamicHavingClause(code)} + " `;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
select += "HAVING " + new expressions_1.SQLCondTranspiler().transpile(havingCond, traversal, table).getCode() + " ";
|
|
119
|
+
}
|
|
110
120
|
}
|
|
111
121
|
const upTo = node.findFirstExpression(abaplint.Expressions.SQLUpTo);
|
|
112
122
|
if (upTo) {
|
|
@@ -126,6 +136,9 @@ class SelectTranspiler {
|
|
|
126
136
|
const groupByDynamics = new Set(groupBy?.findAllExpressionsRecursive(abaplint.Expressions.Dynamic) || []);
|
|
127
137
|
const orderByDynamics = new Set(orderBy?.findAllExpressionsRecursive(abaplint.Expressions.Dynamic) || []);
|
|
128
138
|
for (const d of node.findAllExpressionsRecursive(abaplint.Expressions.Dynamic)) {
|
|
139
|
+
if (d === dynamicHaving) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
129
142
|
const chain = d.findFirstExpression(abaplint.Expressions.FieldChain);
|
|
130
143
|
if (chain) {
|
|
131
144
|
const search = d.concatTokens();
|
|
@@ -282,6 +295,13 @@ abap.builtin.sy.get().dbcnt.set(${target}.getArrayLength());
|
|
|
282
295
|
+ ` ? (${code}.array().length === 0 ? "" : "${keyword} " + ${code}.array().map(row => row.get()).join(", "))`
|
|
283
296
|
+ ` : (("" + ${code}.get()).trim() === "" ? "" : "${keyword} " + ${code}.get()))`;
|
|
284
297
|
}
|
|
298
|
+
dynamicHavingClause(code) {
|
|
299
|
+
return `((value) => {
|
|
300
|
+
const condition = value instanceof abap.types.Table || value instanceof abap.types.HashedTable
|
|
301
|
+
? value.array().map(row => row.get()).join(" ") : value.get();
|
|
302
|
+
return condition.trim() === "" ? "" : "HAVING " + abap.expandDynamic(condition, (name) => {try { return eval(name);} catch {}});
|
|
303
|
+
})(${code})`;
|
|
304
|
+
}
|
|
285
305
|
isWhereExpression(node, expression) {
|
|
286
306
|
// check if previous token before sqlCond is "WHERE". It could also be "ON" in case of join condition
|
|
287
307
|
let prevToken;
|
package/build/src/traversal.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export declare class Traversal {
|
|
|
15
15
|
private readonly doOrWhileIndexBackups;
|
|
16
16
|
private readonly statementsInsideLoop;
|
|
17
17
|
private readonly enclosingDoOrWhile;
|
|
18
|
+
/** DATA, CONSTANTS and FIELD-SYMBOLS nested in blocks, already declared in front of the block */
|
|
19
|
+
private readonly declaredEarly;
|
|
20
|
+
private emittingEarly;
|
|
21
|
+
private blockDepth;
|
|
18
22
|
readonly reg: abaplint.IRegistry;
|
|
19
23
|
readonly options: ITranspilerOptions | undefined;
|
|
20
24
|
constructor(spaghetti: abaplint.ISpaghettiScope, file: abaplint.ABAPFile, obj: abaplint.ABAPObject, reg: abaplint.IRegistry, options?: ITranspilerOptions);
|
|
@@ -106,6 +110,15 @@ export declare class Traversal {
|
|
|
106
110
|
buildInternalName(name: string, def: abaplint.IClassDefinition | abaplint.IInterfaceDefinition | undefined): string;
|
|
107
111
|
lookupClassOrInterface(name: string | undefined, token: abaplint.Token | undefined, directGlobal?: boolean): string;
|
|
108
112
|
buildPrefix(): string;
|
|
113
|
+
/** In ABAP, DATA, CONSTANTS and FIELD-SYMBOLS are visible in the whole method/form/program, but a
|
|
114
|
+
* javascript "let" inside the block of an IF, LOOP, TRY etc. is not. So before each block on
|
|
115
|
+
* method/form/program level the declarations nested in it are output, in source order, and
|
|
116
|
+
* skipped inside the block. They then exist after the block even if the branch did not run,
|
|
117
|
+
* and are initialized once, also when the block is a loop */
|
|
118
|
+
private traverseProcedureLevel;
|
|
119
|
+
private declareNestedEarly;
|
|
120
|
+
private static isDeclaration;
|
|
121
|
+
private static findNestedDeclarations;
|
|
109
122
|
protected traverseStructure(node: abaplint.Nodes.StructureNode): Chunk;
|
|
110
123
|
protected traverseStatement(node: abaplint.Nodes.StatementNode): Chunk;
|
|
111
124
|
protected traverseExpression(node: abaplint.Nodes.ExpressionNode): Chunk;
|
package/build/src/traversal.js
CHANGED
|
@@ -88,6 +88,10 @@ class Traversal {
|
|
|
88
88
|
doOrWhileIndexBackups = new Map();
|
|
89
89
|
statementsInsideLoop = new WeakSet();
|
|
90
90
|
enclosingDoOrWhile = new WeakMap();
|
|
91
|
+
/** DATA, CONSTANTS and FIELD-SYMBOLS nested in blocks, already declared in front of the block */
|
|
92
|
+
declaredEarly = new WeakSet();
|
|
93
|
+
emittingEarly = false;
|
|
94
|
+
blockDepth = 0;
|
|
91
95
|
reg;
|
|
92
96
|
options;
|
|
93
97
|
constructor(spaghetti, file, obj, reg, options) {
|
|
@@ -926,17 +930,15 @@ this.INTERNAL_ID = abap.internalIdCounter++;\n`;
|
|
|
926
930
|
}
|
|
927
931
|
else if (c.get() instanceof abaplint.Expressions.AttributeName
|
|
928
932
|
&& context instanceof abaplint.BasicTypes.ObjectReferenceType) {
|
|
929
|
-
const
|
|
930
|
-
if (
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
context = id.getAttributes().findByName(concat)?.getType();
|
|
939
|
-
}
|
|
933
|
+
const concat = c.concatTokens();
|
|
934
|
+
if (concat.includes("~")) {
|
|
935
|
+
const [iname, aname] = concat.split("~");
|
|
936
|
+
const intf = this.findInterfaceDefinition(iname, scope);
|
|
937
|
+
context = intf?.getAttributes().findByName(aname)?.getType();
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
// Global references can carry an identifier rather than a class definition.
|
|
941
|
+
context = this.narrowContextAttribute(context, concat, scope);
|
|
940
942
|
}
|
|
941
943
|
}
|
|
942
944
|
else if (c.get() instanceof abaplint.Expressions.AttributeName
|
|
@@ -950,7 +952,7 @@ this.INTERNAL_ID = abap.internalIdCounter++;\n`;
|
|
|
950
952
|
return context;
|
|
951
953
|
}
|
|
952
954
|
isInsideLoop(node) {
|
|
953
|
-
return this.statementsInsideLoop.has(node);
|
|
955
|
+
return this.statementsInsideLoop.has(node) && !this.declaredEarly.has(node);
|
|
954
956
|
}
|
|
955
957
|
isInsideDoOrWhile(node) {
|
|
956
958
|
return this.findCurrentDoOrWhileIndexBackup(node) !== undefined;
|
|
@@ -1045,7 +1047,87 @@ this.INTERNAL_ID = abap.internalIdCounter++;\n`;
|
|
|
1045
1047
|
return this.obj.getType() + "-" + this.obj.getName() + "-";
|
|
1046
1048
|
}
|
|
1047
1049
|
////////////////////////////
|
|
1050
|
+
/** In ABAP, DATA, CONSTANTS and FIELD-SYMBOLS are visible in the whole method/form/program, but a
|
|
1051
|
+
* javascript "let" inside the block of an IF, LOOP, TRY etc. is not. So before each block on
|
|
1052
|
+
* method/form/program level the declarations nested in it are output, in source order, and
|
|
1053
|
+
* skipped inside the block. They then exist after the block even if the branch did not run,
|
|
1054
|
+
* and are initialized once, also when the block is a loop */
|
|
1055
|
+
traverseProcedureLevel(node) {
|
|
1056
|
+
const ret = new chunk_1.Chunk();
|
|
1057
|
+
for (const c of node.getChildren()) {
|
|
1058
|
+
if (c instanceof abaplint.Nodes.StructureNode && Traversal.isDeclaration(c) === false) {
|
|
1059
|
+
ret.appendChunk(this.declareNestedEarly(c));
|
|
1060
|
+
this.blockDepth++;
|
|
1061
|
+
try {
|
|
1062
|
+
ret.appendChunk(this.traverseStructure(c));
|
|
1063
|
+
}
|
|
1064
|
+
finally {
|
|
1065
|
+
this.blockDepth--;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
else {
|
|
1069
|
+
ret.appendChunk(this.traverse(c));
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return ret;
|
|
1073
|
+
}
|
|
1074
|
+
declareNestedEarly(block) {
|
|
1075
|
+
const found = [];
|
|
1076
|
+
Traversal.findNestedDeclarations(block, found);
|
|
1077
|
+
for (const node of found) {
|
|
1078
|
+
this.declaredEarly.add(node);
|
|
1079
|
+
if (node instanceof abaplint.Nodes.StructureNode) {
|
|
1080
|
+
for (const statement of node.findAllStatementNodes()) {
|
|
1081
|
+
this.declaredEarly.add(statement);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
const ret = new chunk_1.Chunk();
|
|
1086
|
+
this.emittingEarly = true;
|
|
1087
|
+
try {
|
|
1088
|
+
for (const node of found) {
|
|
1089
|
+
ret.appendChunk(this.traverse(node));
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
finally {
|
|
1093
|
+
this.emittingEarly = false;
|
|
1094
|
+
}
|
|
1095
|
+
return ret;
|
|
1096
|
+
}
|
|
1097
|
+
static isDeclaration(node) {
|
|
1098
|
+
const get = node.get();
|
|
1099
|
+
return get instanceof abaplint.Structures.Data
|
|
1100
|
+
|| get instanceof abaplint.Structures.Constants
|
|
1101
|
+
|| get instanceof abaplint.Structures.Types;
|
|
1102
|
+
}
|
|
1103
|
+
static findNestedDeclarations(node, found) {
|
|
1104
|
+
for (const c of node.getChildren()) {
|
|
1105
|
+
if (c instanceof abaplint.Nodes.StatementNode) {
|
|
1106
|
+
const get = c.get();
|
|
1107
|
+
if (get instanceof abaplint.Statements.Data
|
|
1108
|
+
|| get instanceof abaplint.Statements.Constant
|
|
1109
|
+
|| get instanceof abaplint.Statements.FieldSymbol) {
|
|
1110
|
+
found.push(c);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
else if (c instanceof abaplint.Nodes.StructureNode) {
|
|
1114
|
+
const get = c.get();
|
|
1115
|
+
if (get instanceof abaplint.Structures.Data || get instanceof abaplint.Structures.Constants) {
|
|
1116
|
+
found.push(c);
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
Traversal.findNestedDeclarations(c, found);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1048
1124
|
traverseStructure(node) {
|
|
1125
|
+
if (this.declaredEarly.has(node) && this.emittingEarly === false) {
|
|
1126
|
+
return new chunk_1.Chunk();
|
|
1127
|
+
}
|
|
1128
|
+
else if (this.blockDepth === 0 && node.get() instanceof abaplint.Structures.Normal) {
|
|
1129
|
+
return this.traverseProcedureLevel(node);
|
|
1130
|
+
}
|
|
1049
1131
|
const list = StructureTranspilers;
|
|
1050
1132
|
const ret = new chunk_1.Chunk();
|
|
1051
1133
|
const search = node.get().constructor.name + "Transpiler";
|
|
@@ -1068,6 +1150,9 @@ this.INTERNAL_ID = abap.internalIdCounter++;\n`;
|
|
|
1068
1150
|
return ret;
|
|
1069
1151
|
}
|
|
1070
1152
|
traverseStatement(node) {
|
|
1153
|
+
if (this.declaredEarly.has(node) && this.emittingEarly === false) {
|
|
1154
|
+
return new chunk_1.Chunk();
|
|
1155
|
+
}
|
|
1071
1156
|
const list = StatementTranspilers;
|
|
1072
1157
|
const search = node.get().constructor.name + "Transpiler";
|
|
1073
1158
|
if (list[search]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/transpiler",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.100",
|
|
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.69",
|
|
33
33
|
"source-map": "^0.8.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|