initial commit

This commit is contained in:
2025-11-08 15:33:53 +01:00
commit e854da56aa
60 changed files with 10888 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="lib2" level="project" />
<orderEntry type="library" name="lib" level="project" />
<orderEntry type="module" module-name="MicroJava Compiler" />
<orderEntry type="module" module-name="MicroJava Compiler2" />
</component>
</module>

View File

@@ -0,0 +1,54 @@
program Animals
class Animal {
char[] name;
}
{
void setName(Animal a, char[] name) {
a.name = name;
}
void aPrint(Animal a)
int i, l;
char c;
{
l = len(a.name);
i = 0;
while (i < l) {
print(a.name[i]);
i += 1;
}
}
void main()
int a;
Animal[] animals;
char[] cat;
char[] dog;
char[] octopus;
{
cat = new char[3];
cat[0] = 'c';
cat[1] = 'a';
cat[2] = 't';
dog = new char[3];
dog[0] = 'd';
dog[1] = 'o';
dog[2] = 'g';
octopus = new char[7];
octopus[0] = 'o';
octopus[1] = 'c';
octopus[2] = 't';
octopus[3] = 'o';
octopus[4] = 'p';
octopus[5] = 'u';
octopus[6] = 's';
animals = new Animal[3];
animals[0] = new Animal;
animals[1] = new Animal;
animals[2] = new Animal;
setName(animals[0],cat);
setName(animals[1],dog);
setName(animals[2],octopus);
read(a);
aPrint(animals[a]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
program Test
{
void main()
int a;
{
read(a);
if (a == 1) { print('='); print('='); print(','); }
if (a != 1) { print('!'); print('='); print(','); }
if (a < 1) { print('<'); print(','); }
if (a <= 1) { print('<'); print('='); print(','); }
if (a > 1) { print('>'); print(','); }
if (a >= 1) { print('>'); print('='); print(','); }
}
}

View File

@@ -0,0 +1,880 @@
package ssw.mj.test;
import org.junit.jupiter.api.Test;
import ssw.mj.test.support.BaseCompilerTestCase;
import static ssw.mj.Errors.Message.*;
public class CodeGenerationTest extends BaseCompilerTestCase {
/**
* Symbol table for most examples of this test class.
*/
private void expectExampleSymTab() {
expectSymTabUniverse();
expectSymTab("Program A:");
expectSymTab(" Constant: int max = 12");
expectSymTab(" Global Variable 0: char c");
expectSymTab(" Global Variable 1: int i");
expectSymTab(" Type B: class (2 fields)");
expectSymTab(" Local Variable 0: int x");
expectSymTab(" Local Variable 1: int y");
expectSymTab(" Method: void main (3 locals, 0 parameters)");
expectSymTab(" Local Variable 0: int[] iarr");
expectSymTab(" Local Variable 1: class (2 fields) b");
expectSymTab(" Local Variable 2: int n");
}
private void expectSymTabWithSum() {
expectSymTabUniverse();
expectSymTab("Program A:");
expectSymTab(" Constant: int max = 12");
expectSymTab(" Global Variable 0: char c");
expectSymTab(" Global Variable 1: int i");
expectSymTab(" Type B: class (2 fields)");
expectSymTab(" Local Variable 0: int x");
expectSymTab(" Local Variable 1: int y");
expectSymTab(" Method: void main (4 locals, 0 parameters)");
expectSymTab(" Local Variable 0: int[] iarr");
expectSymTab(" Local Variable 1: class (2 fields) b");
expectSymTab(" Local Variable 2: int n");
expectSymTab(" Local Variable 3: int sum");
}
@Test
public void bsp11() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n;" + LF + //
" {" + LF + //
" read(i); " + LF + //
" if (i <= n) n = 1;" + LF + //
" print(n); " + LF + //
" }" + LF + //
"}");
expectExampleSymTab();
addExpectedRun("0", "1");
addExpectedRun("1", "0");
parseVerifyVisualize();
}
@Test
public void bsp12() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n;" + LF + //
" {" + LF + //
" read(i); " + LF + //
" n = 1; " + LF + //
" if (i <= n && n < 0) n = 2;" + LF + //
" print(n); " + LF + //
" }" + LF + //
"}");
expectExampleSymTab();
addExpectedRun("0", "1");
addExpectedRun("2", "1");
parseVerifyVisualize();
}
@Test
public void bsp13() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n;" + LF + //
" {" + LF + //
" read(i); " + LF + //
" n = 1; " + LF + //
" if (i <= n || i < 10) n = 2;" + LF + //
" print(n); " + LF + //
" }" + LF + //
"}");
expectExampleSymTab();
addExpectedRun("0", "2");
addExpectedRun("2", "2");
addExpectedRun("20", "1");
parseVerifyVisualize();
}
@Test
public void bsp14() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n;" + LF + //
" {" + LF + //
" read(i); " + LF + //
" n = 1; " + LF + //
" if (i <= n || i < 10 && i > 5) n = 2;" + LF + //
" print(n); " + LF + //
" }" + LF + //
"}");
expectExampleSymTab();
addExpectedRun("0", "2");
addExpectedRun("2", "1");
addExpectedRun("6", "2");
addExpectedRun("20", "1");
parseVerifyVisualize();
}
@Test
public void bsp15() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n;" + LF + //
" {" + LF + //
" read(n); " + LF + //
" while (i <= n) { i++; }" + LF + //
" print(i); " + LF + //
" }" + LF + //
"}");
expectExampleSymTab();
addExpectedRun("0", "1");
addExpectedRun("-1", "0");
addExpectedRun("1", "2");
addExpectedRun("10", "11");
parseVerifyVisualize();
}
@Test
public void bsp16() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n;" + LF + //
" {" + LF + //
" read(i); " + LF + //
" if (i <= max) n = 1; else n = 2;" + LF + //
" print(n); " + LF + //
" }" + LF + //
"}");
expectExampleSymTab();
addExpectedRun("0", "1");
addExpectedRun("13", "2");
addExpectedRun("12", "1");
addExpectedRun("-1", "1");
addExpectedRun("-13", "1");
parseVerifyVisualize();
}
@Test
public void bsp17() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n; int sum;" + LF + //
" {" + LF + //
" read(n); " + LF + //
" sum = 0; " + LF + //
" while (i <= n) { sum += i; i++; }" + LF + //
" print(sum); " + LF + //
" }" + LF + //
"}");
expectSymTabWithSum();
addExpectedRun("0", "0");
addExpectedRun("-1", "0");
addExpectedRun("1", "1");
addExpectedRun("10", "55");
parseVerifyVisualize();
}
@Test
public void bsp18() {
initCode("program A" + LF + //
" final int max = 12;" + LF + //
" char c; int i;" + LF + //
" class B { int x, y; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int[] iarr; B b; int n; int sum;" + LF + //
" {" + LF + //
" read(n); " + LF + //
" sum = 0; " + LF + //
" i = 2;" + LF + //
" while (i <= n) { sum += i; i++; }" + LF + //
" print(sum); " + LF + //
" }" + LF + //
"}");
expectSymTabWithSum();
addExpectedRun("0", "0");
addExpectedRun("-1", "0");
addExpectedRun("1", "0");
addExpectedRun("10", "54");
parseVerifyVisualize();
}
@Test
public void methodCall() {
initCode("program A" + LF + // 1
"{" + LF + // 2
" void bar() {" + LF + // 3
" print('b');" + LF + // 4
" print('a');" + LF + // 5
" print('r');" + LF + // 6
" }" + LF + // 7
" void foo() {" + LF + // 8
" print('f');" + LF + // 9
" print('o');" + LF + // 10
" print('o');" + LF + // 11
" }" + LF + // 12
" void main () {" + LF + // 13
" foo();" + LF + // 14
" }" + LF + // 15
"}"); // 16
addExpectedRun("", "foo");
parseVerifyVisualize();
}
@Test
public void fib() {
initCode("program A" + LF + //
"{" + LF + //
" int fib(int n) {" + LF + //
" if (n <= 1) return 1; " + LF + //
" return fib(n-1) + fib(n-2); " + LF + //
" }" + LF + //
" void main ()" + LF + //
" int n;" + LF + //
" {" + LF + //
" read(n); " + LF + //
" print(fib(n)); " + LF + //
" }" + LF + //
"}");
addExpectedRun("-1", "1");
addExpectedRun("0", "1");
addExpectedRun("1", "1");
addExpectedRun("2", "2");
addExpectedRun("3", "3");
addExpectedRun("4", "5");
addExpectedRun("5", "8");
addExpectedRun("6", "13");
addExpectedRun("7", "21");
addExpectedRun("8", "34");
addExpectedRun("9", "55");
addExpectedRun("10", "89");
addExpectedRun("11", "144");
addExpectedRun("22", "28657");
parseVerifyVisualize();
}
@Test
public void fibDyn() {
initCode("program A" + LF + //
" int[] matrix; " + LF + //
"{" + LF + //
" int fib(int n) int r; {" + LF + //
" if (n <= 1) return 1; " + LF + //
" if(matrix[n] != 0) return matrix[n]; " + LF + //
" r = fib(n-1) + fib(n-2); " + LF + //
" matrix[n] = r; " + LF + //
" return r; " + LF + //
" }" + LF + //
" void main ()" + LF + //
" int n;" + LF + //
" {" + LF + //
" matrix = new int[1000]; " + LF + //
" read(n); " + LF + //
" print(fib(n)); " + LF + //
" }" + LF + //
"}");
addExpectedRun("-1", "1");
addExpectedRun("0", "1");
addExpectedRun("1", "1");
addExpectedRun("2", "2");
addExpectedRun("3", "3");
addExpectedRun("4", "5");
addExpectedRun("5", "8");
addExpectedRun("6", "13");
addExpectedRun("7", "21");
addExpectedRun("8", "34");
addExpectedRun("9", "55");
addExpectedRun("10", "89");
addExpectedRun("11", "144");
addExpectedRun("22", "28657");
addExpectedRun("30", "1346269");
addExpectedRun("40", "165580141");
addExpectedRun("45", "1836311903");
parseVerifyVisualize();
}
@Test
public void testElseIf() {
initCode("program Test {" + LF + // 1
" void main() int i; {" + LF + // 2
" read(i);" + LF + // 3
" if (i == 1) print(9);" + LF + // 4
" else if (i == 2) print(8);" + LF + // 5
" else print(7);" + LF + // 6
" }" + LF + // 7
"}");
addExpectedRun("1", "9");
addExpectedRun("2", "8");
addExpectedRun("3", "7");
addExpectedRun("4", "7");
parseVerifyVisualize();
}
@Test
public void mainVar() {
initCode("program Test" + LF + //
" int main;" + LF + //
"{" + LF + //
"}");
expectError(4, 2, MAIN_NOT_FOUND);
parseVerifyVisualize();
}
@Test
public void mainNotVoid() {
initCode("program Test {" + LF + //
" char main() { }" + LF + //
"}");
expectError(2, 15, MAIN_NOT_VOID);
parseVerifyVisualize();
}
@Test
public void noLoop() {
initCode("program Test {" + LF + //
" void main() {" + LF + //
" break;" + LF + //
" }" + LF + //
"}");
expectError(3, 10, BREAK_OUTSIDE_LOOP);
parseVerifyVisualize();
}
@Test
public void returnVoid() {
initCode("program Test {" + LF + //
" void test() {" + LF + //
" return 5;" + LF + //
" }" + LF + //
" void main() {}" + LF + //
"}");
expectError(3, 12, UNEXPECTED_RETURN_VALUE);
parseVerifyVisualize();
}
@Test
public void wrongReturnType() {
initCode("program Test {" + LF + //
" int test() {" + LF + //
" return 'x';" + LF + //
" }" + LF + //
" void main() {}" + LF + //
"}");
expectError(3, 15, RETURN_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void wrongReturnTypeNull() {
initCode("program Test {" + LF + //
" int test() {" + LF + //
" return null;" + LF + //
" }" + LF + //
" void main() {}" + LF + //
"}");
expectError(3, 16, RETURN_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void noReturnVal() {
initCode("program Test {" + LF + //
" int test() {" + LF + //
" return;" + LF + //
" }" + LF + //
" void main() {}" + LF + //
"}");
expectError(3, 11, MISSING_RETURN_VALUE);
parseVerifyVisualize();
}
@Test
public void wrongReturnTypeArr() {
initCode("program Test {" + LF + //
" int[] test() {" + LF + //
" return new int[10];" + LF + //
" }" + LF + //
" void main() {}" + LF + //
"}");
expectError(2, 9, ILLEGAL_METHOD_RETURN_TYPE);
parseVerifyVisualize();
}
@Test
public void wrongReturnClass() {
initCode("program Test" + LF + //
" class C1 { }" + LF + //
"{" + LF + //
" C1 test() {" + LF + //
" return new C1;" + LF + //
" }" + LF + //
" void main() {}" + LF + //
"}");
expectError(4, 6, ILLEGAL_METHOD_RETURN_TYPE);
parseVerifyVisualize();
}
@Test
public void noMeth() {
initCode("program Test {" + LF + //
" void main() int i; {" + LF + //
" i(10);" + LF + //
" }" + LF + //
"}");
expectError(3, 7, CALL_TO_NON_METHOD);
parseVerifyVisualize();
}
@Test
public void paramType() {
initCode("program Test {" + LF + //
" void method(int x) { }" + LF + //
" void main() {" + LF + //
" method('a');" + LF + //
" }" + LF + //
"}");
expectError(4, 15, ARGUMENT_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void paramTypeArr() {
initCode("program Test {" + LF + //
" void method(int[] x) { }" + LF + //
" void main() {" + LF + //
" method(new char[10]);" + LF + //
" }" + LF + //
"}");
expectError(4, 24, ARGUMENT_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void paramTypeClass() {
initCode("program Test" + LF + //
" class C1 { }" + LF + //
" class C2 { }" + LF + //
"{" + LF + //
" void method(C1 c1) { }" + LF + //
" void main() {" + LF + //
" method(new C2);" + LF + //
" }" + LF + //
"}");
expectError(7, 18, ARGUMENT_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void moreParams() {
initCode("program Test {" + LF + //
" void method(int x, char c) { }" + LF + //
" void main() {" + LF + //
" method(1, 'a', 1);" + LF + //
" }" + LF + //
"}");
expectError(4, 21, WRONG_ARGUMENT_COUNT);
parseVerifyVisualize();
}
@Test
public void lessParams() {
initCode("program Test {" + LF + //
" void method(int x, char c) { }" + LF + //
" void main() {" + LF + //
" method(1);" + LF + //
" }" + LF + //
"}");
expectError(4, 13, WRONG_ARGUMENT_COUNT);
parseVerifyVisualize();
}
@Test
public void incompTypesCond() {
initCode("program Test {" + LF + //
" void main() int i; { " + LF + //
" if (i > null) { }" + LF + //
" }" + LF + //
"}");
expectError(3, 17, INCOMPATIBLE_TYPES);
parseVerifyVisualize();
}
@Test
public void incompTypesCondArr() {
initCode("program Test {" + LF + //
" void main() int[] ia; char[] ca; { " + LF + //
" if (ia > ca) { }" + LF + //
" }" + LF + //
"}");
expectError(3, 16, INCOMPATIBLE_TYPES);
parseVerifyVisualize();
}
@Test
public void incompTypesCondClass() {
initCode("program Test" + LF + //
" class C1 { }" + LF + //
"{" + LF + //
" void main() C1 c1; int i; { " + LF + //
" if (c1 > i) { };" + LF + //
" }" + LF + //
"}");
expectError(5, 15, INCOMPATIBLE_TYPES);
parseVerifyVisualize();
}
@Test
public void wrongEqCheck() {
initCode("program Test {" + LF + //
" void main() int[] ia1, ia2; {" + LF + //
" if (ia1 > ia2) { }" + LF + //
" }" + LF + //
"}");
expectError(3, 18, ILLEGAL_REFERENCE_COMPARISON);
parseVerifyVisualize();
}
@Test
public void testSimpleBreak() {
initCode("program Test {" + LF + //
" void main() {" + LF + //
" while(42 > 0) /* while(true) */" + LF + //
" {" + LF + //
" break;" + LF + //
" }" + LF + //
" }" + LF + //
"}");
parseVerifyVisualize();
}
@Test
public void testBreak() {
initCode("program A" + LF + //
" int i;" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int n;" + LF + //
" {" + LF + //
" read(n); " + LF + //
" while (i <= n) { while(1 < 2) { if(1 == 1) { break; } } if(i == 5) break; i++; }"
+ LF + //
" print(i); " + LF + //
" }" + LF + //
"}");
addExpectedRun("10", "5");
parseVerifyVisualize();
}
@Test
public void testNestedBreak() {
initCode("program Test {" + LF + //
" void main() " + LF + //
" int n, o;" + LF + //
" {" + LF + //
" o = 21;" + LF + //
" while(83 < 84)" + LF + //
" {" + LF + //
" while(167 < 168)" + LF + //
" {" + LF + //
" break;" + LF + //
" }" + LF + //
" break;" + LF + //
" }" + LF + //
" }" + LF + //
"}");
parseVerifyVisualize();
}
@Test
public void lenTest() {
initCode("program A" + LF + //
" class A { int[] x; }" + LF + //
" class B { A a; }" + LF + //
" class C { B b; }" + LF + //
"{" + LF + //
" void main ()" + LF + //
" C[] c;" + LF + //
" {" + LF + //
" c = new C[5];" + LF + //
" print(len(c));" + LF + //
" }" + LF + //
"}");
addExpectedRun("5");
parseVerifyVisualize();
}
@Test
public void basicOrdChrTest() {
initCode("program Test {" + LF + //
" void main() int i; char c; {" + LF + //
" i = ord('A');" + LF + //
" print(i);" + LF + //
" i = ord('*');" + LF + //
" print(i);" + LF + //
" c = chr(49);" + LF + //
" print(c);" + LF + //
" }" + LF + //
"}");
addExpectedRun("65421");
parseVerifyVisualize();
}
@Test
public void trappingOrdChrTest() {
initCode("program Test {" + LF + //
" int trap() {" + LF + //
" print(7 * 7);" + LF + //
" }" + LF + //
" void main() int i; char c; {" + LF + //
" ord('!');" + LF + //
" chr(42);" + LF + //
" i = ord('!');" + LF + //
" c = chr(42);" + LF + //
" }" + LF + //
"}");
addExpectedRun("");
parseVerifyVisualize();
}
@Test
public void unusedReturnVal() {
initCode("program Test {" + LF + //
" int getUnused() {" + LF + //
" return 351;" + LF + //
" }" + LF + //
" int polluteAndGet() {" + LF + //
" getUnused();" + LF + //
" return 42;" + LF + //
" }" + LF + //
" void main() {" + LF + //
" print(932 + polluteAndGet());" + LF + //
" }" + LF + //
"}");
addExpectedRun("974");
parseVerifyVisualize();
}
@Test
public void coverUniverseMethod() {
initCode("program Test {" + LF + //
" int cast(char c) { return ord(c); }" + LF + //
" int ord(char c) { return cast(c) - 30; }" + LF + //
" void main() {" + LF + //
" print(chr(ord('A')));" + LF + //
" }" + LF + //
"}");
addExpectedRun("#");
parseVerifyVisualize();
}
@Test
public void paramType2() {
initCode("program Test {" + LF + //
" void method(int x, int y) { }" + LF + //
" void main() {" + LF + //
" method(1, 'a');" + LF + //
" }" + LF + //
"}");
expectError(4, 18, ARGUMENT_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void paramTypeArr2() {
initCode("program Test {" + LF + //
" void method(int[] x, int y) { }" + LF + //
" void main() {" + LF + //
" method(new int[10], new char[10]);" + LF + //
" }" + LF + //
"}");
expectError(4, 37, ARGUMENT_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void paramTypeClass2() {
initCode("program Test" + LF + //
" class C1 { }" + LF + //
" class C2 { }" + LF + //
"{" + LF + //
" void method(C1 c1, C2 c2) { }" + LF + //
" void main() {" + LF + //
" method(new C1, new C1);" + LF + //
" }" + LF + //
"}");
expectError(7, 26, ARGUMENT_TYPE_MISMATCH);
parseVerifyVisualize();
}
@Test
public void testRelops() {
initFile("relops.mj");
addExpectedRun("0", "!=,<,<=,");
addExpectedRun("1", "==,<=,>=,");
addExpectedRun("2", "!=,>,>=,");
parseVerifyVisualize();
}
@Test
public void testAnimals() {
initFile("animals.mj");
addExpectedRun("0", "cat");
addExpectedRun("1", "dog");
addExpectedRun("2", "octopus");
parseVerifyVisualize();
}
@Test
public void compareNeg() {
initCode("program A" + LF + //
"{" + LF + //
" void main ()" + LF + //
" int neg;" + LF + //
" {" + LF + //
" neg = -42;" + LF + //
" if (neg == -42) print(42);" + LF + //
" else print(neg); " + LF + //
" }" + LF + //
"}");
addExpectedRun("42");
parseVerifyVisualize();
}
// index from end tests
@Test
public void arrayFromEndWithFunctionCall() {
initCode("""
program Test
final int len = 2;
{
int const1() {
return 1;
}
void main() int[] a; {
a = new int[len];
a[0] = 13;
a[~const1()] = 42;
print(a[~const1()]);
}
}""");
addExpectedRun("42");
parseVerifyVisualize();
}
@Test
public void createPalindrom() {
initCode("""
program Test {
void toPalindrom(char[] in, char[] out) int i, l; {
l = len(in);
i = 0;
while (i < l) {
out[i] = in[i];
out[~(i + 1)] = in[i];
i++;
}
}
void printText(char[] text) int i; {
i = 0;
while (i < len(text)) {
print(text[i]);
i++;
}
}
void main() char[] a, out; int i; {
a = new char[5];
a[0] = 'l';
a[1] = 'a';
a[2] = 'g';
a[3] = 'e';
a[4] = 'r';
out = new char[10];
toPalindrom(a, out);
printText(out);
a = new char[2];
a[0] = 'o';
a[1] = 't';
out = new char[4];
toPalindrom(a, out);
printText(out);
}
}""");
addExpectedRun("lagerregalotto");
parseVerifyVisualize();
}
@Test
public void iterateArrayFromEnd() {
initCode("""
program Test
final int len = 3;
{
void main() int[] a; int i; {
a = new int[len];
a[0] = 1;
a[1] = 2;
a[2] = 3;
i = 1;
while (i <= len) {
print(a[~i]);
i++;
}
}
}""");
addExpectedRun("321");
parseVerifyVisualize();
}
}

View File

@@ -0,0 +1,406 @@
package ssw.mj.test;
import org.junit.jupiter.api.Test;
import ssw.mj.scanner.Token;
import ssw.mj.test.support.BaseCompilerTestCase;
import static ssw.mj.Errors.Message.*;
public class ParserTest extends BaseCompilerTestCase {
@Test
public void testWorkingFinalDecls() {
initCode("program Test" + LF + // 1
" final int i = 1;" + LF + // 2
" final int j = 1;" + LF + // 3
" final int k = 1;" + LF + // 4
"{ void main() { } }"); // 5
parseVerifyVisualize();
}
@Test
public void testWorkingDecls() {
initCode("program Test" + LF + // 1
" int i;" + LF + // 2
" int j, k;" + LF + // 3
"{ void main() { } }"); // 4
parseVerifyVisualize();
}
@Test
public void testWorkingMethods() {
initCode("program Test" + LF + // 1
" int i;" + LF + // 2
" int j, k;" + LF + // 3
"{" + LF + // 4
" void foo() { }" + LF + // 5
" void bar() { }" + LF + // 6
" void main() { }" + LF + // 7
" }" + LF // 8
);
parseVerifyVisualize();
}
@Test
public void testWorkingMethodsWithParameters() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void foo(int i) { }" + LF + // 3
" void bar(int i, char c) { }" + LF + // 4
" void main() { }" + LF + // 5
" }" + LF // 6
);
parseVerifyVisualize();
}
@Test
public void testWorkingMethodsWithLocals() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void foo() int i; { }" + LF + // 3
" void bar() int i; char c; { }" + LF + // 4
" void main() { }" + LF + // 5
" }" + LF // 6
);
parseVerifyVisualize();
}
@Test
public void testWorkingMethodsWithParametersAndLocals() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void foo(char ch) int i; { }" + LF + // 3
" void bar(int x, int y) int i; char c; { }" + LF + // 4
" void main() { }" + LF + // 5
" }" + LF // 6
);
parseVerifyVisualize();
}
@Test
public void testWorkingMethodCall() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void foo(char ch) int i; { }" + LF + // 3
" void main() { foo('a'); }" + LF + // 4
" }" + LF // 5
);
parseVerifyVisualize();
}
@Test
public void testWorkingMethodCallTwoParams() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void foo(char ch, int x) int i; { }" + LF + // 3
" void main() { foo('a', 1); }" + LF + // 4
" }" + LF // 5
);
parseVerifyVisualize();
}
@Test
public void testWorkingMethodCallThreeParams() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void foo(char ch, int x, char ch2) int i; { }" + LF + // 3
" void main() { foo('a', 1, 'b'); }" + LF + // 4
" }" + LF // 5
);
parseVerifyVisualize();
}
@Test
public void testWorkingClass() {
initCode("program Test" + LF + // 1
"class X { int i; int j; }" + LF + // 2
"{" + LF + // 3
" void main() X x; { x = new X; }" + LF + // 4
"}" + LF // 5
);
parseVerifyVisualize();
}
@Test
public void testWorkingArray() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void main() int[] x; { x = new int[10]; }" + LF + // 3
"}" + LF // 4
);
parseVerifyVisualize();
}
@Test
public void testWorkingIncDec() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void main() int i; { i--; i++; }" + LF + // 3
" }" + LF // 4
);
parseVerifyVisualize();
}
@Test
public void testWorkingElseIf() {
initCode("program Test {" + LF + // 1
" void main() int i; {" + LF + // 2
" if (i > 10) i++;" + LF + // 3
" else if (i < 5) i--;" + LF + // 4
" else i += 8;" + LF + // 5
" }" + LF + // 6
"}");
parseVerifyVisualize();
}
@Test
public void testWorkingLoop() {
initCode("program Test {" + LF + // 1
" void main () int i; {" + LF + // 2
" i = 0;" + LF + // 3
" while (i < 42) {" + LF + // 4
" i++;" + LF + // 5
" }" + LF + // 6
" }" + LF + // 7
"}");
parseVerifyVisualize();
}
@Test
public void mulAssign() {
initCode("program Test {" + LF + //
" void main() int i; {" + LF + //
" i = 2;" + LF + //
" i *= 2;" + LF + //
" }" + LF + //
"}");
parseVerifyVisualize();
}
@Test
public void returnExpr() {
initCode("program Test {" + LF + //
" void main() { }" + LF + //
" int wrong1() { " + LF + //
" return 2 + 3;" + LF + //
" }" + LF + //
"}");
parseVerifyVisualize();
}
@Test
public void wrongConstDecl() {
initCode("program Test" + LF + //
" final int i = a;" + LF + //
"{ void main() { } }");
expectError(2, 17, INVALID_CONST_TYPE);
parseVerifyVisualize();
}
@Test
public void wrongDesignFollow() {
initCode("program Test {" + LF + //
" void main() int i; {" + LF + //
" i**;" + LF + //
" }" + LF + //
"}");
expectError(3, 6, INVALID_DESIGNATOR_STATEMENT);
parseVerifyVisualize();
}
@Test
public void wrongFactor() {
initCode("program Test {" + LF + //
" void main () int i; { " + LF + //
" i = i + if;" + LF + //
" }" + LF + //
"}");
expectError(3, 13, INVALID_FACTOR);
parseVerifyVisualize();
}
@Test
public void wrongRelOp() {
initCode("program Test {" + LF + //
" void main() int i; {" + LF + //
" if (i x 5);" + LF + //
" }" + LF + //
"}");
expectError(3, 11, INVALID_REL_OP);
parseVerifyVisualize();
}
@Test
public void wrongStart() {
initCode("noprogram Test { }");
expectError(1, 1, TOKEN_EXPECTED, "program");
parseVerifyVisualize();
}
@Test
public void noProgName() {
initCode("program { }");
expectError(1, 9, TOKEN_EXPECTED, "identifier");
parseVerifyVisualize();
}
@Test
public void wrongVarDecl() {
initCode("program Test " + LF + //
"int var1,,,,var2;" + LF + //
"{ void main() { } }");
expectError(2, 10, TOKEN_EXPECTED, Token.Kind.ident.label());
parseVerifyVisualize();
}
@Test
public void eofExpected() {
initCode("program Test {" + LF + //
" void main() {}" + LF + //
"}moretext");
expectError(3, 2, TOKEN_EXPECTED, "end of file");
parseVerifyVisualize();
}
@Test
public void invalidEOF1() {
initCode("program Test {" + LF + //
" void main() {");
expectError(2, 16, TOKEN_EXPECTED, "}");
parseVerifyVisualize();
}
@Test
public void invalidEOF2() {
initCode("program Test {" + LF + //
" void main() {" + LF + //
" if ()");
expectError(3, 9, INVALID_FACTOR);
parseVerifyVisualize();
}
@Test
public void invalidEOF3() {
initCode("program Test" + LF + //
" class C {" + LF + //
" int i");
expectError(3, 10, TOKEN_EXPECTED, ";");
parseVerifyVisualize();
}
@Test
public void testWorkingReadAndPrint() {
initCode("program Test {" + LF + // 1
" void main() int i; {" + LF + // 2
" read(i);" + LF +
" print(i);" + LF +
" }" + LF + //3
"}");//4
parseVerifyVisualize();
}
// index from end tests
@Test
public void wrongTildeInExpr() {
initCode("""
program Test {
void main() int i; {
i = ~1;
}
}""");
expectError(3, 9, INVALID_FACTOR);
parseVerifyVisualize();
}
@Test
public void wrongTildeCompoundAssign() {
initCode("""
program Test {
void main() int i; {
i ~= 2;
}
}""");
expectError(3, 7, INVALID_DESIGNATOR_STATEMENT);
parseVerifyVisualize();
}
@Test
public void wrongDoubleTilde() {
initCode("""
program Test {
void main() int[] a; int i; {
i = a[~~1];
}
}
""");
expectError(3, 12, INVALID_FACTOR);
parseVerifyVisualize();
}
@Test
public void constantArrayAccessFromEnd() {
initCode("""
program Test {
void main() int[] a; int i; {
a = new int[3];
a[~1] = 3;
a[~2] = 2;
a[~3] = 1;
i = a[~1];
print(i);
}
}""");
parseVerifyVisualize();
}
@Test
public void computedArrayAccessFromEnd() {
initCode("""
program Test {
void main() int[] a; int i; {
a = new int[3];
a[~(2 * 6 - 11)] = 3;
a[~(9 - 11 + 4)] = 2;
a[~(-(-3))] = 1;
i = a[~(a[0] + 2 * 6 - 11)];
print(i);
}
}""");
parseVerifyVisualize();
}
@Test
public void dynamicArrayAccessFromEnd() {
initCode("""
program Test {
void main() int[] a; int i; {
read(i);
a = new int[i];
a[~i] = 1;
a[~(i - 1)] = 2;
a[~(i - 2)] = 3;
i = a[~i];
print(i);
}
}""");
parseVerifyVisualize();
}
@Test
public void globalArrayAccessFromEnd() {
initCode("""
program Test
final int len = 3;
{
void main() int[] a; int i; {
a = new int[len];
a[~len] = 1;
a[~(len - 1)] = 2;
a[~(len - 2)] = 3;
i = a[~len];
print(i);
}
}""");
parseVerifyVisualize();
}
}

View File

@@ -0,0 +1,177 @@
package ssw.mj.test;
import org.junit.jupiter.api.Test;
import ssw.mj.Errors.Message;
import ssw.mj.test.support.BaseCompilerTestCase;
import static ssw.mj.Errors.Message.*;
public class RecoverTest extends BaseCompilerTestCase {
@Test
public void wrongGlobalDecl() {
initCode("program Test" + LF + //
" 123;" + LF + //
"{ void main() { } }");
expectError(2, 3, DECLARATION_RECOVERY);
parseVerifyVisualize();
}
@Test
public void wrongMethDecl1() {
initCode("program Test {" + LF + //
" void main() { }" + LF + //
" program wrong1() { " + LF + //
" if (1>2);" + LF + //
" }" + LF + //
"}");
expectError(3, 3, METHOD_DECL_RECOVERY);
parseVerifyVisualize();
}
@Test
public void wrongMethDecl2() {
initCode("program Test {" + LF + //
" program wrong1() { " + LF + //
" if (1>2);" + LF + //
" }" + LF + //
" void main() { }" + LF + //
" program wrong2() {" + LF + //
" if (1>2);" + LF + //
" }" + LF + //
"}");
expectError(2, 3, METHOD_DECL_RECOVERY);
expectError(6, 3, METHOD_DECL_RECOVERY);
parseVerifyVisualize();
}
@Test
public void wrongMethDecl3() {
initCode("program Test {" + LF + //
" program wrong1() { }" + LF + //
" void main() { }" + LF + //
" program wrong2() { }" + LF + //
"}");
expectError(2, 3, METHOD_DECL_RECOVERY);
expectError(4, 3, METHOD_DECL_RECOVERY);
parseVerifyVisualize();
}
@Test
public void wrongStat() {
initCode("program Test {" + LF + //
" void main() { " + LF + //
" 123;" + LF + //
" }" + LF + //
"}");
expectError(3, 5, STATEMENT_RECOVERY);
parseVerifyVisualize();
}
@Test
public void multipleErrors() {
initCode("program Test " + LF + //
" int x" + LF + //
"{" + LF + //
" void main( {" + LF + //
" if (1 x 2);" + LF + //
" }" + LF + //
"}");
expectError(3, 1, TOKEN_EXPECTED, ";");
expectError(4, 14, TOKEN_EXPECTED, ")");
expectError(5, 11, INVALID_REL_OP);
parseVerifyVisualize();
}
// ---- multiple errors & recovery
@Test
public void noRecover1() {
initCode("program Test {" + LF + //
" void main this method will never recover");
expectError(2, 13, TOKEN_EXPECTED, "(");
parseVerifyVisualize();
}
@Test
public void noRecover2() {
initCode("program Test {" + LF + //
" void main() { " + LF + //
" if this method will never recover");
expectError(3, 8, TOKEN_EXPECTED, "(");
parseVerifyVisualize();
}
@Test
public void recoverDecl1() {
initCode("program Test" + LF + //
" int i1, if" + LF + //
" in i2;" + LF + //
" final int i3 = 0;" + LF + //
"{" + LF + //
" void main() { " + LF + //
" if (i1 < i3);" + LF + //
" }" + LF + //
"}");
expectError(2, 11, TOKEN_EXPECTED, "identifier");
parseVerifyVisualize();
}
@Test
public void recoverDecl2() {
initCode("program Test" + LF + //
" int i1, if" + LF + //
" in i2;" + LF + //
" int i3;" + LF + //
"{" + LF + //
" void main() { " + LF + //
" if (i1 < i3);" + LF + //
" }" + LF + //
"}");
expectError(2, 11, TOKEN_EXPECTED, "identifier");
parseVerifyVisualize();
}
@Test
public void recoverStat() {
initCode("program Test {" + LF + //
" void main() { " + LF + //
" 567 since distance stays too small no follow up errors here;" + LF + //
" if (1 < 2);" + LF + //
" if (1 x 2);" + LF + //
" }" + LF + //
"}");
expectError(3, 5, STATEMENT_RECOVERY);
expectError(5, 11, INVALID_REL_OP);
parseVerifyVisualize();
}
@Test
public void resetErrDist() {
initCode("program Test {" + LF + //
" void main() {" + LF + //
" if () if () if();" + LF + //
" }" + LF + //
"}");
expectError(3, 9, INVALID_FACTOR);
expectError(3, 15, INVALID_FACTOR);
expectError(3, 20, INVALID_FACTOR);
parseVerifyVisualize();
}
@Test
public void illegalMethodStart() {
initCode("program Test" + LF + // 1
"{" + LF + // 2
" void foo()" + LF + // 3
" void foo(char x) { }" + LF + // 4
" void main() { }" + LF + // 5
"}" + LF // 6
);
expectError(4, 3, Message.TOKEN_EXPECTED, "{");
parseVerifyVisualize();
}
}

View File

@@ -0,0 +1,888 @@
package ssw.mj.test;
import org.junit.jupiter.api.Test;
import ssw.mj.test.support.BaseCompilerTestCase;
import static ssw.mj.Errors.Message.*;
import static ssw.mj.scanner.Token.Kind.*;
public class ScannerTest extends BaseCompilerTestCase {
private static final char invalidChar = (char) 65533;
@Test
public void oneToken() {
initScannerCode(";");
expectToken(semicolon, 1, 1);
expectToken(eof, 1, 2);
scanVerifyVisualize();
}
@Test
public void twoTokens() {
initScannerCode(";;");
expectToken(semicolon, 1, 1);
expectToken(semicolon, 1, 2);
expectToken(eof, 1, 3);
scanVerifyVisualize();
}
@Test
public void space() {
initScannerCode("; ;");
expectToken(semicolon, 1, 1);
expectToken(semicolon, 1, 4);
expectToken(eof, 1, 5);
scanVerifyVisualize();
}
@Test
public void tabulator() {
initScannerCode(";\t\t;");
expectToken(semicolon, 1, 1);
expectToken(semicolon, 1, 4);
expectToken(eof, 1, 5);
scanVerifyVisualize();
}
@Test
public void noToken() {
initScannerCode("");
expectToken(eof, 1, 1);
scanVerifyVisualize();
}
@Test
public void crLfLineSeparators() {
initScannerCode(";" + CR + LF + " ;" + CR + LF + " ; ");
expectToken(semicolon, 1, 1);
expectToken(semicolon, 2, 2);
expectToken(semicolon, 3, 3);
expectToken(eof, 3, 5);
scanVerifyVisualize();
}
@Test
public void lFLineSeparators() {
initScannerCode(";" + LF + " ;" + LF + " ; ");
expectToken(semicolon, 1, 1);
expectToken(semicolon, 2, 2);
expectToken(semicolon, 3, 3);
expectToken(eof, 3, 5);
scanVerifyVisualize();
}
@Test
public void invalidChar1() {
initScannerCode(" {" + invalidChar + "} ");
expectToken(lbrace, 1, 2);
expectToken(none, 1, 3);
expectError(1, 3, INVALID_CHAR, invalidChar);
expectToken(rbrace, 1, 4);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void invalidChar2() {
initScannerCode(" {\0} ");
expectToken(lbrace, 1, 2);
expectToken(none, 1, 3);
expectError(1, 3, INVALID_CHAR, '\0');
expectToken(rbrace, 1, 4);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void invalidChar3() {
initScannerCode(" {&} ");
expectToken(lbrace, 1, 2);
expectToken(none, 1, 3);
expectError(1, 3, INVALID_CHAR, '&');
expectToken(rbrace, 1, 4);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void invalidChar4() {
initScannerCode(" {|} ");
expectToken(lbrace, 1, 2);
expectToken(none, 1, 3);
expectError(1, 3, INVALID_CHAR, '|');
expectToken(rbrace, 1, 4);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void invalidChar5() {
initScannerCode(" {!} ");
expectToken(lbrace, 1, 2);
expectToken(none, 1, 3);
expectError(1, 3, INVALID_CHAR, '!');
expectToken(rbrace, 1, 4);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void invalidChar6() {
initScannerCode(" {ident" + invalidChar + "} ");
expectToken(lbrace, 1, 2);
expectToken(ident, 1, 3, "ident");
expectToken(none, 1, 8);
expectError(1, 8, INVALID_CHAR, invalidChar);
expectToken(rbrace, 1, 9);
expectToken(eof, 1, 11);
scanVerifyVisualize();
}
@Test
public void ident() {
initScannerCode(" {i I i1 i_ i1I_i} ");
expectToken(lbrace, 1, 2);
expectToken(ident, 1, 3, "i");
expectToken(ident, 1, 5, "I");
expectToken(ident, 1, 7, "i1");
expectToken(ident, 1, 10, "i_");
expectToken(ident, 1, 13, "i1I_i");
expectToken(rbrace, 1, 18);
expectToken(eof, 1, 20);
scanVerifyVisualize();
}
@Test
public void indentSepararator() {
initScannerCode(" {i[i<i0i_i>i]i} ");
expectToken(lbrace, 1, 2);
expectToken(ident, 1, 3, "i");
expectToken(lbrack, 1, 4);
expectToken(ident, 1, 5, "i");
expectToken(lss, 1, 6);
expectToken(ident, 1, 7, "i0i_i");
expectToken(gtr, 1, 12);
expectToken(ident, 1, 13, "i");
expectToken(rbrack, 1, 14);
expectToken(ident, 1, 15, "i");
expectToken(rbrace, 1, 16);
expectToken(eof, 1, 18);
scanVerifyVisualize();
}
@Test
public void singleIdent() {
initScannerCode("i");
expectToken(ident, 1, 1, "i");
expectToken(eof, 1, 2);
scanVerifyVisualize();
}
@Test
public void number() {
initScannerCode(" {123 2147483647} ");
expectToken(lbrace, 1, 2);
expectToken(number, 1, 3, 123);
expectToken(number, 1, 7, 2147483647);
expectToken(rbrace, 1, 17);
expectToken(eof, 1, 19);
scanVerifyVisualize();
}
@Test
public void singleNumber() {
initScannerCode("123");
expectToken(number, 1, 1, 123);
expectToken(eof, 1, 4);
scanVerifyVisualize();
}
@Test
public void negativeNumber() {
initScannerCode(" {-123} ");
expectToken(lbrace, 1, 2);
expectToken(minus, 1, 3);
expectToken(number, 1, 4, 123);
expectToken(rbrace, 1, 7);
expectToken(eof, 1, 9);
scanVerifyVisualize();
}
@Test
public void bigNumber() {
initScannerCode(" {2147483648} ");
expectToken(lbrace, 1, 2);
expectInvalidToken(number, 1, 3);
expectError(1, 3, BIG_NUM, "2147483648");
expectToken(rbrace, 1, 13);
expectToken(eof, 1, 15);
scanVerifyVisualize();
}
@Test
public void negativeBigNumber() {
initScannerCode(" {-2147483648} ");
expectToken(lbrace, 1, 2);
expectToken(minus, 1, 3);
expectInvalidToken(number, 1, 4);
expectError(1, 4, BIG_NUM, "2147483648");
expectToken(rbrace, 1, 14);
expectToken(eof, 1, 16);
scanVerifyVisualize();
}
@Test
public void reallyBigNumber() {
initScannerCode(" {1234567890123456789012345678901234567890} ");
expectToken(lbrace, 1, 2);
expectInvalidToken(number, 1, 3);
expectError(1, 3, BIG_NUM, "1234567890123456789012345678901234567890");
expectToken(rbrace, 1, 43);
expectToken(eof, 1, 45);
scanVerifyVisualize();
}
@Test
public void numberIdent() {
initScannerCode(" {123abc123 123break} ");
expectToken(lbrace, 1, 2);
expectToken(number, 1, 3, 123);
expectToken(ident, 1, 6, "abc123");
expectToken(number, 1, 13, 123);
expectToken(break_, 1, 16);
expectToken(rbrace, 1, 21);
expectToken(eof, 1, 23);
scanVerifyVisualize();
}
@Test
public void numbersSeparated() {
initScannerCode("123.456,789");
expectToken(number, 1, 1, 123);
expectToken(period, 1, 4);
expectToken(number, 1, 5, 456);
expectToken(comma, 1, 8);
expectToken(number, 1, 9, 789);
expectToken(eof, 1, 12);
scanVerifyVisualize();
}
@Test
public void identsSeparated() {
initScannerCode("abc.def,ghi\njkl");
expectToken(ident, 1, 1, "abc");
expectToken(period, 1, 4);
expectToken(ident, 1, 5, "def");
expectToken(comma, 1, 8);
expectToken(ident, 1, 9, "ghi");
expectToken(ident, 2, 1, "jkl");
expectToken(eof, 2, 4);
scanVerifyVisualize();
}
@Test
public void newlineBetweenIdentifiersAndTokens() {
initScannerCode("anIdentifier" + LF + "class" + LF + "anotherIdentifier");
expectToken(ident, 1, 1, "anIdentifier");
expectToken(class_, 2, 1);
expectToken(ident, 3, 1, "anotherIdentifier");
scanVerifyVisualize();
}
@Test
public void newlineAndSpacesBetweenIdentifiersAndTokens() {
initScannerCode(" anIdentifier" + LF + " class" + LF + " anotherIdentifier");
expectToken(ident, 1, 2, "anIdentifier");
expectToken(class_, 2, 3);
expectToken(ident, 3, 4, "anotherIdentifier");
scanVerifyVisualize();
}
@Test
public void charConst() {
initScannerCode(" {' ' 'A' 'z' '0' '!' '\"' '" + invalidChar + "' '\0'} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, ' ');
expectToken(charConst, 1, 7, 'A');
expectToken(charConst, 1, 11, 'z');
expectToken(charConst, 1, 15, '0');
expectToken(charConst, 1, 19, '!');
expectToken(charConst, 1, 23, '"');
expectToken(charConst, 1, 27, invalidChar);
expectToken(charConst, 1, 31, '\0');
expectToken(rbrace, 1, 34);
expectToken(eof, 1, 36);
scanVerifyVisualize();
}
@Test
public void singleCharConst() {
initScannerCode("'x'");
expectToken(charConst, 1, 1, 'x');
expectToken(eof, 1, 4);
scanVerifyVisualize();
}
@Test
public void escapeCharConst() {
initScannerCode(" {'\\n' '\\r' '\\\\' '\\''} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\n');
expectToken(charConst, 1, 8, '\r');
expectToken(charConst, 1, 13, '\\');
expectToken(charConst, 1, 18, '\'');
expectToken(rbrace, 1, 22);
expectToken(eof, 1, 24);
scanVerifyVisualize();
}
@Test
public void singleEscapeCharConst() {
initScannerCode("'\\n'");
expectToken(charConst, 1, 1, '\n');
expectToken(eof, 1, 5);
scanVerifyVisualize();
}
@Test
public void emptyCharConst() {
initScannerCode(" {''} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, EMPTY_CHARCONST);
expectToken(rbrace, 1, 5);
expectToken(eof, 1, 7);
scanVerifyVisualize();
}
@Test
public void unclosedCharConst() {
initScannerCode(" {'a} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, MISSING_QUOTE);
expectToken(rbrace, 1, 5);
expectToken(eof, 1, 7);
scanVerifyVisualize();
}
@Test
public void emptyAndUnclosedCharConst() {
initScannerCode(" ''' ");
expectToken(charConst, 1, 2, '\0');
expectError(1, 2, EMPTY_CHARCONST);
expectToken(charConst, 1, 4, '\0');
expectError(1, 4, MISSING_QUOTE);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void unclosedEscapeCharConst() {
initScannerCode(" {'\\r} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, MISSING_QUOTE);
expectToken(rbrace, 1, 6);
expectToken(eof, 1, 8);
scanVerifyVisualize();
}
@Test
public void unclosedBackslashCharConst() {
initScannerCode(" {'\\'} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, MISSING_QUOTE);
expectToken(rbrace, 1, 6);
expectToken(eof, 1, 8);
scanVerifyVisualize();
}
@Test
public void invalidEscapeCharConst() {
initScannerCode(" {'\\a'} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, UNDEFINED_ESCAPE, 'a');
expectToken(rbrace, 1, 7);
expectToken(eof, 1, 9);
scanVerifyVisualize();
}
@Test
public void invalidEscapeCharMissingQuote() {
initScannerCode(" '\\a ");
expectToken(charConst, 1, 2, '\0');
expectError(1, 2, UNDEFINED_ESCAPE, 'a');
expectError(1, 2, MISSING_QUOTE);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void fileEndCharConst() {
initScannerCode(" {'");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, EOF_IN_CHAR);
expectToken(eof, 1, 4);
scanVerifyVisualize();
}
@Test
public void lineEndCharConst() {
initScannerCode(" {'" + LF + "'a'} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, ILLEGAL_LINE_END);
expectToken(charConst, 2, 1, 'a');
expectToken(rbrace, 2, 4);
expectToken(eof, 2, 6);
scanVerifyVisualize();
}
@Test
public void lineEndWithCRCharConst() {
initScannerCode(" {'" + CR + LF + "'a'} ");
expectToken(lbrace, 1, 2);
expectToken(charConst, 1, 3, '\0');
expectError(1, 3, ILLEGAL_LINE_END);
expectToken(charConst, 2, 1, 'a');
expectToken(rbrace, 2, 4);
expectToken(eof, 2, 6);
scanVerifyVisualize();
}
@Test
public void keyword1() {
initScannerCode(" { if } ");
expectToken(lbrace, 1, 2);
expectToken(if_, 1, 4);
expectToken(rbrace, 1, 7);
expectToken(eof, 1, 9);
scanVerifyVisualize();
}
@Test
public void keyword2() {
initScannerCode(" {if} ");
expectToken(lbrace, 1, 2);
expectToken(if_, 1, 3);
expectToken(rbrace, 1, 5);
expectToken(eof, 1, 7);
scanVerifyVisualize();
}
@Test
public void singleKeyword() {
initScannerCode("if");
expectToken(if_, 1, 1);
expectToken(eof, 1, 3);
scanVerifyVisualize();
}
@Test
public void keyword3() {
initScannerCode(" {for_} ");
expectToken(lbrace, 1, 2);
expectToken(ident, 1, 3, "for_");
expectToken(rbrace, 1, 7);
expectToken(eof, 1, 9);
scanVerifyVisualize();
}
@Test
public void keyword4() {
initScannerCode(" {&if} ");
expectToken(lbrace, 1, 2);
expectToken(none, 1, 3);
expectError(1, 3, INVALID_CHAR, '&');
expectToken(if_, 1, 4);
expectToken(rbrace, 1, 6);
expectToken(eof, 1, 8);
scanVerifyVisualize();
}
@Test
public void caseSensitive1() {
initScannerCode(" {For} ");
expectToken(lbrace, 1, 2);
expectToken(ident, 1, 3, "For");
expectToken(rbrace, 1, 6);
expectToken(eof, 1, 8);
scanVerifyVisualize();
}
@Test
public void caseSensitive2() {
initScannerCode(" {FOR} ");
expectToken(lbrace, 1, 2);
expectToken(ident, 1, 3, "FOR");
expectToken(rbrace, 1, 6);
expectToken(eof, 1, 8);
scanVerifyVisualize();
}
@Test
public void simpleSingleLineComment() {
initScannerCode(" {/* Simple / single * line comment. */} ");
expectToken(lbrace, 1, 2);
expectToken(rbrace, 1, 40);
expectToken(eof, 1, 42);
scanVerifyVisualize();
}
@Test
public void simpleMultiLineComment() {
initScannerCode(" {" + LF + " /* Simple " + LF + " / multi * line " + LF //
+ " comment. */ " + LF + " } ");
expectToken(lbrace, 1, 2);
expectToken(rbrace, 5, 2);
expectToken(eof, 5, 4);
scanVerifyVisualize();
}
@Test
public void nestedSingleLineComment2() {
initScannerCode(" {/*//*///****/**/*/} ");
expectToken(lbrace, 1, 2);
expectToken(rbrace, 1, 21);
expectToken(eof, 1, 23);
scanVerifyVisualize();
}
@Test
public void nestedSingleLineComment() {
initScannerCode(" {/* This / is * a /* nested /* single line */ comment. */*/} ");
expectToken(lbrace, 1, 2);
expectToken(rbrace, 1, 62);
expectToken(eof, 1, 64);
scanVerifyVisualize();
}
@Test
public void nestedMultiLineComment() {
initScannerCode(" {" + LF + " /* This / is * a " + LF + " /* nested " + LF //
+ " /* multi line */" + LF + " comment. " + LF + " */" + LF //
+ " */ " + LF + " } ");
expectToken(lbrace, 1, 2);
expectToken(rbrace, 8, 2);
expectToken(eof, 8, 4);
scanVerifyVisualize();
}
@Test
public void nestedMultiLineComment2() {
initScannerCode(" {" + LF + " /* This / is * a " + LF + " /* nested " + LF //
+ " /* multi /*/* double nestet */*/ line */" + LF + " comment. " + LF + " */" + LF //
+ " */ " + LF + " } ");
expectToken(lbrace, 1, 2);
expectToken(rbrace, 8, 2);
expectToken(eof, 8, 4);
scanVerifyVisualize();
}
@Test
public void commentAtEnd1() {
initScannerCode(" {/* This / is * a /* nested /* single line */ comment. */*/ ");
expectToken(lbrace, 1, 2);
expectToken(eof, 1, 63);
scanVerifyVisualize();
}
@Test
public void commentAtEnd2() {
initScannerCode(" {/* This / is * a /* nested /* single line */ comment. */*/");
expectToken(lbrace, 1, 2);
expectToken(eof, 1, 62);
scanVerifyVisualize();
}
@Test
public void unclosedComment() {
initScannerCode(" {/* This / is * a nested unclosed comment. } ");
expectToken(lbrace, 1, 2);
expectError(1, 3, EOF_IN_COMMENT);
expectToken(eof, 1, 47);
scanVerifyVisualize();
}
@Test
public void unclosedComment2() {
initScannerCode(" {/*/");
expectToken(lbrace, 1, 2);
expectError(1, 3, EOF_IN_COMMENT);
expectToken(eof, 1, 6);
scanVerifyVisualize();
}
@Test
public void nestedUnclosedComment() {
initScannerCode(" {/* This / is * a /* nested /* unclosed comment. */} ");
expectToken(lbrace, 1, 2);
expectError(1, 3, EOF_IN_COMMENT);
expectToken(eof, 1, 55);
scanVerifyVisualize();
}
@Test
public void nestedUnclosedComment2() {
initScannerCode(" {/* This / is * a nested unclosed /* comment. } */");
expectToken(lbrace, 1, 2);
expectError(1, 3, EOF_IN_COMMENT);
expectToken(eof, 1, 52);
scanVerifyVisualize();
}
@Test
public void noLineComment() {
initScannerCode(" {This is // no comment} ");
expectToken(lbrace, 1, 2);
expectToken(ident, 1, 3, "This");
expectToken(ident, 1, 8, "is");
expectToken(slash, 1, 11);
expectToken(slash, 1, 12);
expectToken(ident, 1, 14, "no");
expectToken(ident, 1, 17, "comment");
expectToken(rbrace, 1, 24);
expectToken(eof, 1, 26);
scanVerifyVisualize();
}
@Test
public void multipleComments() {
initScannerCode(" {/*a*/ /*b*/ /*c*/ } ");
expectToken(lbrace, 1, 2);
expectToken(rbrace, 1, 23);
expectToken(eof, 1, 25);
scanVerifyVisualize();
}
// index from end tests
@Test
public void negativeIndexFromEnd() {
initScannerCode("~-7");
expectToken(tilde, 1, 1);
expectToken(minus, 1, 2);
expectToken(number, 1, 3, 7);
expectToken(eof, 1, 4);
scanVerifyVisualize();
}
@Test
public void singleTildeChar() {
initScannerCode("'~'");
expectToken(charConst, 1, 1, '~');
expectToken(eof, 1, 4);
scanVerifyVisualize();
}
@Test
public void commentInIndexFromEnd() {
initScannerCode("~/*comment*/7");
expectToken(tilde, 1, 1);
expectToken(number, 1, 13, 7);
expectToken(eof, 1,14);
scanVerifyVisualize();
}
@Test
public void doubleTilde() {
initScannerCode("~~");
expectToken(tilde, 1, 1);
expectToken(tilde, 1, 2);
expectToken(eof, 1,3);
scanVerifyVisualize();
}
@Test
public void allTokens() {
initScannerCode("anIdentifier 123 'c'" + LF //
+ "+ - * / % == != < <= > >= && || = += -= *= /= %= ++ -- ; , . ( ) [ ] { }" + LF //
+ "break class else final if new print program read return void while ~" + LF);
expectToken(ident, 1, 1, "anIdentifier");
expectToken(number, 1, 14, 123);
expectToken(charConst, 1, 18, 'c');
expectToken(plus, 2, 1);
expectToken(minus, 2, 3);
expectToken(times, 2, 5);
expectToken(slash, 2, 7);
expectToken(rem, 2, 9);
expectToken(eql, 2, 11);
expectToken(neq, 2, 14);
expectToken(lss, 2, 17);
expectToken(leq, 2, 19);
expectToken(gtr, 2, 22);
expectToken(geq, 2, 24);
expectToken(and, 2, 27);
expectToken(or, 2, 30);
expectToken(assign, 2, 33);
expectToken(plusas, 2, 35);
expectToken(minusas, 2, 38);
expectToken(timesas, 2, 41);
expectToken(slashas, 2, 44);
expectToken(remas, 2, 47);
expectToken(pplus, 2, 50);
expectToken(mminus, 2, 53);
expectToken(semicolon, 2, 56);
expectToken(comma, 2, 58);
expectToken(period, 2, 60);
expectToken(lpar, 2, 62);
expectToken(rpar, 2, 64);
expectToken(lbrack, 2, 66);
expectToken(rbrack, 2, 68);
expectToken(lbrace, 2, 70);
expectToken(rbrace, 2, 72);
expectToken(break_, 3, 1);
expectToken(class_, 3, 7);
expectToken(else_, 3, 13);
expectToken(final_, 3, 18);
expectToken(if_, 3, 24);
expectToken(new_, 3, 27);
expectToken(print, 3, 31);
expectToken(program, 3, 37);
expectToken(read, 3, 45);
expectToken(return_, 3, 50);
expectToken(void_, 3, 57);
expectToken(while_, 3, 62);
expectToken(tilde, 3, 68);
expectToken(eof, 4, 1);
scanVerifyVisualize();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,401 @@
package ssw.mj.test.support;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Timeout;
import ssw.mj.Errors;
import ssw.mj.Interpreter;
import ssw.mj.Visualizer;
import ssw.mj.codegen.Decoder;
import ssw.mj.impl.Parser;
import ssw.mj.impl.Scanner;
import ssw.mj.scanner.Token;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.StringReader;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Base class for test cases with utility methods used by all tests.
*/
@Timeout(value = Configuration.TIMEOUT, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
public abstract class BaseCompilerTestCase {
public static final String CR = "\r";
public static final String LF = "\n";
private List<String> expectedErrors;
private List<String> expectedTokens;
private List<Token> expectedTokensFull;
private List<String> expectedSymTab;
private List<String> expectedRuntimeErrors;
private String source;
private Scanner scanner;
protected Parser parser;
private String callingClassAndMethod;
private final List<String> runInputs = new ArrayList<>();
private final List<String> expectedOutputs = new ArrayList<>();
@BeforeEach
public void setUp() {
// initialize expected compiler output
expectedErrors = new ArrayList<>();
expectedTokens = new ArrayList<>();
expectedTokensFull = new ArrayList<>();
expectedSymTab = new ArrayList<>();
expectedRuntimeErrors = new ArrayList<>();
if (Configuration.ALSO_PRINT_SUCCESSFUL_TESTCASES) {
// print header for console output
System.out.println("--------------------------------------------------");
}
}
protected void initCode(String code) {
initScannerCode(code);
parser = new Parser(scanner);
}
protected void initFile(String filename) {
initScannerFile(filename);
parser = new Parser(scanner);
}
protected void initScannerCode(String code) {
source = code;
scanner = new Scanner(new StringReader(code));
}
protected void initScannerFile(String filename) {
try {
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(filename);
if (resource == null) {
throw new RuntimeException("resource %s not found".formatted(filename));
}
String urlAsStr = resource.getFile();
// replaces %20 Urlencoding with " " (blank space), as e.g. Linux cannot handle url paths
String path = URLDecoder.decode(urlAsStr, StandardCharsets.UTF_8);
File file = new File(path);
scanner = new Scanner(new FileReader(file));
} catch (FileNotFoundException e) {
throw new RuntimeException(e.getMessage());
}
}
private List<String> splitString(String s) {
StringTokenizer st = new StringTokenizer(s, "\n");
List<String> result = new ArrayList<>();
while (st.hasMoreTokens()) {
result.add(st.nextToken());
}
return result;
}
private void print(String title, List<String> expected, List<String> actual) {
if (expected.isEmpty() && actual.isEmpty()) {
return;
}
System.out.format("%s - %s\n", callingClassAndMethod, title);
if (Configuration.ALSO_PRINT_SUCCESSFUL_TESTCASES || !expected.equals(actual)) {
System.out.format(" %-60s %s\n", "expected", "actual");
int lines = Math.max(expected.size(), actual.size());
for (int i = 0; i < lines; i++) {
String expectedLine = (i < expected.size() ? expected.get(i) : "");
String actualLine = (i < actual.size() ? actual.get(i) : "");
System.out.format("%s %-60s %s\n", (expectedLine.equals(actualLine) ? " " : "x"), expectedLine,
actualLine);
}
} else {
if (expected.equals(actual)) {
System.out.println(" correct (exact comparison hidden, enable via Configuration.ALSO_PRINT_SUCCESSFUL_TESTCASES)");
}
}
}
private void addRun(String input, String output, String error) {
runInputs.add(input);
expectedOutputs.add(output);
expectedRuntimeErrors.add(error);
}
protected void addExpectedRun(String output) {
addExpectedRun("", output);
}
protected void addExpectedRun(String input, String output) {
addRun(input, output, "");
}
protected void addFailingRun(String error) {
addFailingRun("", error);
}
protected void addFailingRun(String input, String error) {
addRun(input, "", error);
}
/**
* Scans the given code and checks the scanned tokens against the expected ones.
* Also checks that expected errors occur.
* Finally, the method creates a visualization of the scanned tokens if the test was run
* with @link ssw.mj.TracingClassLoader as system classloader.
*/
protected void scanVerifyVisualize() {
callingClassAndMethod = getCallingClassAndMethod(1);
List<Token> actualTokens = new ArrayList<>();
// scan only the expected number of tokens to prevent endless loops
for (int i = 0; i < getExpectedTokens().size(); i++) {
actualTokens.add(scanner.next());
}
List<String> actualTokenStrings = actualTokens.stream().map(Token::toString).toList();
Visualizer.createScannerVisualization(source, actualTokens, getExpectedTokensFull(), false);
printErrors();
printTokens(actualTokenStrings);
verifyErrors();
verifyTokens(actualTokenStrings);
}
/**
* Parses the given code and checks it for expected errors, matching sym tab and matching byte code.
* Then it executed the interpreter for all given inputs.
* Finally, the method creates a visualization of the parse tree if the test was run
* with @link ssw.mj.TracingClassLoader as system classloader.
*/
protected void parseVerifyVisualize() {
callingClassAndMethod = getCallingClassAndMethod(1);
try {
parser.parse();
assertEquals(Token.Kind.eof, scanner.next().kind, "Complete input should be scanned");
} catch (Errors.PanicMode error) {
// Ignore, nothing to do
}
printErrors();
printSymTab();
verifyErrors();
verifySymTab();
if (ByteCodeTestSupport.GENERATE_REFERENCE_BYTE_CODE && expectedErrors.isEmpty()) {
ByteCodeTestSupport.generateReferenceByteCode(callingClassAndMethod, parser);
} else {
printAndVerifyByteCode(callingClassAndMethod);
}
for (int i = 0; i < runInputs.size(); i++) {
run(i);
}
Visualizer.createParserVisualization(source, false);
}
private static String getCallingClassAndMethod(int up) {
// [0] getStackTrace -> [1] getCallingMethodName -> [2] caller of getCallingMethodName -> [3] ...
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[2 + up];
String fullyQualifiedClassName = e.getClassName();
String className = fullyQualifiedClassName.substring(Math.max(fullyQualifiedClassName.lastIndexOf(".") + 1, 0));
return className + "." + e.getMethodName() + "()";
}
private void run(int i) {
Interpreter.BufferIO io = new Interpreter.BufferIO(runInputs.get(i));
Interpreter interpreter = new Interpreter(
parser.code.buf,
parser.code.mainpc,
parser.code.dataSize,
io,
Configuration.PRINT_INTERPRETER_DEBUG_OUTPUT);
try {
interpreter.run();
} catch (IllegalStateException e) {
verifyRuntimeError(i, e);
}
String output = io.getOutput();
verifyOutput(i, output);
}
private void printErrors() {
print("Errors", expectedErrors, getActualErrors());
}
private void printTokens(List<String> actualTokens) {
print("Tokens", getExpectedTokens(), actualTokens);
}
private void printSymTab() {
if (!expectedSymTab.isEmpty()) {
print("Symbol Table", getExpectedSymTab(), getActualSymTab());
}
}
private void verifyErrors() {
assertEquals(expectedErrors, getActualErrors(), "Errors");
}
private void verifyTokens(List<String> actualTokens) {
assertEquals(getExpectedTokens(), actualTokens, "Tokens");
assertTrue(scanner.next().toString().contains("end of file"), "Complete Input Scanned");
}
private void verifySymTab() {
if (!expectedSymTab.isEmpty()) {
assertEquals(getExpectedSymTab(), getActualSymTab(), "Symbol Table");
}
}
private void printAndVerifyByteCode(String callingClassAndMethod) {
if (ByteCodeTestSupport.BYTE_CODES.containsKey(callingClassAndMethod)) {
List<String> possibleByteCodes = ByteCodeTestSupport.BYTE_CODES.get(callingClassAndMethod);
if (possibleByteCodes.size() == 1) {
List<String> expected = getExpectedByteCodeLines(possibleByteCodes.get(0));
print("Bytecode", expected, getActualByteCodeLines());
// Verify that the bytecode is correct
assertEquals(expected, getActualByteCodeLines(), "Byte Code");
} else {
int matchIdx = -1;
for (int i = 0; i < possibleByteCodes.size(); i++) {
List<String> expected = getExpectedByteCodeLines(possibleByteCodes.get(i));
if (expected.equals(getActualByteCodeLines())) {
matchIdx = i;
break;
}
}
if (matchIdx < 0) {
// No bytecode matched
// print all
for (int i = 0; i < possibleByteCodes.size(); i++) {
List<String> expected = getExpectedByteCodeLines(possibleByteCodes.get(i));
print("Possible Bytecode %d".formatted(i + 1), expected, getActualByteCodeLines());
}
// fail assert on first
assertEquals(getExpectedByteCodeLines(possibleByteCodes.get(0)), getActualByteCodeLines(), "Byte Code");
} else {
// bytecode at idx matchIdx correctly generated
// print working bytecode
print("Bytecode", getExpectedByteCodeLines(possibleByteCodes.get(matchIdx)), getActualByteCodeLines());
// assert not really necessary since we already know we matched successfully
assertEquals(getExpectedByteCodeLines(possibleByteCodes.get(matchIdx)), getActualByteCodeLines(), "Byte Code");
}
}
}
}
private void verifyOutput(int runIdx, String actualOutput) {
assertEquals(expectedOutputs.get(runIdx), actualOutput, "Unexpected result when input is \"" + runInputs.get(runIdx) + "\": ");
}
private void verifyRuntimeError(int runIdx, IllegalStateException e) {
assertEquals(expectedRuntimeErrors.get(runIdx), e.getMessage(), "Unexpected runtime error message when input is \"" + runInputs.get(runIdx) + "\": ");
}
private List<String> getExpectedByteCodeLines(String bytecode) {
return Arrays.stream(bytecode.split("\n")).toList();
}
private List<String> getActualByteCodeLines() {
return Arrays.stream(new Decoder().decode(parser.code).split("\n")).toList();
}
private List<String> getActualErrors() {
return splitString(scanner.errors.dump());
}
private List<String> getExpectedTokens() {
return expectedTokens;
}
private List<Token> getExpectedTokensFull() {
return expectedTokensFull;
}
private List<String> getExpectedSymTab() {
return expectedSymTab;
}
private List<String> getActualSymTab() {
return splitString(SymTabDumper.dump(parser.tab));
}
protected void expectError(int line, int col, Errors.Message msg, Object... msgParams) {
expectedErrors.add("-- line " + line + " col " + col + ": " + msg.format(msgParams));
}
protected void expectToken(Token.Kind kind, int line, int col) {
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind);
expectedTokensFull.add(new Token(kind, line, col));
}
protected void expectToken(Token.Kind kind, int line, int col, String val) {
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val " + val);
Token token = new Token(kind, line, col);
token.val = val;
expectedTokensFull.add(token);
}
protected void expectToken(Token.Kind kind, int line, int col, int val) {
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val " + val + ", numVal " + val);
Token token = new Token(kind, line, col);
token.val = String.valueOf(val);
token.numVal = val;
expectedTokensFull.add(token);
}
protected void expectToken(Token.Kind kind, int line, int col, char ch) {
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val " + ch + ", numVal " + (int) ch);
Token token = new Token(kind, line, col);
token.val = String.valueOf(ch);
token.numVal = ch;
expectedTokensFull.add(token);
}
protected void expectInvalidToken(Token.Kind kind, int line, int col) {
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val null, numVal 0");
Token token = new Token(kind, line, col);
token.val = null;
token.numVal = 0;
expectedTokensFull.add(token);
}
protected void expectSymTab(String line) {
expectedSymTab.add(line);
}
protected void expectSymTabUniverse() {
// first part of the symbol table (universe) that is equal for all
// programs
expectSymTab("-- begin scope (0 variables) --");
expectSymTab("Type int: int");
expectSymTab("Type char: char");
expectSymTab("Constant: class (0 fields) null = 0");
expectSymTab("Method: char chr (1 locals, 1 parameters)");
expectSymTab(" Local Variable 0: int i");
expectSymTab("Method: int ord (1 locals, 1 parameters)");
expectSymTab(" Local Variable 0: char ch");
expectSymTab("Method: int len (1 locals, 1 parameters)");
expectSymTab(" Local Variable 0: void[] arr");
}
}

View File

@@ -0,0 +1,103 @@
package ssw.mj.test.support;
import ssw.mj.codegen.Decoder;
import ssw.mj.impl.Parser;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;
public class ByteCodeTestSupport {
/**
* This flag is used by the lecturers to generate the reference solutions for
* the bytecodes generated during code generation.
* Students should not change this flag, it should stay false for the whole course.
*/
public static final boolean GENERATE_REFERENCE_BYTE_CODE = false;
// For each test, 0 to n correct byte codes can be added to bytecodes.txt
// If one of these codes is generated by the Parser, the test does not fail.
// This way, we can provide multiple correct solutions for the same test case.
//
// The keys of this map are in the format "TestClass.TestMethodName()"
public static final HashMap<String, List<String>> BYTE_CODES = new HashMap<>();
static {
File bytecodesFile = getBytecodesFile();
if (bytecodesFile.exists()) {
String[] lineArr;
try (Stream<String> lines = Files.lines(bytecodesFile.toPath())) {
lineArr = lines.toArray(String[]::new);
} catch (IOException e) {
throw new RuntimeException(e);
}
String currentlyReadClassAndMethod = null;
StringBuilder currentReadBytecode = null;
for (String line : lineArr) {
if (line.isBlank()) {
continue;
}
if (line.startsWith("#")) {
if (currentlyReadClassAndMethod != null) {
if (!BYTE_CODES.containsKey(currentlyReadClassAndMethod)) {
BYTE_CODES.put(currentlyReadClassAndMethod, new ArrayList<>());
}
BYTE_CODES.get(currentlyReadClassAndMethod).add(currentReadBytecode.toString());
}
currentlyReadClassAndMethod = line.substring(1);
currentReadBytecode = new StringBuilder();
} else {
currentReadBytecode.append(line).append("\n");
}
}
if (currentlyReadClassAndMethod != null) {
if (!BYTE_CODES.containsKey(currentlyReadClassAndMethod)) {
BYTE_CODES.put(currentlyReadClassAndMethod, new ArrayList<>());
}
BYTE_CODES.get(currentlyReadClassAndMethod).add(currentReadBytecode.toString());
}
}
}
public static File getBytecodesFile() {
String filename = "bytecodes.txt";
ClassLoader classLoader = BaseCompilerTestCase.class.getClassLoader();
URL resource = classLoader.getResource(filename);
if (resource == null) {
throw new RuntimeException("resource %s not found".formatted(filename));
}
String urlAsStr = resource.getFile();
// replaces %20 Urlencoding with " " (blank space), as e.g. Linux cannot handle url paths
String path = URLDecoder.decode(urlAsStr, StandardCharsets.UTF_8);
return new File(path);
}
public static void generateReferenceByteCode(String classAndMethod, Parser parser) {
// Generate and store bytecode for correct test programs
// Output is in the form:
// #TestClass.TestMethodName()
// ... output from Decoder.decode() ...
File bytecodesFile = ByteCodeTestSupport.getBytecodesFile();
try (BufferedWriter bw = Files.newBufferedWriter(bytecodesFile.toPath(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
String bytecode = new Decoder().decode(parser.code);
bw.write("#");
bw.write(classAndMethod);
bw.write("\n");
bw.write(bytecode);
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,27 @@
package ssw.mj.test.support;
public class Configuration {
/**
* set to true to print expected and actual values of all testcases,
* not only failing ones (prints expected errors, tokens, symbol table, code)
*/
public static final boolean ALSO_PRINT_SUCCESSFUL_TESTCASES = Boolean.getBoolean("microjava.testcaseOutput");
/**
* Set to true to print debug information of the interpreter. Equal to
* "-debug" on the command line. <br>
* Remark:<br>
* This is a lot of output, some test cases might time out, e.g.
* CodeGenerationTest.fib
*/
public static final boolean PRINT_INTERPRETER_DEBUG_OUTPUT = Boolean.getBoolean("microjava.interpreterOutput");
/**
* Determines the timeout after which a test case should fail automatically.
* Default: 10 seconds. The default should work for all test cases
* on most machines.<br>
* <em>Attention</em>: For most computers it is likely that there is an
* endless loop in the MicroJava compiler if a test fails for a timeout.
*/
public static final long TIMEOUT = 10;
}

View File

@@ -0,0 +1,119 @@
package ssw.mj.test.support;
import ssw.mj.impl.Tab;
import ssw.mj.symtab.Obj;
import ssw.mj.symtab.Scope;
import ssw.mj.symtab.Struct;
import java.util.Collection;
public class SymTabDumper {
public static String dump(Tab tab) {
StringBuilder sb = new StringBuilder();
if (tab.curScope != null) {
dump(tab.curScope, sb);
}
return sb.toString();
}
private static void dump(Scope scope, StringBuilder sb) {
sb.append("-- begin scope (").append(scope.nVars()).append(" variables) --\n");
if (!scope.locals().isEmpty()) {
dump(scope.locals().values(), sb, "");
}
if (scope.outer() != null) {
sb.append("\n");
dump(scope.outer(), sb);
}
}
private static void dump(Collection<Obj> objects, StringBuilder sb, String indent) {
for (Obj obj : objects) {
dump(obj, sb, indent);
}
}
private static void dump(Obj obj, StringBuilder sb, String indent) {
sb.append(indent);
switch (obj.kind) {
case Con -> dumpCon(obj, sb, indent);
case Var -> dumpVar(obj, sb, indent);
case Type -> dumpType(obj, sb, indent);
case Meth -> dumpMethod(obj, sb, indent);
case Prog -> dumpProgram(obj, sb);
}
if (obj.locals != null) {
sb.append("\n");
dump(obj.locals.values(), sb, indent + " ");
}
sb.append("\n");
}
private static void dumpCon(Obj obj, StringBuilder sb, String indent) {
sb.append("Constant: ");
if (obj.type != null) {
dump(obj.type, sb, indent, false);
}
sb.append(" ").append(obj.name).append(" = ");
if (obj.type == Tab.charType) {
sb.append("'").append((char) obj.val).append("'");
} else {
sb.append(obj.val);
}
}
private static void dumpVar(Obj obj, StringBuilder sb, String indent) {
if (obj.level == 0) {
sb.append("Global Variable ");
} else {
sb.append("Local Variable ");
}
sb.append(obj.adr).append(": ");
if (obj.type != null) {
dump(obj.type, sb, indent, false);
}
sb.append(" ").append(obj.name);
}
private static void dumpType(Obj type, StringBuilder sb, String indent) {
sb.append("Type ").append(type.name).append(": ");
if (type.type != null) {
dump(type.type, sb, indent + " ", true);
}
}
private static void dumpMethod(Obj meth, StringBuilder sb, String indent) {
sb.append("Method: ");
if (meth.type != null) {
dump(meth.type, sb, indent, false);
}
sb.append(" ").append(meth.name).append(" (").append(meth.locals.size()).append(" locals, ").append(meth.nPars).append(" parameters").append(")");
}
private static void dumpProgram(Obj obj, StringBuilder sb) {
sb.append("Program ").append(obj.name).append(":");
}
private static void dump(Struct struct, StringBuilder sb, String indent, boolean dumpFields) {
switch (struct.kind) {
case None -> sb.append("void");
case Int -> sb.append("int");
case Char -> sb.append("char");
case Arr -> {
if (struct.elemType != null) {
dump(struct.elemType, sb, indent, dumpFields);
}
sb.append("[]");
}
case Class -> {
sb.append("class (").append(struct.nrFields()).append(" fields)");
if (dumpFields && struct.fields != null) {
sb.append("\n");
dump(struct.fields.values(), sb, indent);
}
}
}
}
}