diff options
| author | Cliff Click <cliffc@acm.org> | 2026-11-01 10:33:55 -0800 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-11-01 10:33:55 -0800 |
| commit | f4bd723d929e53475f7990374dca7b7dfcbbdc42 (patch) | |
| tree | 7bb080119e7b88ad4ac9b30af019fe6a5e4ba9dd /chapter29/src/test/java/com/seaofnodes/simple/Chapter17bTest.java | |
| parent | 0ac0c9e10287972ac3af5a4ee43d26f0d5b318fe (diff) | |
| download | Simple-main.tar.gz Simple-main.zip | |
Add Chapter 29 for tech debt cleanup with the parser splitmain
Keep Chapter 28b intact and carry the grammar-oriented parser package, Var move, IterPeeps move, and parser regression tests into Chapter 29. Defer privacy and exclude its experimental tests and fixture changes.
Register the chapter in the indexes, IntelliJ modules and run configurations, Maven reactor, roadmap, and linear-history publisher. Windows and Linux each pass 573 tests, release builds, 199 allocation replays, and 29 native checks.
Diffstat (limited to 'chapter29/src/test/java/com/seaofnodes/simple/Chapter17bTest.java')
| -rw-r--r-- | chapter29/src/test/java/com/seaofnodes/simple/Chapter17bTest.java | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/chapter29/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter29/src/test/java/com/seaofnodes/simple/Chapter17bTest.java new file mode 100644 index 00000000..1e99bcd9 --- /dev/null +++ b/chapter29/src/test/java/com/seaofnodes/simple/Chapter17bTest.java @@ -0,0 +1,623 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.parser.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter17bTest { + + @Test public void testSinkLoopStores() { + var code = new CodeGen(""" + struct S { int x; }; + !S !a = new S; + a.x = 10; + int i = 0; + while (i < arg) { a.x = i + 1; i++; } + return a; + """).driver(CodeGen.Phase.LocalSched); + int[] stores = {0}; + code._stop.walk(n -> { + // Constructor zero Stores remain in separate private memory. + if( n instanceof com.seaofnodes.simple.node.StoreNode st && st._name.equals("x") && + st.val() instanceof com.seaofnodes.simple.node.PhiNode ) { + assertEquals(0,st.cfg0().loopDepth()); // One final Store outside the loop. + stores[0]++; + } + return null; + }); + assertEquals(1,stores[0]); + for( int arg : new int[]{-1,0,1,4} ) { + long expected = arg<=0 ? 10 : arg; + assertEquals("Test.S{x="+expected+"}",Eval2.eval(code,arg)); + } + } + + + @Ignore + @Test + public void testJig() { + CodeGen code = new CodeGen( +""" +return 1; +"""); + code.driver(CodeGen.Phase.TypeCheck); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + + // --------------------------------------------------------------- + @Test + public void testInc0() { + CodeGen code = new CodeGen(""" +return arg++; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return arg;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testInc1() { + CodeGen code = new CodeGen(""" +return arg+++arg++; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return ((arg<<1)+1);", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testInc2() { + CodeGen code = new CodeGen(""" +// -(arg--)-(arg--) +return -arg---arg--; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return (-((arg<<1)+-1));", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test + public void testInc3() { + CodeGen code = new CodeGen(""" +int[] !xs = new int[arg]; +xs[0]++; +xs[1]++; +return xs[0]; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 2)); + } + + @Test + public void testInc4() { + CodeGen code = new CodeGen(""" +u8[] !xs = new u8[1]; +xs[0]--; +return xs[0]; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 255;", code.print()); + assertEquals("255", Eval2.eval(code, 2)); + } + + @Test + public void testInc5() { + CodeGen code = new CodeGen(""" +struct _S { u16 x; }; +!_S !s = new _S; +s.x--; +return s.x; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 65535;", code.print()); + assertEquals("65535", Eval2.eval(code, 2)); + } + + @Test + public void testInc6() { + CodeGen code = new CodeGen("return --arg;"); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return (arg+-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + } + + @Test + public void testInc7() { + CodeGen code = new CodeGen("u8 x=0; return --x;"); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 255;", code.print()); + assertEquals("255", Eval2.eval(code, 0)); + } + + @Test + public void testInc8() { + CodeGen code = new CodeGen("int x; x+=2; return x+=3;"); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 5;", code.print()); + assertEquals("5", Eval2.eval(code, 0)); + } + + @Test + public void testInc9() { + CodeGen code = new CodeGen("flt x = 1.5; x++; return x;"); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 2.5f;", code.print()); + assertEquals("2.5", Eval2.eval(code, 0)); + } + + @Test public void testInt10() { + CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;"); + code.driver(CodeGen.Phase.TypeCheck); + assertEquals("2",Eval2.eval(code,0)); + } + + + // --------------------------------------------------------------- + @Test public void testVar0() { + CodeGen code = new CodeGen("var d; return d;"); + try { code.driver(CodeGen.Phase.FinalizeIR); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); } + } + + @Test public void testVar1() { + CodeGen code = new CodeGen("val d; return d;"); + try { code.driver(CodeGen.Phase.FinalizeIR); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); } + } + + @Test public void testVar2() { + CodeGen code = new CodeGen("int x; x=3; x++; return x; // Ok, no initializer so x is mutable "); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar3() { + CodeGen code = new CodeGen("int x=3; x++; return x; // Ok, primitive so x is mutable despite initializer "); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar4() { + CodeGen code = new CodeGen("struct _S{int x;}; !_S? !s; s=new _S; s.x++; return s.x; // Ok, no initializer so x is mutable "); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar5() { + CodeGen code = new CodeGen("struct _S{int x; new _S={ int xx -> x=xx; };}; !_S? !s; s=new _S(3); s.x++; return s.x; // Ok, no initializer so x is mutable "); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 4;", code.print()); + assertEquals("4", Eval2.eval(code, 0)); + } + + @Test public void testVar6() { + CodeGen code = new CodeGen("struct S{int x;}; ~S s = new S; s.x++; return s.x; // Error initializer so x is immutable "); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar7() { + CodeGen code = new CodeGen("struct S{int x; new S={ int xx -> x=xx; };}; ~S s = new S(3); s.x++; return s.x; // Error initializer so x is immutable "); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar8() { + CodeGen code = new CodeGen("struct _S{int x;}; !_S !s=new _S; s.x++; return s.x; // Ok, has '!' so s.x is mutable "); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar9() { + CodeGen code = new CodeGen("struct _S{int x;}; var s=new _S; s.x++; return s.x; // Ok, has var so s.x is mutable "); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 1;", code.print()); + assertEquals("1", Eval2.eval(code, 0)); + } + + @Test public void testVar10() { + CodeGen code = new CodeGen("struct S{int x;}; ~S s=new S; s.x++; return s.x; // Error, has val so x is immutable "); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar11() { + CodeGen code = new CodeGen(""" +struct Bar { int x; }; +!Bar !bar = new Bar; +bar.x = 3; // Ok, bar is mutable + +struct Foo { !Bar? !bar; int y; new Foo = { !Bar? b -> bar = b; }; }; +!Foo !foo = new Foo(bar); +foo.bar = bar; // Ok foo is mutable +foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable + +~Foo xfoo = foo; // Throw away mutability +xfoo.bar.x++; // Error, cannot mutate through xfoo +return xfoo.bar.x; + """); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + } + + @Test public void testVar12() { + CodeGen code = new CodeGen(""" +struct _Bar { int x; }; +!_Bar !bar = new _Bar; +bar.x = 3; // Ok, bar is mutable + +struct _Foo { !_Bar? !bar; int y; }; +!_Foo !foo = new _Foo; +foo.bar = bar; // Ok bar is mutable +foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable + +~_Foo xfoo = foo; // Throw away mutability +int x4 = xfoo.bar.x; // Ok to read through xfoo, gets 4 +foo.bar.x++; // Bumps to 5 +int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5 +return x4*10+x5; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 45;", code.print()); + assertEquals("45", Eval2.eval(code, 0)); + } + + @Test + public void testVar13() { + CodeGen code = new CodeGen(""" +int i,i++; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); } + } + + + @Test + public void testVar14() { + CodeGen code = new CodeGen(""" +struct B {}; +struct A { !B !b; new A = { -> + return b; // read before init + b = new B; +}; }; +A x = new A; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Field 'b' might not be initialized",e.getMessage()); } + } + + @Test + public void testVar15() { + for( String fields : new String[] { + "!B !b; new A = { -> if (arg) b = new B; };", + "!B !b; new A = { -> if (arg) return 0; b = new B; };", + "!B !b; new A = { !B !b -> b = new B; };", // Argument shadows field + "new A = { -> }; !B !b;", // Field declared later + } ) { + CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return 0;"); + try { code.parse(); fail(fields); } + catch( Parser.ParseException e ) { assertEquals("'Test.A' is not fully initialized, field 'b' is only partially set in the constructor",e.getMessage()); } + } + } + + @Test + public void testDefaultConstructorFields() { + for( String field : new String[] { "!B !b;", "!B !b;" } ) { + CodeGen code = new CodeGen("struct B {}; struct A { "+field+" }; return new A;"); + try { code.parse(); fail(field); } + catch( Parser.ParseException e ) { assertEquals("'Test.A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); } + } + for( String fields : new String[] { + "B b = new B;", + "!B !b; b = new B;", + "new A = { -> }; B b = new B;", + "!B !b; new A = { -> if (arg) { b = new B; return 0; } b = new B; };", + } ) { + CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return new A.b != null;"); + code.driver(CodeGen.Phase.TypeCheck); + assertEquals("1",Eval2.eval(code,0)); + assertEquals("1",Eval2.eval(code,1)); + } + // No returning path means no object can escape uninitialized. + new CodeGen("struct B {}; struct A { !B !b; new A = { -> while (true) {} }; }; return 0;").parse(); + } + + @Test + public void testVar16() { + CodeGen code = new CodeGen( +""" +struct S{ new S = { -> x = 2; }; }; +val x = 1; +~S s = new S; +return x; +""" +); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Undefined name 'x'",e.getMessage()); } + } + + @Test + public void testVar17() { + CodeGen code = new CodeGen( +""" +struct Person { + bool focused; +}; +val focus = { !Person p -> + p.focused = true; +}; +!Person !me; // !Person !me = new Person; +focus(me); +return me; +""" +); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); } + } + + // --------------------------------------------------------------- + @Test + public void testTrinary0() { + CodeGen code = new CodeGen(""" +return arg ? 1 : 2; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Region,1,2);", code.print()); + assertEquals("2", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary1() { + CodeGen code = new CodeGen(""" +return arg ? 0 : arg; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 0;", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + } + + @Test + public void testShortCircuitGuard() { + CodeGen code = new CodeGen(""" +struct S { int !x; }; +val get = { !S s -> s.x; }; +!S? !s = arg ? new S; +if( !s || !arg ) return 0; +return get(s); +"""); + code.driver(CodeGen.Phase.TypeCheck); + } + + @Test + public void testTrinary2() { + CodeGen code = new CodeGen(""" +struct _Bar { int x; }; +var b = arg ? new _Bar : null; +return b ? b.x++ + b.x++ : -1; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary3() { + CodeGen code = new CodeGen(""" +struct _Bar { int x; }; +var b = arg ? new _Bar; +return b ? b.x++ + b.x++ : -1; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); + assertEquals("-1", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + } + + @Test + public void testTrinary4() { + // This test case will benefit from an unzipping transformation + CodeGen code = new CodeGen(""" +struct _Bar { !_Bar? !next; int x; new _Bar = { !_Bar? !n, int xx -> next=n; x=xx; }; }; +var b = arg ? new _Bar((arg==2) ? new _Bar(null,2), 1); +return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0" +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Region,.x,0,.x);", code.print()); + assertEquals("0", Eval2.eval(code, 0)); + assertEquals("1", Eval2.eval(code, 1)); + assertEquals("2", Eval2.eval(code, 2)); + } + + @Test + public void testTrinary5() { + CodeGen code = new CodeGen(""" +flt f=arg?1:1.2; +return f; // missing widening +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Region,1.0f,1.2);", code.print()); + assertEquals("1.2", Eval2.eval(code, 0)); + } + + + @Test + public void testTrinary6() { + CodeGen code = new CodeGen(""" +struct S{}; +return arg ? 7 : new S; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Cannot return generic scalar",e.getMessage()); } + } + + // --------------------------------------------------------------- + @Test + public void testFor0() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return sum; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor1() { + CodeGen code = new CodeGen(""" +int sum=0, i=0; +for( ; i<arg; i++ ) + sum += i; +return sum; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor2() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; ; i++ ) { + if( i>=arg ) break; + sum += i; +} +return sum; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor3() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; ) + sum += i++; +return sum; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); + assertEquals("3", Eval2.eval(code, 3)); + assertEquals("45", Eval2.eval(code, 10)); + } + @Test + public void testFor4() { + CodeGen code = new CodeGen(""" +int sum=0; +for( int i=0; i<arg; i++ ) + sum += i; +return i; +"""); + try { code.driver(CodeGen.Phase.FinalizeIR); fail(); } + catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); } + } + @Test + public void testFor5() { + CodeGen code = new CodeGen(""" +for(;;arg++;) {} +"""); + try { code.driver(CodeGen.Phase.FinalizeIR); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `Unexpected code after expression` but found `;`",e.getMessage()); } + } + + + + // --------------------------------------------------------------- + @Test + public void testForward0() { + CodeGen code = new CodeGen(""" +struct _A { + !B? !f1; + !B? !f2; +}; +return new _A; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Test._A;", code.print()); + assertEquals("Test._A{f1=null,f2=null}", Eval2.eval(code,0)); + } + + @Test + public void testForward1() { + CodeGen code = new CodeGen(""" +struct _A { + !B?[]? !nil_array_of_b; + !B?[] !array_of_b; + new _A = { !B?[] bs -> array_of_b = bs; }; +}; +return new _A(new !B?[0]).array_of_b; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return []*B?;", code.print()); + assertEquals("*B {}?[]", Eval2.eval(code,0)); + } + + // --------------------------------------------------------------- + @Test + public void testLinkedList2() { + CodeGen code = new CodeGen(""" +struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int ii -> next=n; i=ii; }; }; +!_LLI? !head = null; +while( arg-- ) + head = new _LLI(head,arg); +int sum=0; +var ptr = head; // A read-only ptr, to be assigned from read-only next fields +for( ; ptr; ptr = ptr.next ) + sum += ptr.i; +return sum; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print()); + assertEquals("45", Eval2.eval(code, 10)); + } + + @Test + public void sieveOfEratosthenes() { + CodeGen code = new CodeGen( +""" +var ary = new bool[arg], primes = new int[arg]; +var nprimes=0, p=0; +// Find primes while p^2 < arg +for( p=2; p*p < arg; p++ ) { + // skip marked non-primes + while( ary[p] ) p++; + // p is now a prime + primes[nprimes++] = p; + // Mark out the rest non-primes + for( int i = p + p; i < ary#; i += p ) + ary[i] = true; +} +// Now just collect the remaining primes, no more marking +for( ; p < arg; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; +// Copy/shrink the result array +var !rez = new int[nprimes]; +for( int j=0; j<nprimes; j++ ) + rez[j] = primes[j]; +return rez; +"""); + code.driver(CodeGen.Phase.FinalizeIR).driver(CodeGen.Phase.Schedule); + assertEquals("return []i64;", code.print()); + assertEquals("i64[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + } + +} |
