package nebula.vm; /*** * Excerpted from "The Definitive ANTLR Reference", * published by The Pragmatic Bookshelf. * Copyrights apply to this code. It may not be used to create training material, * courses, books, articles, and the like. Contact us if you are in doubt. * We make no guarantees that this code is fit for any purpose. * Visit http://www.pragmaticprogrammer.com/titles/tpantlr for more book information. ***/ import java.io.StringWriter; import junit.framework.TestCase; import org.antlr.runtime.ANTLRFileStream; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CharStream; import org.antlr.runtime.CommonTokenStream; public class TestSourceCompiler extends TestCase { VMInterpreter cpu = new VMInterpreter(true, false); private NebulaRegisterParser loadFromString(String code) throws Exception { return parse(new ANTLRStringStream(code)); } private NebulaRegisterParser loadFromFile(String filename) throws Exception { return parse(new ANTLRFileStream(filename)); } private NebulaRegisterParser parse(CharStream stream) throws Exception { NebulaRegisterLexer assemblerLexer = new NebulaRegisterLexer(stream); CommonTokenStream tokens = new CommonTokenStream(assemblerLexer); NebulaRegisterParser parser = new SourceCompiler(tokens); return parser; } private String disassemble(ClassSymbol clz) { StringWriter out = new StringWriter(); DisAssembler dis = new DisAssembler(out); dis.disassemble(clz); return out.toString(); } private String disassemble(MethodSymbol method) { StringWriter out = new StringWriter(); DisAssembler dis = new DisAssembler(out); dis.disassemble(method); return out.toString(); } public void testClassDefineFile() throws Exception { String filename = "ClsDefineOnly.n"; NebulaRegisterParser parser = loadFromFile(filename); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); String actual = disassemble(clz); System.out.println(actual); assertEquals("ClsDefineOnly", clz.name); } public void testClsFieldGet() throws Exception { NebulaRegisterParser result = loadFromFile("ClsFieldGet.n"); System.out.println(result); String filename = "ClsFieldGet.n"; NebulaRegisterParser parser = loadFromFile(filename); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); String actual = disassemble(clz); System.out.println(actual); assertEquals("ClsFieldGet", clz.name); } public void test_classDefinition() throws Exception { //@formatter:off String text = "" + "class Test { " + " int i;" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("Test", clz.name); assertEquals(1, clz.fields.length); assertEquals("i", clz.fields[0].name); assertEquals("I", clz.fields[0].type.getName()); String actual = disassemble(clz); System.out.println(actual); //@formatter:off String expected = "" + ".class Test\n" + "\n" + ".field i\n" + "\n"; //@formatter:on assertEquals(expected, actual); } public void test_methodDefinition_define_i() throws Exception { //@formatter:off String text = "" + "class Test {" + " void funcSayHello(){" + " int i = 9;" + " return i;" + " }" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expected = "" + ".def funcSayHello: args=0, locals=2\n" + "0000: ICONST r1, 9 \n" + "0001: RET r1, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off expected = "" + ".def funcSayHello: args=0, locals=2\n" + "0000: ICONST r1, 9 \n" + "0001: RET r1, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.exec(clz.methods[0]); } public void test_methodDefinition_add_1_2() throws Exception { //@formatter:off String text = "" + "class Test {" + " void funcSayHello(){" + " int i = 1+2;" + " }" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expected = "" + ".def funcSayHello: args=0, locals=3\n" + "0000: ICONST r1, 1 \n" + "0001: ICONST r2, 2 \n" + "0002: IADD r1, r1, r2 \n" + "0003: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off expected = "" + ".def funcSayHello: args=0, locals=3\n" + "0000: ICONST r1, 1 \n" + "0001: ICONST r2, 2 \n" + "0002: IADD r1, r1, r2 \n" + "0003: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.exec(clz.methods[0]); } public void test_methodDefinition_add_1_complex() throws Exception { //@formatter:off String text = "" + "class Test {" + " void funcSayHello(){" + " int a = 3;" + " int b = 5;" + " int c = a + b;" + " c = a + b + a * b;" + " }" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expected = "" + ".def funcSayHello: args=0, locals=6\n" + "0000: ICONST r1, 3 \n" + "0001: ICONST r2, 5 \n" + "0002: IADD r3, r1, r2 \n" + "0003: IADD r4, r1, r2 \n" + "0004: IMUL r5, r1, r2 \n" + "0005: IADD r3, r4, r5 \n" + "0006: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off expected = "" + ".def funcSayHello: args=0, locals=6\n" + "0000: ICONST r1, 3 \n" + "0001: ICONST r2, 5 \n" + "0002: IADD r3, r1, r2 \n" + "0003: IADD r4, r1, r2 \n" + "0004: IMUL r5, r1, r2 \n" + "0005: IADD r3, r4, r5 \n" + "0006: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.exec(clz.methods[0]); } public void test_methodDefinition_invoke() throws Exception { //@formatter:off String text = "" + "class Test {" + " void funcSayHello(){" + " int i = this.test();" + " }" + " void test(){" + " return 4;" + " }" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expected = "" + ".def funcSayHello: args=0, locals=2\n" + "0000: CALL r0, #2:@Test.test(), r1\n" + "0001: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); assertEquals(expected, actual); cpu.exec(clz.methods[0]); } public void test_methodDefinition_invoke_1() throws Exception { //@formatter:off String text = "" + "class Test {" + " void funcSayHello(){" + " int i = this.test(2);" + " }" + " int test(){" + " return 4;" + " }" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expected = "" + ".def funcSayHello: args=0, locals=2\n" + "0000: ICONST r1, 2 \n" + "0001: CALL r0, #2:@Test.test(), r1\n" + "0002: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); assertEquals(expected, actual); cpu.exec(clz.methods[0]); } public void test_methodDefinition_invoke_1_a() throws Exception { //@formatter:off String text = "" + "class Test {" + " void funcSayHello2a(){" + " int a = 10;" + " int i = this.test(2,a+9);" + " return i;" + " }" + " int test(int a,int b ){" + " return a + b + b;" + " }" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello2a", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expectedMain = "" + ".def funcSayHello2a: args=0, locals=4\n" + "0000: ICONST r1, 10 \n" + "0001: ICONST r2, 2 \n" + "0002: ICONST r3, 9 \n" + "0003: IADD r3, r1, r3 \n" + "0004: CALL r0, #2:@Test.test(), r2\n" + "0005: RET r2, 0 \n" + "\n" ; //@formatter:on assertEquals(expectedMain, actual); actual = disassemble(clz.methods[1]); System.out.println(actual); //@formatter:off String expectedTest = "" + ".def test: args=2, locals=2\n" + "0000: IADD r3, r1, r2 \n" + "0001: IADD r3, r3, r2 \n" + "0002: RET r3, 0 \n" + "\n" ; //@formatter:on assertEquals(expectedTest, actual); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); assertEquals(expectedMain, actual); int result = cpu.exec(clz.methods[0]); assertEquals(40,result); } public void test_method_complex_compute() throws Exception { NebulaRegisterParser parser = loadFromFile("Person.n"); ClassSymbol clzPerson = parser.classDefinition(); //@formatter:off String text = "" + "class Test {" + " void funcSayHello2a(){" + " int a = 10;" + " Person p = new Person();" + " p.age = 9;" + " int d = this.test(p.age+1,10);" + " return d;" + " }" + " int test(int a,int b ){" + " return a + b + b;" + " }" + "}"; //@formatter:on parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello2a", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expectedMain = "" + ".def funcSayHello2a: args=0, locals=5\n" + "0000: ICONST r1, 10 \n" + "0001: STRUCT r2, 1024 \n" + "0002: ICONST r3, 9 \n" + "0003: FSTORE r2, #3:@Person.age, r3\n" + "0004: FLOAD r3, r2, #3:@Person.age\n" + "0005: ICONST r4, 1 \n" + "0006: IADD r3, r3, r4 \n" + "0007: ICONST r4, 10 \n" + "0008: CALL r0, #4:@Test.test(), r3\n" + "0009: RET r3, 0 \n" + "\n" ; //@formatter:on assertEquals(expectedMain, actual); actual = disassemble(clz.methods[1]); System.out.println(actual); //@formatter:off String expectedTest = "" + ".def test: args=2, locals=2\n" + "0000: IADD r3, r1, r2 \n" + "0001: IADD r3, r3, r2 \n" + "0002: RET r3, 0 \n" + "\n" ; //@formatter:on assertEquals(expectedTest, actual); cpu.resolve(clzPerson); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); assertEquals(expectedMain, actual); cpu.resolve(clz.methods[0]); int result = cpu.exec(clz.methods[0]); assertEquals(30,result); } public void test_methodDefinition_params() throws Exception { //@formatter:off String text = "" + "class Test {" + " void funcSayHello(int a, int b){" + " int c = a + b;" + " }" + "}"; //@formatter:on NebulaRegisterParser parser = loadFromString(text); ClassSymbol clz = parser.classDefinition(); assertTrue(parser.getNumberOfSyntaxErrors() == 0); assertEquals("funcSayHello", clz.methods[0].name); String actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off String expected = "" + ".def funcSayHello: args=2, locals=2\n" + "0000: IADD r3, r1, r2 \n" + "0001: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.resolve(clz); actual = disassemble(clz.methods[0]); System.out.println(actual); //@formatter:off expected = "" + ".def funcSayHello: args=2, locals=2\n" + "0000: IADD r3, r1, r2 \n" + "0001: RET r0, 0 \n" + "\n"; //@formatter:on assertEquals(expected, actual); cpu.exec(clz.methods[0]); } }