Compare commits

..

1 Commits

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

1
.gitignore vendored
View File

@@ -17,4 +17,3 @@ src/main/antlr4/de/hsrm/compiler/Klang/.antlr
# build output
out
src/test/test
/.idea/uiDesigner.xml

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:

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

16
.idea/compiler.xml generated
View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="klang" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="klang" target="17" />
</bytecodeTargetLevel>
</component>
</project>

8
.idea/encodings.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/target/generated-sources/antlr4" charset="UTF-8" />
</component>
</project>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: org.antlr:antlr4-runtime:4.7.2">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.7.2/antlr4-runtime-4.7.2.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.7.2/antlr4-runtime-4.7.2-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.7.2/antlr4-runtime-4.7.2-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: org.apiguardian:apiguardian-api:1.1.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: org.junit.jupiter:junit-jupiter-api:5.6.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.6.0/junit-jupiter-api-5.6.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.6.0/junit-jupiter-api-5.6.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.6.0/junit-jupiter-api-5.6.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: org.junit.jupiter:junit-jupiter-engine:5.6.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.6.0/junit-jupiter-engine-5.6.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.6.0/junit-jupiter-engine-5.6.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.6.0/junit-jupiter-engine-5.6.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: org.junit.platform:junit-platform-commons:1.6.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.6.0/junit-platform-commons-1.6.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.6.0/junit-platform-commons-1.6.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.6.0/junit-platform-commons-1.6.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: org.junit.platform:junit-platform-engine:1.6.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.6.0/junit-platform-engine-1.6.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.6.0/junit-platform-engine-1.6.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.6.0/junit-platform-engine-1.6.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Maven: org.opentest4j:opentest4j:1.2.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0-sources.jar!/" />
</SOURCES>
</library>
</component>

28
.idea/misc.xml generated
View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ANTLRGenerationPreferences">
<option name="perGrammarGenerationSettings">
<list>
<PerGrammarGenerationSettings>
<option name="fileName" value="$PROJECT_DIR$/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4" />
<option name="outputDir" value="" />
<option name="libDir" value="" />
<option name="encoding" value="" />
<option name="pkg" value="" />
<option name="language" value="" />
<option name="generateVisitor" value="true" />
</PerGrammarGenerationSettings>
</list>
</option>
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
</component>
</project>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/klang.iml" filepath="$PROJECT_DIR$/klang.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

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

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_17">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/antlr4" isTestSource="false" generated="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.antlr:antlr4-runtime:4.7.2" level="project" />
<orderEntry type="library" name="Maven: org.junit.jupiter:junit-jupiter-api:5.6.0" level="project" />
<orderEntry type="library" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
<orderEntry type="library" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
<orderEntry type="library" name="Maven: org.junit.platform:junit-platform-commons:1.6.0" level="project" />
<orderEntry type="library" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.6.0" level="project" />
<orderEntry type="library" name="Maven: org.junit.platform:junit-platform-engine:1.6.0" level="project" />
</component>
</module>

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

28
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>
@@ -40,7 +31,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>17</release>
<release>11</release>
</configuration>
</plugin>
<!-- Plugin to compile the g4 files ahead of the java files
@@ -72,11 +63,6 @@
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>de.hsrm.compiler.Klang.Klang</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
@@ -88,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

@@ -5,11 +5,7 @@ parse
;
program
: (functionDef | structDef | enumDef)* expression SCOL
;
enumDef
: ENUM enumName=IDENT OBRK (IDENT (COMMA IDENT)*)+ CBRK
: (functionDef | structDef)* expression SCOL
;
structDef
@@ -82,7 +78,7 @@ destroy_statement
expression
: atom #atomExpression
| IDENT (PERIOD IDENT)+ #memberAccessExpression
| IDENT (PERIOD IDENT)+ #structFieldAccessExpression
| OPAR expression CPAR #parenthesisExpression
| lhs=expression MUL rhs=expression #multiplicationExpression
| lhs=expression DIV rhs=expression #divisionExpression
@@ -107,7 +103,6 @@ atom
: INTEGER_LITERAL #intAtom
| BOOLEAN_LITERAL #boolAtom
| FLOAT_LITERAL #floatAtom
| CHAR_LITERAL #charAtom
| NULL # nullAtom
| IDENT #variable
;
@@ -120,7 +115,6 @@ type
: INTEGER
| BOOLEAN
| FLOAT
| CHAR
| IDENT
| VOID
;
@@ -150,7 +144,6 @@ forLoop
IF: 'if';
ELSE: 'else';
FUNC: 'function';
ENUM: 'enum';
STRUCT: 'struct';
RETURN: 'return';
LET: 'let';
@@ -186,12 +179,9 @@ SUB: '-';
MOD: '%';
DIV: '/';
SQUOT: '\'';
BOOLEAN: 'bool';
INTEGER: 'int';
FLOAT: 'float';
CHAR: 'char';
VOID: 'void';
INTEGER_LITERAL
@@ -207,10 +197,6 @@ BOOLEAN_LITERAL
| 'false'
;
CHAR_LITERAL
: SQUOT [ -~] SQUOT
;
IDENT
: [a-zA-Z][a-zA-Z0-9]*
;

View File

@@ -1,5 +1,9 @@
package de.hsrm.compiler.Klang;
import java.util.Map;
import java.util.HashMap;
import de.hsrm.compiler.Klang.helper.FunctionInformation;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*;
@@ -8,18 +12,12 @@ import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
import de.hsrm.compiler.Klang.nodes.statements.*;
import de.hsrm.compiler.Klang.types.Type;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.*;
public class ContextAnalysis extends KlangBaseVisitor<Node> {
Map<String, VariableDeclaration> params = new HashMap<>();
Map<String, VariableDeclaration> vars = new HashMap<>();
Map<String, FunctionDefinition> functionDefs;
Map<String, StructDefinition> structDefs;
Map<String, EnumDefinition> enumDefs;
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()) {
@@ -28,35 +26,25 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
}
}
public ContextAnalysis(
Map<String, FunctionDefinition> functionDefs,
Map<String, StructDefinition> structDefs,
Map<String, EnumDefinition> enumDefs
) {
this.functionDefs = functionDefs;
this.structDefs = structDefs;
this.enumDefs = enumDefs;
public ContextAnalysis(Map<String, FunctionInformation> funcs, Map<String, StructDefinition> structs) {
this.funcs = funcs;
this.structs = structs;
}
@Override
public Node visitProgram(KlangParser.ProgramContext ctx) {
var typeCheckedFunctionDefs = new FunctionDefinition[ctx.functionDef().size()];
var typeCheckedStructDefs = new HashMap<String, StructDefinition>();
FunctionDefinition[] funcs = new FunctionDefinition[ctx.functionDef().size()];
for (int i = 0; i < ctx.functionDef().size(); i++) {
typeCheckedFunctionDefs[i] = (FunctionDefinition) visit(ctx.functionDef(i));
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
}
for (int i = 0; i < ctx.structDef().size(); i++) {
typeCheckedStructDefs.put(ctx.structDef(i).structName.getText(), (StructDefinition) visit(ctx.structDef(i)));
}
var expression = (Expression) visit(ctx.expression());
var program = new Program(typeCheckedFunctionDefs, typeCheckedStructDefs, enumDefs, expression);
program.type = expression.type;
program.line = ctx.start.getLine();
program.col = ctx.start.getCharPositionInLine();
return program;
Expression expression = (Expression) this.visit(ctx.expression());
Program result = new Program(funcs, this.structs, expression);
result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
@@ -68,32 +56,25 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
var statementsOrFunctionCalls = new ArrayList<Node>();
var hasReturn = false;
int actualStatementCount = 0;
int declaredStatementCount = ctx.statement().size();
boolean hasReturn = false;
Statement[] statements = new Statement[declaredStatementCount];
for (var child: ctx.children) {
var statementOrFunctionCall = visit(child);
for (int i = 0; i < declaredStatementCount; i++) {
Node currentStatement = this.visit(ctx.statement(i));
statements[i] = (Statement) currentStatement;
actualStatementCount += 1;
// The children array contains more than just the statements or function calls
// but everything else evaluates to null, so we can skip it.
if (statementOrFunctionCall == null) {
continue;
}
statementsOrFunctionCalls.add(statementOrFunctionCall);
if (
statementOrFunctionCall.type != null
&& !(statementOrFunctionCall instanceof VariableDeclaration)
&& !statementOrFunctionCall.type.equals(Type.getVoidType())
) {
// We use the existance of a type to indicate that this statement returns
// something for which the VariableDeclaration is an exception
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
// check whether the type matches
try {
currentDeclaredReturnType.combine(statementOrFunctionCall.type);
this.currentDeclaredReturnType.combine(currentStatement.type);
} catch (Exception e) {
var line = statementOrFunctionCall.line;
var col = statementOrFunctionCall.col;
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
throw new RuntimeException(
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
}
// since we have a return guaranteed, every statement
@@ -103,12 +84,21 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
}
}
// If there was unreachable code in this block,
// create a shorter statements array and copy the statements to there
if (actualStatementCount < declaredStatementCount) {
Statement[] newStatements = new Statement[actualStatementCount];
for (int i = 0; i < actualStatementCount; i++) {
newStatements[i] = statements[i];
}
statements = newStatements;
}
// if this block contains at least one statement that guarantees a return value,
// we indicate that this block guarantees a return value by setting result.type
var result = new Block(statementsOrFunctionCalls.toArray(new Node[0]));
Block result = new Block(statements);
if (hasReturn) {
result.type = currentDeclaredReturnType;
result.type = this.currentDeclaredReturnType;
}
result.line = ctx.start.getLine();
@@ -159,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();
@@ -181,89 +171,77 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
var variableName = ctx.IDENT().getText();
var declaredType = Type.getByName(ctx.type_annotation().type().getText());
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
if (declaredType.equals(Type.getVoidType())) {
var error = "Type " + declaredType.getName() + " can not be used to declare variables.";
String error = "Type " + declaredType.getName() + " can not be used to declare variables.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
var error = "Type " + declaredType.getName() + " not defined.";
if (!declaredType.isPrimitiveType() && this.structs.get(declaredType.getName()) == null) {
String error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (structDefs.containsKey(variableName)) {
var error = "Variable name " + variableName + " shadows a struct of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (enumDefs.containsKey(variableName)) {
var error = "Variable name " + variableName + " shadows an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (vars.get(variableName) != null || params.get(variableName) != null) {
var error = "Redeclaration of variable or parameter with name \"" + variableName + "\".";
if (this.vars.get(name) != null) {
String error = "Redeclaration of variable with name \"" + name + "\".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Create the appropriate instance
VariableDeclaration variableDeclaration;
VariableDeclaration result;
if (ctx.expression() != null) {
var expression = visit(ctx.expression());
Node expression = this.visit(ctx.expression());
try {
declaredType.combine(expression.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
variableDeclaration = new VariableDeclaration(variableName, (Expression) expression);
variableDeclaration.initialized = true;
result = new VariableDeclaration(name, (Expression) expression);
result.initialized = true;
} else {
if (enumDefs.containsKey(declaredType.getName())) {
var error = "Variable " + variableName + " references an enum but is not initialized.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
variableDeclaration = new VariableDeclaration(variableName);
result = new VariableDeclaration(name);
}
// Add it to the global map of variable declarations
vars.put(variableName, variableDeclaration);
this.vars.put(name, result);
variableDeclaration.line = line;
variableDeclaration.col = col;
variableDeclaration.type = declaredType;
return variableDeclaration;
result.line = line;
result.col = col;
result.type = declaredType;
return result;
}
@Override
public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) {
var name = ctx.IDENT().getText();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
var variableOrParameter = getVariableOrParameter(name, line, col);
VariableDeclaration var = this.vars.get(name);
if (var == null) {
String error = "Variable with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Evaluate the expression
var expression = (Expression) visit(ctx.expression());
Expression expression = (Expression) this.visit(ctx.expression());
// Make sure expression can be assigned to the variable
try {
expression.type.combine(variableOrParameter.type);
expression.type.combine(var.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
// Since we assigned a value to this variable, we can consider it initialized
variableOrParameter.initialized = true;
var.initialized = true;
// Create a new node and add the type of the expression to it
var result = new VariableAssignment(name, expression);
Node result = new VariableAssignment(name, expression);
result.line = line;
result.col = col;
return result;
@@ -272,28 +250,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
if (currentDeclaredReturnType.equals(Type.getVoidType())) {
var result = new ReturnStatement();
result.type = Type.getVoidType();
ReturnStatement result = new ReturnStatement();
result.line = ctx.start.getLine();
result.type = Type.getVoidType();
result.col = ctx.start.getCharPositionInLine();
if (ctx.expression() != null) {
var error = "Cannot return an expression from a void function.";
String error = "Cannot return an expression from a void function.";
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
}
return result;
}
var expression = (Expression) visit(ctx.expression());
// Check if this expression is a tail recursion
if (expression instanceof FunctionCall funCall) {
if (funCall.name.equals(currentFunctionDefinitionName)) {
// Flag this function call
funCall.isTailRecursive = true;
}
}
var result = new ReturnStatement(expression);
Expression expression = (Expression) this.visit(ctx.expression());
ReturnStatement result = new ReturnStatement(expression);
result.line = ctx.start.getLine();
result.type = expression.type;
result.col = ctx.start.getCharPositionInLine();
@@ -302,71 +270,89 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitField_assignment(KlangParser.Field_assignmentContext ctx) {
var varName = ctx.IDENT(0).getText();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var path = createStructPath(ctx.IDENT());
String varName = ctx.IDENT(0).getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
String[] path = new String[ctx.IDENT().size() - 1];
var variableOrParameter = getVariableOrParameter(varName, line, col);
ensureReferencesStruct(variableOrParameter, line, col);
var fieldType = drillType(variableOrParameter, path, line, col);
for (int i = 1; i < ctx.IDENT().size(); i++) {
path[i - 1] = ctx.IDENT(i).getText();
}
// Visit the expression and make sure the type combines properly
var expression = (Expression) visit(ctx.expression());
// Get the referenced variable, make sure it is defined
var variableDef = this.vars.get(varName);
if (variableDef == null) {
String error = "Variable with name " + varName + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Make sure it references a struct
if (variableDef.type.isPrimitiveType()) {
String error = "Variable must reference a struct but references " + variableDef.type.getName() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Get the type of the result of this expression
String structName = variableDef.type.getName();
Type fieldType;
try {
fieldType = Helper.drillType(this.structs, structName, path, 0);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
// Get the expression and make sure the type combines properly
Expression expression = (Expression) this.visit(ctx.expression());
try {
fieldType.combine(expression.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
var result = new FieldAssignment(varName, variableOrParameter.type.getName(), path, expression);
Node result = new FieldAssignment(varName, structName, path, expression);
result.col = col;
result.line = line;
return result;
}
@Override
public Node visitMemberAccessExpression(KlangParser.MemberAccessExpressionContext ctx) {
var baseName = ctx.IDENT(0).getText();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var path = createStructPath(ctx.IDENT());
public Node visitStructFieldAccessExpression(KlangParser.StructFieldAccessExpressionContext ctx) {
String varName = ctx.IDENT(0).getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
String[] path = new String[ctx.IDENT().size() - 1];
// Determine if the base name points to an enum or a variable
var enumDef = enumDefs.get(baseName);
if (enumDef != null) {
if (path.length != 1) {
var error = "Illegal access to enum " + enumDef.name + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var enumValueName = path[0];
var enumValue = Arrays.stream(enumDef.enums)
.filter(e -> e.value.equals(enumValueName))
.findFirst()
.orElseThrow(() -> {
var error = "Unknown enum value " + enumValueName + " of enum " + enumDef.name + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
});
var enumAccessExpression = new EnumAccessExpression(baseName, enumValueName, enumValue);
enumAccessExpression.type = enumDef.type;
enumAccessExpression.line = line;
enumAccessExpression.col = col;
return enumAccessExpression;
for (int i = 1; i < ctx.IDENT().size(); i++) {
path[i - 1] = ctx.IDENT(i).getText();
}
var variableOrParameter = getVariableOrParameter(baseName, line, col);
ensureReferencesStruct(variableOrParameter, line, col);
var resultType = drillType(variableOrParameter, path, line, col);
// Get the referenced variable, make sure it is defined
var variableDef = this.vars.get(varName);
if (variableDef == null) {
String error = "Variable with name " + varName + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var memberAccessExpression = new MemberAccessExpression(baseName, variableOrParameter.type.getName(), path);
memberAccessExpression.type = resultType;
memberAccessExpression.line = line;
memberAccessExpression.col = col;
// Make sure it references a struct
if (variableDef.type.isPrimitiveType()) {
String error = "Variable must reference a struct but references " + variableDef.type.getName() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
return memberAccessExpression;
// Get the type of the result of this expression
String structName = variableDef.type.getName();
Type resultType;
try {
resultType = Helper.drillType(this.structs, structName, path, 0);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
Node result = new StructFieldAccessExpression(varName, structName, path);
result.type = resultType;
result.line = line;
result.col = col;
return result;
}
@Override
@@ -427,21 +413,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) {
var lhs = visit(ctx.lhs);
var rhs = visit(ctx.rhs);
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
// Since there are countless combinations of types that are comparable
// to each other we use Type::combine to figure out if two types
// may be compared to each other
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
var result = new EqualityExpression((Expression) lhs, (Expression) rhs);
EqualityExpression result = new EqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
@@ -450,17 +433,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) {
var lhs = visit(ctx.lhs);
var rhs = visit(ctx.rhs);
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
var error = "Can only compare primitives.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
var result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
NotEqualityExpression result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
@@ -469,17 +453,20 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) {
var lhs = visit(ctx.lhs);
var rhs = visit(ctx.rhs);
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
var error = "Can only compare primitives.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
var result = new LTExpression((Expression) lhs, (Expression) rhs);
checkNumeric(lhs, rhs, line, col);
LTExpression result = new LTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
@@ -488,17 +475,23 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) {
var lhs = visit(ctx.lhs);
var rhs = visit(ctx.rhs);
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
var error = "Can only compare primitives.";
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
if (!lhs.type.isNumericType() || !rhs.type.isNumericType()) {
String error = "Only numeric types are allowed for this expression.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var result = new GTExpression((Expression) lhs, (Expression) rhs);
GTExpression result = new GTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
@@ -507,17 +500,20 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) {
var lhs = visit(ctx.lhs);
var rhs = visit(ctx.rhs);
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
var error = "Can only compare primitives.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
var result = new LTEExpression((Expression) lhs, (Expression) rhs);
checkNumeric(lhs, rhs, line, col);
LTEExpression result = new LTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
@@ -526,17 +522,20 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) {
var lhs = visit(ctx.lhs);
var rhs = visit(ctx.rhs);
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
var error = "Can only compare primitives.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
var result = new GTEExpression((Expression) lhs, (Expression) rhs);
checkNumeric(lhs, rhs, line, col);
GTEExpression result = new GTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
@@ -666,20 +665,24 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitVariable(KlangParser.VariableContext ctx) {
var variableName = ctx.IDENT().getText();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
var variableOrParameter = getVariableOrParameter(variableName, line, col);
// Make sure the variable has been initialized before it can be used
if (!variableOrParameter.initialized) {
var error = "Variable with name \"" + variableName + "\" has not been initialized.";
VariableDeclaration var = this.vars.get(name);
if (var == null) {
String error = "Variable with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var result = new Variable(ctx.IDENT().getText());
result.type = variableOrParameter.type;
// Make sure the variable has been initialized before it can be used
if (!var.initialized) {
String error = "Variable with name \"" + name + "\" has not been initialized.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
Variable result = new Variable(ctx.IDENT().getText());
result.type = var.type;
result.line = line;
result.col = col;
return result;
@@ -710,24 +713,13 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) {
Node n = new BooleanExpression(ctx.getText().equals("true"));
Node n = new BooleanExpression(ctx.getText().equals("true") ? true : false);
n.type = Type.getBooleanType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n;
}
@Override
public Node visitCharAtom(KlangParser.CharAtomContext ctx) {
// I had no idea how to design the grammar so that the quotes
// are separate from the char, so I cheat here...
var n = new CharExpression(ctx.CHAR_LITERAL().getText().charAt(1));
n.type = Type.getCharType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n;
}
@Override
public Node visitNullAtom(KlangParser.NullAtomContext ctx) {
Node n = new NullExpression();
@@ -737,181 +729,72 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
return n;
}
@Override
public Node visitStructDef(KlangParser.StructDefContext ctx) {
var structName = ctx.structName.getText();
var structFieldCount = ctx.structField().size();
var structFields = new StructField[structFieldCount];
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
for (int i = 0; i < structFieldCount; i++) {
structFields[i] = (StructField) visit(ctx.structField(i));
}
var structDef = new StructDefinition(structName, structFields);
structDef.type = Type.getByName(structName);
structDef.line = line;
structDef.col = col;
return structDef;
}
@Override
public Node visitStructField(KlangParser.StructFieldContext ctx) {
var structFieldName = ctx.IDENT().getText();
var structFieldType = Type.getByName(ctx.type_annotation().type().getText());
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
if (structFieldType.equals(Type.getVoidType())) {
var error = "Type void can not be used as a struct field type.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!structFieldType.isPrimitiveType() && !structDefs.containsKey(structFieldType.getName()) && !enumDefs.containsKey(structFieldType.getName())) {
var error = "Type " + structFieldType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (structDefs.containsKey(structFieldName)) {
var error = "Struct field name " + structFieldName + " shadows a struct of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (enumDefs.containsKey(structFieldName)) {
var error = "Struct field name " + structFieldName + " shadows an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var structField = new StructField(structFieldName);
structField.type = structFieldType;
structField.line = line;
structField.col = col;
return structField;
}
@Override
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
var name = ctx.funcName.getText();
var returnType = Type.getByName(ctx.returnType.type().getText());
currentDeclaredReturnType = returnType;
currentFunctionDefinitionName = name;
String name = ctx.funcName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type returnType = Type.getByName(ctx.returnType.type().getText());
this.currentDeclaredReturnType = returnType;
var typeNotDefined = !returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName());
if (!returnType.equals(Type.getVoidType()) && typeNotDefined) {
var line = ctx.returnType.start.getLine();
var col = ctx.returnType.start.getCharPositionInLine();
var error = "Type " + returnType.getName() + " not defined.";
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);
}
// Create a new set for the variables and parameters of the current function
// this will be filled in the variable declaration visitor as well
vars = new HashMap<>();
params = new HashMap<>();
// Create a new set for the variables of the current function
// this will be filled in the variable declaration visitor aswell
this.vars = new HashMap<>();
var paramCount = ctx.params.parameter().size();
var functionParameters = new Parameter[paramCount]; // the list of parameters that get passed to FunctionDefinition
// Process the paremter list by visiting every paremter in it
int paramCount = ctx.params.parameter().size();
Parameter[] params = new Parameter[paramCount];
for (int i = 0; i < paramCount; i++) {
functionParameters[i] = (Parameter) visit(ctx.params.parameter(i));
// Add the parameter to the list of parameters
Parameter param = (Parameter) this.visit(ctx.params.parameter(i));
params[i] = param;
// add the param as a variable to the global parameter map so that
// child nodes can access them.
var param = new VariableDeclaration(functionParameters[i].name);
param.initialized = true; // parameters can always be considered initialized
param.type = functionParameters[i].type;
params.put(param.name, param);
// add the param as a variable
VariableDeclaration var = new VariableDeclaration(param.name);
var.initialized = true; // parameters can always be considered initialized
var.type = param.type;
this.vars.put(param.name, var);
}
// Visit the block, make sure that a return value is guaranteed
var block = visit(ctx.braced_block());
if (block.type == null && !returnType.equals(Type.getVoidType())) {
var line = ctx.braced_block().start.getLine();
var col = ctx.braced_block().start.getCharPositionInLine();
var error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
Node block = this.visit(ctx.braced_block());
if (block.type == null) {
String error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var functionDef = new FunctionDefinition(name, functionParameters, vars.values().toArray(new VariableDeclaration[0]), (Block) block);
functionDef.type = returnType;
functionDef.line = ctx.start.getLine();
functionDef.col = ctx.start.getCharPositionInLine();
FunctionDefinition result = new FunctionDefinition(name, params, (Block) block);
result.type = returnType;
return functionDef;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitParameter(KlangParser.ParameterContext ctx) {
var parameterName = ctx.IDENT().getText();
var parameterType = Type.getByName(ctx.type_annotation().type().getText());
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type type = Type.getByName(ctx.type_annotation().type().getText());
if (parameterType.equals(Type.getVoidType())) {
var error = "Type " + parameterType.getName() + " cannot be used to declare a parameter.";
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 (structDefs.containsKey(parameterName)) {
var error = "Parameter name " + parameterName + " duplicates a struct of the same name.";
if (!type.isPrimitiveType() && this.structs.get(type.getName()) == null) {
String error = "Type " + type.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (enumDefs.containsKey(parameterName)) {
var error = "Parameter name " + parameterName + " duplicates an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) {
var typeLine = ctx.type_annotation().start.getLine();
var typeCol = ctx.type_annotation().start.getCharPositionInLine();
var error = "Type " + parameterType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(typeLine, typeCol) + error);
}
var parameter = new Parameter(parameterName);
parameter.type = parameterType;
parameter.line = ctx.start.getLine();
parameter.col = ctx.start.getCharPositionInLine();
return parameter;
}
@Override
public Node visitFunctionCall(KlangParser.FunctionCallContext ctx) {
var name = ctx.IDENT().getText();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var functionDef = functionDefs.get(name);
if (functionDef == null) {
var error = "Function with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Make sure the number of arguments matches the number of parameters
var argCount = ctx.arguments().expression().size();
var paramCount = functionDef.parameters.length;
if (argCount != paramCount) {
var error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Evaluate every argument
var args = new Expression[argCount];
for (int i = 0; i < argCount; i++) {
var expression = (Expression) visit(ctx.arguments().expression(i));
if (!expression.type.equals(functionDef.parameters[i].type)) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + functionDef.parameters[i].type.getName() + " but got: " + expression.type.getName());
}
args[i] = expression;
}
var result = new FunctionCall(name, args);
result.type = functionDef.type;
Parameter result = new Parameter(name);
result.type = type;
result.line = line;
result.col = col;
return result;
@@ -919,7 +802,39 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) {
return visit(ctx.functionCall());
String name = ctx.functionCall().IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
FunctionInformation func = this.funcs.get(name);
if (func == null) {
String error = "Function with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Make sure the number of arguments matches the number of parameters
int argCount = ctx.functionCall().arguments().expression().size();
int paramCount = func.parameters.size();
if (argCount != paramCount) {
String error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Evaluate every argument
Expression[] args = new Expression[argCount];
for (int i = 0; i < argCount; i++) {
Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
if (!expression.type.equals(func.signature[i])) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + func.signature[i].getName() + " but got: " + expression.type.getName());
}
args[i] = expression;
}
FunctionCall result = new FunctionCall(name, args);
result.type = func.returnType;
result.line = line;
result.col = col;
return result;
}
@Override
@@ -929,7 +844,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
int col = ctx.start.getCharPositionInLine();
// Get the corresponding struct definition
var struct = this.structDefs.get(name);
var struct = this.structs.get(name);
if (struct == null) {
String error = "Struct with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
@@ -964,52 +879,19 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitDestroy_statement(KlangParser.Destroy_statementContext ctx) {
var varName = ctx.IDENT().getText();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
var variableOrParameter = getVariableOrParameter(varName, line, col);
ensureReferencesStruct(variableOrParameter, line, col);
VariableDeclaration var = this.vars.get(name);
if (var == null) {
String error = "Variable with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var result = new DestructorCall(varName);
Node result = new DestructorCall(name);
result.line = line;
result.col = col;
return result;
}
private VariableDeclaration getVariableOrParameter(String name, int line, int col) {
var variable = vars.get(name);
var parameter = params.get(name);
if (variable == null && parameter == null) {
var error = "Variable or parameter with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
return variable != null ? variable : parameter;
}
private void ensureReferencesStruct(VariableDeclaration variableOrParameter, int line, int col) {
if (variableOrParameter.type.isPrimitiveType() || enumDefs.containsKey(variableOrParameter.type.getName())) {
var error = "Variable or parameter must reference a struct but references " + variableOrParameter.type.getName() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
}
private Type drillType(VariableDeclaration variableOrParameter, String[] path, int line, int col) {
try {
var structName = variableOrParameter.type.getName();
return Helper.drillType(structDefs, structName, path, 0);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
}
private String[] createStructPath(List<TerminalNode> identifiers) {
// Create a list of member names. This excludes the first entry as it is the base name.
var path = new String[identifiers.size() - 1];
for (int i = 1; i < identifiers.size(); i++) {
path[i - 1] = identifiers.get(i).getText();
}
return path;
}
}

View File

@@ -1,220 +0,0 @@
package de.hsrm.compiler.Klang;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.types.NamedType;
import de.hsrm.compiler.Klang.types.Type;
import java.util.*;
public class GetDefinitions extends KlangBaseVisitor<Node> {
private final Map<String, FunctionDefinition> functionDefs;
private final Map<String, StructDefinition> structDefs;
private final Map<String, EnumDefinition> enumDefs;
private Set<String> functionNames;
private Set<String> structNames;
private Set<String> enumNames;
public GetDefinitions(
Map<String, FunctionDefinition> functionDefs,
Map<String, StructDefinition> structDefs,
Map<String, EnumDefinition> enumDefs
) {
this.functionDefs = functionDefs;
this.structDefs = structDefs;
this.enumDefs = enumDefs;
}
private Set<String> collectFunctionNames(KlangParser.ProgramContext ctx) {
var result = new HashSet<String>();
for (int i = 0; i < ctx.functionDef().size(); i++) {
var currentFunctionDef = ctx.functionDef(i);
var funcName = currentFunctionDef.funcName.getText();
if (result.contains(funcName)) {
var line = currentFunctionDef.funcName.getLine();
var col = currentFunctionDef.funcName.getCharPositionInLine();
var error = "Function " + funcName + " defined multiple times.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
result.add(funcName);
}
return result;
}
private Set<String> collectStructNames(KlangParser.ProgramContext ctx) {
var result = new HashSet<String>();
for (int i = 0; i < ctx.structDef().size(); i++) {
var currentStructDef = ctx.structDef(i);
var structName = currentStructDef.structName.getText();
if (result.contains(structName)) {
var line = currentStructDef.structName.getLine();
var col = currentStructDef.structName.getCharPositionInLine();
var error = "Struct " + structName + " defined multiple times.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
result.add(structName);
}
return result;
}
private Set<String> collectEnumNames(KlangParser.ProgramContext ctx) {
var result = new HashSet<String>();
for (int i = 0; i < ctx.enumDef().size(); i++) {
var currentEnumDef = ctx.enumDef(i);
var enumName = currentEnumDef.enumName.getText();
if (result.contains(enumName)) {
var line = currentEnumDef.enumName.getLine();
var col = currentEnumDef.enumName.getCharPositionInLine();
var error = "Enum " + enumName + " defined multiple times.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
result.add(enumName);
}
return result;
}
@Override
public Node visitProgram(KlangParser.ProgramContext ctx) {
functionNames = collectFunctionNames(ctx);
structNames = collectStructNames(ctx);
enumNames = collectEnumNames(ctx);
for (int i = 0; i < ctx.functionDef().size(); i++) {
visit(ctx.functionDef(i));
}
for (int i = 0; i < ctx.structDef().size(); i++) {
visit(ctx.structDef(i));
}
for (int i = 0; i < ctx.enumDef().size(); i++) {
visit(ctx.enumDef(i));
}
return null;
}
@Override
public Node visitEnumDef(KlangParser.EnumDefContext ctx) {
// Check that there isn't a function or struct with the same name
var enumName = ctx.enumName.getText();
if (functionNames.contains(enumName) || structNames.contains(enumName)) {
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var error = "Duplicate use of name " + enumName + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// IDENT() includes the enumName as the first entry, which we skip
var enumValues = new LinkedHashMap<String, EnumValue>();
for (int i = 1; i < ctx.IDENT().size(); i++) {
var currentEnumField = ctx.IDENT(i);
var currentEnumFieldName = currentEnumField.getText();
var line = currentEnumField.getSymbol().getLine();
var col = currentEnumField.getSymbol().getCharPositionInLine();
if (enumValues.containsKey(currentEnumFieldName)) {
var error = " Duplicate enum value " + currentEnumFieldName + " in enum " + enumName + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var enumValue = new EnumValue(currentEnumFieldName, i - 1);
enumValue.line = line;
enumValue.col = col;
enumValues.put(currentEnumFieldName, enumValue);
}
var enumDef = new EnumDefinition(enumName, enumValues.values().toArray(new EnumValue[0]));
enumDef.line = ctx.start.getLine();
enumDef.col = ctx.start.getCharPositionInLine();
enumDef.type = new NamedType(enumName);
enumDefs.put(enumName, enumDef);
return null;
}
@Override
public Node visitStructDef(KlangParser.StructDefContext ctx) {
var structName = ctx.structName.getText();
var structFieldCount = ctx.structField().size();
var structFields = new LinkedHashMap<String, StructField>();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
// Check that there isn't a function or enum with the same name
if (functionNames.contains(structName) || enumNames.contains(structName)) {
var error = "Duplicate use of name " + structName + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
for (int i = 0; i < structFieldCount; i++) {
var currentStructField = ctx.structField(i);
var structFieldName = currentStructField.IDENT().getText();
var structFieldLine = currentStructField.start.getLine();
var structFieldCol = currentStructField.start.getCharPositionInLine();
if (structFields.containsKey(structFieldName)) {
var error = "Duplicate struct field " + structFieldName + " in struct " + structName + ".";
throw new RuntimeException(Helper.getErrorPrefix(structFieldLine, structFieldCol) + error);
}
var structField = new StructField(structFieldName);
structField.type = Type.getByName(currentStructField.type_annotation().type().getText());
structField.line = structFieldLine;
structField.col = structFieldCol;
structFields.put(structFieldName, structField);
}
var structDef = new StructDefinition(structName, structFields.values().toArray(new StructField[0]));
structDef.line = line;
structDef.col = col;
structDef.type = new NamedType(structName);
structDefs.put(structName, structDef);
return null;
}
@Override
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
var funcName = ctx.funcName.getText();
var paramCount = ctx.params.parameter().size();
var parameters = new LinkedHashMap<String, Parameter>();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
// Check that there isn't a struct or enum with the same name
if (structNames.contains(funcName) || enumNames.contains(funcName)) {
var error = "Duplicate use of name " + funcName + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
for (int i = 0; i < paramCount; i++) {
var currentParam = ctx.params.parameter(i);
var paramName = currentParam.IDENT().getText();
var paramLine = currentParam.start.getLine();
var paramCol = currentParam.start.getCharPositionInLine();
if (parameters.containsKey(paramName)) {
var error = "Duplicate parameter name " + paramName + " in function " + funcName + ".";
throw new RuntimeException(Helper.getErrorPrefix(paramLine, paramCol) + error);
}
var parameter = new Parameter(paramName);
parameter.type = Type.getByName(currentParam.type_annotation().type().getText());
parameter.line = paramLine;
parameter.col = paramCol;
parameters.put(paramName, parameter);
}
var functionDef = new FunctionDefinition(funcName, parameters.values().toArray(new Parameter[0]), null, null);
functionDef.type = Type.getByName(ctx.returnType.type().getText());
functionDef.line = line;
functionDef.col = col;
functionDefs.put(funcName, functionDef);
return null;
}
}

View File

@@ -0,0 +1,54 @@
package de.hsrm.compiler.Klang;
import java.util.Map;
import java.util.TreeMap;
import de.hsrm.compiler.Klang.types.*;
import de.hsrm.compiler.Klang.helper.*;
public class GetFunctions extends KlangBaseVisitor<Void> {
private Map<String, FunctionInformation> funcs;
public GetFunctions(Map<String, FunctionInformation> funcs) {
this.funcs = funcs;
}
@Override
public Void visitProgram(KlangParser.ProgramContext ctx) {
for (int i = 0; i < ctx.functionDef().size(); i++) {
this.visit(ctx.functionDef(i));
}
return null;
}
@Override
public Void visitFunctionDef(KlangParser.FunctionDefContext ctx) {
String name = ctx.funcName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (this.funcs.containsKey(name)) {
String error = "Function " + name + " defined multiple times.";
throw new Error(Helper.getErrorPrefix(line, col) + error);
}
Type returnType = Type.getByName(ctx.returnType.type().getText());
TreeMap<String, Type> parameters = new TreeMap<String, Type>();
// Process the paremter list by visiting every paremter in it
int paramCount = ctx.params.parameter().size();
Type[] signature = new Type[paramCount];
for (int i = 0; i < paramCount; i++) {
Type paramType = Type.getByName(ctx.params.parameter(i).type_annotation().type().getText());
String paramName = ctx.params.parameter(i).IDENT().getText();
parameters.put(paramName, paramType);
signature[i] = paramType;
}
FunctionInformation information = new FunctionInformation(name, returnType, parameters, signature);
this.funcs.put(name, information);
return null;
}
}

View File

@@ -0,0 +1,37 @@
package de.hsrm.compiler.Klang;
import java.util.Set;
import de.hsrm.compiler.Klang.helper.Helper;
public class GetStructNames extends KlangBaseVisitor<Void> {
private Set<String> structNames;
public GetStructNames(Set<String> structNames) {
this.structNames = structNames;
}
@Override
public Void visitProgram(KlangParser.ProgramContext ctx) {
for (int i = 0; i < ctx.structDef().size(); i++) {
this.visit(ctx.structDef(i));
}
return null;
}
@Override
public Void visitStructDef(KlangParser.StructDefContext ctx) {
String name = ctx.structName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (this.structNames.contains(name)) {
String error = "Struct " + name + " defined multiple times.";
throw new Error(Helper.getErrorPrefix(line, col) + error);
}
this.structNames.add(name);
return null;
}
}

View File

@@ -0,0 +1,70 @@
package de.hsrm.compiler.Klang;
import java.util.Map;
import java.util.Set;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.Node;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.types.StructType;
import de.hsrm.compiler.Klang.types.Type;
public class GetStructs extends KlangBaseVisitor<Node> {
private Set<String> structNames;
private Map<String, StructDefinition> structs;
public GetStructs(Set<String> structNames, Map<String, StructDefinition> structs) {
this.structs = structs;
this.structNames = structNames;
}
@Override
public Node visitProgram(KlangParser.ProgramContext ctx) {
for (int i = 0; i < ctx.structDef().size(); i++) {
this.visit(ctx.structDef(i));
}
return null;
}
@Override
public Node visitStructDef(KlangParser.StructDefContext ctx) {
String name = ctx.structName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
StructField[] fields = new StructField[ctx.structField().size()];
for (int i = 0; i < ctx.structField().size(); i++) {
StructField field = (StructField) this.visit(ctx.structField(i));
fields[i] = field;
}
StructDefinition result = new StructDefinition(name, fields);
result.line = line;
result.col = col;
result.type = new StructType(name);
this.structs.put(name, result);
return null;
}
@Override
public Node visitStructField(KlangParser.StructFieldContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type type = Type.getByName(ctx.type_annotation().type().getText());
if (!type.isPrimitiveType() && !this.structNames.contains(type.getName())) {
String error = "Type " + type.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
Node result = new StructField(name);
result.type = type;
result.line = line;
result.col = col;
return result;
}
}

View File

@@ -1,24 +1,20 @@
package de.hsrm.compiler.Klang;
import de.hsrm.compiler.Klang.helper.*;
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
// import ANTLR's runtime libraries
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
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.EvalVisitor;
import de.hsrm.compiler.Klang.visitors.GenASM;
import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import java.io.FileWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import de.hsrm.compiler.Klang.visitors.*;
import de.hsrm.compiler.Klang.helper.*;
public class Klang {
@@ -51,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
@@ -92,15 +88,22 @@ public class Klang {
// Context Analysis and DAST generation
Node root;
var functionDefs = new HashMap<String, FunctionDefinition>();
var structDefs = new HashMap<String, StructDefinition>();
var enumDefs = new HashMap<String, EnumDefinition>();
HashMap<String, StructDefinition> structs;
try {
// Extract information about all definitions
new GetDefinitions(functionDefs, structDefs, enumDefs).visit(tree);
// Extract information about all functions
var functionDefinitions = new HashMap<String, FunctionInformation>();
new GetFunctions(functionDefinitions).visit(tree);
// Extract names of all structs
var structNames = new HashSet<String>();
new GetStructNames(structNames).visit(tree);
// Extract information about all structs
structs = new HashMap<String, StructDefinition>();
new GetStructs(structNames, structs).visit(tree);
// Create the DAST
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefs, structDefs, enumDefs);
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions, structs);
root = ctxAnal.visit(tree);
} catch (Exception e) {
System.err.println(e.getMessage());
@@ -120,7 +123,7 @@ public class Klang {
if (evaluate) {
// Evaluate the sourcecode and print the result
System.out.println("\nEvaluating the source code:");
EvalVisitor evalVisitor = new EvalVisitor(structDefs);
EvalVisitor evalVisitor = new EvalVisitor(structs);
Value result = root.welcome(evalVisitor);
if (result.type.equals(Type.getVoidType())) {
generateOutput(out, "Result was void");
@@ -131,8 +134,11 @@ public class Klang {
}
// Generate assembler code
GenASM genasm = new GenASM(mainName, structDefs);
// System.out.println("\nPrinting the assembler code");
StringWriter wAsm = new StringWriter();
GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm);
GenASM genasm = new GenASM(exAsm, mainName, structs);
root.welcome(genasm);
generateOutput(out, genasm.toAsm());
generateOutput(out, wAsm.toString());
}
}

View File

@@ -4,8 +4,8 @@ import de.hsrm.compiler.Klang.types.Type;
import java.util.Map;
public class Value {
private final Object value;
public Type type;
private Object value;
public Value(Object value) {
this.value = value;
@@ -17,23 +17,19 @@ public class Value {
}
public Object asObject() {
return value;
return this.value;
}
public int asInteger() {
return (int) value;
return (int) this.value;
}
public double asFloat() {
return (double) value;
return (double) this.value;
}
public boolean asBoolean() {
return (boolean) value;
}
public char asChar() {
return (char) value;
return (boolean) this.value;
}
@SuppressWarnings("unchecked")

View File

@@ -1,222 +0,0 @@
package de.hsrm.compiler.Klang.asm;
import java.util.ArrayList;
import java.util.List;
import de.hsrm.compiler.Klang.asm.mnemonics.*;
public class ASM {
private List<Mnemonic> mnemonics;
public ASM() {
this.mnemonics = new ArrayList<Mnemonic>();
}
public void push(String dataType, int immediate) {
mnemonics.add(new Push(dataType, immediate));
}
public void push(String dataType, String operand) {
mnemonics.add(new Push(dataType, operand));
}
public void pop(String dataType, String operand) {
mnemonics.add(new Pop(dataType, operand));
}
public void mov(String dataType, String src, String dst) {
mnemonics.add(new Mov(dataType, src, dst));
}
public void mov(String dataType, String label, String src, String dst) {
mnemonics.add(new Mov(dataType, label, src, dst));
}
public void mov(String dataType, int offset, String src, String dst) {
mnemonics.add(new Mov(dataType, offset, src, dst));
}
public void mov(String dataType, String src, int offset, String dst) {
mnemonics.add(new Mov(dataType, src, offset, dst));
}
public void mov(String dataType, int immediate, String dst) {
mnemonics.add(new Mov(dataType, immediate, dst));
}
public void ucomi(String dataType, String src, String dst) {
mnemonics.add(new Ucomi(dataType, src, dst));
}
public void cmp(String dataType, String src, String dst) {
mnemonics.add(new Cmp(dataType, src, dst));
}
public void cmp(String dataType, int immediate, String dst) {
mnemonics.add(new Cmp(dataType, immediate, dst));
}
public void je(int label) {
mnemonics.add(new Je(label));
}
public void je(String labelPrefix, int label) {
mnemonics.add(new Je(labelPrefix, label));
}
public void jmp(int label) {
mnemonics.add(new Jmp(label));
}
public void jmp(String labelPrefix, int label) {
mnemonics.add(new Jmp(labelPrefix, label));
}
public void jne(int label) {
mnemonics.add(new Jne(label));
}
public void jne(String labelPrefix, int label) {
mnemonics.add(new Jne(labelPrefix, label));
}
public void jg(int label) {
mnemonics.add(new Jg(label));
}
public void jg(String labelPrefix, int label) {
mnemonics.add(new Jg(labelPrefix, label));
}
public void jge(int label) {
mnemonics.add(new Jge(label));
}
public void jge(String labelPrefix, int label) {
mnemonics.add(new Jge(labelPrefix, label));
}
public void jl(int label) {
mnemonics.add(new Jl(label));
}
public void jl(String labelPrefix, int label) {
mnemonics.add(new Jl(labelPrefix, label));
}
public void jle(int label) {
mnemonics.add(new Jle(label));
}
public void jle(String labelPrefix, int label) {
mnemonics.add(new Jle(labelPrefix, label));
}
public void jz(int label) {
mnemonics.add(new Jz(label));
}
public void jz(String labelPrefix, int label) {
mnemonics.add(new Jz(labelPrefix, label));
}
public void jnz(int label) {
mnemonics.add(new Jnz(label));
}
public void jnz(String labelPrefix, int label) {
mnemonics.add(new Jnz(labelPrefix, label));
}
public void label(int label) {
mnemonics.add(new Label(label));
}
public void label(String labelPrefix, int label) {
mnemonics.add(new Label(labelPrefix, label));
}
public void add(String dataType, String src, String dst) {
mnemonics.add(new Add(dataType, src, dst));
}
public void add(String dataType, String src, int dstOffset, String dst) {
mnemonics.add(new Add(dataType, src, dstOffset, dst));
}
public void add(String dataType, int immediate, String dst) {
mnemonics.add(new Add(dataType, immediate, dst));
}
public void sub(String dataType, String src, String dst) {
mnemonics.add(new Sub(dataType, src, dst));
}
public void mul(String dataType, String src, String dst) {
mnemonics.add(new Mul(dataType, src, dst));
}
public void div(String dataType, String src, String dst) {
mnemonics.add(new Div(dataType, src, dst));
}
public void idiv(String dataType, String operand) {
mnemonics.add(new Idiv(dataType, operand));
}
public void imul(String dataType, String src, String dst) {
mnemonics.add(new Imul(dataType, src, dst));
}
public void cqto() {
mnemonics.add(new Cqto());
}
public void leave() {
mnemonics.add(new Leave());
}
public void ret() {
mnemonics.add(new Ret());
}
public void xor(String dataType, String src, String dst) {
mnemonics.add(new Xor(dataType, src, dst));
}
public void neg(String operand) {
mnemonics.add(new Neg(operand));
}
public void functionHead(String functionName) {
mnemonics.add(new FunctionHead(functionName));
}
public void call(String operand) {
mnemonics.add(new Call(operand));
}
public void text(String text) {
mnemonics.add(new Text(text));
}
public void newline() {
mnemonics.add(new Newline());
}
public void cvtsi2sd(String src, String dst) {
mnemonics.add(new Cvtsi2sd(src, dst));
}
public String toAsm() {
StringBuilder sb = new StringBuilder();
mnemonics.stream().forEach(x -> {
for (int i = 0; i < x.indentation; i++) {
sb.append("\t");
}
sb.append(x.toAsm());
sb.append("\n");
});
return sb.toString();
}
}

View File

@@ -1,29 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Add extends TwoOperandMnemonic {
public String dataType;
public Add(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
public Add(String dataType, String src, int dstOffset, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dstOffset + "(" + dst + ")";
}
public Add(String dataType, int immediate, String dst) {
this.dataType = dataType;
this.src = "$" + immediate;
this.dst = dst;
}
@Override
public String toAsm() {
return "add" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,14 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Call extends OneOperandMnemonic {
public Call(String operand) {
this.operand = operand;
}
@Override
public String toAsm() {
return "call " + this.operand;
}
}

View File

@@ -1,22 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Cmp extends TwoOperandMnemonic {
public String dataType;
public Cmp(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
public Cmp(String dataType, int immediate, String dst) {
this.dataType = dataType;
this.src = "$" + immediate;
this.dst = dst;
}
@Override
public String toAsm() {
return "cmp" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,8 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Cqto extends NoOperandMnemonic {
@Override
public String toAsm() {
return "cqto";
}
}

View File

@@ -1,15 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Cvtsi2sd extends TwoOperandMnemonic {
public Cvtsi2sd(String src, String dst) {
this.src = src;
this.dst = dst;
}
@Override
public String toAsm() {
return "cvtsi2sd " + this.src + ", " + this.dst;
}
}

View File

@@ -1,17 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Div extends TwoOperandMnemonic {
public String dataType;
public Div(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
@Override
public String toAsm() {
return "div" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,26 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class FunctionHead extends NoOperandMnemonic {
public String functionName;
public FunctionHead(String functionName) {
this.functionName = functionName;
this.indentation = 0;
}
@Override
public String toAsm() {
StringBuilder sb = new StringBuilder();
sb.append(".globl ");
sb.append(this.functionName);
sb.append("\n");
sb.append(".type ");
sb.append(this.functionName);
sb.append(", @function\n");
sb.append(this.functionName);
sb.append(":");
return sb.toString();
}
}

View File

@@ -1,16 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Idiv extends OneOperandMnemonic{
public String dataType;
public Idiv(String dataType, String operand) {
this.dataType = dataType;
this.operand = operand;
}
@Override
public String toAsm() {
return "idiv" + this.dataType + " " + this.operand;
}
}

View File

@@ -1,17 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Imul extends TwoOperandMnemonic {
public String dataType;
public Imul(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
@Override
public String toAsm() {
return "imul" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,12 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Je extends Jump {
public Je(int label) {
super("je", label);
}
public Je(String labelPrefix, int label) {
super("je", labelPrefix, label);
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jg extends Jump {
public Jg(int label) {
super("jg", label);
}
public Jg(String labelPrefix, int label) {
super("jg", labelPrefix, label);
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jge extends Jump {
public Jge(int label) {
super("jge", label);
}
public Jge(String labelPrefix, int label) {
super("jge", labelPrefix, label);
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jl extends Jump {
public Jl(int label) {
super("jl", label);
}
public Jl(String labelPrefix, int label) {
super("jl", labelPrefix, label);
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jle extends Jump {
public Jle(int label) {
super("jle", label);
}
public Jle(String labelPrefix, int label) {
super("jle", labelPrefix, label);
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jmp extends Jump {
public Jmp(String labelPrefix, int label) {
super("jmp", labelPrefix, label);
}
public Jmp(int label) {
super("jmp", label);
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jne extends Jump {
public Jne(int label) {
super("jne", label);
}
public Jne(String labelPrefix, int label) {
super("jne", labelPrefix, label);
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jnz extends Jump {
public Jnz(int label) {
super("jnz", label);
}
public Jnz(String labelPrefix, int label) {
super("jnz", labelPrefix, label);
}
}

View File

@@ -1,21 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public abstract class Jump extends Mnemonic {
protected String opcode;
public String labelPrefix = "L";
public int label;
public Jump(String opcode, String labelPrefix, int label) {
this.opcode = opcode;
this.labelPrefix = labelPrefix;
this.label = label;
}
public Jump(String opcode, int label) {
this.opcode = opcode;
this.label = label;
}
@Override
public String toAsm() {
return this.opcode + " ." + this.labelPrefix + this.label;
}
}

View File

@@ -1,11 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Jz extends Jump {
public Jz(int label) {
super("jz", label);
}
public Jz(String labelPrefix, int label) {
super("jz", labelPrefix, label);
}
}

View File

@@ -1,22 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Label extends Mnemonic {
public String labelPrefix = "L";
public int label;
public Label(int label) {
this.label = label;
this.indentation = 0;
}
public Label(String labelPrefix, int label) {
this.labelPrefix = labelPrefix;
this.label = label;
this.indentation = 0;
}
@Override
public String toAsm() {
return "." + this.labelPrefix + this.label + ":";
}
}

View File

@@ -1,8 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Leave extends NoOperandMnemonic {
@Override
public String toAsm() {
return "leave";
}
}

View File

@@ -1,6 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public abstract class Mnemonic {
public abstract String toAsm();
public int indentation = 2;
}

View File

@@ -1,40 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Mov extends TwoOperandMnemonic{
public String dataType = "q";
public Mov(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
public Mov(String dataType, String label, String src, String dst) {
this.dataType = dataType;
this.src = label + "(" + src + ")";
this.dst = dst;
}
public Mov(String dataType, int offset, String src, String dst) {
this.dataType = dataType;
this.src = offset + "(" + src + ")";
this.dst = dst;
}
public Mov(String dataType, int immediate, String dst) {
this.dataType = dataType;
this.src = "$" + immediate;
this.dst = dst;
}
public Mov(String dataType, String src, int offset, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = offset + "(" + dst + ")";
}
@Override
public String toAsm() {
return "mov" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,17 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Mul extends TwoOperandMnemonic {
public String dataType;
public Mul(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
@Override
public String toAsm() {
return "mul" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,14 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Neg extends OneOperandMnemonic {
public Neg(String operand) {
this.operand = operand;
}
@Override
public String toAsm() {
return "neg " + this.operand;
}
}

View File

@@ -1,9 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Newline extends NoOperandMnemonic {
@Override
public String toAsm() {
return "";
}
}

View File

@@ -1,5 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public abstract class NoOperandMnemonic extends Mnemonic {
}

View File

@@ -1,5 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public abstract class OneOperandMnemonic extends Mnemonic {
public String operand;
}

View File

@@ -1,16 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Pop extends OneOperandMnemonic {
public String dataType;
public Pop(String dataType, String operand) {
this.dataType = dataType;
this.operand = operand;
}
@Override
public String toAsm() {
return "pop" + this.dataType + " " + this.operand;
}
}

View File

@@ -1,21 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Push extends OneOperandMnemonic {
public String dataType;
public Push(String dataType, int immediate){
this.dataType = dataType;
this.operand = "$" + immediate;
}
public Push(String dataType, String operand) {
this.dataType = dataType;
this.operand = operand;
}
@Override
public String toAsm() {
return "push" + this.dataType + " " + this.operand;
}
}

View File

@@ -1,8 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Ret extends NoOperandMnemonic {
@Override
public String toAsm() {
return "ret";
}
}

View File

@@ -1,17 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Sub extends TwoOperandMnemonic {
public String dataType;
public Sub(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
@Override
public String toAsm() {
return "sub" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,15 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Text extends NoOperandMnemonic {
public String text;
public Text(String text) {
this.text = text;
this.indentation = 0;
}
@Override
public String toAsm() {
return this.text;
}
}

View File

@@ -1,6 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public abstract class TwoOperandMnemonic extends Mnemonic {
public String src;
public String dst;
}

View File

@@ -1,17 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Ucomi extends TwoOperandMnemonic {
public String dataType;
public Ucomi(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
@Override
public String toAsm() {
return "ucomi" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -1,17 +0,0 @@
package de.hsrm.compiler.Klang.asm.mnemonics;
public class Xor extends TwoOperandMnemonic {
public String dataType;
public Xor(String dataType, String src, String dst) {
this.dataType = dataType;
this.src = src;
this.dst = dst;
}
@Override
public String toAsm() {
return "xor" + this.dataType + " " + this.src + ", " + this.dst;
}
}

View File

@@ -0,0 +1,19 @@
package de.hsrm.compiler.Klang.helper;
import java.util.Map;
import de.hsrm.compiler.Klang.types.Type;
public class FunctionInformation {
public String name;
public Type returnType;
public Map<String, Type> parameters;
public Type[] signature;
public FunctionInformation(String name, Type returnType, Map<String,Type> parameters, Type[] signature) {
this.name = name;
this.returnType = returnType;
this.parameters = parameters;
this.signature = signature;
}
}

View File

@@ -1,13 +1,14 @@
package de.hsrm.compiler.Klang.nodes;
import de.hsrm.compiler.Klang.nodes.statements.Statement;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class Block extends Node {
public Node[] statementsOrFunctionCalls;
public Statement[] statements;
public Block(Node[] statements) {
this.statementsOrFunctionCalls = statements;
public Block(Statement[] statements) {
this.statements = statements;
}
@Override

View File

@@ -1,19 +0,0 @@
package de.hsrm.compiler.Klang.nodes;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class EnumDefinition extends Node {
public String name;
public EnumValue[] enums;
public EnumDefinition(String name, EnumValue[] enums) {
this.name = name;
this.enums = enums;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -1,18 +0,0 @@
package de.hsrm.compiler.Klang.nodes;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class EnumValue extends Node {
public String value;
public int index;
public EnumValue(String value, int index) {
this.value = value;
this.index = index;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -1,20 +1,17 @@
package de.hsrm.compiler.Klang.nodes;
import de.hsrm.compiler.Klang.nodes.statements.VariableDeclaration;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class FunctionDefinition extends Node {
public String name;
public Parameter[] parameters;
public VariableDeclaration[] localVariables;
public Block block;
public FunctionDefinition(String name, Parameter[] parameters, VariableDeclaration[] localVariables, Block block) {
public FunctionDefinition(String name, Parameter[] parameters, Block block) {
this.name = name;
this.parameters = parameters;
this.block = block;
this.localVariables = localVariables;
}
@Override

View File

@@ -9,18 +9,11 @@ public class Program extends Node {
public FunctionDefinition[] funcs;
public Map<String, StructDefinition> structs;
public Map<String, EnumDefinition> enums;
public Expression expression;
public Program(
FunctionDefinition[] funcs,
Map<String, StructDefinition> structs,
Map<String, EnumDefinition> enums,
Expression expression
) {
public Program(FunctionDefinition[] funcs, Map<String, StructDefinition> structs, Expression expression) {
this.funcs = funcs;
this.structs = structs;
this.enums = enums;
this.expression = expression;
}

View File

@@ -1,16 +0,0 @@
package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class CharExpression extends Expression {
public char c;
public CharExpression(char c) {
this.c = c;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -1,26 +0,0 @@
package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.EnumValue;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class EnumAccessExpression extends Expression {
public String enumName;
public String enumValueName;
public EnumValue enumValue;
public EnumAccessExpression(
String enumName,
String enumValueName,
EnumValue enumValue
) {
this.enumName = enumName;
this.enumValueName = enumValueName;
this.enumValue = enumValue;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

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

@@ -2,12 +2,12 @@ package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class MemberAccessExpression extends Expression {
public class StructFieldAccessExpression extends Expression {
public String varName;
public String structName;
public String[] path;
public MemberAccessExpression(String varName, String structName, String[] path) {
public StructFieldAccessExpression(String varName, String structName, String[] path) {
this.varName = varName;
this.structName = structName;
this.path = path;

View File

@@ -1,7 +1,5 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class BooleanType extends PrimitiveType {
private static BooleanType instance = null;
@@ -32,11 +30,7 @@ public class BooleanType extends PrimitiveType {
}
// Every remaining type will throw a RuntimeException
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asBoolean() == b.asBoolean();
}
}

View File

@@ -1,47 +0,0 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class CharType extends PrimitiveType {
private static CharType instance = null;
public static CharType getType() {
if (instance != null) {
return instance;
}
instance = new CharType();
return instance;
}
@Override
public String getName() {
return "char";
}
@Override
public Type combine(Type that) {
if (this.equals(that)) {
return this;
}
if (that.equals(Type.getIntegerType())) {
return this;
}
throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return ((int) a.asChar()) == ((int) b.asChar());
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
return that instanceof CharType;
}
}

View File

@@ -1,7 +1,5 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class FloatType extends NumericType {
private static FloatType instance = null;
@@ -36,11 +34,7 @@ public class FloatType extends NumericType {
}
// Every remaining type will throw a RuntimeException
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asFloat() == b.asFloat();
}
}

View File

@@ -1,7 +1,5 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class IntegerType extends NumericType {
private static IntegerType instance = null;
@@ -35,17 +33,8 @@ public class IntegerType extends NumericType {
return Type.getFloatType();
}
if (that.equals(Type.getCharType())) {
return Type.getCharType();
}
// Every remaining type will throw a RuntimeException
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asInteger() == b.asInteger();
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
}

View File

@@ -1,53 +0,0 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class NamedType extends Type {
public String name;
public NamedType(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public Type combine(Type that) {
if(this.equals(that) || (that instanceof NullType)) {
return this;
}
throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asObject().equals(b.asObject());
}
@Override
public boolean isPrimitiveType() {
return false;
}
@Override
public boolean isNumericType() {
return false;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that instanceof NamedType thatType) {
return getName().equals(thatType.getName());
}
return false;
}
}

View File

@@ -1,7 +1,5 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class NullType extends Type {
private static NullType instance = null;
@@ -23,18 +21,13 @@ public class NullType extends Type {
public Type combine(Type that) {
// You can not combine null with a primitive type
if (that.isPrimitiveType()) {
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
// Everything else combines with null to the type it was before
return that;
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asObject() == b.asObject();
}
@Override
public boolean isPrimitiveType() {
return false;

View File

@@ -0,0 +1,54 @@
package de.hsrm.compiler.Klang.types;
public class StructType extends Type {
public String name;
public StructType(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public Type combine(Type that) {
if (that.equals(this)) {
return this;
}
// If you combine a null type with a struct type, you
// always get the struct type back.
if (that == NullType.getType()) {
return this;
}
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean isPrimitiveType() {
return false;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that instanceof StructType) {
StructType thatType = (StructType) that;
return this.getName().equals(thatType.getName());
}
return false;
}
@Override
public boolean isNumericType() {
return false;
}
}

View File

@@ -1,12 +1,5 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import java.util.Map;
public abstract class Type {
// Returns an instance of IntegerType
@@ -23,10 +16,6 @@ public abstract class Type {
return FloatType.getType();
}
public static CharType getCharType() {
return CharType.getType();
}
public static NullType getNullType() {
return NullType.getType();
}
@@ -40,16 +29,14 @@ public abstract class Type {
case "bool": return getBooleanType();
case "int": return getIntegerType();
case "float": return getFloatType();
case "char": return getCharType();
case "null": return getNullType();
case "void": return getVoidType();
default: return new NamedType(name);
default: return new StructType(name);
}
}
public abstract String getName();
public abstract Type combine(Type that);
public abstract boolean valuesEqual(Value a, Value b);
public abstract boolean isPrimitiveType();
public abstract boolean isNumericType();
}

View File

@@ -1,7 +1,5 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class VoidType extends Type {
private static VoidType instance;
@@ -24,12 +22,7 @@ public class VoidType extends Type {
if (that.equals(this)) {
return this;
}
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
throw new RuntimeException("Can not compare void types.");
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override

View File

@@ -4,13 +4,17 @@ import java.util.HashMap;
import java.util.Map;
import de.hsrm.compiler.Klang.Value;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.Block;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.Parameter;
import de.hsrm.compiler.Klang.nodes.Program;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
import de.hsrm.compiler.Klang.nodes.statements.*;
import de.hsrm.compiler.Klang.types.NullType;
import de.hsrm.compiler.Klang.types.Type;
public class EvalVisitor implements Visitor<Value> {
@@ -45,18 +49,27 @@ public class EvalVisitor implements Visitor<Value> {
return result;
}
@Override
public Value visit(CharExpression e) {
return new Value(e.c, Type.getCharType());
}
@Override
public Value visit(EqualityExpression e) {
var lhs = e.lhs.welcome(this);
var rhs = e.rhs.welcome(this);
var combinedType = lhs.type.combine(rhs.type);
Value lhs = e.lhs.welcome(this);
Value rhs = e.rhs.welcome(this);
Type resultType = Type.getBooleanType();
Type combineType = lhs.type.combine(rhs.type);
return new Value(combinedType.valuesEqual(lhs, rhs), Type.getBooleanType());
switch(combineType.getName()) {
case "bool": {
return new Value(lhs.asBoolean() == rhs.asBoolean(), resultType);
}
case "int": {
return new Value(lhs.asInteger() == rhs.asInteger(), resultType);
}
case "float": {
return new Value(lhs.asFloat() == rhs.asFloat(), resultType);
}
default: {
return new Value(lhs.asObject() == rhs.asObject(), resultType);
}
}
}
@Override
@@ -408,8 +421,8 @@ public class EvalVisitor implements Visitor<Value> {
@Override
public Value visit(Block e) {
for (var stmt : e.statementsOrFunctionCalls) {
var result = stmt.welcome(this);
for (var stmt : e.statements) {
Value result = stmt.welcome(this);
if (result != null) {
return result;
}
@@ -437,7 +450,7 @@ public class EvalVisitor implements Visitor<Value> {
this.env = newEnv;
// Execute
var result = func.block.welcome(this);
Value result = func.block.welcome(this);
// Das alte env wiederherstellen
this.env = oldEnv;
@@ -461,16 +474,6 @@ public class EvalVisitor implements Visitor<Value> {
return null;
}
@Override
public Value visit(EnumDefinition e) {
return null;
}
@Override
public Value visit(EnumValue e) {
return null;
}
@Override
public Value visit(StructDefinition e) {
// We get these from a previous visitor
@@ -484,7 +487,7 @@ public class EvalVisitor implements Visitor<Value> {
}
@Override
public Value visit(MemberAccessExpression e) {
public Value visit(StructFieldAccessExpression e) {
Value var = this.env.get(e.varName);
Map<String, Value> struct = var.asStruct();
@@ -496,11 +499,6 @@ public class EvalVisitor implements Visitor<Value> {
return currentValue;
}
@Override
public Value visit(EnumAccessExpression e) {
return new Value(e.enumValueName, e.type);
}
@Override
public Value visit(ConstructorCall e) {
StructDefinition structDef = this.structs.get(e.structName);
@@ -511,17 +509,12 @@ public class EvalVisitor implements Visitor<Value> {
struct.put(structDef.fields[i].name, arg);
}
var structValue = new Value(struct);
structValue.type = structDef.type;
return structValue;
return new Value(struct);
}
@Override
public Value visit(NullExpression e) {
var nullValue = new Value(null);
nullValue.type = new NullType();
return nullValue;
return null;
}
@Override

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,273 @@
package de.hsrm.compiler.Klang.visitors;
import java.util.Map;
import java.util.Set;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
import de.hsrm.compiler.Klang.nodes.statements.*;
import de.hsrm.compiler.Klang.types.Type;
class GetVars implements Visitor<Void> {
public Set<String> vars;
public Map<String, Type> types;
public GetVars(Set<String> vars, Map<String, Type> types) {
this.vars = vars;
this.types = types;
}
@Override
public Void visit(IntegerExpression e) {
return null;
}
@Override
public Void visit(FloatExpression e) {
return null;
}
@Override
public Void visit(BooleanExpression e) {
return null;
}
@Override
public Void visit(Variable e) {
return null;
}
@Override
public Void visit(EqualityExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(NotEqualityExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(GTExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(GTEExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(LTExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(LTEExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(AdditionExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(SubstractionExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(MultiplicationExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(DivisionExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(ModuloExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(NegateExpression e) {
e.lhs.welcome(this);
return null;
}
@Override
public Void visit(OrExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(AndExpression e) {
e.lhs.welcome(this);
e.rhs.welcome(this);
return null;
}
@Override
public Void visit(NotExpression e) {
e.lhs.welcome(this);
return null;
}
@Override
public Void visit(IfStatement e) {
e.cond.welcome(this);
e.then.welcome(this);
if (e.alt != null) {
e.alt.welcome(this);
} else if (e.elif != null) {
e.elif.welcome(this);
}
return null;
}
@Override
public Void visit(WhileLoop e) {
e.cond.welcome(this);
e.block.welcome(this);
return null;
}
@Override
public Void visit(DoWhileLoop e) {
e.cond.welcome(this);
e.block.welcome(this);
return null;
}
@Override
public Void visit(ForLoop e) {
e.init.welcome(this);
e.condition.welcome(this);
e.step.welcome(this);
e.block.welcome(this);
return null;
}
@Override
public Void visit(VariableDeclaration e) {
vars.add(e.name);
types.put(e.name, e.type);
return null;
}
@Override
public Void visit(VariableAssignment e) {
e.expression.welcome(this);
return null;
}
@Override
public Void visit(ReturnStatement e) {
if (e.expression != null) {
e.expression.welcome(this);
}
return null;
}
@Override
public Void visit(Block e) {
for (var statement : e.statements) {
statement.welcome(this);
}
return null;
}
@Override
public Void visit(FunctionDefinition e) {
e.block.welcome(this);
return null;
}
@Override
public Void visit(FunctionCall e) {
for (var expression : e.arguments) {
expression.welcome(this);
}
return null;
}
@Override
public Void visit(Program e) {
e.expression.welcome(this);
for (var func : e.funcs) {
func.welcome(this);
}
return null;
}
@Override
public Void visit(Parameter e) {
return null;
}
@Override
public Void visit(StructDefinition e) {
return null;
}
@Override
public Void visit(StructField e) {
return null;
}
@Override
public Void visit(StructFieldAccessExpression e) {
return null;
}
@Override
public Void visit(ConstructorCall e) {
return null;
}
@Override
public Void visit(NullExpression e) {
return null;
}
@Override
public Void visit(DestructorCall e) {
return null;
}
@Override
public Void visit(FieldAssignment e) {
return null;
}
}

View File

@@ -68,12 +68,6 @@ public class PrettyPrintVisitor implements Visitor<Void> {
ex.nl();
}
for (var enumDef: e.enums.values()) {
enumDef.welcome(this);
ex.nl();
ex.nl();
}
e.expression.welcome(this);
ex.write(";");
return null;
@@ -97,14 +91,6 @@ public class PrettyPrintVisitor implements Visitor<Void> {
return null;
}
@Override
public Void visit(CharExpression e) {
ex.write("'");
ex.write(e.c);
ex.write("'");
return null;
}
@Override
public Void visit(EqualityExpression e) {
ex.write("(");
@@ -331,7 +317,7 @@ public class PrettyPrintVisitor implements Visitor<Void> {
public Void visit(Block e) {
ex.write("{");
ex.addIndent();
for (var stmt : e.statementsOrFunctionCalls) {
for (Statement stmt : e.statements) {
ex.nl();
stmt.welcome(this);
if (stmt.getClass() == VariableAssignment.class || stmt.getClass() == VariableDeclaration.class) {
@@ -394,28 +380,6 @@ public class PrettyPrintVisitor implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumDefinition e) {
ex.write("enum " + e.name + " { ");
var first = true;
for(var enumValue: e.enums) {
if (!first) {
ex.write(", ");
} else {
first = false;
}
enumValue.welcome(this);
}
ex.write(" }");
return null;
}
@Override
public Void visit(EnumValue e) {
ex.write(e.value);
return null;
}
@Override
public Void visit(StructDefinition e) {
ex.write("struct " + e.name + " {");
@@ -437,7 +401,7 @@ public class PrettyPrintVisitor implements Visitor<Void> {
}
@Override
public Void visit(MemberAccessExpression e) {
public Void visit(StructFieldAccessExpression e) {
ex.write(e.varName);
for (int i = 0; i < e.path.length; i++) {
ex.write(".");
@@ -446,15 +410,6 @@ public class PrettyPrintVisitor implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumAccessExpression e) {
ex.write(e.enumName);
ex.write(".");
ex.write(e.enumValueName);
return null;
}
@Override
public Void visit(ConstructorCall e) {
ex.write("create " + e.structName + "(");

View File

@@ -1,6 +1,11 @@
package de.hsrm.compiler.Klang.visitors;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.Block;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.Parameter;
import de.hsrm.compiler.Klang.nodes.Program;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.*;
import de.hsrm.compiler.Klang.nodes.statements.*;
@@ -12,7 +17,6 @@ public interface Visitor<R> {
R visit(IntegerExpression e);
R visit(FloatExpression e);
R visit(BooleanExpression e);
R visit(CharExpression e);
R visit(Variable e);
R visit(AdditionExpression e);
R visit(EqualityExpression e);
@@ -38,12 +42,9 @@ public interface Visitor<R> {
R visit(FunctionCall e);
R visit(Program e);
R visit(Parameter e);
R visit(EnumDefinition e);
R visit(EnumValue e);
R visit(StructDefinition e);
R visit(StructField e);
R visit(MemberAccessExpression e);
R visit(EnumAccessExpression e);
R visit(StructFieldAccessExpression e);
R visit(ConstructorCall e);
R visit(NullExpression e);
R visit(DestructorCall e);

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,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 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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:30 && is only defined for bool.", e.getMessage());
}
}

View File

@@ -1,164 +0,0 @@
import de.hsrm.compiler.Klang.ContextAnalysis;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class CharTest {
@Test
void shouldNotThrowIfComparingCharsEquality() {
// given
var tree = Helper.prepareParser("""
function bar(): int {
let b: char = 'b';
if ('a' == b) {
return 1;
}
return -1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfComparingCharsInequality() {
// given
var tree = Helper.prepareParser("""
function bar(): int {
let b: char = 'b';
if ('a' > b) {
return 1;
}
return -1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfCharLiteralIsAssignedToCharVariable() {
// given
var tree = Helper.prepareParser("""
function bar(): int {
let c: char = 'c';
return 1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfCharLiteralIsReturnedFromFunction() {
// given
var tree = Helper.prepareParser("""
function bar(): char {
return 'c';
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfCharIsUsedInStruct() {
// given
var tree = Helper.prepareParser("""
struct myChar { c: char; }
function bar(): myChar {
let x: myChar = create myChar('c');
return x;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldThrowIfCharIsAssignedToOtherTypedVariable() {
// given
var tree = Helper.prepareParser("""
struct foo { f: float; }
function bar(): int {
let c: foo = 'c';
return 1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 3:4 Type mismatch: cannot combine foo and char", e.getMessage());
}
@Test
void shouldThrowWhenReturningCharFromOtherTypedFunction() {
// given
var tree = Helper.prepareParser("""
function bar(): float {
return 'c';
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 2:4 Type mismatch: cannot combine float and char", e.getMessage());
}
@Test
void shouldThrowPassingCharToOtherTypedFunctionParameter() {
// given
var tree = Helper.prepareParser("""
function foo(a: float): void {
let x: bool = false;
}
function bar(): void {
foo('c');
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 5:4 argument 0 Expected float but got: char", e.getMessage());
}
@Test
void shouldThrowWhenAssigningCharToOtherTypedStructField() {
// given
var tree = Helper.prepareParser("""
struct foo { a: bool; }
function bar(): void {
let x: foo = create foo(false);
x.a = 'c';
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 4:4 Type mismatch: cannot combine bool and char", e.getMessage());
}
}

View File

@@ -1,46 +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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:52 Struct with name \"schwurbel\" not defined.", e.getMessage());
}
@Test
void numConstructorParametermismatch() {
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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:63 argument 0 Type mismatch: cannot combine bool and int", e.getMessage());
}
}

View File

@@ -1,22 +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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:45 Variable or parameter with name \"x\" not defined.", e.getMessage());
}
}

View File

@@ -1,34 +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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:46 Variable or parameter 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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:62 Variable or parameter must reference a struct but references int.", e.getMessage());
}
}

View File

@@ -1,46 +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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
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);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
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 parameterTypeMismatch() {
ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
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,119 +0,0 @@
import de.hsrm.compiler.Klang.ContextAnalysis;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class FunctionDefinitionTest {
@Test
void shouldNotThrowIfReturnTypeIsReferringToAnEnum() {
// given
var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo(1);");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfParameterTypeIsReferringToAnEnum() {
// given
var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: bar): int { return 1; } foo(bar.A);");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldThrowExceptionIfParameterTypeIsNotDefined() {
// given
var tree = Helper.prepareParser("function foo(a: schwurbel): int { return 1; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage());
}
@Test
void shouldThrowExceptionIfReturnTypeIsNotDefined() {
// given
var tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage());
}
@Test
void shouldThrowExceptionIfReturnStatementIsMissing() {
// given
var tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:20 Function foo has to return something of type int.", e.getMessage());
}
@Test
void shouldThrowExceptionIfParameterNameMatchesEnumName() {
// given
var tree = Helper.prepareParser("enum Bar { A, B } function foo(Bar: int): int { return 1; } foo(1);");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:31 Parameter name Bar duplicates an enum of the same name.", e.getMessage());
}
@Test
void shouldResetVariablesAndParameterEnvironment() {
// given
var tree = Helper.prepareParser("""
function foo(x: int): int {
return 1;
}
function bar(): int {
let x: int = 0;
return x;
}
bar();
""");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
}

Some files were not shown because too many files have changed in this diff Show More