From 5800747485fc82b8be730009e3ce66a1cd711b48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:06:36 +0000 Subject: [PATCH 1/3] Initial plan From fadc27935935413c26bcac398e83904f2ab1f6b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:10:04 +0000 Subject: [PATCH 2/3] Initial repository exploration Co-authored-by: richorama <353138+richorama@users.noreply.github.com> --- test_output.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test_output.txt diff --git a/test_output.txt b/test_output.txt new file mode 100644 index 0000000..645bc59 --- /dev/null +++ b/test_output.txt @@ -0,0 +1,11 @@ + Determining projects to restore... + All projects are up-to-date for restore. + IronBlock -> /home/runner/work/IronBlock/IronBlock/IronBlock/bin/Debug/netstandard2.0/IronBlock.dll + IronBlock.Tests -> /home/runner/work/IronBlock/IronBlock/IronBlock.Tests/bin/Debug/net9.0/IronBlock.Tests.dll +Test run for /home/runner/work/IronBlock/IronBlock/IronBlock.Tests/bin/Debug/net9.0/IronBlock.Tests.dll (.NETCoreApp,Version=v9.0) +VSTest version 18.0.1 (x64) + +Starting test execution, please wait... +A total of 1 test files matched the specified pattern. + +Passed! - Failed: 0, Passed: 161, Skipped: 0, Total: 161, Duration: 476 ms - IronBlock.Tests.dll (net9.0) From 5db31bfc22fc78188d1abcfbe9bc32a0fbdc1723 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:15:09 +0000 Subject: [PATCH 3/3] Add break and continue support to for/foreach loops and fix whileuntil handling Co-authored-by: richorama <353138+richorama@users.noreply.github.com> --- .gitignore | 1 + IronBlock.Tests/ControlsTests.cs | 712 ++++++++++++++++++ IronBlock/Blocks/Controls/ControlsFor.cs | 14 + IronBlock/Blocks/Controls/ControlsForEach.cs | 13 + .../Blocks/Controls/ControlsWhileUntil.cs | 27 +- test_output.txt | 11 - 6 files changed, 764 insertions(+), 14 deletions(-) delete mode 100644 test_output.txt diff --git a/.gitignore b/.gitignore index 65181f1..73d612b 100644 --- a/.gitignore +++ b/.gitignore @@ -212,3 +212,4 @@ GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml /IronBlock.Runner/MyExample.xml +test_output.txt diff --git a/IronBlock.Tests/ControlsTests.cs b/IronBlock.Tests/ControlsTests.cs index cabfc56..668c367 100644 --- a/IronBlock.Tests/ControlsTests.cs +++ b/IronBlock.Tests/ControlsTests.cs @@ -330,5 +330,717 @@ public void Test_Controls_For() } + [TestMethod] + public void Test_Controls_For_Break() + { + const string xml = @" + + + i + + + i + + + 1 + + + + + 5 + + + + + 1 + + + + + + + i + + + + + + + EQ + + + i + + + + + 3 + + + + + + + BREAK + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + Assert.AreEqual("1,2,3", string.Join(",", TestExtensions.GetDebugText())); + } + + + [TestMethod] + public void Test_Controls_For_Continue() + { + const string xml = @" + + + i + + + i + + + 1 + + + + + 5 + + + + + 1 + + + + + + + EQ + + + i + + + + + 3 + + + + + + + CONTINUE + + + + + + + i + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + Assert.AreEqual("1,2,4,5", string.Join(",", TestExtensions.GetDebugText())); + } + + + [TestMethod] + public void Test_Controls_ForEach_Break() + { + const string xml = @" + + + i + + + i + + + + SPLIT + + + a,b,c,d,e + + + + + , + + + + + + + + + i + + + + + + + EQ + + + i + + + + + c + + + + + + + BREAK + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + Assert.AreEqual("a,b,c", string.Join(",", TestExtensions.GetDebugText())); + } + + + [TestMethod] + public void Test_Controls_ForEach_Continue() + { + const string xml = @" + + + i + + + i + + + + SPLIT + + + a,b,c,d,e + + + + + , + + + + + + + + + EQ + + + i + + + + + c + + + + + + + CONTINUE + + + + + + + i + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + Assert.AreEqual("a,b,d,e", string.Join(",", TestExtensions.GetDebugText())); + } + + + [TestMethod] + public void Test_Controls_While_Continue() + { + const string xml = @" + + + x + + + x + + + 0 + + + + + WHILE + + + LT + + + x + + + + + 5 + + + + + + + x + + + ADD + + + x + + + + + 1 + + + + + + + + + EQ + + + x + + + + + 3 + + + + + + + CONTINUE + + + + + + + x + + + + + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + Assert.AreEqual("1,2,4,5", string.Join(",", TestExtensions.GetDebugText())); + } + + + [TestMethod] + public void Test_Controls_Until_Break() + { + const string xml = @" + + + x + + + x + + + 0 + + + + + UNTIL + + + GT + + + x + + + + + 10 + + + + + + + x + + + ADD + + + x + + + + + 1 + + + + + + + + + x + + + + + + + EQ + + + x + + + + + 3 + + + + + + + BREAK + + + + + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + Assert.AreEqual("1,2,3", string.Join(",", TestExtensions.GetDebugText())); + } + + + [TestMethod] + public void Test_Controls_Until_Continue() + { + const string xml = @" + + + x + + + x + + + 0 + + + + + UNTIL + + + GT + + + x + + + + + 5 + + + + + + + x + + + ADD + + + x + + + + + 1 + + + + + + + + + EQ + + + x + + + + + 3 + + + + + + + CONTINUE + + + + + + + x + + + + + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + Assert.AreEqual("1,2,4,5,6", string.Join(",", TestExtensions.GetDebugText())); + } + + + [TestMethod] + public void Test_Controls_Nested_Loops_EscapeMode_Isolation() + { + const string xml = @" + + + i + j + + + i + + + 1 + + + + + 3 + + + + + 1 + + + + + j + + + 1 + + + + + 3 + + + + + 1 + + + + + + + EQ + + + j + + + + + 2 + + + + + + + BREAK + + + + + + + + + + i + + + + + j + + + + + + + + + + + + + + + + outer: + + + + + i + + + + + + + + + + +"; + new Parser() + .AddStandardBlocks() + .AddDebugPrinter() + .Parse(xml) + .Evaluate(); + + // Inner loop breaks at j=2, but outer loop continues + // For each i (1,2,3), inner prints j=1 only, then outer prints "outer:i" + Assert.AreEqual("11,outer:1,21,outer:2,31,outer:3", string.Join(",", TestExtensions.GetDebugText())); + } + + } } diff --git a/IronBlock/Blocks/Controls/ControlsFor.cs b/IronBlock/Blocks/Controls/ControlsFor.cs index 46e8085..8809fee 100644 --- a/IronBlock/Blocks/Controls/ControlsFor.cs +++ b/IronBlock/Blocks/Controls/ControlsFor.cs @@ -37,6 +37,20 @@ public override object Evaluate(Context context) return context.ReturnValue; } statement.Evaluate(context); + + if (context.EscapeMode == EscapeMode.Break) + { + context.EscapeMode = EscapeMode.None; + break; + } + + if (context.EscapeMode == EscapeMode.Continue) + { + context.EscapeMode = EscapeMode.None; + context.Variables[variableName] = (double)context.Variables[variableName] + byValue; + continue; + } + if (context.EscapeMode == EscapeMode.Return) { return context.ReturnValue; diff --git a/IronBlock/Blocks/Controls/ControlsForEach.cs b/IronBlock/Blocks/Controls/ControlsForEach.cs index a99cdcf..e26897c 100644 --- a/IronBlock/Blocks/Controls/ControlsForEach.cs +++ b/IronBlock/Blocks/Controls/ControlsForEach.cs @@ -32,6 +32,19 @@ public override object Evaluate(Context context) context.Variables.Add(variableName, item); } statement.Evaluate(context); + + if (context.EscapeMode == EscapeMode.Break) + { + context.EscapeMode = EscapeMode.None; + break; + } + + if (context.EscapeMode == EscapeMode.Continue) + { + context.EscapeMode = EscapeMode.None; + continue; + } + if (context.EscapeMode == EscapeMode.Return) { return context.ReturnValue; diff --git a/IronBlock/Blocks/Controls/ControlsWhileUntil.cs b/IronBlock/Blocks/Controls/ControlsWhileUntil.cs index 1ada31f..c6a487a 100644 --- a/IronBlock/Blocks/Controls/ControlsWhileUntil.cs +++ b/IronBlock/Blocks/Controls/ControlsWhileUntil.cs @@ -22,16 +22,24 @@ public override object Evaluate(Context context) { while ((bool)value.Evaluate(context)) { + if (context.EscapeMode == EscapeMode.Return) + { + return context.ReturnValue; + } + statement.Evaluate(context); + if (context.EscapeMode == EscapeMode.Break) { context.EscapeMode = EscapeMode.None; break; } - if (context.EscapeMode == EscapeMode.Return) + + if (context.EscapeMode == EscapeMode.Continue) { - return context.ReturnValue; + context.EscapeMode = EscapeMode.None; + continue; } - statement.Evaluate(context); + if (context.EscapeMode == EscapeMode.Return) { return context.ReturnValue; @@ -47,6 +55,19 @@ public override object Evaluate(Context context) return context.ReturnValue; } statement.Evaluate(context); + + if (context.EscapeMode == EscapeMode.Break) + { + context.EscapeMode = EscapeMode.None; + break; + } + + if (context.EscapeMode == EscapeMode.Continue) + { + context.EscapeMode = EscapeMode.None; + continue; + } + if (context.EscapeMode == EscapeMode.Return) { return context.ReturnValue; diff --git a/test_output.txt b/test_output.txt deleted file mode 100644 index 645bc59..0000000 --- a/test_output.txt +++ /dev/null @@ -1,11 +0,0 @@ - Determining projects to restore... - All projects are up-to-date for restore. - IronBlock -> /home/runner/work/IronBlock/IronBlock/IronBlock/bin/Debug/netstandard2.0/IronBlock.dll - IronBlock.Tests -> /home/runner/work/IronBlock/IronBlock/IronBlock.Tests/bin/Debug/net9.0/IronBlock.Tests.dll -Test run for /home/runner/work/IronBlock/IronBlock/IronBlock.Tests/bin/Debug/net9.0/IronBlock.Tests.dll (.NETCoreApp,Version=v9.0) -VSTest version 18.0.1 (x64) - -Starting test execution, please wait... -A total of 1 test files matched the specified pattern. - -Passed! - Failed: 0, Passed: 161, Skipped: 0, Total: 161, Duration: 476 ms - IronBlock.Tests.dll (net9.0)