Compare commits

..

1 Commits

Author SHA1 Message Date
Marvin Kaiser
af1021ed66 31: Add void type 2020-03-09 21:08:04 +01:00
34 changed files with 108 additions and 750 deletions

View File

@@ -9,18 +9,7 @@ build:
script:
- mvn package
test_parsing:
image: ubuntu:rolling
stage: test
tags:
- docker
before_script:
- apt update
- apt install -y build-essential openjdk-13-jre-headless maven
script:
- make testJava
test_compilation:
test:
image: ubuntu:rolling
stage: test
tags:

View File

@@ -1 +0,0 @@
--add-opens java.base/java.lang=ALL-UNNAMED

View File

@@ -1,5 +1,4 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/test/java=UTF-8
encoding//target/generated-sources/antlr4=UTF-8
encoding/<project>=UTF-8

View File

@@ -5,10 +5,11 @@ This is the project for Klang - the Kaiser language.
This code was in equal parts developed by `Dennis Kaiser` and `Marvin Kaiser` at the RheinMain University of Applied Sciences for the Compilers course.
# Usage
Pass source code via stdin
example call to print help `java -cp target/klang-1.0-jar-with-dependencies.jar de.hsrm.compiler.Klang.Klang -h`
example call: `java -cp <jar> <cp> -out <input> <output>`
Arguments:
- -out <file> File to write to
- -h Print this help
- --evaluate: Evaluates the given source code
- --pretty: Pretty print the given source code
@@ -22,47 +23,8 @@ The makefile can be used to perform various functions more easily:
- `make pretty` prettifies code.k and writes to pretty.k
- `make eval` evaluates code.k
- `make test` runs tests from src/test/
- `make testJava` runs JUnit tests
- `make cleanTests` cleans files generated from tests
# Boilerplate Example
A simple program in the KLang Language consits of some struct definitions and some function definition and a single expression that is used as the start for the compilation
```
struct node {
value: int;
tail: node;
}
function makeList(anz: int): node {
if (anz == 0) {
return naught;
}
return create node(anz - 1, makeList(anz - 1));
}
function get(ll: node, index: int): int {
if (index == 0 || ll == naught) {
return ll.value;
} else {
return get(ll.tail, index - 1);
}
}
function sum(list: node, length: int): int {
if (length == 0) {
return list.value;
}
return list.value + sum(list.tail, length -1);
}
function init(pos: int): int {
let n: node = makeList(5);
return sum(n, pos);
}
init(0);
```
# Functionality
The KLang compiler supports generation of AMD64 assembly code, as well as prettifying and evaluating the KLang code.
@@ -82,13 +44,13 @@ The following simple expressions are supported. Expressions need to be put in pa
### Examples:
```
5 + 4
8 % 2
8 == 0
(5 + 4)
(8 % 2)
(8 == 0)
```
## Functions
Functions can be defined and called. A function call can be used like any other expression. Recursion is supported aswell as linking agaings c object files since we are following the calling convention
Functions can be defined and called. A function call can be used like any other expression. Recursion is supported
### Examples
```
@@ -99,46 +61,6 @@ function fun(x: int, y: int, z: bool): int {
fun(1, 2, 3);
```
## Structs
Structs can be defined, created and destroyed. Structs can reference other structs as well as themselves. You can reference structs that are defined later in the code. Our structs are compatible to c structs. When defining a struct, a constructor function is implicitly defined so that you can create instances of your struct. To denote a non existing reference to a struct, use the reserved word "naught";
### Examples
```
struct myStruct {
a: int;
b: bool;
c: float;
d: myStruct;
}
function add(x: myStruct, y: myStruct): float {
return x.c + y.c;
}
function isOk(x: myStruct, y: myStruct): bool {
return x.b && y.b;
}
function getReferenced(x: myStruct): myStruct {
return x.d;
}
function start(): int {
let x: myStruct = create myStruct(1, false, 42.0, naught);
let y: myStruct = create myStruct(12, true, 13.37, x);
let z: int = add(x, y);
let a: bool = isOk(x, y);
let y2: myStruct = getReferenced(x);
let isSame: bool = y == y2;
destroy y;
destroy x;
return 0;
}
start();
```
## Statements
Several statements are supported:
- if
@@ -190,9 +112,6 @@ function forExample(end: int): int {
```
## Tail Call Optimized
Recursive tail calls are optimized at compile time.
## Statically typed
KLang statically verifies the integrity of your code. These checks include:
- Type checking
@@ -201,10 +120,9 @@ KLang statically verifies the integrity of your code. These checks include:
- Ensuring that a function returns something
- Ensuring that a function only returns data of the correct type
### Primitive Data Types
### Data Types
- Integer "int"
- Boolean "bool"
- Floats "float"
### Examples
You can declare types for parameters, return values and variables

View File

@@ -13,14 +13,11 @@ eval: code.k target/klang-1.0-jar-with-dependencies.jar
build: clean target/klang-1.0-jar-with-dependencies.jar
target/klang-1.0-jar-with-dependencies.jar:
mvn -Dmaven.test.skip=true package
mvn package
test: ./src/test/test
./src/test/test
testJava:
mvn test
./src/test/test: ./src/test/test.s
gcc -o ./src/test/test ./src/test/test.s ./src/test/**/*.c ./src/test/test.c

21
pom.xml
View File

@@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.hsrm.compiler</groupId>
@@ -20,16 +21,6 @@
<artifactId>antlr4-runtime</artifactId>
<version>4.7.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
<build>
@@ -83,14 +74,6 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>

View File

@@ -29,7 +29,7 @@ parameter
;
braced_block
: OBRK statement+ CBRK
: OBRK (statement | functionCall SCOL)+ CBRK
;
@@ -69,7 +69,7 @@ field_assignment
;
return_statement
: RETURN expression SCOL
: RETURN expression? SCOL
;
destroy_statement
@@ -116,6 +116,7 @@ type
| BOOLEAN
| FLOAT
| IDENT
| VOID
;
functionCall
@@ -181,6 +182,7 @@ DIV: '/';
BOOLEAN: 'bool';
INTEGER: 'int';
FLOAT: 'float';
VOID: 'void';
INTEGER_LITERAL
: [0-9]+

View File

@@ -18,7 +18,6 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Map<String, FunctionInformation> funcs;
Map<String, StructDefinition> structs;
Type currentDeclaredReturnType;
String currentFunctionDefinitionName;
private void checkNumeric(Node lhs, Node rhs, int line, int col) {
if (!lhs.type.isNumericType() || !rhs.type.isNumericType()) {
@@ -150,8 +149,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitDoWhileLoop(KlangParser.DoWhileLoopContext ctx) {
Node block = this.visit(ctx.braced_block());
Node condition = this.visit(ctx.cond);
Node block = this.visit(ctx.braced_block());
Node result = new DoWhileLoop((Expression) condition, (Block) block);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
@@ -177,6 +176,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
int col = ctx.start.getCharPositionInLine();
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
if (declaredType.equals(Type.getVoidType())) {
String error = "Type " + declaredType.getName() + " can not be used to declare variables.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!declaredType.isPrimitiveType() && this.structs.get(declaredType.getName()) == null) {
String error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
@@ -245,20 +249,21 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
if (currentDeclaredReturnType.equals(Type.getVoidType())) {
ReturnStatement result = new ReturnStatement();
result.line = ctx.start.getLine();
result.type = Type.getVoidType();
result.col = ctx.start.getCharPositionInLine();
if (ctx.expression() != null) {
String error = "Cannot return an expression from a void function.";
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
}
return result;
}
Expression expression = (Expression) this.visit(ctx.expression());
ReturnStatement result = new ReturnStatement(expression);
// Check if this expression is a tail recursion
if (expression instanceof FunctionCall) {
var funCall = (FunctionCall) expression;
if (funCall.name.equals(this.currentFunctionDefinitionName)) {
// Flag this function call
funCall.isTailRecursive = true;
}
}
result.type = expression.type;
result.line = ctx.start.getLine();
result.type = expression.type;
result.col = ctx.start.getCharPositionInLine();
return result;
}
@@ -731,9 +736,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
int col = ctx.start.getCharPositionInLine();
Type returnType = Type.getByName(ctx.returnType.type().getText());
this.currentDeclaredReturnType = returnType;
this.currentFunctionDefinitionName = name;
if (!returnType.isPrimitiveType() && this.structs.get(returnType.getName()) == null) {
if (!returnType.isPrimitiveType() && this.structs.get(returnType.getName()) == null && !returnType.equals(Type.getVoidType())) {
String error = "Type " + returnType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
@@ -779,6 +783,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
int col = ctx.start.getCharPositionInLine();
Type type = Type.getByName(ctx.type_annotation().type().getText());
if (type.equals(Type.getVoidType())) {
String error = "Type " + type.getName() + " cannot be used to declare a parameter.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!type.isPrimitiveType() && this.structs.get(type.getName()) == null) {
String error = "Type " + type.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);

View File

@@ -12,6 +12,7 @@ import java.util.HashSet;
import de.hsrm.compiler.Klang.nodes.Node;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.types.Type;
import de.hsrm.compiler.Klang.visitors.*;
import de.hsrm.compiler.Klang.helper.*;
@@ -46,7 +47,7 @@ public class Klang {
System.out.println("Last argument must be file");
System.out.println("");
System.out.println("Arguments:");
System.out.println("--o <file>:\t File to write to");
System.out.println("--out <file>:\t File to write to");
System.out.println("--evaluate:\t Evaluates the given source code");
System.out.println("--pretty:\t Pretty print the given source code");
System.out
@@ -124,7 +125,11 @@ public class Klang {
System.out.println("\nEvaluating the source code:");
EvalVisitor evalVisitor = new EvalVisitor(structs);
Value result = root.welcome(evalVisitor);
generateOutput(out, "Result was: " + result.asObject().toString());
if (result.type.equals(Type.getVoidType())) {
generateOutput(out, "Result was void");
} else {
generateOutput(out, "Result was: " + result.asObject().toString());
}
return;
}

View File

@@ -6,7 +6,6 @@ public class FunctionCall extends Expression {
public String name;
public Expression[] arguments;
public boolean isTailRecursive = false;
public FunctionCall(String name, Expression[] arguments) {
this.name = name;

View File

@@ -11,6 +11,10 @@ public class ReturnStatement extends Statement {
this.expression = expression;
}
public ReturnStatement() {
this.expression = null;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);

View File

@@ -20,12 +20,17 @@ public abstract class Type {
return NullType.getType();
}
public static VoidType getVoidType() {
return VoidType.getType();
}
public static Type getByName(String name) {
switch (name) {
case "bool": return getBooleanType();
case "int": return getIntegerType();
case "float": return getFloatType();
case "null": return getNullType();
case "void": return getVoidType();
default: return new StructType(name);
}
}

View File

@@ -0,0 +1,38 @@
package de.hsrm.compiler.Klang.types;
public class VoidType extends Type {
private static VoidType instance;
public static VoidType getType() {
if (instance != null) {
return instance;
}
instance = new VoidType();
return instance;
}
@Override
public String getName() {
return "void";
}
@Override
public Type combine(Type that) {
if (that.equals(this)) {
return this;
}
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean isPrimitiveType() {
return false;
}
@Override
public boolean isNumericType() {
return false;
}
}

View File

@@ -413,6 +413,9 @@ public class EvalVisitor implements Visitor<Value> {
@Override
public Value visit(ReturnStatement e) {
if (e.expression == null) {
return new Value(null, Type.getVoidType());
}
return e.expression.welcome(this);
}

View File

@@ -113,8 +113,6 @@ public class GenASM implements Visitor<Void> {
String[] registers = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" };
String[] floatRegisters = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" };
private int lCount = 0; // Invariante: lCount ist benutzt
private int currentFunctionStartLabel = 0;
private Parameter[] currentFunctionParams;
private void intToFloat(String src, String dst) {
this.ex.write(" cvtsi2sd " + src + ", " + dst + "\n");
@@ -568,11 +566,6 @@ public class GenASM implements Visitor<Void> {
if (e.expression != null) {
e.expression.welcome(this);
int offset = this.env.get(e.name);
if (e.expression.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n");
}
this.ex.write(" movq %rax, " + offset + "(%rbp)\n");
}
return null;
@@ -596,7 +589,9 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(ReturnStatement e) {
e.expression.welcome(this);
if (e.expression != null) {
e.expression.welcome(this);
}
this.ex.write(" movq %rbp, %rsp\n");
this.ex.write(" popq %rbp\n");
this.ex.write(" ret\n");
@@ -613,15 +608,11 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(FunctionDefinition e) {
int lblStart = ++lCount;
this.currentFunctionStartLabel = lblStart;
this.currentFunctionParams = e.parameters;
this.ex.write(".globl " + e.name + "\n");
this.ex.write(".type " + e.name + ", @function\n");
this.ex.write(e.name + ":\n");
this.ex.write(" pushq %rbp\n");
this.ex.write(" movq %rsp, %rbp\n");
this.ex.write(".L" + lblStart + ":\n");
// hole die anzahl der lokalen variablen
this.vars = new TreeSet<String>();
@@ -693,29 +684,6 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(FunctionCall e) {
if (e.isTailRecursive) {
// Visit the arguments and move them into the stack
for(int i = 0; i < e.arguments.length; i++) {
e.arguments[i].welcome(this);
if (e.arguments[i].type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n");
}
this.ex.write(" pushq %rax\n");
}
// push args into local var locations, last arg is ontop of the stack
for (int i = e.arguments.length - 1; i >= 0; i--) {
this.ex.write(" popq " + this.env.get(this.currentFunctionParams[i].name) + "(%rbp)\n");
}
this.ex.write(" jmp .L" + this.currentFunctionStartLabel + "\n");
return null;
}
if (e.arguments.length > 0) {
// Mapping arguments index -> xmm registers index
int[] xmmIdxs = new int[this.floatRegisters.length];

View File

@@ -194,7 +194,9 @@ class GetVars implements Visitor<Void> {
@Override
public Void visit(ReturnStatement e) {
e.expression.welcome(this);
if (e.expression != null) {
e.expression.welcome(this);
}
return null;
}

View File

@@ -304,8 +304,11 @@ public class PrettyPrintVisitor implements Visitor<Void> {
@Override
public Void visit(ReturnStatement e) {
ex.write("return ");
e.expression.welcome(this);
ex.write("return");
if (e.expression != null) {
ex.write(" ");
e.expression.welcome(this);
}
ex.write(";");
return null;
}

View File

@@ -92,28 +92,4 @@ int runFunctionCallTests () {
argumentTest("fgetMix8(...args)", 8, fgetMix8());
argumentTest_f("fgetMix9(...args)", 9.0, fgetMix9());
argumentTest("fgetMix10(...args)", 10, fgetMix10());
printf("\nTail Call Tests \n");
// Checks that tails calls are properly invoked
argumentTest("arg1Tail(...args)", 1, arg1Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg2Tail(...args)", 2, arg2Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg3Tail(...args)", 3, arg3Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg4Tail(...args)", 4, arg4Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg5Tail(...args)", 5, arg5Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg6Tail(...args)", 6, arg6Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg7Tail(...args)", 7, arg7Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg8Tail(...args)", 8, arg8Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg9Tail(...args)", 9, arg9Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
argumentTest("arg10Tail(...args)", 10, arg10Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10));
// Checks that parameters are correctly passed from klang to functions
argumentTest("get1Tail(...args)", 1, get1Tail(10));
argumentTest("get2Tail(...args)", 2, get2Tail(10));
argumentTest("get3Tail(...args)", 3, get3Tail(10));
argumentTest("get4Tail(...args)", 4, get4Tail(10));
argumentTest("get5Tail(...args)", 5, get5Tail(10));
argumentTest("get6Tail(...args)", 6, get6Tail(10));
argumentTest("get7Tail(...args)", 7, get7Tail(10));
argumentTest("get8Tail(...args)", 8, get8Tail(10));
argumentTest("get9Tail(...args)", 9, get9Tail(10));
argumentTest("get10Tail(...args)", 10, get10Tail(10));
}

View File

@@ -20,28 +20,6 @@ long get8();
long get9();
long get10();
long arg1Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg2Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg3Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg4Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg5Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg6Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg7Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg8Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg9Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long arg10Tail(long a, long b, long c, long d, long e, long f, long g, long h, long i, long j, long count);
long get1Tail(long count);
long get2Tail(long count);
long get3Tail(long count);
long get4Tail(long count);
long get5Tail(long count);
long get6Tail(long count);
long get7Tail(long count);
long get8Tail(long count);
long get9Tail(long count);
long get10Tail(long count);
double farg1(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j);
double farg2(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j);
double farg3(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j);

View File

@@ -1,20 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class AndTest {
@Test
void onlyForBool() {
ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 && 2; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:30 && is only defined for bool.", e.getMessage());
}
}

View File

@@ -1,43 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class ConstructorCallTest {
@Test
void structNotDefined() {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create schwurbel(1); } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:52 Struct with name \"schwurbel\" not defined.", e.getMessage());
}
@Test
void numConstructorParameterMissmatch() {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:52 Struct \"bar\" defined 1 fields, but got 2 constructor parameters.", e.getMessage());
}
@Test
void constructorParameterTypeMismatch() {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(false); } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:63 argument 0 Type missmatch: cannot combine bool and int", e.getMessage());
}
}

View File

@@ -1,21 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class DestroyStatementTest {
@Test
void variableNotDefined() {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { destroy x; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:45 Variable with name \"x\" not defined.", e.getMessage());
}
}

View File

@@ -1,32 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class FieldAssignmentTest {
@Test
void variableNotDefined() {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { str.a = 1; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:46 Variable with name str not defined.", e.getMessage());
}
@Test
void fieldAssignmentOnNonStruct() {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { let x: int = 0; x.a = 0; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:62 Variable must reference a struct but references int.", e.getMessage());
}
}

View File

@@ -1,43 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class FunctionCallTest {
@Test
void funcNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } bar();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:34 Function with name \"bar\" not defined.", e.getMessage());
}
@Test
void numParameterMismatch() {
ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } foo(5);");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:34 Function \"foo\" expects 0 parameters, but got 1.", e.getMessage());
}
@Test
void parameterTypeMissmatch() {
ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:40 argument 0 Expected int but got: bool", e.getMessage());
}
}

View File

@@ -1,32 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class FunctionDefinitionTest {
@Test
void typeNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:0 Type schwurbel not defined.", e.getMessage());
}
@Test
void noReturnExpression() {
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:0 Function foo has to return something of type int.", e.getMessage());
}
}

View File

@@ -1,36 +0,0 @@
import java.util.*;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import de.hsrm.compiler.Klang.*;
import de.hsrm.compiler.Klang.helper.*;
import de.hsrm.compiler.Klang.nodes.*;
public class Helper {
public static ParseTree prepareParser(String input) {
CharStream inStream = CharStreams.fromString(input);
KlangLexer lexer = new KlangLexer(inStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
KlangParser parser = new KlangParser(tokens);
return parser.parse();
}
public static Map<String, FunctionInformation> getFuncs(ParseTree tree) {
var functionDefinitions = new HashMap<String, FunctionInformation>();
new GetFunctions(functionDefinitions).visit(tree);
return functionDefinitions;
}
public static Set<String> getStructNames(ParseTree tree) {
var structNames = new HashSet<String>();
new GetStructNames(structNames).visit(tree);
return structNames;
}
public static Map<String, StructDefinition> getStructs(ParseTree tree) {
var structs = new HashMap<String, StructDefinition>();
new GetStructs(getStructNames(tree), structs).visit(tree);
return structs;
}
}

View File

@@ -1,20 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class ModuloTest {
@Test
void onlyForInt() {
ParseTree tree = Helper.prepareParser("function foo(): float { return 1.0 % 2.3; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:31 Only integers are allowed for modulo.", e.getMessage());
}
}

View File

@@ -1,21 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class OrTest {
@Test
void onlyForBool() {
ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 || 2; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:30 || is only defined for bool.", e.getMessage());
}
}

View File

@@ -1,14 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
public class ParameterTest {
@Test
void typeNotDefined() {
ParseTree tree = Helper.prepareParser("struct test { a: schwurbel; } function foo(): int { return 1; } foo();");
Exception e = assertThrows(RuntimeException.class, () -> Helper.getStructs(tree));
assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage());
}
}

View File

@@ -1,31 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class StructFieldAccessTest {
@Test
void variableNotDefined() {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { return str.a; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:53 Variable with name str not defined.", e.getMessage());
}
@Test
void fieldAssignmentOnNonStruct() {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { let x: int = 0; return x.a; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:69 Variable must reference a struct but references int.", e.getMessage());
}
}

View File

@@ -1,21 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class VariableAssignmentTest {
@Test
void variableNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): int { x = 1; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:22 Variable with name \"x\" not defined.", e.getMessage());
}
}

View File

@@ -1,32 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class VariableDeclarationTest {
@Test
void typeNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): int { let X: unk; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:22 Type unk not defined.", e.getMessage());
}
@Test
void variableRedeclaration()
{
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:34 Redeclaration of variable with name \"x\".", e.getMessage());
}
}

View File

@@ -1,32 +0,0 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
public class VariableTest {
@Test
void variableNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): int { return x; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:29 Variable with name \"x\" not defined.", e.getMessage());
}
@Test
void variableNotInitialized() {
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; return x; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:41 Variable with name \"x\" has not been initialized.", e.getMessage());
}
}

View File

@@ -106,127 +106,6 @@ function get10(): int {
return arg10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
// TAIL CALL
function arg1Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return a;
}
return arg1Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get1Tail(count: int): int {
return arg1Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg2Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return b;
}
return arg2Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get2Tail(count: int): int {
return arg2Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg3Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return c;
}
return arg3Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get3Tail(count: int): int {
return arg3Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg4Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return d;
}
return arg4Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get4Tail(count: int): int {
return arg4Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg5Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return e;
}
return arg5Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get5Tail(count: int): int {
return arg5Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg6Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return f;
}
return arg6Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get6Tail(count: int): int {
return arg6Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg7Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return g;
}
return arg7Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get7Tail(count: int): int {
return arg7Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg8Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return h;
}
return arg8Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get8Tail(count: int): int {
return arg8Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg9Tail(a: int, b: int, c: int,d: int,e: int,f: int,g: int, h: int,i: int,j: int, count: int): int {
if (count <= 0) {
return i;
}
return arg9Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get9Tail(count: int): int {
return arg9Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
function arg10Tail(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int, j: int, count: int): int {
if (count <= 0) {
return j;
}
return arg10Tail(a, b, c, d, e, f, g, h, i, j, count - 1);
}
function get10Tail(count: int): int {
return arg10Tail(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10);
}
// FLOATS
function farg1(a: float, b: float, c: float, d: float, e: float, f: float, g: float, h: float, i: float ,j: float): float {