@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.
Files changed (48) hide show
  1. package/build/src/chunk.js +2 -1
  2. package/build/src/db/schema_generation/pg_database_schema.js +3 -0
  3. package/build/src/db/schema_generation/snowflake_database_schema.js +3 -0
  4. package/build/src/db/schema_generation/sqlite_database_schema.js +3 -0
  5. package/build/src/expressions/reduce_body.js +12 -16
  6. package/build/src/expressions/source.d.ts +3 -1
  7. package/build/src/expressions/source.js +29 -5
  8. package/build/src/expressions/sql_cond.d.ts +1 -0
  9. package/build/src/expressions/sql_cond.js +15 -3
  10. package/build/src/expressions/value_body.js +25 -22
  11. package/build/src/statements/_dataset.d.ts +17 -0
  12. package/build/src/statements/_dataset.js +82 -0
  13. package/build/src/statements/_selection.d.ts +18 -0
  14. package/build/src/statements/_selection.js +82 -0
  15. package/build/src/statements/assign.js +9 -8
  16. package/build/src/statements/call.js +3 -3
  17. package/build/src/statements/call_function.js +10 -3
  18. package/build/src/statements/close_dataset.d.ts +1 -1
  19. package/build/src/statements/close_dataset.js +40 -2
  20. package/build/src/statements/create_data.js +1 -1
  21. package/build/src/statements/delete_dataset.d.ts +1 -1
  22. package/build/src/statements/delete_dataset.js +40 -2
  23. package/build/src/statements/generate_subroutine.d.ts +3 -1
  24. package/build/src/statements/generate_subroutine.js +72 -2
  25. package/build/src/statements/get_dataset.d.ts +1 -1
  26. package/build/src/statements/get_dataset.js +14 -2
  27. package/build/src/statements/loop.d.ts +1 -0
  28. package/build/src/statements/loop.js +12 -0
  29. package/build/src/statements/move.d.ts +2 -0
  30. package/build/src/statements/move.js +70 -1
  31. package/build/src/statements/open_dataset.d.ts +1 -1
  32. package/build/src/statements/open_dataset.js +32 -2
  33. package/build/src/statements/parameter.d.ts +2 -1
  34. package/build/src/statements/parameter.js +68 -2
  35. package/build/src/statements/perform.js +13 -1
  36. package/build/src/statements/read_dataset.d.ts +1 -1
  37. package/build/src/statements/read_dataset.js +16 -2
  38. package/build/src/statements/select.js +78 -13
  39. package/build/src/statements/select_option.d.ts +1 -1
  40. package/build/src/statements/select_option.js +63 -2
  41. package/build/src/statements/set_dataset.d.ts +1 -1
  42. package/build/src/statements/set_dataset.js +10 -2
  43. package/build/src/statements/sort.d.ts +2 -0
  44. package/build/src/statements/sort.js +33 -4
  45. package/build/src/statements/transfer.d.ts +1 -1
  46. package/build/src/statements/transfer.js +14 -2
  47. package/build/src/structures/class_definition.js +10 -9
  48. package/package.json +2 -2
@@ -102,6 +102,12 @@ class SelectTranspiler {
102
102
  if (groupBy) {
103
103
  select += new sql_group_by_1.SQLGroupByTranspiler().transpile(groupBy, traversal).getCode() + " ";
104
104
  }
105
+ const having = node.findDirectExpression(abaplint.Expressions.Select)
106
+ ?.findDirectExpression(abaplint.Expressions.SQLHaving);
107
+ const havingCond = having?.findFirstExpression(abaplint.Expressions.SQLCond);
108
+ if (havingCond) {
109
+ select += "HAVING " + new expressions_1.SQLCondTranspiler().transpile(havingCond, traversal, table).getCode() + " ";
110
+ }
105
111
  const upTo = node.findFirstExpression(abaplint.Expressions.SQLUpTo);
106
112
  if (upTo) {
107
113
  const s = upTo.findFirstExpression(abaplint.Expressions.SimpleSource3);
@@ -168,26 +174,85 @@ class SelectTranspiler {
168
174
  const faeTranspiled = new expressions_1.SQLSourceTranspiler().transpile(fn, traversal).getCode();
169
175
  // ABAP semantics: with an empty driving table the whole WHERE condition is ignored
170
176
  const selectEmpty = select.replace(whereClause, "");
171
- select = select.replace(new RegExp(" " + escapeRegExp(faeTranspiled), "g"), " " + unique);
172
- select = select.replace(unique + ".get().table_line.get()", unique + ".get()"); // there can be only one?
177
+ const bindRow = (text) => text
178
+ .replace(new RegExp(" " + escapeRegExp(faeTranspiled), "g"), " " + unique)
179
+ .replace(unique + ".get().table_line.get()", unique + ".get()"); // there can be only one?
180
+ select = bindRow(select);
181
+ const where = bindRow(whereClause);
173
182
  // FOR ALL ENTRIES removes duplicate rows from the result: duplicates of
174
183
  // the selected columns, so de-duplicate by the target's components. The
175
184
  // DB key alone is wrong for projections (INTO CORRESPONDING FIELDS
176
185
  // without the key fields crashed on the missing component names)
177
186
  const by = `Object.keys(${target}.getRowType().get())`;
187
+ // generated code is not indented, Chunk.runIndentationLogic indents by brace depth
188
+ const dedup = `if (!(${target} instanceof abap.types.HashedTable) && ${target}.getOptions()?.primaryKey?.type !== "SORTED") {
189
+ abap.statements.sort(${target}, {by: ${by}.map(k => { return {component: k}; })});
190
+ await abap.statements.deleteInternal(${target}, {adjacent: true, allFields: true});
191
+ }`;
192
+ const at = where.startsWith("WHERE ") ? select.indexOf(where) : -1;
193
+ // UP TO with ORDER BY keeps the first n rows in that order; the blocks
194
+ // trim after the de-duplicating sort, so that pair stays row by row
195
+ if (concat.startsWith("SELECT SINGLE ") || at < 0 || (upTo && orderBy)) {
196
+ const code = `if (${faeTranspiled}.array().length === 0) {
197
+ await abap.statements.select(${target}, {select: "${selectEmpty.trim()}"${extra}});
198
+ } else {
199
+ const ${unique2} = ${faeTranspiled}.array();
200
+ ${target}.clear();
201
+ for await (const ${unique} of ${unique2}) {
202
+ await abap.statements.select(${target}, {select: "${select.trim()}"${extra}}, {appending: true});
203
+ }
204
+ ${dedup}
205
+ abap.builtin.sy.get().dbcnt.set(${target}.getArrayLength());
206
+ }`;
207
+ return new chunk_1.Chunk().append(code, node, traversal);
208
+ }
209
+ // One SELECT per block of driving rows, similar to the kernel's blocking
210
+ // of FOR ALL ENTRIES (rsdb/max_blocking_factor): the condition of each row in
211
+ // parentheses, joined with OR. Not one SELECT per row. UP TO n ROWS
212
+ // counts the whole de-duplicated result, not each block (measured on
213
+ // a 7.5x system: two driving rows and UP TO 3 give 3 rows).
214
+ let tail = select.substring(at + where.length);
215
+ let upToCode = "0";
216
+ if (upTo) {
217
+ const s = upTo.findFirstExpression(abaplint.Expressions.SimpleSource3);
218
+ if (s) {
219
+ const n = new expressions_1.SourceTranspiler(true).transpile(s, traversal).getCode();
220
+ tail = tail.replace(`UP TO " + ${n} + " ROWS `, "");
221
+ upToCode = `parseInt(${n}, 10)`;
222
+ }
223
+ else {
224
+ tail = tail.replace(upTo.concatTokens() + " ", "");
225
+ upToCode = String(parseInt(upTo.findFirstExpression(abaplint.Expressions.Integer)?.concatTokens() ?? "0", 10));
226
+ }
227
+ }
228
+ const head = select.substring(0, at);
229
+ const condition = where.substring("WHERE ".length).trimEnd();
230
+ const unique3 = unique_identifier_1.UniqueIdentifier.get();
231
+ const unique4 = unique_identifier_1.UniqueIdentifier.get();
178
232
  const code = `if (${faeTranspiled}.array().length === 0) {
179
- await abap.statements.select(${target}, {select: "${selectEmpty.trim()}"${extra}});
233
+ await abap.statements.select(${target}, {select: "${selectEmpty.trim()}"${extra}});
234
+ } else {
235
+ const ${unique2} = ${faeTranspiled}.array();
236
+ ${target}.clear();
237
+ const ${unique3} = (${unique}) => "(${condition})";
238
+ for (let ${unique4} = 0; ${unique4} < ${unique2}.length; ${unique4} += 50) {
239
+ const ${unique4}where = ${unique2}.slice(${unique4}, ${unique4} + 50).map(${unique3}).join(" OR ");
240
+ await abap.statements.select(${target}, {select: "${head}WHERE " + ${unique4}where + " ${tail.trim()}"${extra}}, {appending: true});
241
+ }
242
+ ${dedup}
243
+ const ${unique4}max = ${upToCode};
244
+ if (${unique4}max > 0 && ${target}.getArrayLength() > ${unique4}max) {
245
+ if (${target} instanceof abap.types.HashedTable) {
246
+ for (const ${unique4}row of ${target}.array().slice(${unique4}max)) {
247
+ await abap.statements.deleteInternal(${target}, {fromValue: ${unique4}row});
248
+ }
180
249
  } else {
181
- const ${unique2} = ${faeTranspiled}.array();
182
- ${target}.clear();
183
- for await (const ${unique} of ${unique2}) {
184
- await abap.statements.select(${target}, {select: "${select.trim()}"${extra}}, {appending: true});
185
- }
186
- if (!(${target} instanceof abap.types.HashedTable) && ${target}.getOptions()?.primaryKey?.type !== "SORTED") {
187
- abap.statements.sort(${target}, {by: ${by}.map(k => { return {component: k}; })});
188
- await abap.statements.deleteInternal(${target}, {adjacent: true, allFields: true});
189
- }
190
- abap.builtin.sy.get().dbcnt.set(${target}.getArrayLength());
250
+ while (${target}.getArrayLength() > ${unique4}max) {
251
+ ${target}.deleteIndex(${target}.getArrayLength() - 1);
252
+ }
253
+ }
254
+ }
255
+ abap.builtin.sy.get().dbcnt.set(${target}.getArrayLength());
191
256
  }`;
192
257
  return new chunk_1.Chunk().append(code, node, traversal);
193
258
  }
@@ -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 SelectOptionTranspiler implements IStatementTranspiler {
6
- transpile(_node: abaplint.Nodes.StatementNode, _traversal: Traversal): Chunk;
6
+ transpile(node: abaplint.Nodes.StatementNode, traversal: Traversal): Chunk;
7
7
  }
@@ -1,10 +1,71 @@
1
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
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.SelectOptionTranspiler = void 0;
37
+ const abaplint = __importStar(require("@abaplint/core"));
4
38
  const chunk_1 = require("../chunk");
39
+ const transpile_types_1 = require("../transpile_types");
40
+ const _selection_1 = require("./_selection");
5
41
  class SelectOptionTranspiler {
6
- transpile(_node, _traversal) {
7
- return new chunk_1.Chunk(`throw new Error("SelectOption, not supported, transpiler");`);
42
+ transpile(node, traversal) {
43
+ const variable = _selection_1.SelectionDefault.variable(node, traversal);
44
+ const row = variable?.type instanceof abaplint.BasicTypes.TableType ? variable.type.getRowType() : undefined;
45
+ const lowType = row instanceof abaplint.BasicTypes.StructureType ? row.getComponentByName("LOW") : undefined;
46
+ if (variable === undefined || lowType === undefined) {
47
+ // eg. FOR (dynamic)
48
+ return new chunk_1.Chunk(`throw new Error("SelectOption, not supported, transpiler");`);
49
+ }
50
+ const { name, type } = variable;
51
+ const ret = new chunk_1.Chunk().appendString("let " + name + " = " + transpile_types_1.TranspileTypes.toType(type) + ";");
52
+ // DEFAULT low [TO high] [OPTION o] [SIGN s] is the first row of the table and
53
+ // stays in the header line: I EQ, or I BT with TO, unless OPTION or SIGN say otherwise
54
+ const low = _selection_1.SelectionDefault.after(node, "DEFAULT");
55
+ if (low === undefined) {
56
+ return ret;
57
+ }
58
+ const high = _selection_1.SelectionDefault.after(node, "TO");
59
+ const option = _selection_1.SelectionDefault.after(node, "OPTION")?.concatTokens().toUpperCase() ?? (high ? "BT" : "EQ");
60
+ const sign = _selection_1.SelectionDefault.after(node, "SIGN")?.concatTokens().toUpperCase() ?? "I";
61
+ ret.appendString("\n" + name + ".get().sign.set('" + sign + "');");
62
+ ret.appendString("\n" + name + ".get().option.set('" + option + "');");
63
+ ret.appendString(_selection_1.SelectionDefault.set(node, name + ".get().low", lowType, low, traversal));
64
+ if (high) {
65
+ ret.appendString(_selection_1.SelectionDefault.set(node, name + ".get().high", lowType, high, traversal));
66
+ }
67
+ ret.appendString("\nabap.statements.append({source: " + name + "});");
68
+ return ret;
8
69
  }
9
70
  }
10
71
  exports.SelectOptionTranspiler = SelectOptionTranspiler;
@@ -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 SetDatasetTranspiler implements IStatementTranspiler {
6
- transpile(_node: abaplint.Nodes.StatementNode, _traversal: Traversal): Chunk;
6
+ transpile(node: abaplint.Nodes.StatementNode, traversal: Traversal): Chunk;
7
7
  }
@@ -2,9 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SetDatasetTranspiler = void 0;
4
4
  const chunk_1 = require("../chunk");
5
+ const _dataset_1 = require("./_dataset");
5
6
  class SetDatasetTranspiler {
6
- transpile(_node, _traversal) {
7
- return new chunk_1.Chunk(`throw new Error("SetDataset, not supported, transpiler");`);
7
+ transpile(node, traversal) {
8
+ const operands = new _dataset_1.DatasetOperands(node, traversal);
9
+ const option = operands.after("POSITION") !== undefined
10
+ ? "position: " + operands.after("POSITION")
11
+ : "endOfFile: true";
12
+ return new chunk_1.Chunk()
13
+ .append("await abap.statements.setDataset(", node, traversal)
14
+ .appendString(operands.first() + ", {" + option + "}")
15
+ .append(");", node.getLastToken(), traversal);
8
16
  }
9
17
  }
10
18
  exports.SetDatasetTranspiler = SetDatasetTranspiler;
@@ -4,5 +4,7 @@ import { Traversal } from "../traversal";
4
4
  import { Chunk } from "../chunk";
5
5
  export declare class SortTranspiler implements IStatementTranspiler {
6
6
  transpile(node: abaplint.Nodes.StatementNode, traversal: Traversal): Chunk;
7
+ private isDynamicName;
8
+ private dynamicName;
7
9
  private findNextText;
8
10
  }
@@ -40,7 +40,8 @@ const expressions_1 = require("../expressions");
40
40
  class SortTranspiler {
41
41
  transpile(node, traversal) {
42
42
  const concat = node.concatTokens().toUpperCase();
43
- const components = node.findDirectExpressions(abaplint.Expressions.ComponentChain);
43
+ const components = node.getChildren().filter(c => c instanceof abaplint.Nodes.ExpressionNode
44
+ && (c.get() instanceof abaplint.Expressions.ComponentChain || this.isDynamicName(c, traversal)));
44
45
  const options = [];
45
46
  if (concat.includes(" BY ") === false) {
46
47
  const descending = node.findDirectTokenByText("DESCENDING") !== undefined;
@@ -52,12 +53,14 @@ class SortTranspiler {
52
53
  const by = [];
53
54
  for (const c of components) {
54
55
  const next = this.findNextText(c, node);
55
- const concat = expressions_1.ComponentChainTranspiler.concat(c, traversal);
56
+ const component = c.get() instanceof abaplint.Expressions.Dynamic
57
+ ? this.dynamicName(c, traversal)
58
+ : `"${expressions_1.ComponentChainTranspiler.concat(c, traversal)}"`;
56
59
  if (next === "DESCENDING") {
57
- by.push(`{component: "${concat}", descending: true}`);
60
+ by.push(`{component: ${component}, descending: true}`);
58
61
  }
59
62
  else {
60
- by.push(`{component: "${concat}"}`);
63
+ by.push(`{component: ${component}}`);
61
64
  }
62
65
  }
63
66
  options.push(`by: [${by.join(",")}]`);
@@ -65,6 +68,32 @@ class SortTranspiler {
65
68
  const target = traversal.traverse(node.findDirectExpression(abaplint.Expressions.Target)).getCode();
66
69
  return new chunk_1.Chunk().append("abap.statements.sort(" + target + ",{" + options.join(",") + "});", node, traversal);
67
70
  }
71
+ // "BY (name)" with the component name in a literal or a character-like field; a sort order table
72
+ // (abap_sortorder_tab) is not handled here
73
+ isDynamicName(c, traversal) {
74
+ if (!(c.get() instanceof abaplint.Expressions.Dynamic)) {
75
+ return false;
76
+ }
77
+ if (c.findFirstExpression(abaplint.Expressions.ConstantString)) {
78
+ return true;
79
+ }
80
+ const field = c.findFirstExpression(abaplint.Expressions.FieldChain);
81
+ if (field === undefined) {
82
+ return false;
83
+ }
84
+ const token = field.getFirstToken();
85
+ const type = traversal.findCurrentScopeByToken(token)?.findVariable(token.getStr())?.getType();
86
+ return !(type instanceof abaplint.BasicTypes.TableType);
87
+ }
88
+ dynamicName(c, traversal) {
89
+ const constant = c.findFirstExpression(abaplint.Expressions.ConstantString);
90
+ if (constant) {
91
+ const str = constant.getFirstToken().getStr();
92
+ return JSON.stringify(str.substring(1, str.length - 1).toLowerCase().trimEnd());
93
+ }
94
+ const field = c.findFirstExpression(abaplint.Expressions.FieldChain);
95
+ return new expressions_1.FieldChainTranspiler(true).transpile(field, traversal).getCode() + ".toLowerCase().trimEnd()";
96
+ }
68
97
  findNextText(c, parent) {
69
98
  const children = parent.getChildren();
70
99
  for (let i = 0; i < children.length; i++) {
@@ -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 TransferTranspiler implements IStatementTranspiler {
6
- transpile(_node: abaplint.Nodes.StatementNode, _traversal: Traversal): Chunk;
6
+ transpile(node: abaplint.Nodes.StatementNode, traversal: Traversal): Chunk;
7
7
  }
@@ -2,9 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TransferTranspiler = void 0;
4
4
  const chunk_1 = require("../chunk");
5
+ const _dataset_1 = require("./_dataset");
5
6
  class TransferTranspiler {
6
- transpile(_node, _traversal) {
7
- return new chunk_1.Chunk(`throw new Error("Transfer, not supported, transpiler");`);
7
+ transpile(node, traversal) {
8
+ const operands = new _dataset_1.DatasetOperands(node, traversal);
9
+ const options = [];
10
+ if (operands.after("LENGTH", ["MAXIMUM", "ACTUAL"]) !== undefined) {
11
+ options.push("length: " + operands.after("LENGTH", ["MAXIMUM", "ACTUAL"]));
12
+ }
13
+ if (operands.has("NO END OF LINE")) {
14
+ options.push("noEndOfLine: true");
15
+ }
16
+ return new chunk_1.Chunk()
17
+ .append("await abap.statements.transfer(", node, traversal)
18
+ .appendString(operands.first() + ", " + operands.after("TO") + ", {" + options.join(", ") + "}")
19
+ .append(");", node.getLastToken(), traversal);
8
20
  }
9
21
  }
10
22
  exports.TransferTranspiler = TransferTranspiler;
@@ -52,17 +52,18 @@ class ClassDefinitionTranspiler {
52
52
  }
53
53
  else {
54
54
  // its an abstract class with only abstract methods
55
+ // not indented, Chunk.runIndentationLogic indents by brace depth
55
56
  return new chunk_1.Chunk(`
56
57
  class ${className?.toLowerCase()} {
57
- static INTERNAL_TYPE = 'CLAS';
58
- static IMPLEMENTED_INTERFACES = [];
59
- static INTERNAL_NAME = 'ABSTRACT_CLASS_INTERNAL_NAME';
60
- static ATTRIBUTES = {};
61
- async constructor_() {
62
- this.me = new abap.types.ABAPObject();
63
- this.me.set(this);
64
- return this;
65
- }
58
+ static INTERNAL_TYPE = 'CLAS';
59
+ static IMPLEMENTED_INTERFACES = [];
60
+ static INTERNAL_NAME = 'ABSTRACT_CLASS_INTERNAL_NAME';
61
+ static ATTRIBUTES = {};
62
+ async constructor_() {
63
+ this.me = new abap.types.ABAPObject();
64
+ this.me.set(this);
65
+ return this;
66
+ }
66
67
  }`);
67
68
  }
68
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler",
3
- "version": "2.13.95",
3
+ "version": "2.13.97",
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.63",
32
+ "@abaplint/core": "^2.120.65",
33
33
  "source-map": "^0.8.0"
34
34
  },
35
35
  "devDependencies": {