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;