From 30e5fd29536b04faff8d17d8da8729a8f78bf734 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Fri, 20 Oct 2023 15:21:08 -0700 Subject: Chapter4 (#8) * Initial work on chapter 4. Updated language grammar for chapter 4 Enhanced Parser to implement comparison operators Parser now allows setting 'arg' as input parameter to program The StartNode now has a tuple as result with two values. We set up two projection nodes as output of Start - the first is bound to scope as '$ctrl' and second as 'arg'. Return node now obtains the ctrl input by looking up '$ctrl' in scope; thus it gets the projection node. Added TypeTuple - represents a tuple of values. This is the type of Start. Added BoolNode - for comparison operators. Added MultiNode - interface for nodes that generate tuple results. Start implements this interface. Added ProjNode - projections to extract values from Start node. Each projection takes a MultiNode and an index value. Initial writeup of chapter 4 in README. GraphVisualizer now searches node outputs as well to locate all nodes * Add TypeControl. The first member of Start's tuple is of TypeControl. Correct the projType() method * Quote port names in edges as unquoted $ctrl is not valid. Revise how we decide an edge is a control edge so that it works correctly for projections. * We used to compute initial type in Node but this is problematic for new node types such as ProjNode and BoolNode as the construction is not yet complete when Node calls compute(). So we have to move compute() call down to the sub type's constructor. Add some tests for comparison ops. * Describe the $ctrl name binding and how it represents the Ptri net model. Add a diagram to show why we need algebraic manipulation of expressions. * Implement a simplification for expressions such as (con1 + arg) + con2 -> arg + con3 con1 + (arg + con2) -> arg + con3 * Add diagram to show what the graph looks like post peephole * Explain projection nodes * Cleanup Chapter 4, add peepholes. Add like 6 peepholes. Replace the 2 prior ones with 1-liners. BoolNode: replace the switch with 6 1-line classes. Giant comment discussion on idealize() calling convention. TypeInteger shorten isTop and isBot, and use them instead of calling "equals()". * 2 more peeps, no tests yet * Cleanup Chapter 4 `peephole()` kills nodes killed by `idealize()` calls... and works recursively. This requires keeping "mid-peephole" nodes alive. The edge-swap code in Add and Mul now keeps their inputs alive mid-swap. MultiNode is a class, not an interface. Move the initial 'compute()' to peephole, instead of requiring each constructor to do it. Start and Constant remember their original types, even if the `_type` field changes. `kill()` flags nodes as DEAD. Print DEAD nodes with "DEAD". Change recursive `_print()` to a tik-tok style, so each individual print does not need to handle DEAD printing. This printing will soon need a "visit" bit to avoid infinite recursion. Projs pick up a debugging name. GraphViz: combine Multis and their Projs. Drop Constant inputs from Start. Fix coloring Control edges red. * minor WS cleanup * Cleaned up Node.peephole Use a smart DCE call after peephole. More comments. Tidy interfaces. * minor comment cleanup * But the proj nodes inside a table * No need to set column span on multi node as we put the proj nodes in a table * Update the dot diagrams * Rename method to make it more obvious to the reader * Start to add a description of peephole * Rename del() to delUse() and make it an instance method as it reads better that way. Add new method addUse() again to improve readability. Adjust removeDeadCode() to make the intention clear. * Update docs explaining how peephole works Update the diagrams * Fix errors * Add diagram to show what the graph looks like post peephole * 2 more peeps, no tests yet * Cleanup Chapter 4 `peephole()` kills nodes killed by `idealize()` calls... and works recursively. This requires keeping "mid-peephole" nodes alive. The edge-swap code in Add and Mul now keeps their inputs alive mid-swap. MultiNode is a class, not an interface. Move the initial 'compute()' to peephole, instead of requiring each constructor to do it. Start and Constant remember their original types, even if the `_type` field changes. `kill()` flags nodes as DEAD. Print DEAD nodes with "DEAD". Change recursive `_print()` to a tik-tok style, so each individual print does not need to handle DEAD printing. This printing will soon need a "visit" bit to avoid infinite recursion. Projs pick up a debugging name. GraphViz: combine Multis and their Projs. Drop Constant inputs from Start. Fix coloring Control edges red. * minor WS cleanup * Cleaned up Node.peephole Use a smart DCE call after peephole. More comments. Tidy interfaces. * minor comment cleanup * But the proj nodes inside a table * No need to set column span on multi node as we put the proj nodes in a table * Update the dot diagrams * Rename method to make it more obvious to the reader * Start to add a description of peephole * Rename del() to delUse() and make it an instance method as it reads better that way. Add new method addUse() again to improve readability. Adjust removeDeadCode() to make the intention clear. * Update docs explaining how peephole works Update the diagrams * Minor cleanups Split isUnused from isDead, with comments. Rename removeDeadCode back to deadCodeElim after discussion; matches the historical compiler wording. Fixup large javadoc example with a table. * match doc and code * Fix name of method * Fix name of method --------- Co-authored-by: dibyendumajumdar --- .../java/com/seaofnodes/simple/Chapter04Test.java | 252 +++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java (limited to 'chapter04/src/test/java') diff --git a/chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java new file mode 100644 index 00000000..988e3608 --- /dev/null +++ b/chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java @@ -0,0 +1,252 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.junit.Assert.*; + +public class Chapter04Test { + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void testChapter4Peephole() { + Parser parser = new Parser("return 1+arg+2; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return (Proj_3+3);", ret.print()); + } + + @Test + public void testChapter4Peephole2() { + Parser parser = new Parser("return (1+arg)+2;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return (Proj_3+3);", ret.print()); + } + + @Test + public void testChapter4Add0() { + Parser parser = new Parser("return 0+arg;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return Proj_3;", ret.print()); + } + + @Test + public void testChapter4AddAddMul() { + Parser parser = new Parser("return arg+0+arg;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return (Proj_3*2);", ret.print()); + } + + @Test + public void testChapter4Peephole3() { + Parser parser = new Parser("return 1+arg+2+arg+3; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return ((Proj_3*2)+6);", ret.print()); + } + + @Test + public void testChapter4Mul1() { + Parser parser = new Parser("return 1*arg;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return Proj_3;", ret.print()); + } + + @Test + public void testChapter4VarArg() { + Parser parser = new Parser("return arg; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertTrue(ret.in(0) instanceof ProjNode); + assertTrue(ret.in(1) instanceof ProjNode); + } + + @Test + public void testChapter4ConstantArg() { + Parser parser = new Parser("return arg; #showGraph;", TypeInteger.constant(2)); + ReturnNode ret = parser.parse(); + assertEquals("return 2;", ret.print()); + } + + @Test + public void testChapter4CompEq() { + Parser parser = new Parser("return 3==3; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return 1;", ret.print()); + } + + @Test + public void testChapter4CompEq2() { + Parser parser = new Parser("return 3==4; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return 0;", ret.print()); + } + + @Test + public void testChapter4CompNEq() { + Parser parser = new Parser("return 3!=3; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return 0;", ret.print()); + } + + @Test + public void testChapter4CompNEq2() { + Parser parser = new Parser("return 3!=4; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertEquals("return 1;", ret.print()); + } + + @Test + public void testVarDecl() { + Parser parser = new Parser("int a=1; return a;"); + ReturnNode ret = parser.parse(); + assertEquals("return 1;", ret.print()); + } + + @Test + public void testVarAdd() { + Parser parser = new Parser("int a=1; int b=2; return a+b;"); + ReturnNode ret = parser.parse(); + assertEquals("return 3;", ret.print()); + } + + @Test + public void testVarScope() { + Parser parser = new Parser("int a=1; int b=2; int c=0; { int b=3; c=a+b; } return c;"); + ReturnNode ret = parser.parse(); + assertEquals("return 4;", ret.print()); + } + + @Test + public void testVarScopeNoPeephole() { + Node._disablePeephole = true; + Parser parser = new Parser("int a=1; int b=2; int c=0; { int b=3; c=a+b; #showGraph; } return c; #showGraph;"); + ReturnNode ret = parser.parse(); + Node._disablePeephole = false; + assertEquals("return (1+3);", ret.print()); + } + + @Test + public void testVarDist() { + Parser parser = new Parser("int x0=1; int y0=2; int x1=3; int y1=4; return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1); #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return 8;", ret.print()); + } + + + @Test + public void testChapter2ParseGrammar() { + Node._disablePeephole = true; // disable peephole so we can observe full graph + Parser parser = new Parser("return 1+2*3+-5;"); + ReturnNode ret = parser.parse(); + assertEquals("return (1+((2*3)+(-5)));", ret.print()); + GraphVisualizer gv = new GraphVisualizer(); + System.out.println(gv.generateDotOutput(parser)); + Node._disablePeephole = false; + } + + @Test + public void testChapter2AddPeephole() { + Parser parser = new Parser("return 1+2;"); + ReturnNode ret = parser.parse(); + assertEquals("return 3;", ret.print()); + } + + @Test + public void testChapter2SubPeephole() { + Parser parser = new Parser("return 1-2;"); + ReturnNode ret = parser.parse(); + assertEquals("return -1;", ret.print()); + } + + @Test + public void testChapter2MulPeephole() { + Parser parser = new Parser("return 2*3;"); + ReturnNode ret = parser.parse(); + assertEquals("return 6;", ret.print()); + } + + @Test + public void testChapter2DivPeephole() { + Parser parser = new Parser("return 6/3;"); + ReturnNode ret = parser.parse(); + assertEquals("return 2;", ret.print()); + } + + @Test + public void testChapter2MinusPeephole() { + Parser parser = new Parser("return 6/-3;"); + ReturnNode ret = parser.parse(); + assertEquals("return -2;", ret.print()); + } + + @Test + public void testChapter2Example() { + Parser parser = new Parser("return 1+2*3+-5;"); + ReturnNode ret = parser.parse(); + assertEquals("return 2;", ret.print()); + GraphVisualizer gv = new GraphVisualizer(); + System.out.println(gv.generateDotOutput(parser)); + } + + @Test + public void testSimpleProgram() { + Parser parser = new Parser("return 1;"); + ReturnNode ret = parser.parse(); + StartNode start = Parser.START; + + assertTrue(ret.ctrl() instanceof ProjNode); + Node expr = ret.expr(); + if( expr instanceof ConstantNode con ) { + assertEquals(start,con.in(0)); + assertEquals(TypeInteger.constant(1), con._type); + } else { + fail(); + } + } + + @Test + public void testZero() { + Parser parser = new Parser("return 0;"); + parser.parse(); + StartNode start = Parser.START; + for( Node use : start._outputs ) + if( use instanceof ConstantNode con ) + assertEquals(TypeInteger.constant(0),con._type); + } + + @Test + public void testBad1() { + exceptionRule.expect(RuntimeException.class); + exceptionRule.expectMessage("Syntax error, expected =: "); + new Parser("ret").parse(); + } + + @Test + public void testBad2() { + exceptionRule.expect(RuntimeException.class); + exceptionRule.expectMessage("Syntax error: integer values cannot start with '0'"); + new Parser("return 0123;").parse(); + } + + @Test + public void testNotBad3() { + // this test used to fail in chapter 1 + assertEquals("return 12;", new Parser("return --12;").parse().print()); + } + + @Test + public void testBad4() { + exceptionRule.expect(RuntimeException.class); + exceptionRule.expectMessage("Syntax error, expected ;:"); + new Parser("return 100").parse(); + } + + @Test + public void testNotBad5() { + // this test used to fail in chapter 1 + assertEquals("return -100;", new Parser("return -100;").parse().print()); + } +} -- cgit v1.2.3