@abaplint/transpiler 2.12.9 → 2.12.11

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.
@@ -218,7 +218,7 @@ class SourceTranspiler {
218
218
  }
219
219
  }
220
220
  ret.appendChunk(post);
221
- // console.dir(ret.getCode());
221
+ // console.dir("return: " + ret.getCode());
222
222
  return ret;
223
223
  }
224
224
  }
@@ -3,4 +3,8 @@ import { Traversal } from "../traversal";
3
3
  import { Chunk } from "../chunk";
4
4
  export declare class ValueBodyTranspiler {
5
5
  transpile(typ: Nodes.ExpressionNode, body: Nodes.ExpressionNode, traversal: Traversal): Chunk;
6
+ private buildForChain;
7
+ private appendBlocks;
8
+ private appendBlock;
9
+ private blocksToString;
6
10
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ValueBodyTranspiler = void 0;
4
4
  const core_1 = require("@abaplint/core");
5
+ const traversal_1 = require("../traversal");
5
6
  const chunk_1 = require("../chunk");
6
7
  const type_name_or_infer_1 = require("./type_name_or_infer");
7
8
  const value_body_line_1 = require("./value_body_line");
@@ -24,7 +25,9 @@ class ValueBodyTranspiler {
24
25
  let post = "";
25
26
  let extraFields = "";
26
27
  const hasLines = body.findDirectExpression(core_1.Expressions.ValueBodyLine) !== undefined;
27
- for (const child of body.getChildren()) {
28
+ const children = body.getChildren();
29
+ for (let i = 0; i < children.length; i++) {
30
+ const child = children[i];
28
31
  if (child.get() instanceof core_1.Expressions.FieldAssignment && child instanceof core_1.Nodes.ExpressionNode) {
29
32
  const transpiled = new field_assignment_1.FieldAssignmentTranspiler().transpile(child, traversal, context).getCode();
30
33
  if (hasLines === false) {
@@ -50,15 +53,58 @@ class ValueBodyTranspiler {
50
53
  ret.appendString(".set(" + source.getCode() + ".clone())");
51
54
  }
52
55
  else if (child.get() instanceof core_1.Expressions.For && child instanceof core_1.Nodes.ExpressionNode) {
53
- if (["THEN", "UNTIL", "WHILE", "FROM", "TO", "GROUPS"].some(token => child.findDirectTokenByText(token))) {
54
- throw new Error("ValueBody FOR todo, " + body.concatTokens());
56
+ const forNodes = [];
57
+ let idx = i;
58
+ while (idx < children.length) {
59
+ const candidate = children[idx];
60
+ if (candidate.get() instanceof core_1.Expressions.For && candidate instanceof core_1.Nodes.ExpressionNode) {
61
+ forNodes.push(candidate);
62
+ idx++;
63
+ }
64
+ else {
65
+ break;
66
+ }
55
67
  }
56
- const loop = child.findDirectExpression(core_1.Expressions.InlineLoopDefinition);
57
- if (loop === undefined) {
68
+ i = idx - 1;
69
+ const result = this.buildForChain(forNodes, typ, traversal, body);
70
+ ret = result.chunk;
71
+ post = result.post;
72
+ }
73
+ else if (child instanceof core_1.Nodes.TokenNode && child.getFirstToken().getStr().toUpperCase() === "DEFAULT") {
74
+ // note: this is last in the body, so its okay to prepend and postpend
75
+ const sources = body.findDirectExpressions(core_1.Expressions.Source);
76
+ const deflt = traversal.traverse(sources[1]).getCode();
77
+ const pre = `(await (async () => { try { return `;
78
+ ret = new chunk_1.Chunk().appendString(pre + ret.getCode());
79
+ post += `; } catch (error) { if (abap.isLineNotFound(error)) { return ${deflt}; } throw error; } })())`;
80
+ }
81
+ else if (child instanceof core_1.Nodes.TokenNode && child.getFirstToken().getStr().toUpperCase() === "OPTIONAL") {
82
+ // note: this is last in the body, so its okay to prepend and postpend
83
+ const pre = `(await (async () => { try { return `;
84
+ ret = new chunk_1.Chunk().appendString(pre + ret.getCode());
85
+ post += `; } catch (error) { if (abap.isLineNotFound(error)) { return ${transpile_types_1.TranspileTypes.toType(context)}; } throw error; } })())`;
86
+ }
87
+ else {
88
+ throw new Error("ValueBodyTranspiler, unknown " + child.get().constructor.name + " \"" + child.concatTokens()) + "\"";
89
+ }
90
+ }
91
+ return ret.appendString(post);
92
+ }
93
+ buildForChain(forNodes, typ, traversal, body) {
94
+ const val = new type_name_or_infer_1.TypeNameOrInfer().transpile(typ, traversal).getCode();
95
+ const chunk = new chunk_1.Chunk();
96
+ const preLoopDecls = [];
97
+ const descriptors = [];
98
+ const levelIndents = [];
99
+ let uniqueCounter = 1;
100
+ for (const child of forNodes) {
101
+ const loop = child.findDirectExpression(core_1.Expressions.InlineLoopDefinition);
102
+ if (loop) {
103
+ if (["THEN", "UNTIL", "WHILE", "FROM", "TO", "GROUPS"].some(token => child.findDirectTokenByText(token))) {
58
104
  throw new Error("ValueBody FOR todo, " + body.concatTokens());
59
105
  }
60
106
  let loopWhere = "";
61
- const whereNode = child?.findDirectExpression(core_1.Expressions.ComponentCond);
107
+ const whereNode = child.findDirectExpression(core_1.Expressions.ComponentCond);
62
108
  if (whereNode) {
63
109
  const where = traversal.traverse(whereNode).getCode();
64
110
  loopWhere = `, {"where": async ` + where + `}`;
@@ -70,51 +116,176 @@ class ValueBodyTranspiler {
70
116
  let targetDeclare = "";
71
117
  let targetAction = "";
72
118
  const fs = loop.findDirectExpression(core_1.Expressions.TargetFieldSymbol);
119
+ const uniqueName = `unique${uniqueCounter++}`;
73
120
  if (fs) {
74
121
  targetDeclare = new statements_1.FieldSymbolTranspiler().transpile(fs, traversal).getCode();
75
122
  const targetName = new source_field_symbol_1.SourceFieldSymbolTranspiler().transpile(fs, traversal).getCode();
76
- targetAction = `${targetName}.assign(unique1);`;
123
+ targetAction = `${targetName}.assign(${uniqueName});`;
77
124
  }
78
125
  else {
79
126
  const field = traversal.traverse(loop.findDirectExpression(core_1.Expressions.TargetField));
80
127
  if (field === undefined) {
81
128
  throw new Error("ValueBody FOR empty field todo, " + body.concatTokens());
82
129
  }
83
- targetAction = `const ${field.getCode()} = unique1.clone();`;
130
+ targetAction = `const ${field.getCode()} = ${uniqueName}.clone();`;
84
131
  }
85
132
  const llet = child.findDirectExpression(core_1.Expressions.Let);
86
133
  if (llet) {
87
134
  targetAction += new let_1.LetTranspiler().transpile(llet, traversal).getCode();
88
135
  }
136
+ const preBodyStatements = [];
137
+ if (targetAction !== "") {
138
+ preBodyStatements.push(targetAction);
139
+ }
89
140
  const source = traversal.traverse(loop.findDirectExpression(core_1.Expressions.Source)).getCode();
90
- const val = new type_name_or_infer_1.TypeNameOrInfer().transpile(typ, traversal).getCode();
91
- ret = new chunk_1.Chunk().appendString(`await (async () => {
92
- ${targetDeclare}
93
- const VAL = ${val};
94
- for await (const unique1 of abap.statements.loop(${source}${loopWhere})) {
95
- ${targetAction}
96
- VAL`);
97
- post = ";\n}\nreturn VAL;\n})()";
141
+ if (targetDeclare !== "") {
142
+ preLoopDecls.push(targetDeclare);
143
+ }
144
+ const descriptor = {
145
+ beforeLoop: [],
146
+ open: `for await (const ${uniqueName} of abap.statements.loop(${source}${loopWhere})) {`,
147
+ preBody: preBodyStatements,
148
+ postBody: [],
149
+ close: "}",
150
+ };
151
+ const loopTokens = loop.concatTokens().toUpperCase();
152
+ const indexTarget = loopTokens.includes("INDEX INTO") ? loop.findExpressionAfterToken("INTO") : undefined;
153
+ if (indexTarget && indexTarget instanceof core_1.Nodes.ExpressionNode) {
154
+ const indexCode = traversal.traverse(indexTarget).getCode();
155
+ const counterName = `unique${uniqueCounter++}`;
156
+ descriptor.beforeLoop.push(`let ${counterName} = 1;`);
157
+ descriptor.preBody.push(`const ${indexCode} = new abap.types.Integer().set(${counterName});`);
158
+ descriptor.postBody.push(`${counterName}++;`);
159
+ }
160
+ descriptors.push(descriptor);
98
161
  }
99
- else if (child instanceof core_1.Nodes.TokenNode && child.getFirstToken().getStr().toUpperCase() === "DEFAULT") {
100
- // note: this is last in the body, so its okay to prepend and postpend
101
- const sources = body.findDirectExpressions(core_1.Expressions.Source);
102
- const deflt = traversal.traverse(sources[1]).getCode();
103
- const pre = `(await (async () => { try { return `;
104
- ret = new chunk_1.Chunk().appendString(pre + ret.getCode());
105
- post += `; } catch (error) { if (abap.isLineNotFound(error)) { return ${deflt}; } throw error; } })())`;
162
+ else {
163
+ const counter = child.findDirectExpression(core_1.Expressions.InlineFieldDefinition);
164
+ if (counter === undefined) {
165
+ throw new Error("ValueBody FOR todo, " + body.concatTokens());
166
+ }
167
+ if (["GROUPS", "FROM", "TO"].some(token => child.findDirectTokenByText(token))) {
168
+ throw new Error("ValueBody FOR todo, " + body.concatTokens());
169
+ }
170
+ if (child.findDirectExpression(core_1.Expressions.ComponentCond)) {
171
+ throw new Error("ValueBody FOR todo, component cond, " + body.concatTokens());
172
+ }
173
+ const cond = child.findDirectExpression(core_1.Expressions.Cond);
174
+ if (cond === undefined) {
175
+ throw new Error("ValueBody FOR missing condition, " + body.concatTokens());
176
+ }
177
+ const hasUntil = child.findDirectTokenByText("UNTIL") !== undefined;
178
+ const hasWhile = child.findDirectTokenByText("WHILE") !== undefined;
179
+ if ((hasUntil ? 1 : 0) + (hasWhile ? 1 : 0) !== 1) {
180
+ throw new Error("ValueBody FOR todo, condition, " + body.concatTokens());
181
+ }
182
+ const fieldExpr = counter.findDirectExpression(core_1.Expressions.Field);
183
+ const fieldName = fieldExpr?.concatTokens().toLowerCase();
184
+ if (fieldName === undefined) {
185
+ throw new Error("ValueBody FOR todo, inline field, " + body.concatTokens());
186
+ }
187
+ const scope = traversal.findCurrentScopeByToken(counter.getFirstToken());
188
+ const variable = scope?.findVariable(fieldName);
189
+ if (variable === undefined) {
190
+ throw new Error("ValueBody FOR todo, variable, " + body.concatTokens());
191
+ }
192
+ const declare = transpile_types_1.TranspileTypes.declare(variable);
193
+ const counterName = traversal_1.Traversal.prefixVariable(fieldName);
194
+ const startSource = counter.findDirectExpression(core_1.Expressions.Source);
195
+ if (startSource === undefined) {
196
+ throw new Error("ValueBody FOR missing initial value, " + body.concatTokens());
197
+ }
198
+ const start = traversal.traverse(startSource).getCode();
199
+ const thenExpr = child.findExpressionAfterToken("THEN");
200
+ let incrementExpression = "";
201
+ if (thenExpr && thenExpr instanceof core_1.Nodes.ExpressionNode) {
202
+ incrementExpression = traversal.traverse(thenExpr).getCode();
203
+ }
204
+ else {
205
+ incrementExpression = `abap.operators.add(${counterName}, new abap.types.Integer().set(1))`;
206
+ }
207
+ const incrementLine = `${counterName}.set(${incrementExpression});`;
208
+ const llet = child.findDirectExpression(core_1.Expressions.Let);
209
+ let letCode = "";
210
+ if (llet) {
211
+ letCode = new let_1.LetTranspiler().transpile(llet, traversal).getCode();
212
+ }
213
+ const condCode = traversal.traverse(cond).getCode();
214
+ const preCheck = hasWhile ? `if (!(${condCode})) {\n break;\n}` : "";
215
+ const postLoop = [];
216
+ postLoop.push(incrementLine);
217
+ if (hasUntil) {
218
+ postLoop.push(`if (${condCode}) {\n break;\n}`);
219
+ }
220
+ descriptors.push({
221
+ beforeLoop: [declare, `${counterName}.set(${start});`],
222
+ open: "while (true) {",
223
+ preBody: [preCheck, letCode].filter(s => s !== ""),
224
+ postBody: postLoop,
225
+ close: "}",
226
+ });
106
227
  }
107
- else if (child instanceof core_1.Nodes.TokenNode && child.getFirstToken().getStr().toUpperCase() === "OPTIONAL") {
108
- // note: this is last in the body, so its okay to prepend and postpend
109
- const pre = `(await (async () => { try { return `;
110
- ret = new chunk_1.Chunk().appendString(pre + ret.getCode());
111
- post += `; } catch (error) { if (abap.isLineNotFound(error)) { return ${transpile_types_1.TranspileTypes.toType(context)}; } throw error; } })())`;
228
+ }
229
+ if (descriptors.length === 0) {
230
+ throw new Error("ValueBodyTranspiler FOR internal error");
231
+ }
232
+ chunk.appendString("await (async () => {\n");
233
+ this.appendBlocks(chunk, preLoopDecls, "");
234
+ chunk.appendString(`const VAL = ${val};\n`);
235
+ let indent = "";
236
+ for (const desc of descriptors) {
237
+ this.appendBlocks(chunk, desc.beforeLoop, indent);
238
+ chunk.appendString(indent + desc.open + "\n");
239
+ indent += " ";
240
+ levelIndents.push(indent);
241
+ this.appendBlocks(chunk, desc.preBody, indent);
242
+ }
243
+ chunk.appendString(indent + "VAL");
244
+ let post = ";\n";
245
+ for (let i = descriptors.length - 1; i >= 0; i--) {
246
+ const desc = descriptors[i];
247
+ const currentIndent = levelIndents[i] ?? "";
248
+ post += this.blocksToString(desc.postBody, currentIndent);
249
+ const parentIndent = currentIndent.substring(0, Math.max(0, currentIndent.length - 2));
250
+ post += parentIndent + desc.close + "\n";
251
+ }
252
+ post += "return VAL;\n})()";
253
+ return { chunk, post };
254
+ }
255
+ appendBlocks(chunk, blocks, indent) {
256
+ for (const block of blocks) {
257
+ this.appendBlock(chunk, block, indent);
258
+ }
259
+ }
260
+ appendBlock(chunk, block, indent) {
261
+ if (block === "") {
262
+ return;
263
+ }
264
+ const lines = block.split("\n");
265
+ for (const line of lines) {
266
+ const clean = line.replace(/\r/g, "");
267
+ if (clean.trim() === "") {
268
+ continue;
112
269
  }
113
- else {
114
- throw new Error("ValueBodyTranspiler, unknown " + child.get().constructor.name + " \"" + child.concatTokens()) + "\"";
270
+ chunk.appendString(indent + clean + "\n");
271
+ }
272
+ }
273
+ blocksToString(blocks, indent) {
274
+ let ret = "";
275
+ for (const block of blocks) {
276
+ if (block === "") {
277
+ continue;
278
+ }
279
+ const lines = block.split("\n");
280
+ for (const line of lines) {
281
+ const clean = line.replace(/\r/g, "");
282
+ if (clean.trim() === "") {
283
+ continue;
284
+ }
285
+ ret += indent + clean + "\n";
115
286
  }
116
287
  }
117
- return ret.appendString(post);
288
+ return ret;
118
289
  }
119
290
  }
120
291
  exports.ValueBodyTranspiler = ValueBodyTranspiler;
@@ -51,7 +51,7 @@ class Transpiler {
51
51
  databaseSetup: dbSetup,
52
52
  reg: reg,
53
53
  };
54
- progress?.set(reg.getObjectCount(false), "Building");
54
+ progress?.set(reg.getObjectCount().total, "Building");
55
55
  for (const obj of reg.getObjects()) {
56
56
  await progress?.tick("Building, " + obj.getName());
57
57
  if (obj instanceof abaplint.Objects.TypePool) {
@@ -101,6 +101,10 @@ class Rearranger {
101
101
  || !(secondLast.get() instanceof core_1.Expressions.ArithOperator)) {
102
102
  return node;
103
103
  }
104
+ const nestedArith = last.findDirectExpressions(core_1.Expressions.ArithOperator);
105
+ if (nestedArith.length === 0) {
106
+ return node;
107
+ }
104
108
  const withoutLast = node.getChildren().slice(0, children.length - 1);
105
109
  const flat = withoutLast.concat(last.getChildren());
106
110
  node.setChildren(flat);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler",
3
- "version": "2.12.9",
3
+ "version": "2.12.11",
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.113.234",
32
+ "@abaplint/core": "^2.113.240",
33
33
  "source-map": "^0.7.6"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/chai": "^4.3.20",
37
37
  "@types/mocha": "^10.0.10",
38
38
  "chai": "^4.5.0",
39
- "mocha": "^11.7.4",
39
+ "mocha": "^11.7.5",
40
40
  "source-map-support": "^0.5.21",
41
41
  "typescript": "^5.9.3"
42
42
  }