diff options
| author | Cliff Click <cliffc@acm.org> | 2026-09-17 15:34:28 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-09-17 15:34:28 -0700 |
| commit | 42681f41318796afc58bc14ce04620b7c5c7eb18 (patch) | |
| tree | 6c8a90f441a4a0edd2b4e5f3a8293d45b9c55834 | |
| parent | 5eb00d3306e6e8738b8033c5ba8401770f9d7ffd (diff) | |
| download | Simple-42681f41318796afc58bc14ce04620b7c5c7eb18.tar.gz Simple-42681f41318796afc58bc14ce04620b7c5c7eb18.zip | |
Parser fix OR temps
5 files changed, 86 insertions, 4 deletions
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java index 8a6b1967..76898bea 100644 --- a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java @@ -590,7 +590,7 @@ public class Parser { }; rhs.keep(); _scope.removeGuards(ifF); - if( doRHS ) + if( doRHS || fside.equals("||") ) fScope = _scope; pred.unkeep(); diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java index 9d500266..435b675c 100644 --- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java +++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java @@ -13,6 +13,48 @@ import static org.junit.Assert.*; public class Chapter23Test { @Test + public void testOrRhsLoopLocal() throws IOException { + checkOrRhsLoop(""" + struct S { int f; }; + int x = 0; + int result = (arg & 1) || (new S { + while (x < 3) x++; + f = ++x; + }).f; + return x * 10 + result; + """); + } + + @Test + public void testOrRhsLoopMemory() throws IOException { + checkOrRhsLoop(""" + struct S { int f; }; + S !s = new S; + int result = (arg & 1) || (new S { + while (s.f < 3) s.f++; + s.f++; + f = s.f; + }).f; + return s.f * 10 + result; + """); + } + + private static void checkOrRhsLoop(String src) throws IOException { + CodeGen code = new CodeGen(src).driver("riscv", "SystemV", null); + for (int arg : new int[]{0, 1, 2, 3}) { + byte[] image = new byte[1 << 20]; + System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding._bits.size()); + EvalRisc5 r5 = new EvalRisc5(image, 1 << 16); + r5.regs[riscv.A0] = arg; + assertEquals("Execution must finish", 0, r5.step(1000)); + // False LHS runs the loop and final increment, returning 4. + // True LHS skips all RHS effects and returns the LHS value, 1. + assertEquals("OR result and side effects for arg=" + arg, + (arg & 1) == 0 ? 44 : 1, r5.regs[riscv.A0]); + } + } + + @Test public void testJig() throws IOException { String src = """ diff --git a/chapter24/src/main/java/com/seaofnodes/simple/Parser.java b/chapter24/src/main/java/com/seaofnodes/simple/Parser.java index f80e8f2c..42aac299 100644 --- a/chapter24/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter24/src/main/java/com/seaofnodes/simple/Parser.java @@ -597,7 +597,7 @@ public class Parser { }; rhs.keep(); _scope.removeGuards(ifF); - if( doRHS ) + if( doRHS || fside.equals("||") ) fScope = _scope; pred.unkeep(); diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java index 94e13a87..400f6b52 100644 --- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java +++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java @@ -13,6 +13,48 @@ import static org.junit.Assert.*; public class Chapter23Test { @Test + public void testOrRhsLoopLocal() throws IOException { + checkOrRhsLoop(""" + struct S { int f; }; + int x = 0; + int result = (arg & 1) || (new S { + while (x < 3) x++; + f = ++x; + }).f; + return x * 10 + result; + """); + } + + @Test + public void testOrRhsLoopMemory() throws IOException { + checkOrRhsLoop(""" + struct S { int f; }; + S !s = new S; + int result = (arg & 1) || (new S { + while (s.f < 3) s.f++; + s.f++; + f = s.f; + }).f; + return s.f * 10 + result; + """); + } + + private static void checkOrRhsLoop(String src) throws IOException { + CodeGen code = new CodeGen(src).driver("riscv", "SystemV", null); + for (int arg : new int[]{0, 1, 2, 3}) { + byte[] image = new byte[1 << 20]; + System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding._bits.size()); + EvalRisc5 r5 = new EvalRisc5(image, 1 << 16); + r5.regs[riscv.A0] = arg; + assertEquals("Execution must finish", 0, r5.step(1000)); + // False LHS runs the loop and final increment, returning 4. + // True LHS skips all RHS effects and returns the LHS value, 1. + assertEquals("OR result and side effects for arg=" + arg, + (arg & 1) == 0 ? 44 : 1, r5.regs[riscv.A0]); + } + } + + @Test public void testJig() throws IOException { //String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); String src = Files.readString(Path.of("docs/examples/BubbleSort.smp")); diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md index 9761bade..6af70cff 100644 --- a/docs/chapter-backports.md +++ b/docs/chapter-backports.md @@ -19,8 +19,6 @@ the correction into the chapter's representation, not the entire modern file. | ID | Change | Proposed destination | Scope and acceptance evidence | |---|---|---|---| -| B04 | Preserve comparison results while releasing parser temporaries | 24 | `parseComparison` lifetime fix in `414931c8`. Reduce seed `-8212834489697770130`; exclude unrelated `<clinit>` cleanup in that commit. | -| B05 | Preserve RHS control, memory, and local updates in logical OR | 23 through 24; **audit** earliest implementation | Chapter 25 `parseIf` retains `_scope` for the false-side `||` expression. Test scalar and field updates and short-circuit behavior. Avoid importing GuardNode. | | B06 | Decode string escapes; reject unknown/truncated escapes | 22 through 24 | Literal scanner only. Check newline, tab, carriage return, backslash, quote, NUL, and malformed input. | | B07 | Decode character escapes; reject truncated literals | 22 through 24 | Separate patch sharing B06's helper. Check values and errors; `parseChar` first appears in 22. | | B08 | Complete `&=` and `|=` syntax sugar | 17 onward; **audit** availability of bitwise operators | A small language addition, not a correctness-only patch. Move lexer and assignment expansion together. Check locals, fields, arrays, and exactly-once address evaluation. | |
