From dbc640b3bd8317d4151f8eb5fdd172addc27979c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Jul 2026 10:58:45 +0000 Subject: [PATCH 1/2] Initial plan From 8d708814e4dbfde6e28005d290db1ca70b9e36eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:07:33 +0000 Subject: [PATCH 2/2] Fix dynamic LOOP WHERE with IN: upper case operands, NOT IN and field symbol ranges --- packages/runtime/src/statements/loop.ts | 14 +++-- test/statements/loop.ts | 71 +++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/packages/runtime/src/statements/loop.ts b/packages/runtime/src/statements/loop.ts index 5646ff9f4..3331b48e3 100644 --- a/packages/runtime/src/statements/loop.ts +++ b/packages/runtime/src/statements/loop.ts @@ -51,10 +51,10 @@ function dynamicToWhere(condition: string, evaluate: (name: string) => FieldSymb throw new Error("Dynamic WHERE evaluation function is not defined"); } - const matches = text.matchAll(/([\w-]+)\s+(\w+)\s+([<>\w-]+)/gi); + const matches = text.matchAll(/([\w-]+)\s+(NOT\s+IN|\w+)\s+([<>\w-]+)/gi); for (const match of matches) { const left = match[1]; - const comparator = match[2].toLowerCase(); + const comparator = match[2].toLowerCase().replace(/\s+/g, " "); let right = ""; // console.dir({left, right}); @@ -66,13 +66,21 @@ function dynamicToWhere(condition: string, evaluate: (name: string) => FieldSymb right = `evaluate("${name}").get()["${rightMatch[2].toLowerCase()}"]`; } + const fieldSymbol = match[3].match(/^<(\w+)>$/); + if (right === "" && fieldSymbol) { + right = `evaluate("fs_${fieldSymbol[1].toLowerCase()}_").getPointer()`; + } + if (right === "") { - right = `evaluate("${match[3]}")`; + right = `evaluate("${match[3].toLowerCase()}")`; } if (comparator === "in") { const cnew = `abap.compare.in(${cleft}, ${right})`; text = text.replace(match[0], cnew); + } else if (comparator === "not in") { + const cnew = `!abap.compare.in(${cleft}, ${right})`; + text = text.replace(match[0], cnew); } else { const cnew = `abap.compare.${comparator}(${cleft}, ${right})`; text = text.replace(match[0], cnew); diff --git a/test/statements/loop.ts b/test/statements/loop.ts index 4fb4b48bd..c56a2cd2d 100644 --- a/test/statements/loop.ts +++ b/test/statements/loop.ts @@ -1047,6 +1047,77 @@ ENDLOOP.`; expect(abap.console.getTrimmed()).to.equal("2"); }); + it("LOOP dynamic WHERE IN, upper case", async () => { + const code = ` +TYPES: BEGIN OF ty, + field TYPE i, + END OF ty. +DATA range TYPE RANGE OF i. +DATA dynamic TYPE string. +DATA tab TYPE STANDARD TABLE OF ty. +DATA row LIKE LINE OF tab. + +INSERT VALUE #( field = 2 ) INTO TABLE tab. +INSERT VALUE #( field = 3 ) INTO TABLE tab. +INSERT VALUE #( low = 2 option = 'EQ' sign = 'I' ) INTO TABLE range. +dynamic = 'FIELD IN RANGE'. +LOOP AT tab INTO row WHERE (dynamic). + WRITE / row-field. +ENDLOOP.`; + const js = await run(code); + const f = new AsyncFunction("abap", js); + await f(abap); + expect(abap.console.getTrimmed()).to.equal("2"); + }); + + it("LOOP dynamic WHERE NOT IN", async () => { + const code = ` +TYPES: BEGIN OF ty, + field TYPE i, + END OF ty. +DATA range TYPE RANGE OF i. +DATA dynamic TYPE string. +DATA tab TYPE STANDARD TABLE OF ty. +DATA row LIKE LINE OF tab. + +INSERT VALUE #( field = 2 ) INTO TABLE tab. +INSERT VALUE #( field = 3 ) INTO TABLE tab. +INSERT VALUE #( low = 2 option = 'EQ' sign = 'I' ) INTO TABLE range. +dynamic = 'FIELD NOT IN RANGE'. +LOOP AT tab INTO row WHERE (dynamic). + WRITE / row-field. +ENDLOOP.`; + const js = await run(code); + const f = new AsyncFunction("abap", js); + await f(abap); + expect(abap.console.getTrimmed()).to.equal("3"); + }); + + it("LOOP dynamic WHERE IN, field symbol range", async () => { + const code = ` +TYPES: BEGIN OF ty, + field TYPE i, + END OF ty. +DATA range TYPE RANGE OF i. +DATA dynamic TYPE string. +DATA tab TYPE STANDARD TABLE OF ty. +DATA row LIKE LINE OF tab. +FIELD-SYMBOLS LIKE range. + +INSERT VALUE #( field = 2 ) INTO TABLE tab. +INSERT VALUE #( field = 3 ) INTO TABLE tab. +INSERT VALUE #( low = 2 option = 'EQ' sign = 'I' ) INTO TABLE range. +ASSIGN range TO . +dynamic = 'FIELD IN '. +LOOP AT tab INTO row WHERE (dynamic). + WRITE / row-field. +ENDLOOP.`; + const js = await run(code); + const f = new AsyncFunction("abap", js); + await f(abap); + expect(abap.console.getTrimmed()).to.equal("2"); + }); + it("LOOP GROUP BY", async () => { const code = ` TYPES: