package com.seaofnodes.simple; 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 { @Ignore @Test public void testJig() { CodeGen code = new CodeGen( """ return 1; """); code.parse().opto().typeCheck(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } // --------------------------------------------------------------- @Test public void testInc0() { CodeGen code = new CodeGen(""" return arg++; """); code.parse().opto(); assertEquals("return arg;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @Test public void testInc1() { CodeGen code = new CodeGen(""" return arg+++arg++; """); code.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto(); assertEquals("return 65535;", code.print()); assertEquals("65535", Eval2.eval(code, 2)); } @Test public void testInc6() { CodeGen code = new CodeGen("return --arg;"); code.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto().typeCheck(); assertEquals("2",Eval2.eval(code,0)); } // --------------------------------------------------------------- @Test public void testVar0() { CodeGen code = new CodeGen("var d; return d;"); try { code.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto().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.parse().opto().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.parse().opto(); 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.parse().opto(); 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.parse().opto().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.parse().opto().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.parse().opto(); assertEquals("return 45;", code.print()); assertEquals("45", Eval2.eval(code, 0)); } @Test public void testVar13() { CodeGen code = new CodeGen(""" int i,i++; """); try { code.parse().opto().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.parse().opto().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.parse().opto().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.parse().opto().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.parse().opto(); 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.parse().opto(); 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.parse().opto().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.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto(); 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.parse().opto().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 ) break; sum += i; } return sum; """); code.parse().opto(); 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 array_of_b = bs; }; }; return new _A(new !B?[0]).array_of_b; """); code.parse().opto(); 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.parse().opto(); 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