@abaplint/transpiler 2.13.48 → 2.13.50

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.
@@ -3,3 +3,4 @@ export interface DatabaseSchemaGenerator {
3
3
  buildVIEW(view: abaplint.Objects.View): string;
4
4
  buildTABL(view: abaplint.Objects.Table): string;
5
5
  }
6
+ export declare function packedTypeToDatabase(type: abaplint.BasicTypes.PackedType): string;
@@ -1,3 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.packedTypeToDatabase = packedTypeToDatabase;
4
+ function packedTypeToDatabase(type) {
5
+ const precision = type.getLength() * 2 - 1;
6
+ return `DECIMAL(${precision},${type.getDecimals()})`;
7
+ }
3
8
  //# sourceMappingURL=database_schema_generator.js.map
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.PGDatabaseSchema = void 0;
37
37
  const abaplint = __importStar(require("@abaplint/core"));
38
+ const database_schema_generator_1 = require("./database_schema_generator");
38
39
  const _database_schema_reuse_1 = require("./_database_schema_reuse");
39
40
  const QUOTE = "\"";
40
41
  class PGDatabaseSchema {
@@ -99,7 +100,7 @@ class PGDatabaseSchema {
99
100
  return `REAL`;
100
101
  }
101
102
  else if (type instanceof abaplint.BasicTypes.PackedType) {
102
- return `DECIMAL(${type.getLength()},${type.getDecimals()})`;
103
+ return (0, database_schema_generator_1.packedTypeToDatabase)(type);
103
104
  }
104
105
  else if (type instanceof abaplint.BasicTypes.VoidType) {
105
106
  throw `Type of ${errorInfo}-${fieldname} is VoidType(${type.getVoided()}), make sure the type is known, enable strict syntax checking`;
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.SnowflakeDatabaseSchema = void 0;
37
37
  const abaplint = __importStar(require("@abaplint/core"));
38
+ const database_schema_generator_1 = require("./database_schema_generator");
38
39
  class SnowflakeDatabaseSchema {
39
40
  reg;
40
41
  constructor(reg) {
@@ -98,7 +99,7 @@ class SnowflakeDatabaseSchema {
98
99
  return `REAL`;
99
100
  }
100
101
  else if (type instanceof abaplint.BasicTypes.PackedType) {
101
- return `DECIMAL(${type.getLength()},${type.getDecimals()})`;
102
+ return (0, database_schema_generator_1.packedTypeToDatabase)(type);
102
103
  }
103
104
  else if (type instanceof abaplint.BasicTypes.VoidType) {
104
105
  throw `Type of ${errorInfo}-${fieldname} is VoidType(${type.getVoided()}), make sure the type is known, enable strict syntax checking`;
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.SQLiteDatabaseSchema = void 0;
37
37
  const abaplint = __importStar(require("@abaplint/core"));
38
+ const database_schema_generator_1 = require("./database_schema_generator");
38
39
  const _database_schema_reuse_1 = require("./_database_schema_reuse");
39
40
  const QUOTE = "'";
40
41
  class SQLiteDatabaseSchema {
@@ -100,7 +101,7 @@ class SQLiteDatabaseSchema {
100
101
  return `REAL`;
101
102
  }
102
103
  else if (type instanceof abaplint.BasicTypes.PackedType) {
103
- return `DECIMAL(${type.getLength()},${type.getDecimals()})`;
104
+ return (0, database_schema_generator_1.packedTypeToDatabase)(type);
104
105
  }
105
106
  else if (type instanceof abaplint.BasicTypes.VoidType) {
106
107
  throw `Type of ${errorInfo}-${fieldname} is VoidType(${type.getVoided()}), make sure the type is known, enable strict syntax checking`;
@@ -3,4 +3,9 @@ import { Traversal } from "../traversal";
3
3
  import { Chunk } from "../chunk";
4
4
  export declare class FilterBodyTranspiler {
5
5
  transpile(typ: Nodes.ExpressionNode, body: Nodes.ExpressionNode, traversal: Traversal): Chunk;
6
+ private transpileSingle;
7
+ private transpileIn;
8
+ private transpileInCondition;
9
+ private transpileInCompare;
10
+ private filterOperand;
6
11
  }
@@ -2,39 +2,151 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FilterBodyTranspiler = 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 transpile_types_1 = require("../transpile_types");
8
9
  const unique_identifier_1 = require("../unique_identifier");
10
+ const component_chain_simple_1 = require("./component_chain_simple");
9
11
  class FilterBodyTranspiler {
10
12
  transpile(typ, body, traversal) {
11
13
  if (!(typ.get() instanceof core_1.Expressions.TypeNameOrInfer)) {
12
14
  throw new Error("FilterBodyTranspiler, Expected TypeNameOrInfer");
13
15
  }
14
- else if (body.findDirectTokenByText("EXCEPT")) {
15
- return new chunk_1.Chunk(`(() => { throw new Error("FilterBodyTranspiler EXCEPT in, not supported, transpiler"); })()`);
16
+ const sources = body.findDirectExpressions(core_1.Expressions.Source);
17
+ if (sources.length === 0) {
18
+ throw new Error("FilterBodyTranspiler, source not found");
16
19
  }
17
- const source = traversal.traverse(body.findDirectExpression(core_1.Expressions.Source)).getCode();
20
+ const source = traversal.traverse(sources[0]).getCode();
18
21
  const type = new type_name_or_infer_1.TypeNameOrInfer().findType(typ, traversal);
19
22
  const target = transpile_types_1.TranspileTypes.toType(type);
23
+ const whereNode = body.findDirectExpression(core_1.Expressions.ComponentCond);
24
+ if (whereNode === undefined) {
25
+ throw new Error("FilterBodyTranspiler, WHERE not found");
26
+ }
27
+ if (sources.length > 1) {
28
+ return this.transpileIn(target, source, traversal.traverse(sources[1]).getCode(), whereNode, body.findDirectTokenByText("EXCEPT") !== undefined, body, traversal);
29
+ }
30
+ return this.transpileSingle(target, source, whereNode, body.findDirectTokenByText("EXCEPT") !== undefined, body, traversal);
31
+ }
32
+ transpileSingle(target, source, whereNode, except, body, traversal) {
33
+ const result = unique_identifier_1.UniqueIdentifier.get();
34
+ const row = unique_identifier_1.UniqueIdentifier.get();
35
+ const where = traversal.traverse(whereNode).getCode();
36
+ const options = [];
37
+ options.push(except ? `where: async (I) => !((${where})(I))` : `where: async ${where}`);
38
+ const key = body.findDirectExpression(core_1.Expressions.SimpleName);
39
+ if (key) {
40
+ options.push(`usingKey: "${key.concatTokens().toLowerCase()}"`);
41
+ }
20
42
  const ret = new chunk_1.Chunk();
21
43
  ret.appendString("(await (async () => {\n");
22
- let loopWhere = "";
23
- const whereNode = body.findDirectExpression(core_1.Expressions.ComponentCond);
24
- if (whereNode) {
25
- const where = traversal.traverse(whereNode).getCode();
26
- loopWhere = `, {"where": async ` + where + `}`;
27
- }
28
- const id = unique_identifier_1.UniqueIdentifier.get();
29
- const loop = unique_identifier_1.UniqueIdentifier.get();
30
- ret.appendString(`const ${id} = ${target};\n`);
31
- ret.appendString(`for await (const ${loop} of abap.statements.loop(${source}${loopWhere})) {\n`);
32
- ret.appendString(`abap.statements.insertInternal({"table": ${id}, "data": ${loop}});\n`);
33
- ret.appendString(`}\n`);
34
- ret.appendString(`return ${id};\n`);
44
+ ret.appendString(`const ${result} = ${target};\n`);
45
+ ret.appendString(`for await (const ${row} of abap.statements.loop(${source}, {${options.join(", ")}})) {\n`);
46
+ ret.appendString(`abap.statements.insertInternal({"table": ${result}, "data": ${row}});\n`);
47
+ ret.appendString("}\n");
48
+ ret.appendString(`return ${result};\n`);
35
49
  ret.appendString("})())");
36
50
  return ret;
37
51
  }
52
+ transpileIn(target, source, filterSource, whereNode, except, body, traversal) {
53
+ const result = unique_identifier_1.UniqueIdentifier.get();
54
+ const sourceRow = unique_identifier_1.UniqueIdentifier.get();
55
+ const filterRow = unique_identifier_1.UniqueIdentifier.get();
56
+ const matched = unique_identifier_1.UniqueIdentifier.get();
57
+ const condition = this.transpileInCondition(whereNode, traversal, sourceRow, filterRow);
58
+ const key = body.findDirectExpression(core_1.Expressions.SimpleName);
59
+ const filterOptions = key ? `, {usingKey: "${key.concatTokens().toLowerCase()}"}` : "";
60
+ const selection = except ? `!${matched}` : matched;
61
+ const ret = new chunk_1.Chunk();
62
+ ret.appendString("(await (async () => {\n");
63
+ ret.appendString(`const ${result} = ${target};\n`);
64
+ ret.appendString(`for await (const ${sourceRow} of abap.statements.loop(${source})) {\n`);
65
+ ret.appendString(`let ${matched} = false;\n`);
66
+ ret.appendString(`for await (const ${filterRow} of abap.statements.loop(${filterSource}${filterOptions})) {\n`);
67
+ ret.appendString(`if (${condition}) {\n`);
68
+ ret.appendString(`${matched} = true;\n`);
69
+ ret.appendString("break;\n}\n}\n");
70
+ ret.appendString(`if (${selection}) {\n`);
71
+ ret.appendString(`abap.statements.insertInternal({"table": ${result}, "data": ${sourceRow}});\n`);
72
+ ret.appendString("}\n}\n");
73
+ ret.appendString(`return ${result};\n`);
74
+ ret.appendString("})())");
75
+ return ret;
76
+ }
77
+ transpileInCondition(node, traversal, sourceRow, filterRow) {
78
+ if (node.get() instanceof core_1.Expressions.ComponentCompare) {
79
+ return this.transpileInCompare(node, traversal, sourceRow, filterRow);
80
+ }
81
+ let ret = "";
82
+ for (const child of node.getChildren()) {
83
+ if (child instanceof core_1.Nodes.ExpressionNode) {
84
+ ret += this.transpileInCondition(child, traversal, sourceRow, filterRow);
85
+ }
86
+ else {
87
+ switch (child.concatTokens().toUpperCase()) {
88
+ case "AND":
89
+ ret += " && ";
90
+ break;
91
+ case "OR":
92
+ ret += " || ";
93
+ break;
94
+ case "NOT":
95
+ ret += "!";
96
+ break;
97
+ case "(":
98
+ ret += "(";
99
+ break;
100
+ case ")":
101
+ ret += ")";
102
+ break;
103
+ default: throw new Error("FilterBodyTranspiler, unexpected condition token " + child.concatTokens());
104
+ }
105
+ }
106
+ }
107
+ return ret;
108
+ }
109
+ transpileInCompare(node, traversal, sourceRow, filterRow) {
110
+ const leftNode = node.findDirectExpression(core_1.Expressions.ComponentChainSimple);
111
+ if (leftNode === undefined) {
112
+ throw new Error("FilterBodyTranspiler, comparison component not found");
113
+ }
114
+ const left = new component_chain_simple_1.ComponentChainSimpleTranspiler(`${sourceRow}.get().`).transpile(leftNode, traversal).getCode();
115
+ const sources = node.findDirectExpressions(core_1.Expressions.Source);
116
+ const concat = node.concatTokens().toUpperCase();
117
+ const negate = concat.startsWith("NOT ") ? "!" : "";
118
+ const operator = node.findDirectExpression(core_1.Expressions.CompareOperator);
119
+ if (operator && sources[0]) {
120
+ const compare = traversal.traverse(operator).getCode();
121
+ return `${negate}abap.compare.${compare}(${left}, ${this.filterOperand(sources[0], traversal, filterRow)})`;
122
+ }
123
+ if (concat.includes(" BETWEEN ") && sources.length === 2) {
124
+ const between = `abap.compare.ge(${left}, ${this.filterOperand(sources[0], traversal, filterRow)}) && `
125
+ + `abap.compare.le(${left}, ${this.filterOperand(sources[1], traversal, filterRow)})`;
126
+ return concat.includes(" NOT BETWEEN ") ? `!(${between})` : `(${between})`;
127
+ }
128
+ if (concat.endsWith("IS INITIAL")) {
129
+ return `${negate}abap.compare.initial(${left})`;
130
+ }
131
+ else if (concat.endsWith("IS NOT INITIAL")) {
132
+ return `!abap.compare.initial(${left})`;
133
+ }
134
+ throw new Error("FilterBodyTranspiler, unsupported IN comparison " + node.concatTokens());
135
+ }
136
+ filterOperand(source, traversal, filterRow) {
137
+ const code = traversal.traverse(source).getCode();
138
+ const sourceField = source.findFirstExpression(core_1.Expressions.SourceField);
139
+ const name = sourceField?.findDirectExpression(core_1.Expressions.Field)?.concatTokens();
140
+ if (name === undefined) {
141
+ return code;
142
+ }
143
+ const escaped = traversal_1.Traversal.escapeNamespace(name)?.replace("~", "$").toLowerCase();
144
+ const variable = traversal_1.Traversal.prefixVariable(traversal_1.Traversal.escapeNamespace(name));
145
+ if (escaped === undefined || code.startsWith(variable) === false) {
146
+ return code;
147
+ }
148
+ return `${filterRow}.get().${escaped}` + code.substring(variable.length);
149
+ }
38
150
  }
39
151
  exports.FilterBodyTranspiler = FilterBodyTranspiler;
40
152
  //# sourceMappingURL=filter_body.js.map
@@ -3,7 +3,11 @@ import { Traversal } from "../traversal";
3
3
  import { Chunk } from "../chunk";
4
4
  export declare class ReduceBodyTranspiler {
5
5
  transpile(typ: Nodes.ExpressionNode, body: Nodes.ExpressionNode, traversal: Traversal): Chunk;
6
- private transpileIndex;
6
+ private describeFor;
7
+ private describeGroupsFor;
8
+ private describeIndexFor;
7
9
  private declareInit;
8
10
  private transpileNext;
11
+ private appendBlocks;
12
+ private appendBlock;
9
13
  }
@@ -6,141 +6,278 @@ const traversal_1 = require("../traversal");
6
6
  const chunk_1 = require("../chunk");
7
7
  const transpile_types_1 = require("../transpile_types");
8
8
  const target_1 = require("./target");
9
+ const let_1 = require("./let");
10
+ const statements_1 = require("../statements");
11
+ const source_field_symbol_1 = require("./source_field_symbol");
12
+ const unique_identifier_1 = require("../unique_identifier");
9
13
  class ReduceBodyTranspiler {
10
14
  transpile(typ, body, traversal) {
11
15
  if (!(typ.get() instanceof core_1.Expressions.TypeNameOrInfer)) {
12
16
  throw new Error("ReduceBodyTranspiler, Expected TypeNameOrInfer");
13
17
  }
14
- else if (body.findDirectExpression(core_1.Expressions.Let) !== undefined) {
15
- return new chunk_1.Chunk(`(() => { throw new Error("ReduceBodyTranspiler LET, not supported, transpiler"); })()`);
16
- }
17
18
  const forExpressions = body.findDirectExpressions(core_1.Expressions.For);
18
- const forExpression = forExpressions[0];
19
19
  if (forExpressions.length === 0) {
20
20
  throw new Error("ReduceBodyTranspiler, expected FOR");
21
21
  }
22
- else if (forExpressions.length > 1) {
23
- throw new Error("ReduceBodyTranspiler, multiple FOR not supported, " + body.concatTokens());
24
- }
25
- const loopExpression = forExpression.findDirectExpression(core_1.Expressions.InlineLoopDefinition);
26
- if (loopExpression === undefined) {
27
- // index based FOR, eg. "FOR i = 1 WHILE i <= 5"
28
- return this.transpileIndex(body, forExpression, traversal);
29
- }
30
- else if (["THEN", "UNTIL", "WHILE", "FROM", "TO", "GROUPS"].some(token => forExpression.findDirectTokenByText(token))) {
31
- throw new Error("ValueBody FOR todo, " + body.concatTokens());
32
- }
33
- const loopSource = traversal.traverse(loopExpression?.findDirectExpression(core_1.Expressions.Source)).getCode();
34
- const loopVariable = traversal.traverse(loopExpression?.findDirectExpression(core_1.Expressions.TargetField)
35
- || loopExpression?.findDirectExpression(core_1.Expressions.TargetFieldSymbol)).getCode();
36
- // const type = new TypeNameOrInfer().findType(typ, traversal);
37
- // const target = TranspileTypes.toType(type);
38
22
  const ret = new chunk_1.Chunk();
39
23
  ret.appendString("(await (async () => {\n");
40
- let loopWhere = "";
41
- const whereNode = forExpression?.findDirectExpression(core_1.Expressions.ComponentCond);
42
- if (whereNode) {
43
- const where = traversal.traverse(whereNode).getCode();
44
- loopWhere = `, {"where": async ` + where + `}`;
24
+ const outerLet = body.findDirectExpression(core_1.Expressions.Let);
25
+ if (outerLet) {
26
+ ret.appendString(new let_1.LetTranspiler().transpile(outerLet, traversal).getCode() + "\n");
45
27
  }
46
- /*
47
- const returnId = UniqueIdentifier.get();
48
- ret.appendString(`const ${returnId} = ${target};\n`);
49
- */
50
28
  const returnField = this.declareInit(body, traversal, ret);
51
- ret.appendString(`for await (const ${loopVariable} of abap.statements.loop(${loopSource}${loopWhere})) {\n`);
52
- ret.appendString(this.transpileNext(body, traversal));
53
- ret.appendString(`}\n`);
29
+ const declarations = [];
30
+ const descriptors = forExpressions.map(forExpression => this.describeFor(forExpression, body, traversal, declarations));
31
+ for (const declaration of declarations) {
32
+ ret.appendString(declaration + "\n");
33
+ }
34
+ let indent = "";
35
+ const levelIndents = [];
36
+ for (const descriptor of descriptors) {
37
+ this.appendBlocks(ret, descriptor.beforeLoop, indent);
38
+ ret.appendString(indent + descriptor.open + "\n");
39
+ indent += " ";
40
+ levelIndents.push(indent);
41
+ this.appendBlocks(ret, descriptor.preBody, indent);
42
+ }
43
+ this.appendBlock(ret, this.transpileNext(body, traversal), indent);
44
+ for (let i = descriptors.length - 1; i >= 0; i--) {
45
+ const descriptor = descriptors[i];
46
+ const currentIndent = levelIndents[i];
47
+ this.appendBlocks(ret, descriptor.postBody, currentIndent);
48
+ indent = currentIndent.substring(0, Math.max(0, currentIndent.length - 2));
49
+ ret.appendString(indent + descriptor.close + "\n");
50
+ }
54
51
  ret.appendString(`return ${returnField};\n`);
55
52
  ret.appendString("})())");
56
53
  return ret;
57
54
  }
58
- transpileIndex(body, forExpression, traversal) {
59
- if (["FROM", "TO", "GROUPS"].some(token => forExpression.findDirectTokenByText(token))) {
60
- throw new Error("ValueBody FOR todo, " + body.concatTokens());
55
+ describeFor(forExpression, body, traversal, declarations) {
56
+ if (forExpression.findDirectTokenByText("GROUPS")) {
57
+ return this.describeGroupsFor(forExpression, body, traversal);
61
58
  }
62
- const counter = forExpression.findDirectExpression(core_1.Expressions.InlineFieldDefinition);
63
- if (counter === undefined) {
64
- throw new Error("ValueBody FOR todo, " + body.concatTokens());
59
+ const loopExpression = forExpression.findDirectExpression(core_1.Expressions.InlineLoopDefinition);
60
+ if (loopExpression === undefined) {
61
+ return this.describeIndexFor(forExpression, body, traversal);
62
+ }
63
+ const sourceNode = loopExpression.findDirectExpression(core_1.Expressions.Source);
64
+ if (sourceNode === undefined) {
65
+ throw new Error("ReduceBodyTranspiler FOR missing source, " + body.concatTokens());
66
+ }
67
+ const loopSource = traversal.traverse(sourceNode).getCode();
68
+ const options = [];
69
+ const whereNode = forExpression.findDirectExpression(core_1.Expressions.ComponentCond);
70
+ if (whereNode) {
71
+ options.push("where: async " + traversal.traverse(whereNode).getCode());
72
+ }
73
+ const fromNode = forExpression.findExpressionAfterToken("FROM");
74
+ if (fromNode && fromNode instanceof core_1.Nodes.ExpressionNode) {
75
+ options.push("from: " + traversal.traverse(fromNode).getCode());
76
+ }
77
+ const toNode = forExpression.findExpressionAfterToken("TO");
78
+ if (toNode && toNode instanceof core_1.Nodes.ExpressionNode) {
79
+ options.push("to: " + traversal.traverse(toNode).getCode());
80
+ }
81
+ const keyNode = loopExpression.findExpressionAfterToken("KEY");
82
+ if (keyNode) {
83
+ options.push(`usingKey: "${keyNode.concatTokens().toLowerCase()}"`);
84
+ }
85
+ const unique = unique_identifier_1.UniqueIdentifier.get();
86
+ const preBody = [];
87
+ const postBody = [];
88
+ const fieldSymbol = loopExpression.findDirectExpression(core_1.Expressions.TargetFieldSymbol);
89
+ if (fieldSymbol) {
90
+ declarations.push(new statements_1.FieldSymbolTranspiler().transpile(fieldSymbol, traversal).getCode());
91
+ const target = new source_field_symbol_1.SourceFieldSymbolTranspiler().transpile(fieldSymbol, traversal).getCode();
92
+ preBody.push(`${target}.assign(${unique});`);
93
+ postBody.push(`${target}.unassign();`);
94
+ }
95
+ else {
96
+ const field = loopExpression.findDirectExpression(core_1.Expressions.TargetField);
97
+ if (field === undefined) {
98
+ throw new Error("ReduceBodyTranspiler FOR missing target, " + body.concatTokens());
99
+ }
100
+ preBody.push(`const ${traversal.traverse(field).getCode()} = ${unique}.clone();`);
101
+ }
102
+ const indexTarget = loopExpression.findExpressionAfterToken("INTO");
103
+ const beforeLoop = [];
104
+ if (indexTarget && indexTarget instanceof core_1.Nodes.ExpressionNode) {
105
+ const indexName = unique_identifier_1.UniqueIdentifier.get();
106
+ const indexCode = traversal.traverse(indexTarget).getCode();
107
+ beforeLoop.push(`let ${indexName} = 1;`);
108
+ preBody.push(`const ${indexCode} = new abap.types.Integer().set(${indexName});`);
109
+ postBody.push(`${indexName}++;`);
110
+ }
111
+ const letNode = forExpression.findDirectExpression(core_1.Expressions.Let);
112
+ if (letNode) {
113
+ preBody.push(new let_1.LetTranspiler().transpile(letNode, traversal).getCode());
65
114
  }
115
+ const opts = options.length === 0 ? "" : `, {${options.join(", ")}}`;
116
+ return {
117
+ beforeLoop,
118
+ open: `for await (const ${unique} of abap.statements.loop(${loopSource}${opts})) {`,
119
+ preBody,
120
+ postBody,
121
+ close: "}",
122
+ };
123
+ }
124
+ describeGroupsFor(forExpression, body, traversal) {
125
+ const targets = forExpression.findDirectExpressions(core_1.Expressions.TargetField);
126
+ const source = forExpression.findDirectExpression(core_1.Expressions.Source);
127
+ const groupBy = forExpression.findDirectExpression(core_1.Expressions.FieldChain);
128
+ if (targets.length !== 2 || source === undefined || groupBy === undefined) {
129
+ throw new Error("ReduceBodyTranspiler invalid GROUPS FOR, " + body.concatTokens());
130
+ }
131
+ const groupTarget = traversal.traverse(targets[0]).getCode();
132
+ const memberTarget = traversal.traverse(targets[1]).getCode();
133
+ const sourceCode = traversal.traverse(source).getCode();
134
+ const groupByCode = traversal.traverse(groupBy).getCode();
135
+ const groups = unique_identifier_1.UniqueIdentifier.get();
136
+ const row = unique_identifier_1.UniqueIdentifier.get();
137
+ const key = unique_identifier_1.UniqueIdentifier.get();
138
+ const rawKey = unique_identifier_1.UniqueIdentifier.get();
139
+ const entry = unique_identifier_1.UniqueIdentifier.get();
140
+ const generator = `(async function*() {\n`
141
+ + `const ${groups} = new Map();\n`
142
+ + `for await (const ${row} of abap.statements.loop(${sourceCode})) {\n`
143
+ + `const ${memberTarget} = ${row}.clone();\n`
144
+ + `const ${key} = ${groupByCode};\n`
145
+ + `const ${rawKey} = ${key}.get();\n`
146
+ + `let ${entry} = ${groups}.get(${rawKey});\n`
147
+ + `if (${entry} === undefined) {\n`
148
+ + `${entry} = {key: ${key}.clone(), members: []};\n`
149
+ + `${groups}.set(${rawKey}, ${entry});\n`
150
+ + `}\n`
151
+ + `${entry}.members.push(${row});\n`
152
+ + `}\n`
153
+ + `for (const value of ${groups}.values()) { yield value; }\n`
154
+ + `})()`;
155
+ const loopEntry = unique_identifier_1.UniqueIdentifier.get();
156
+ return {
157
+ beforeLoop: [],
158
+ open: `for await (const ${loopEntry} of ${generator}) {`,
159
+ preBody: [
160
+ `const ${groupTarget} = ${loopEntry}.key.clone();`,
161
+ `const ${memberTarget} = ${loopEntry}.members[0].clone();`,
162
+ ],
163
+ postBody: [],
164
+ close: "}",
165
+ };
166
+ }
167
+ describeIndexFor(forExpression, body, traversal) {
168
+ const counter = forExpression.findDirectExpression(core_1.Expressions.InlineFieldDefinition);
66
169
  const cond = forExpression.findDirectExpression(core_1.Expressions.Cond);
67
- if (cond === undefined) {
68
- throw new Error("ValueBody FOR missing condition, " + body.concatTokens());
170
+ if (counter === undefined || cond === undefined) {
171
+ throw new Error("ReduceBodyTranspiler invalid index FOR, " + body.concatTokens());
69
172
  }
70
173
  const hasUntil = forExpression.findDirectTokenByText("UNTIL") !== undefined;
71
174
  const hasWhile = forExpression.findDirectTokenByText("WHILE") !== undefined;
72
175
  if ((hasUntil ? 1 : 0) + (hasWhile ? 1 : 0) !== 1) {
73
- throw new Error("ValueBody FOR todo, condition, " + body.concatTokens());
176
+ throw new Error("ReduceBodyTranspiler index FOR requires WHILE or UNTIL, " + body.concatTokens());
74
177
  }
75
178
  const fieldName = counter.findDirectExpression(core_1.Expressions.Field)?.concatTokens().toLowerCase();
76
- if (fieldName === undefined) {
77
- throw new Error("ValueBody FOR todo, inline field, " + body.concatTokens());
179
+ const source = counter.findDirectExpression(core_1.Expressions.Source);
180
+ if (fieldName === undefined || source === undefined) {
181
+ throw new Error("ReduceBodyTranspiler invalid index definition, " + body.concatTokens());
78
182
  }
79
- const scope = traversal.findCurrentScopeByToken(counter.getFirstToken());
80
- const variable = scope?.findVariable(fieldName);
183
+ const variable = traversal.findCurrentScopeByToken(counter.getFirstToken())?.findVariable(fieldName);
81
184
  if (variable === undefined) {
82
- throw new Error("ValueBody FOR todo, variable, " + body.concatTokens());
185
+ throw new Error(`ReduceBodyTranspiler: variable ${fieldName} not found`);
83
186
  }
84
187
  const counterName = traversal_1.Traversal.prefixVariable(fieldName);
85
- const startSource = counter.findDirectExpression(core_1.Expressions.Source);
86
- if (startSource === undefined) {
87
- throw new Error("ValueBody FOR missing initial value, " + body.concatTokens());
88
- }
89
- const start = traversal.traverse(startSource).getCode();
90
188
  const thenExpr = forExpression.findExpressionAfterToken("THEN");
91
- let incrementExpression = "";
92
- if (thenExpr && thenExpr instanceof core_1.Nodes.ExpressionNode) {
93
- incrementExpression = traversal.traverse(thenExpr).getCode();
94
- }
95
- else {
96
- incrementExpression = `abap.operators.add(${counterName}, new abap.types.Integer().set(1))`;
97
- }
189
+ const increment = thenExpr && thenExpr instanceof core_1.Nodes.ExpressionNode
190
+ ? traversal.traverse(thenExpr).getCode()
191
+ : `abap.operators.add(${counterName}, new abap.types.Integer().set(1))`;
98
192
  const condCode = traversal.traverse(cond).getCode();
99
- const ret = new chunk_1.Chunk();
100
- ret.appendString("(await (async () => {\n");
101
- const returnField = this.declareInit(body, traversal, ret);
102
- ret.appendString(transpile_types_1.TranspileTypes.declare(variable) + `\n`);
103
- ret.appendString(`${counterName}.set(${start});\n`);
104
- ret.appendString(`while (true) {\n`);
193
+ const preBody = [];
194
+ const postBody = [`${counterName}.set(${increment});`];
105
195
  if (hasWhile) {
106
- ret.appendString(`if (!(${condCode})) {\nbreak;\n}\n`);
196
+ preBody.push(`if (!(${condCode})) {\nbreak;\n}`);
197
+ }
198
+ const letNode = forExpression.findDirectExpression(core_1.Expressions.Let);
199
+ if (letNode) {
200
+ preBody.push(new let_1.LetTranspiler().transpile(letNode, traversal).getCode());
107
201
  }
108
- ret.appendString(this.transpileNext(body, traversal));
109
- ret.appendString(`${counterName}.set(${incrementExpression});\n`);
110
202
  if (hasUntil) {
111
- ret.appendString(`if (${condCode}) {\nbreak;\n}\n`);
203
+ postBody.push(`if (${condCode}) {\nbreak;\n}`);
112
204
  }
113
- ret.appendString(`}\n`);
114
- ret.appendString(`return ${returnField};\n`);
115
- ret.appendString("})())");
116
- return ret;
205
+ return {
206
+ beforeLoop: [transpile_types_1.TranspileTypes.declare(variable), `${counterName}.set(${traversal.traverse(source).getCode()});`],
207
+ open: "while (true) {",
208
+ preBody,
209
+ postBody,
210
+ close: "}",
211
+ };
117
212
  }
118
213
  declareInit(body, traversal, ret) {
119
214
  let returnField = "";
120
215
  for (const init of body.findDirectExpressions(core_1.Expressions.InlineFieldDefinition)) {
121
- const fieldName = init.findDirectExpression(core_1.Expressions.Field).concatTokens().toLowerCase();
122
- returnField = fieldName;
123
- const scope = traversal.findCurrentScopeByToken(init.getFirstToken());
124
- const variable = scope?.findVariable(fieldName);
216
+ const fieldName = init.findDirectExpression(core_1.Expressions.Field)?.concatTokens().toLowerCase();
217
+ if (fieldName === undefined) {
218
+ throw new Error("ReduceBodyTranspiler INIT missing field");
219
+ }
220
+ if (returnField === "") {
221
+ returnField = traversal_1.Traversal.prefixVariable(fieldName);
222
+ }
223
+ const variable = traversal.findCurrentScopeByToken(init.getFirstToken())?.findVariable(fieldName);
125
224
  if (variable === undefined) {
126
225
  throw new Error(`ReduceBodyTranspiler: variable ${fieldName} not found`);
127
226
  }
128
- ret.appendString(transpile_types_1.TranspileTypes.declare(variable) + `\n`);
227
+ const target = traversal_1.Traversal.prefixVariable(fieldName);
228
+ ret.appendString(transpile_types_1.TranspileTypes.declare(variable) + "\n");
229
+ const source = init.findDirectExpression(core_1.Expressions.Source);
230
+ if (source) {
231
+ ret.appendString(`${target}.set(${traversal.traverse(source).getCode()});\n`);
232
+ }
233
+ }
234
+ if (returnField === "") {
235
+ throw new Error("ReduceBodyTranspiler INIT missing");
129
236
  }
130
237
  return returnField;
131
238
  }
132
239
  transpileNext(body, traversal) {
133
240
  let ret = "";
134
- for (const nextChild of body.findDirectExpression(core_1.Expressions.ReduceNext)?.getChildren() || []) {
135
- if (nextChild.get() instanceof core_1.Expressions.SimpleTarget && nextChild instanceof core_1.Nodes.ExpressionNode) {
136
- ret += new target_1.TargetTranspiler().transpile(nextChild, traversal).getCode() + ".set(";
241
+ const children = body.findDirectExpression(core_1.Expressions.ReduceNext)?.getChildren() || [];
242
+ for (let i = 0; i < children.length; i++) {
243
+ const child = children[i];
244
+ if (!(child instanceof core_1.Nodes.ExpressionNode) || !(child.get() instanceof core_1.Expressions.SimpleTarget)) {
245
+ continue;
137
246
  }
138
- else if (nextChild.get() instanceof core_1.Expressions.Source && nextChild instanceof core_1.Nodes.ExpressionNode) {
139
- ret += traversal.traverse(nextChild).getCode() + ");\n";
247
+ const source = children.slice(i + 1).find(candidate => candidate instanceof core_1.Nodes.ExpressionNode && candidate.get() instanceof core_1.Expressions.Source);
248
+ if (!(source instanceof core_1.Nodes.ExpressionNode)) {
249
+ throw new Error("ReduceBodyTranspiler NEXT missing source");
140
250
  }
251
+ const target = new target_1.TargetTranspiler().transpile(child, traversal).getCode();
252
+ const value = traversal.traverse(source).getCode();
253
+ const between = children.slice(i + 1, children.indexOf(source)).map(candidate => candidate.concatTokens()).join("");
254
+ const operators = {
255
+ "+=": "add",
256
+ "-=": "minus",
257
+ "*=": "multiply",
258
+ "/=": "divide",
259
+ "&&=": "concat",
260
+ };
261
+ const operator = operators[between];
262
+ ret += operator
263
+ ? `${target}.set(abap.operators.${operator}(${target}, ${value}));\n`
264
+ : `${target}.set(${value});\n`;
265
+ i = children.indexOf(source);
141
266
  }
142
267
  return ret;
143
268
  }
269
+ appendBlocks(ret, blocks, indent) {
270
+ for (const block of blocks) {
271
+ this.appendBlock(ret, block, indent);
272
+ }
273
+ }
274
+ appendBlock(ret, block, indent) {
275
+ for (const line of block.split("\n")) {
276
+ if (line.trim() !== "") {
277
+ ret.appendString(indent + line.replace(/\r/g, "") + "\n");
278
+ }
279
+ }
280
+ }
144
281
  }
145
282
  exports.ReduceBodyTranspiler = ReduceBodyTranspiler;
146
283
  //# sourceMappingURL=reduce_body.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler",
3
- "version": "2.13.48",
3
+ "version": "2.13.50",
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.9",
32
+ "@abaplint/core": "^2.120.18",
33
33
  "source-map": "^0.7.6"
34
34
  },
35
35
  "devDependencies": {