@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.
@@ -7,5 +7,6 @@ export declare class SelectTranspiler implements IStatementTranspiler {
7
7
  private findTable;
8
8
  private dynamicSelectList;
9
9
  private dynamicSQLClause;
10
+ private dynamicHavingClause;
10
11
  private isWhereExpression;
11
12
  }
@@ -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
- select += "HAVING " + new expressions_1.SQLCondTranspiler().transpile(havingCond, traversal, table).getCode() + " ";
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;
@@ -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;
@@ -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 id = context.getIdentifier();
930
- if (id instanceof abaplint.Types.ClassDefinition || id instanceof abaplint.Types.InterfaceDefinition) {
931
- const concat = c.concatTokens();
932
- if (concat.includes("~")) {
933
- const [iname, aname] = concat.split("~");
934
- const intf = this.findInterfaceDefinition(iname, scope);
935
- context = intf?.getAttributes().findByName(aname)?.getType();
936
- }
937
- else {
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.98",
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.66",
32
+ "@abaplint/core": "^2.120.69",
33
33
  "source-map": "^0.8.0"
34
34
  },
35
35
  "devDependencies": {