Compare commits
63 Commits
af1021ed66
...
7965c89a60
| Author | SHA1 | Date | |
|---|---|---|---|
| 7965c89a60 | |||
| 26eff47057 | |||
|
|
53976615e1 | ||
| c124587983 | |||
| 07e5a338a4 | |||
| 76419d86bb | |||
| f55f2661de | |||
| 06609ae899 | |||
| c5c01041e4 | |||
| 9751a1da2f | |||
| 534b507f7a | |||
| cce58b6e38 | |||
| 8b17ced533 | |||
| bacc40d844 | |||
| f38bd3d69e | |||
| 441d0122f8 | |||
| e835bd0f06 | |||
| ea1c04ae0a | |||
| 198bd74a47 | |||
| 0594542167 | |||
| 77fe360ffa | |||
| 55a5b8f54a | |||
| 2768b4429c | |||
| 30dfbbbbba | |||
| f77d6a002d | |||
| 22634c9652 | |||
| 6fd3f5a2e6 | |||
| 3b928d621b | |||
| 9a58afb550 | |||
| 6e4431652c | |||
| 7af815042b | |||
| 7c40a50196 | |||
|
|
8529e24a37 | ||
|
|
49b024b95f | ||
|
|
982fc6417d | ||
|
|
bd173b1d45 | ||
| 221b928d0e | |||
|
|
8dd0b6cffa | ||
|
|
f288d5585f | ||
|
|
e05ca07d23 | ||
| fd17a25f29 | |||
|
|
500cfaffbe | ||
| 5a5191612e | |||
|
|
36a38ee7ab | ||
|
|
c38a330fda | ||
| ac870460e6 | |||
| da56e1c05e | |||
| ba17c7e2b6 | |||
| d6c0131d8f | |||
| d90581f0cd | |||
| 6714d2136d | |||
| fe9c9b79b8 | |||
| 89ec828499 | |||
| 9df0da89ff | |||
| 649e690ac4 | |||
| 6d60dcc4a3 | |||
| 35de3c7de4 | |||
| 5701d3e918 | |||
| 704e6441ca | |||
| cb5ceafbbc | |||
| d96b083c41 | |||
| acaa37b3b1 | |||
| d1cf626934 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,4 +16,5 @@ src/main/antlr4/de/hsrm/compiler/Klang/.antlr
|
|||||||
|
|
||||||
# build output
|
# build output
|
||||||
out
|
out
|
||||||
src/test/test
|
src/test/test
|
||||||
|
/.idea/uiDesigner.xml
|
||||||
|
|||||||
@@ -9,7 +9,18 @@ build:
|
|||||||
script:
|
script:
|
||||||
- mvn package
|
- mvn package
|
||||||
|
|
||||||
test:
|
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:
|
||||||
image: ubuntu:rolling
|
image: ubuntu:rolling
|
||||||
stage: test
|
stage: test
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# 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
Normal file
16
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?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
Normal file
8
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
20
.idea/jarRepositories.xml
generated
Normal file
20
.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?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>
|
||||||
13
.idea/libraries/Maven__org_antlr_antlr4_runtime_4_7_2.xml
generated
Normal file
13
.idea/libraries/Maven__org_antlr_antlr4_runtime_4_7_2.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<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>
|
||||||
13
.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml
generated
Normal file
13
.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<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>
|
||||||
13
.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_0.xml
generated
Normal file
13
.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_0.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<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>
|
||||||
13
.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_0.xml
generated
Normal file
13
.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_0.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<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>
|
||||||
13
.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_0.xml
generated
Normal file
13
.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_0.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<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>
|
||||||
13
.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_0.xml
generated
Normal file
13
.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_0.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<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>
|
||||||
13
.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml
generated
Normal file
13
.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<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
Normal file
28
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?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
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?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
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
.mvn/jvm.config
Normal file
1
.mvn/jvm.config
Normal file
@@ -0,0 +1 @@
|
|||||||
|
--add-opens java.base/java.lang=ALL-UNNAMED
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
encoding//src/main/java=UTF-8
|
encoding//src/main/java=UTF-8
|
||||||
|
encoding//src/test/java=UTF-8
|
||||||
encoding//target/generated-sources/antlr4=UTF-8
|
encoding//target/generated-sources/antlr4=UTF-8
|
||||||
encoding/<project>=UTF-8
|
encoding/<project>=UTF-8
|
||||||
|
|||||||
98
README.md
98
README.md
@@ -5,11 +5,10 @@ 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.
|
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
|
# 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 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:
|
Arguments:
|
||||||
|
- -out <file> File to write to
|
||||||
- -h Print this help
|
- -h Print this help
|
||||||
- --evaluate: Evaluates the given source code
|
- --evaluate: Evaluates the given source code
|
||||||
- --pretty: Pretty print the given source code
|
- --pretty: Pretty print the given source code
|
||||||
@@ -23,8 +22,47 @@ The makefile can be used to perform various functions more easily:
|
|||||||
- `make pretty` prettifies code.k and writes to pretty.k
|
- `make pretty` prettifies code.k and writes to pretty.k
|
||||||
- `make eval` evaluates code.k
|
- `make eval` evaluates code.k
|
||||||
- `make test` runs tests from src/test/
|
- `make test` runs tests from src/test/
|
||||||
|
- `make testJava` runs JUnit tests
|
||||||
- `make cleanTests` cleans files generated from 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
|
# Functionality
|
||||||
The KLang compiler supports generation of AMD64 assembly code, as well as prettifying and evaluating the KLang code.
|
The KLang compiler supports generation of AMD64 assembly code, as well as prettifying and evaluating the KLang code.
|
||||||
|
|
||||||
@@ -44,13 +82,13 @@ The following simple expressions are supported. Expressions need to be put in pa
|
|||||||
|
|
||||||
### Examples:
|
### Examples:
|
||||||
```
|
```
|
||||||
(5 + 4)
|
5 + 4
|
||||||
(8 % 2)
|
8 % 2
|
||||||
(8 == 0)
|
8 == 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
Functions can be defined and called. A function call can be used like any other expression. Recursion is supported
|
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
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
```
|
```
|
||||||
@@ -61,6 +99,46 @@ function fun(x: int, y: int, z: bool): int {
|
|||||||
fun(1, 2, 3);
|
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
|
## Statements
|
||||||
Several statements are supported:
|
Several statements are supported:
|
||||||
- if
|
- if
|
||||||
@@ -112,6 +190,9 @@ function forExample(end: int): int {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Tail Call Optimized
|
||||||
|
Recursive tail calls are optimized at compile time.
|
||||||
|
|
||||||
## Statically typed
|
## Statically typed
|
||||||
KLang statically verifies the integrity of your code. These checks include:
|
KLang statically verifies the integrity of your code. These checks include:
|
||||||
- Type checking
|
- Type checking
|
||||||
@@ -120,9 +201,10 @@ KLang statically verifies the integrity of your code. These checks include:
|
|||||||
- Ensuring that a function returns something
|
- Ensuring that a function returns something
|
||||||
- Ensuring that a function only returns data of the correct type
|
- Ensuring that a function only returns data of the correct type
|
||||||
|
|
||||||
### Data Types
|
### Primitive Data Types
|
||||||
- Integer "int"
|
- Integer "int"
|
||||||
- Boolean "bool"
|
- Boolean "bool"
|
||||||
|
- Floats "float"
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
You can declare types for parameters, return values and variables
|
You can declare types for parameters, return values and variables
|
||||||
|
|||||||
22
klang.iml
Normal file
22
klang.iml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?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>
|
||||||
5
makefile
5
makefile
@@ -13,10 +13,13 @@ eval: code.k target/klang-1.0-jar-with-dependencies.jar
|
|||||||
build: clean 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:
|
target/klang-1.0-jar-with-dependencies.jar:
|
||||||
mvn package
|
mvn -Dmaven.test.skip=true package
|
||||||
|
|
||||||
test: ./src/test/test
|
test: ./src/test/test
|
||||||
./src/test/test
|
./src/test/test
|
||||||
|
|
||||||
|
testJava:
|
||||||
|
mvn test
|
||||||
|
|
||||||
./src/test/test: ./src/test/test.s
|
./src/test/test: ./src/test/test.s
|
||||||
gcc -o ./src/test/test ./src/test/test.s ./src/test/**/*.c ./src/test/test.c
|
gcc -o ./src/test/test ./src/test/test.s ./src/test/**/*.c ./src/test/test.c
|
||||||
|
|||||||
28
pom.xml
28
pom.xml
@@ -1,5 +1,4 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<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">
|
||||||
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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>de.hsrm.compiler</groupId>
|
<groupId>de.hsrm.compiler</groupId>
|
||||||
@@ -21,6 +20,16 @@
|
|||||||
<artifactId>antlr4-runtime</artifactId>
|
<artifactId>antlr4-runtime</artifactId>
|
||||||
<version>4.7.2</version>
|
<version>4.7.2</version>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -31,7 +40,7 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.0</version>
|
<version>3.8.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>11</release>
|
<release>17</release>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- Plugin to compile the g4 files ahead of the java files
|
<!-- Plugin to compile the g4 files ahead of the java files
|
||||||
@@ -63,6 +72,11 @@
|
|||||||
<descriptorRefs>
|
<descriptorRefs>
|
||||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
</descriptorRefs>
|
</descriptorRefs>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>de.hsrm.compiler.Klang.Klang</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
@@ -74,6 +88,14 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</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>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
@@ -5,7 +5,11 @@ parse
|
|||||||
;
|
;
|
||||||
|
|
||||||
program
|
program
|
||||||
: (functionDef | structDef)* expression SCOL
|
: (functionDef | structDef | enumDef)* expression SCOL
|
||||||
|
;
|
||||||
|
|
||||||
|
enumDef
|
||||||
|
: ENUM enumName=IDENT OBRK (IDENT (COMMA IDENT)*)+ CBRK
|
||||||
;
|
;
|
||||||
|
|
||||||
structDef
|
structDef
|
||||||
@@ -29,7 +33,7 @@ parameter
|
|||||||
;
|
;
|
||||||
|
|
||||||
braced_block
|
braced_block
|
||||||
: OBRK statement+ CBRK
|
: OBRK (statement | functionCall SCOL)+ CBRK
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +73,7 @@ field_assignment
|
|||||||
;
|
;
|
||||||
|
|
||||||
return_statement
|
return_statement
|
||||||
: RETURN expression SCOL
|
: RETURN expression? SCOL
|
||||||
;
|
;
|
||||||
|
|
||||||
destroy_statement
|
destroy_statement
|
||||||
@@ -78,7 +82,7 @@ destroy_statement
|
|||||||
|
|
||||||
expression
|
expression
|
||||||
: atom #atomExpression
|
: atom #atomExpression
|
||||||
| IDENT (PERIOD IDENT)+ #structFieldAccessExpression
|
| IDENT (PERIOD IDENT)+ #memberAccessExpression
|
||||||
| OPAR expression CPAR #parenthesisExpression
|
| OPAR expression CPAR #parenthesisExpression
|
||||||
| lhs=expression MUL rhs=expression #multiplicationExpression
|
| lhs=expression MUL rhs=expression #multiplicationExpression
|
||||||
| lhs=expression DIV rhs=expression #divisionExpression
|
| lhs=expression DIV rhs=expression #divisionExpression
|
||||||
@@ -116,6 +120,7 @@ type
|
|||||||
| BOOLEAN
|
| BOOLEAN
|
||||||
| FLOAT
|
| FLOAT
|
||||||
| IDENT
|
| IDENT
|
||||||
|
| VOID
|
||||||
;
|
;
|
||||||
|
|
||||||
functionCall
|
functionCall
|
||||||
@@ -143,6 +148,7 @@ forLoop
|
|||||||
IF: 'if';
|
IF: 'if';
|
||||||
ELSE: 'else';
|
ELSE: 'else';
|
||||||
FUNC: 'function';
|
FUNC: 'function';
|
||||||
|
ENUM: 'enum';
|
||||||
STRUCT: 'struct';
|
STRUCT: 'struct';
|
||||||
RETURN: 'return';
|
RETURN: 'return';
|
||||||
LET: 'let';
|
LET: 'let';
|
||||||
@@ -181,6 +187,7 @@ DIV: '/';
|
|||||||
BOOLEAN: 'bool';
|
BOOLEAN: 'bool';
|
||||||
INTEGER: 'int';
|
INTEGER: 'int';
|
||||||
FLOAT: 'float';
|
FLOAT: 'float';
|
||||||
|
VOID: 'void';
|
||||||
|
|
||||||
INTEGER_LITERAL
|
INTEGER_LITERAL
|
||||||
: [0-9]+
|
: [0-9]+
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package de.hsrm.compiler.Klang;
|
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.helper.Helper;
|
||||||
import de.hsrm.compiler.Klang.nodes.*;
|
import de.hsrm.compiler.Klang.nodes.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.expressions.*;
|
import de.hsrm.compiler.Klang.nodes.expressions.*;
|
||||||
@@ -12,12 +8,18 @@ import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
|
|||||||
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
|
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
|
||||||
import de.hsrm.compiler.Klang.nodes.statements.*;
|
import de.hsrm.compiler.Klang.nodes.statements.*;
|
||||||
import de.hsrm.compiler.Klang.types.Type;
|
import de.hsrm.compiler.Klang.types.Type;
|
||||||
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||||
|
Map<String, VariableDeclaration> params = new HashMap<>();
|
||||||
Map<String, VariableDeclaration> vars = new HashMap<>();
|
Map<String, VariableDeclaration> vars = new HashMap<>();
|
||||||
Map<String, FunctionInformation> funcs;
|
Map<String, FunctionDefinition> functionDefs;
|
||||||
Map<String, StructDefinition> structs;
|
Map<String, StructDefinition> structDefs;
|
||||||
|
Map<String, EnumDefinition> enumDefs;
|
||||||
Type currentDeclaredReturnType;
|
Type currentDeclaredReturnType;
|
||||||
|
String currentFunctionDefinitionName;
|
||||||
|
|
||||||
private void checkNumeric(Node lhs, Node rhs, int line, int col) {
|
private void checkNumeric(Node lhs, Node rhs, int line, int col) {
|
||||||
if (!lhs.type.isNumericType() || !rhs.type.isNumericType()) {
|
if (!lhs.type.isNumericType() || !rhs.type.isNumericType()) {
|
||||||
@@ -26,25 +28,35 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContextAnalysis(Map<String, FunctionInformation> funcs, Map<String, StructDefinition> structs) {
|
public ContextAnalysis(
|
||||||
this.funcs = funcs;
|
Map<String, FunctionDefinition> functionDefs,
|
||||||
this.structs = structs;
|
Map<String, StructDefinition> structDefs,
|
||||||
|
Map<String, EnumDefinition> enumDefs
|
||||||
|
) {
|
||||||
|
this.functionDefs = functionDefs;
|
||||||
|
this.structDefs = structDefs;
|
||||||
|
this.enumDefs = enumDefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitProgram(KlangParser.ProgramContext ctx) {
|
public Node visitProgram(KlangParser.ProgramContext ctx) {
|
||||||
FunctionDefinition[] funcs = new FunctionDefinition[ctx.functionDef().size()];
|
var typeCheckedFunctionDefs = new FunctionDefinition[ctx.functionDef().size()];
|
||||||
|
var typeCheckedStructDefs = new HashMap<String, StructDefinition>();
|
||||||
|
|
||||||
for (int i = 0; i < ctx.functionDef().size(); i++) {
|
for (int i = 0; i < ctx.functionDef().size(); i++) {
|
||||||
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
|
typeCheckedFunctionDefs[i] = (FunctionDefinition) visit(ctx.functionDef(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression expression = (Expression) this.visit(ctx.expression());
|
for (int i = 0; i < ctx.structDef().size(); i++) {
|
||||||
Program result = new Program(funcs, this.structs, expression);
|
typeCheckedStructDefs.put(ctx.structDef(i).structName.getText(), (StructDefinition) visit(ctx.structDef(i)));
|
||||||
result.type = expression.type;
|
}
|
||||||
result.line = ctx.start.getLine();
|
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
var expression = (Expression) visit(ctx.expression());
|
||||||
return result;
|
var program = new Program(typeCheckedFunctionDefs, typeCheckedStructDefs, enumDefs, expression);
|
||||||
|
program.type = expression.type;
|
||||||
|
program.line = ctx.start.getLine();
|
||||||
|
program.col = ctx.start.getCharPositionInLine();
|
||||||
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,22 +68,22 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
|
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
|
||||||
int actualStatementCount = 0;
|
var actualStatementCount = 0;
|
||||||
int declaredStatementCount = ctx.statement().size();
|
var declaredStatementCount = ctx.statement().size();
|
||||||
boolean hasReturn = false;
|
var hasReturn = false;
|
||||||
Statement[] statements = new Statement[declaredStatementCount];
|
var statements = new Statement[declaredStatementCount];
|
||||||
|
|
||||||
for (int i = 0; i < declaredStatementCount; i++) {
|
for (int i = 0; i < declaredStatementCount; i++) {
|
||||||
Node currentStatement = this.visit(ctx.statement(i));
|
var currentStatement = visit(ctx.statement(i));
|
||||||
statements[i] = (Statement) currentStatement;
|
statements[i] = (Statement) currentStatement;
|
||||||
actualStatementCount += 1;
|
actualStatementCount += 1;
|
||||||
|
|
||||||
// We use the existance of a type to indicate that this statement returns
|
// We use the existence of a type to indicate that this statement returns
|
||||||
// something for which the VariableDeclaration is an exception
|
// something for which the VariableDeclaration is an exception
|
||||||
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
|
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
|
||||||
// check whether the type matches
|
// check whether the type matches
|
||||||
try {
|
try {
|
||||||
this.currentDeclaredReturnType.combine(currentStatement.type);
|
currentDeclaredReturnType.combine(currentStatement.type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
|
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
|
||||||
@@ -87,16 +99,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
// If there was unreachable code in this block,
|
// If there was unreachable code in this block,
|
||||||
// create a shorter statements array and copy the statements to there
|
// create a shorter statements array and copy the statements to there
|
||||||
if (actualStatementCount < declaredStatementCount) {
|
if (actualStatementCount < declaredStatementCount) {
|
||||||
Statement[] newStatements = new Statement[actualStatementCount];
|
var newStatements = new Statement[actualStatementCount];
|
||||||
for (int i = 0; i < actualStatementCount; i++) {
|
System.arraycopy(statements, 0, newStatements, 0, actualStatementCount);
|
||||||
newStatements[i] = statements[i];
|
|
||||||
}
|
|
||||||
statements = newStatements;
|
statements = newStatements;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this block contains at least one statement that guarantees a return value,
|
// 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
|
// we indicate that this block guarantees a return value by setting result.type
|
||||||
Block result = new Block(statements);
|
var result = new Block(statements);
|
||||||
|
|
||||||
if (hasReturn) {
|
if (hasReturn) {
|
||||||
result.type = this.currentDeclaredReturnType;
|
result.type = this.currentDeclaredReturnType;
|
||||||
}
|
}
|
||||||
@@ -149,8 +160,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitDoWhileLoop(KlangParser.DoWhileLoopContext ctx) {
|
public Node visitDoWhileLoop(KlangParser.DoWhileLoopContext ctx) {
|
||||||
Node condition = this.visit(ctx.cond);
|
|
||||||
Node block = this.visit(ctx.braced_block());
|
Node block = this.visit(ctx.braced_block());
|
||||||
|
Node condition = this.visit(ctx.cond);
|
||||||
Node result = new DoWhileLoop((Expression) condition, (Block) block);
|
Node result = new DoWhileLoop((Expression) condition, (Block) block);
|
||||||
result.line = ctx.start.getLine();
|
result.line = ctx.start.getLine();
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
result.col = ctx.start.getCharPositionInLine();
|
||||||
@@ -171,72 +182,89 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
|
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
var variableName = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var declaredType = Type.getByName(ctx.type_annotation().type().getText());
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var line = ctx.start.getLine();
|
||||||
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
if (!declaredType.isPrimitiveType() && this.structs.get(declaredType.getName()) == null) {
|
if (declaredType.equals(Type.getVoidType())) {
|
||||||
String error = "Type " + declaredType.getName() + " not defined.";
|
var error = "Type " + declaredType.getName() + " can not be used to declare variables.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.vars.get(name) != null) {
|
if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
|
||||||
String error = "Redeclaration of variable with name \"" + name + "\".";
|
var 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 + "\".";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the appropriate instance
|
// Create the appropriate instance
|
||||||
VariableDeclaration result;
|
VariableDeclaration variableDeclaration;
|
||||||
if (ctx.expression() != null) {
|
if (ctx.expression() != null) {
|
||||||
Node expression = this.visit(ctx.expression());
|
var expression = visit(ctx.expression());
|
||||||
try {
|
try {
|
||||||
declaredType.combine(expression.type);
|
declaredType.combine(expression.type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||||
}
|
}
|
||||||
result = new VariableDeclaration(name, (Expression) expression);
|
variableDeclaration = new VariableDeclaration(variableName, (Expression) expression);
|
||||||
result.initialized = true;
|
variableDeclaration.initialized = true;
|
||||||
} else {
|
} else {
|
||||||
result = new VariableDeclaration(name);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add it to the global map of variable declarations
|
// Add it to the global map of variable declarations
|
||||||
this.vars.put(name, result);
|
vars.put(variableName, variableDeclaration);
|
||||||
|
|
||||||
result.line = line;
|
variableDeclaration.line = line;
|
||||||
result.col = col;
|
variableDeclaration.col = col;
|
||||||
result.type = declaredType;
|
variableDeclaration.type = declaredType;
|
||||||
return result;
|
|
||||||
|
return variableDeclaration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) {
|
public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
var name = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
VariableDeclaration var = this.vars.get(name);
|
var variableOrParameter = getVariableOrParameter(name, line, col);
|
||||||
if (var == null) {
|
|
||||||
String error = "Variable with name \"" + name + "\" not defined.";
|
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the expression
|
// Evaluate the expression
|
||||||
Expression expression = (Expression) this.visit(ctx.expression());
|
var expression = (Expression) visit(ctx.expression());
|
||||||
|
|
||||||
// Make sure expression can be assigned to the variable
|
// Make sure expression can be assigned to the variable
|
||||||
try {
|
try {
|
||||||
expression.type.combine(var.type);
|
expression.type.combine(variableOrParameter.type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since we assigned a value to this variable, we can consider it initialized
|
// Since we assigned a value to this variable, we can consider it initialized
|
||||||
var.initialized = true;
|
variableOrParameter.initialized = true;
|
||||||
|
|
||||||
// Create a new node and add the type of the expression to it
|
// Create a new node and add the type of the expression to it
|
||||||
Node result = new VariableAssignment(name, expression);
|
var result = new VariableAssignment(name, expression);
|
||||||
result.line = line;
|
result.line = line;
|
||||||
result.col = col;
|
result.col = col;
|
||||||
return result;
|
return result;
|
||||||
@@ -244,99 +272,102 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
|
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
|
||||||
Expression expression = (Expression) this.visit(ctx.expression());
|
if (currentDeclaredReturnType.equals(Type.getVoidType())) {
|
||||||
ReturnStatement result = new ReturnStatement(expression);
|
var result = new ReturnStatement();
|
||||||
result.type = expression.type;
|
result.type = Type.getVoidType();
|
||||||
|
result.line = ctx.start.getLine();
|
||||||
|
result.col = ctx.start.getCharPositionInLine();
|
||||||
|
if (ctx.expression() != null) {
|
||||||
|
var 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);
|
||||||
result.line = ctx.start.getLine();
|
result.line = ctx.start.getLine();
|
||||||
|
result.type = expression.type;
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
result.col = ctx.start.getCharPositionInLine();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitField_assignment(KlangParser.Field_assignmentContext ctx) {
|
public Node visitField_assignment(KlangParser.Field_assignmentContext ctx) {
|
||||||
String varName = ctx.IDENT(0).getText();
|
var varName = ctx.IDENT(0).getText();
|
||||||
int line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var col = ctx.start.getCharPositionInLine();
|
||||||
String[] path = new String[ctx.IDENT().size() - 1];
|
var path = createStructPath(ctx.IDENT());
|
||||||
|
|
||||||
for (int i = 1; i < ctx.IDENT().size(); i++) {
|
var variableOrParameter = getVariableOrParameter(varName, line, col);
|
||||||
path[i - 1] = ctx.IDENT(i).getText();
|
ensureReferencesStruct(variableOrParameter, line, col);
|
||||||
}
|
var fieldType = drillType(variableOrParameter, path, line, col);
|
||||||
|
|
||||||
// Get the referenced variable, make sure it is defined
|
// Visit the expression and make sure the type combines properly
|
||||||
var variableDef = this.vars.get(varName);
|
var expression = (Expression) visit(ctx.expression());
|
||||||
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 {
|
try {
|
||||||
fieldType.combine(expression.type);
|
fieldType.combine(expression.type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Node result = new FieldAssignment(varName, structName, path, expression);
|
var result = new FieldAssignment(varName, variableOrParameter.type.getName(), path, expression);
|
||||||
result.col = col;
|
result.col = col;
|
||||||
result.line = line;
|
result.line = line;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitStructFieldAccessExpression(KlangParser.StructFieldAccessExpressionContext ctx) {
|
public Node visitMemberAccessExpression(KlangParser.MemberAccessExpressionContext ctx) {
|
||||||
String varName = ctx.IDENT(0).getText();
|
var baseName = ctx.IDENT(0).getText();
|
||||||
int line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var col = ctx.start.getCharPositionInLine();
|
||||||
String[] path = new String[ctx.IDENT().size() - 1];
|
var path = createStructPath(ctx.IDENT());
|
||||||
|
|
||||||
for (int i = 1; i < ctx.IDENT().size(); i++) {
|
// Determine if the base name points to an enum or a variable
|
||||||
path[i - 1] = ctx.IDENT(i).getText();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the referenced variable, make sure it is defined
|
var variableOrParameter = getVariableOrParameter(baseName, line, col);
|
||||||
var variableDef = this.vars.get(varName);
|
ensureReferencesStruct(variableOrParameter, line, col);
|
||||||
if (variableDef == null) {
|
var resultType = drillType(variableOrParameter, path, line, col);
|
||||||
String error = "Variable with name " + varName + " not defined.";
|
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it references a struct
|
var memberAccessExpression = new MemberAccessExpression(baseName, variableOrParameter.type.getName(), path);
|
||||||
if (variableDef.type.isPrimitiveType()) {
|
memberAccessExpression.type = resultType;
|
||||||
String error = "Variable must reference a struct but references " + variableDef.type.getName() + ".";
|
memberAccessExpression.line = line;
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
memberAccessExpression.col = col;
|
||||||
}
|
|
||||||
|
|
||||||
// Get the type of the result of this expression
|
return memberAccessExpression;
|
||||||
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
|
@Override
|
||||||
@@ -649,24 +680,20 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitVariable(KlangParser.VariableContext ctx) {
|
public Node visitVariable(KlangParser.VariableContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
var variableName = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
VariableDeclaration var = this.vars.get(name);
|
var variableOrParameter = getVariableOrParameter(variableName, line, col);
|
||||||
if (var == null) {
|
|
||||||
String error = "Variable with name \"" + name + "\" not defined.";
|
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the variable has been initialized before it can be used
|
// Make sure the variable has been initialized before it can be used
|
||||||
if (!var.initialized) {
|
if (!variableOrParameter.initialized) {
|
||||||
String error = "Variable with name \"" + name + "\" has not been initialized.";
|
var error = "Variable with name \"" + variableName + "\" has not been initialized.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
Variable result = new Variable(ctx.IDENT().getText());
|
var result = new Variable(ctx.IDENT().getText());
|
||||||
result.type = var.type;
|
result.type = variableOrParameter.type;
|
||||||
result.line = line;
|
result.line = line;
|
||||||
result.col = col;
|
result.col = col;
|
||||||
return result;
|
return result;
|
||||||
@@ -697,7 +724,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) {
|
public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) {
|
||||||
Node n = new BooleanExpression(ctx.getText().equals("true") ? true : false);
|
Node n = new BooleanExpression(ctx.getText().equals("true"));
|
||||||
n.type = Type.getBooleanType();
|
n.type = Type.getBooleanType();
|
||||||
n.line = ctx.start.getLine();
|
n.line = ctx.start.getLine();
|
||||||
n.col = ctx.start.getCharPositionInLine();
|
n.col = ctx.start.getCharPositionInLine();
|
||||||
@@ -714,69 +741,146 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
public Node visitStructDef(KlangParser.StructDefContext ctx) {
|
||||||
String name = ctx.funcName.getText();
|
var structName = ctx.structName.getText();
|
||||||
int line = ctx.start.getLine();
|
var structFieldCount = ctx.structField().size();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var structFields = new StructField[structFieldCount];
|
||||||
Type returnType = Type.getByName(ctx.returnType.type().getText());
|
var line = ctx.start.getLine();
|
||||||
this.currentDeclaredReturnType = returnType;
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
if (!returnType.isPrimitiveType() && this.structs.get(returnType.getName()) == null) {
|
for (int i = 0; i < structFieldCount; i++) {
|
||||||
String error = "Type " + returnType.getName() + " not defined.";
|
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);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new set for the variables of the current function
|
if (!structFieldType.isPrimitiveType() && !structDefs.containsKey(structFieldType.getName()) && !enumDefs.containsKey(structFieldType.getName())) {
|
||||||
// this will be filled in the variable declaration visitor aswell
|
var error = "Type " + structFieldType.getName() + " not defined.";
|
||||||
this.vars = new HashMap<>();
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
|
}
|
||||||
|
|
||||||
// Process the paremter list by visiting every paremter in it
|
if (structDefs.containsKey(structFieldName)) {
|
||||||
int paramCount = ctx.params.parameter().size();
|
var error = "Struct field name " + structFieldName + " shadows a struct of the same name.";
|
||||||
Parameter[] params = new Parameter[paramCount];
|
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;
|
||||||
|
|
||||||
|
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.";
|
||||||
|
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<>();
|
||||||
|
|
||||||
|
|
||||||
|
var paramCount = ctx.params.parameter().size();
|
||||||
|
var functionParameters = new Parameter[paramCount]; // the list of parameters that get passed to FunctionDefinition
|
||||||
for (int i = 0; i < paramCount; i++) {
|
for (int i = 0; i < paramCount; i++) {
|
||||||
// Add the parameter to the list of parameters
|
functionParameters[i] = (Parameter) visit(ctx.params.parameter(i));
|
||||||
Parameter param = (Parameter) this.visit(ctx.params.parameter(i));
|
|
||||||
params[i] = param;
|
|
||||||
|
|
||||||
// add the param as a variable
|
// add the param as a variable to the global parameter map so that
|
||||||
VariableDeclaration var = new VariableDeclaration(param.name);
|
// child nodes can access them.
|
||||||
var.initialized = true; // parameters can always be considered initialized
|
var param = new VariableDeclaration(functionParameters[i].name);
|
||||||
var.type = param.type;
|
param.initialized = true; // parameters can always be considered initialized
|
||||||
this.vars.put(param.name, var);
|
param.type = functionParameters[i].type;
|
||||||
|
params.put(param.name, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visit the block, make sure that a return value is guaranteed
|
// Visit the block, make sure that a return value is guaranteed
|
||||||
Node block = this.visit(ctx.braced_block());
|
var block = visit(ctx.braced_block());
|
||||||
if (block.type == null) {
|
if (block.type == null && !returnType.equals(Type.getVoidType())) {
|
||||||
String error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
|
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() + ".";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionDefinition result = new FunctionDefinition(name, params, (Block) block);
|
var functionDef = new FunctionDefinition(name, functionParameters, vars.values().toArray(new VariableDeclaration[0]), (Block) block);
|
||||||
result.type = returnType;
|
functionDef.type = returnType;
|
||||||
|
functionDef.line = ctx.start.getLine();
|
||||||
|
functionDef.col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
result.line = ctx.start.getLine();
|
return functionDef;
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
var parameterName = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var parameterType = Type.getByName(ctx.type_annotation().type().getText());
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var line = ctx.start.getLine();
|
||||||
Type type = Type.getByName(ctx.type_annotation().type().getText());
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
if (!type.isPrimitiveType() && this.structs.get(type.getName()) == null) {
|
if (parameterType.equals(Type.getVoidType())) {
|
||||||
String error = "Type " + type.getName() + " not defined.";
|
var error = "Type " + parameterType.getName() + " cannot be used to declare a parameter.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
Parameter result = new Parameter(name);
|
if (structDefs.containsKey(parameterName)) {
|
||||||
result.type = type;
|
var error = "Parameter name " + parameterName + " duplicates a struct of the same name.";
|
||||||
result.line = line;
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
result.col = col;
|
}
|
||||||
return result;
|
|
||||||
|
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
|
@Override
|
||||||
@@ -785,15 +889,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
int line = ctx.start.getLine();
|
int line = ctx.start.getLine();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
int col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
FunctionInformation func = this.funcs.get(name);
|
var functionDef = this.functionDefs.get(name);
|
||||||
if (func == null) {
|
if (functionDef == null) {
|
||||||
String error = "Function with name \"" + name + "\" not defined.";
|
String error = "Function with name \"" + name + "\" not defined.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the number of arguments matches the number of parameters
|
// Make sure the number of arguments matches the number of parameters
|
||||||
int argCount = ctx.functionCall().arguments().expression().size();
|
int argCount = ctx.functionCall().arguments().expression().size();
|
||||||
int paramCount = func.parameters.size();
|
int paramCount = functionDef.parameters.length;
|
||||||
if (argCount != paramCount) {
|
if (argCount != paramCount) {
|
||||||
String error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
|
String error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
@@ -803,14 +907,14 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
Expression[] args = new Expression[argCount];
|
Expression[] args = new Expression[argCount];
|
||||||
for (int i = 0; i < argCount; i++) {
|
for (int i = 0; i < argCount; i++) {
|
||||||
Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
|
Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
|
||||||
if (!expression.type.equals(func.signature[i])) {
|
if (!expression.type.equals(functionDef.parameters[i].type)) {
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + func.signature[i].getName() + " but got: " + expression.type.getName());
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + functionDef.parameters[i].type.getName() + " but got: " + expression.type.getName());
|
||||||
}
|
}
|
||||||
args[i] = expression;
|
args[i] = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionCall result = new FunctionCall(name, args);
|
FunctionCall result = new FunctionCall(name, args);
|
||||||
result.type = func.returnType;
|
result.type = functionDef.type;
|
||||||
result.line = line;
|
result.line = line;
|
||||||
result.col = col;
|
result.col = col;
|
||||||
return result;
|
return result;
|
||||||
@@ -823,7 +927,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
int col = ctx.start.getCharPositionInLine();
|
int col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
// Get the corresponding struct definition
|
// Get the corresponding struct definition
|
||||||
var struct = this.structs.get(name);
|
var struct = this.structDefs.get(name);
|
||||||
if (struct == null) {
|
if (struct == null) {
|
||||||
String error = "Struct with name \"" + name + "\" not defined.";
|
String error = "Struct with name \"" + name + "\" not defined.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
@@ -858,19 +962,52 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitDestroy_statement(KlangParser.Destroy_statementContext ctx) {
|
public Node visitDestroy_statement(KlangParser.Destroy_statementContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
var varName = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node result = new DestructorCall(name);
|
var variableOrParameter = getVariableOrParameter(varName, line, col);
|
||||||
|
ensureReferencesStruct(variableOrParameter, line, col);
|
||||||
|
|
||||||
|
var result = new DestructorCall(varName);
|
||||||
result.line = line;
|
result.line = line;
|
||||||
result.col = col;
|
result.col = col;
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
220
src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java
Normal file
220
src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,24 @@
|
|||||||
package de.hsrm.compiler.Klang;
|
package de.hsrm.compiler.Klang;
|
||||||
|
|
||||||
// import ANTLR's runtime libraries
|
import de.hsrm.compiler.Klang.helper.*;
|
||||||
import org.antlr.v4.runtime.*;
|
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
|
||||||
import org.antlr.v4.runtime.tree.*;
|
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
|
||||||
|
|
||||||
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.Node;
|
||||||
import de.hsrm.compiler.Klang.nodes.StructDefinition;
|
import de.hsrm.compiler.Klang.nodes.StructDefinition;
|
||||||
import de.hsrm.compiler.Klang.visitors.*;
|
import de.hsrm.compiler.Klang.types.Type;
|
||||||
import de.hsrm.compiler.Klang.helper.*;
|
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;
|
||||||
|
|
||||||
public class Klang {
|
public class Klang {
|
||||||
|
|
||||||
@@ -46,7 +51,7 @@ public class Klang {
|
|||||||
System.out.println("Last argument must be file");
|
System.out.println("Last argument must be file");
|
||||||
System.out.println("");
|
System.out.println("");
|
||||||
System.out.println("Arguments:");
|
System.out.println("Arguments:");
|
||||||
System.out.println("--out <file>:\t File to write to");
|
System.out.println("--o <file>:\t File to write to");
|
||||||
System.out.println("--evaluate:\t Evaluates the given source code");
|
System.out.println("--evaluate:\t Evaluates the given source code");
|
||||||
System.out.println("--pretty:\t Pretty print the given source code");
|
System.out.println("--pretty:\t Pretty print the given source code");
|
||||||
System.out
|
System.out
|
||||||
@@ -87,22 +92,15 @@ public class Klang {
|
|||||||
|
|
||||||
// Context Analysis and DAST generation
|
// Context Analysis and DAST generation
|
||||||
Node root;
|
Node root;
|
||||||
HashMap<String, StructDefinition> structs;
|
var functionDefs = new HashMap<String, FunctionDefinition>();
|
||||||
|
var structDefs = new HashMap<String, StructDefinition>();
|
||||||
|
var enumDefs = new HashMap<String, EnumDefinition>();
|
||||||
try {
|
try {
|
||||||
// Extract information about all functions
|
// Extract information about all definitions
|
||||||
var functionDefinitions = new HashMap<String, FunctionInformation>();
|
new GetDefinitions(functionDefs, structDefs, enumDefs).visit(tree);
|
||||||
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
|
// Create the DAST
|
||||||
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions, structs);
|
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefs, structDefs, enumDefs);
|
||||||
root = ctxAnal.visit(tree);
|
root = ctxAnal.visit(tree);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
@@ -115,24 +113,26 @@ public class Klang {
|
|||||||
PrettyPrintVisitor.ExWriter ex = new PrettyPrintVisitor.ExWriter(w);
|
PrettyPrintVisitor.ExWriter ex = new PrettyPrintVisitor.ExWriter(w);
|
||||||
PrettyPrintVisitor printVisitor = new PrettyPrintVisitor(ex);
|
PrettyPrintVisitor printVisitor = new PrettyPrintVisitor(ex);
|
||||||
root.welcome(printVisitor);
|
root.welcome(printVisitor);
|
||||||
System.out.println(w.toString());
|
generateOutput(out, w.toString());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evaluate) {
|
if (evaluate) {
|
||||||
// Evaluate the sourcecode and print the result
|
// Evaluate the sourcecode and print the result
|
||||||
System.out.println("\nEvaluating the source code:");
|
System.out.println("\nEvaluating the source code:");
|
||||||
EvalVisitor evalVisitor = new EvalVisitor(structs);
|
EvalVisitor evalVisitor = new EvalVisitor(structDefs);
|
||||||
Value result = root.welcome(evalVisitor);
|
Value result = root.welcome(evalVisitor);
|
||||||
generateOutput(out, "Result was: " + result.asObject().toString());
|
if (result.type.equals(Type.getVoidType())) {
|
||||||
|
generateOutput(out, "Result was void");
|
||||||
|
} else {
|
||||||
|
generateOutput(out, "Result was: " + result.asObject().toString());
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate assembler code
|
// Generate assembler code
|
||||||
// System.out.println("\nPrinting the assembler code");
|
GenASM genasm = new GenASM(mainName, structDefs);
|
||||||
StringWriter wAsm = new StringWriter();
|
|
||||||
GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm);
|
|
||||||
GenASM genasm = new GenASM(exAsm, mainName, structs);
|
|
||||||
root.welcome(genasm);
|
root.welcome(genasm);
|
||||||
generateOutput(out, wAsm.toString());
|
generateOutput(out, genasm.toAsm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
222
src/main/java/de/hsrm/compiler/Klang/asm/ASM.java
Normal file
222
src/main/java/de/hsrm/compiler/Klang/asm/ASM.java
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Add.java
Normal file
29
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Add.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Call.java
Normal file
14
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Call.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
22
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Cmp.java
Normal file
22
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Cmp.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public class Cqto extends NoOperandMnemonic {
|
||||||
|
@Override
|
||||||
|
public String toAsm() {
|
||||||
|
return "cqto";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Div.java
Normal file
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Div.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Idiv.java
Normal file
16
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Idiv.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Imul.java
Normal file
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Imul.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
12
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Je.java
Normal file
12
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Je.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jg.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jg.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jge.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jge.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jl.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jl.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jle.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jle.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jmp.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jmp.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jne.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jne.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jnz.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jnz.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jump.java
Normal file
21
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jump.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jz.java
Normal file
11
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Jz.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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 + ":";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public class Leave extends NoOperandMnemonic {
|
||||||
|
@Override
|
||||||
|
public String toAsm() {
|
||||||
|
return "leave";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public abstract class Mnemonic {
|
||||||
|
public abstract String toAsm();
|
||||||
|
public int indentation = 2;
|
||||||
|
}
|
||||||
40
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Mov.java
Normal file
40
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Mov.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Mul.java
Normal file
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Mul.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Neg.java
Normal file
14
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Neg.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public class Newline extends NoOperandMnemonic {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toAsm() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public abstract class NoOperandMnemonic extends Mnemonic {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public abstract class OneOperandMnemonic extends Mnemonic {
|
||||||
|
public String operand;
|
||||||
|
}
|
||||||
16
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Pop.java
Normal file
16
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Pop.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
21
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Push.java
Normal file
21
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Push.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public class Ret extends NoOperandMnemonic {
|
||||||
|
@Override
|
||||||
|
public String toAsm() {
|
||||||
|
return "ret";
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Sub.java
Normal file
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Sub.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Text.java
Normal file
15
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Text.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.hsrm.compiler.Klang.asm.mnemonics;
|
||||||
|
|
||||||
|
public abstract class TwoOperandMnemonic extends Mnemonic {
|
||||||
|
public String src;
|
||||||
|
public String dst;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Xor.java
Normal file
17
src/main/java/de/hsrm/compiler/Klang/asm/mnemonics/Xor.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/java/de/hsrm/compiler/Klang/nodes/EnumValue.java
Normal file
18
src/main/java/de/hsrm/compiler/Klang/nodes/EnumValue.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
package de.hsrm.compiler.Klang.nodes;
|
package de.hsrm.compiler.Klang.nodes;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.nodes.statements.VariableDeclaration;
|
||||||
import de.hsrm.compiler.Klang.visitors.Visitor;
|
import de.hsrm.compiler.Klang.visitors.Visitor;
|
||||||
|
|
||||||
public class FunctionDefinition extends Node {
|
public class FunctionDefinition extends Node {
|
||||||
|
|
||||||
public String name;
|
public String name;
|
||||||
public Parameter[] parameters;
|
public Parameter[] parameters;
|
||||||
|
public VariableDeclaration[] localVariables;
|
||||||
public Block block;
|
public Block block;
|
||||||
|
|
||||||
public FunctionDefinition(String name, Parameter[] parameters, Block block) {
|
public FunctionDefinition(String name, Parameter[] parameters, VariableDeclaration[] localVariables, Block block) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
this.block = block;
|
this.block = block;
|
||||||
|
this.localVariables = localVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,11 +9,18 @@ public class Program extends Node {
|
|||||||
|
|
||||||
public FunctionDefinition[] funcs;
|
public FunctionDefinition[] funcs;
|
||||||
public Map<String, StructDefinition> structs;
|
public Map<String, StructDefinition> structs;
|
||||||
|
public Map<String, EnumDefinition> enums;
|
||||||
public Expression expression;
|
public Expression expression;
|
||||||
|
|
||||||
public Program(FunctionDefinition[] funcs, Map<String, StructDefinition> structs, Expression expression) {
|
public Program(
|
||||||
|
FunctionDefinition[] funcs,
|
||||||
|
Map<String, StructDefinition> structs,
|
||||||
|
Map<String, EnumDefinition> enums,
|
||||||
|
Expression expression
|
||||||
|
) {
|
||||||
this.funcs = funcs;
|
this.funcs = funcs;
|
||||||
this.structs = structs;
|
this.structs = structs;
|
||||||
|
this.enums = enums;
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ public class FunctionCall extends Expression {
|
|||||||
|
|
||||||
public String name;
|
public String name;
|
||||||
public Expression[] arguments;
|
public Expression[] arguments;
|
||||||
|
public boolean isTailRecursive = false;
|
||||||
|
|
||||||
public FunctionCall(String name, Expression[] arguments) {
|
public FunctionCall(String name, Expression[] arguments) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ package de.hsrm.compiler.Klang.nodes.expressions;
|
|||||||
|
|
||||||
import de.hsrm.compiler.Klang.visitors.Visitor;
|
import de.hsrm.compiler.Klang.visitors.Visitor;
|
||||||
|
|
||||||
public class StructFieldAccessExpression extends Expression {
|
public class MemberAccessExpression extends Expression {
|
||||||
public String varName;
|
public String varName;
|
||||||
public String structName;
|
public String structName;
|
||||||
public String[] path;
|
public String[] path;
|
||||||
|
|
||||||
public StructFieldAccessExpression(String varName, String structName, String[] path) {
|
public MemberAccessExpression(String varName, String structName, String[] path) {
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.structName = structName;
|
this.structName = structName;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
@@ -11,6 +11,10 @@ public class ReturnStatement extends Statement {
|
|||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReturnStatement() {
|
||||||
|
this.expression = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R> R welcome(Visitor<R> v) {
|
public <R> R welcome(Visitor<R> v) {
|
||||||
return v.visit(this);
|
return v.visit(this);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package de.hsrm.compiler.Klang.types;
|
package de.hsrm.compiler.Klang.types;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.Value;
|
||||||
|
|
||||||
public class BooleanType extends PrimitiveType {
|
public class BooleanType extends PrimitiveType {
|
||||||
|
|
||||||
private static BooleanType instance = null;
|
private static BooleanType instance = null;
|
||||||
@@ -30,7 +32,11 @@ public class BooleanType extends PrimitiveType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Every remaining type will throw a RuntimeException
|
// Every remaining type will throw a RuntimeException
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean valuesEqual(Value a, Value b) {
|
||||||
|
return a.asBoolean() == b.asBoolean();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package de.hsrm.compiler.Klang.types;
|
package de.hsrm.compiler.Klang.types;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.Value;
|
||||||
|
|
||||||
public class FloatType extends NumericType {
|
public class FloatType extends NumericType {
|
||||||
|
|
||||||
private static FloatType instance = null;
|
private static FloatType instance = null;
|
||||||
@@ -34,7 +36,11 @@ public class FloatType extends NumericType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Every remaining type will throw a RuntimeException
|
// Every remaining type will throw a RuntimeException
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean valuesEqual(Value a, Value b) {
|
||||||
|
return a.asFloat() == b.asFloat();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package de.hsrm.compiler.Klang.types;
|
package de.hsrm.compiler.Klang.types;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.Value;
|
||||||
|
|
||||||
public class IntegerType extends NumericType {
|
public class IntegerType extends NumericType {
|
||||||
|
|
||||||
private static IntegerType instance = null;
|
private static IntegerType instance = null;
|
||||||
@@ -34,7 +36,12 @@ public class IntegerType extends NumericType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Every remaining type will throw a RuntimeException
|
// Every remaining type will throw a RuntimeException
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
53
src/main/java/de/hsrm/compiler/Klang/types/NamedType.java
Normal file
53
src/main/java/de/hsrm/compiler/Klang/types/NamedType.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package de.hsrm.compiler.Klang.types;
|
package de.hsrm.compiler.Klang.types;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.Value;
|
||||||
|
|
||||||
public class NullType extends Type {
|
public class NullType extends Type {
|
||||||
|
|
||||||
private static NullType instance = null;
|
private static NullType instance = null;
|
||||||
@@ -21,13 +23,18 @@ public class NullType extends Type {
|
|||||||
public Type combine(Type that) {
|
public Type combine(Type that) {
|
||||||
// You can not combine null with a primitive type
|
// You can not combine null with a primitive type
|
||||||
if (that.isPrimitiveType()) {
|
if (that.isPrimitiveType()) {
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything else combines with null to the type it was before
|
// Everything else combines with null to the type it was before
|
||||||
return that;
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean valuesEqual(Value a, Value b) {
|
||||||
|
return a.asObject() == b.asObject();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPrimitiveType() {
|
public boolean isPrimitiveType() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
package de.hsrm.compiler.Klang.types;
|
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 {
|
public abstract class Type {
|
||||||
|
|
||||||
// Returns an instance of IntegerType
|
// Returns an instance of IntegerType
|
||||||
@@ -20,18 +27,24 @@ public abstract class Type {
|
|||||||
return NullType.getType();
|
return NullType.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static VoidType getVoidType() {
|
||||||
|
return VoidType.getType();
|
||||||
|
}
|
||||||
|
|
||||||
public static Type getByName(String name) {
|
public static Type getByName(String name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "bool": return getBooleanType();
|
case "bool": return getBooleanType();
|
||||||
case "int": return getIntegerType();
|
case "int": return getIntegerType();
|
||||||
case "float": return getFloatType();
|
case "float": return getFloatType();
|
||||||
case "null": return getNullType();
|
case "null": return getNullType();
|
||||||
default: return new StructType(name);
|
case "void": return getVoidType();
|
||||||
|
default: return new NamedType(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
public abstract Type combine(Type that);
|
public abstract Type combine(Type that);
|
||||||
|
public abstract boolean valuesEqual(Value a, Value b);
|
||||||
public abstract boolean isPrimitiveType();
|
public abstract boolean isPrimitiveType();
|
||||||
public abstract boolean isNumericType();
|
public abstract boolean isNumericType();
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/main/java/de/hsrm/compiler/Klang/types/VoidType.java
Normal file
45
src/main/java/de/hsrm/compiler/Klang/types/VoidType.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package de.hsrm.compiler.Klang.types;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.Value;
|
||||||
|
|
||||||
|
public class VoidType extends Type {
|
||||||
|
|
||||||
|
private static VoidType instance;
|
||||||
|
|
||||||
|
public static VoidType getType() {
|
||||||
|
if (instance != null) {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
instance = new VoidType();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "void";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type combine(Type that) {
|
||||||
|
if (that.equals(this)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean valuesEqual(Value a, Value b) {
|
||||||
|
throw new RuntimeException("Can not compare void types.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNumericType() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,17 +4,14 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.Value;
|
import de.hsrm.compiler.Klang.Value;
|
||||||
import de.hsrm.compiler.Klang.nodes.Block;
|
import de.hsrm.compiler.Klang.nodes.*;
|
||||||
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.expressions.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
|
import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
|
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
|
||||||
import de.hsrm.compiler.Klang.nodes.statements.*;
|
import de.hsrm.compiler.Klang.nodes.statements.*;
|
||||||
|
import de.hsrm.compiler.Klang.types.NamedType;
|
||||||
|
import de.hsrm.compiler.Klang.types.NullType;
|
||||||
import de.hsrm.compiler.Klang.types.Type;
|
import de.hsrm.compiler.Klang.types.Type;
|
||||||
|
|
||||||
public class EvalVisitor implements Visitor<Value> {
|
public class EvalVisitor implements Visitor<Value> {
|
||||||
@@ -51,25 +48,11 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(EqualityExpression e) {
|
public Value visit(EqualityExpression e) {
|
||||||
Value lhs = e.lhs.welcome(this);
|
var lhs = e.lhs.welcome(this);
|
||||||
Value rhs = e.rhs.welcome(this);
|
var rhs = e.rhs.welcome(this);
|
||||||
Type resultType = Type.getBooleanType();
|
var combinedType = lhs.type.combine(rhs.type);
|
||||||
Type combineType = lhs.type.combine(rhs.type);
|
|
||||||
|
|
||||||
switch(combineType.getName()) {
|
return new Value(combinedType.valuesEqual(lhs, rhs), Type.getBooleanType());
|
||||||
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
|
@Override
|
||||||
@@ -413,13 +396,16 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(ReturnStatement e) {
|
public Value visit(ReturnStatement e) {
|
||||||
|
if (e.expression == null) {
|
||||||
|
return new Value(null, Type.getVoidType());
|
||||||
|
}
|
||||||
return e.expression.welcome(this);
|
return e.expression.welcome(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(Block e) {
|
public Value visit(Block e) {
|
||||||
for (var stmt : e.statements) {
|
for (var stmt : e.statements) {
|
||||||
Value result = stmt.welcome(this);
|
var result = stmt.welcome(this);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -447,7 +433,7 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
this.env = newEnv;
|
this.env = newEnv;
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
Value result = func.block.welcome(this);
|
var result = func.block.welcome(this);
|
||||||
|
|
||||||
// Das alte env wiederherstellen
|
// Das alte env wiederherstellen
|
||||||
this.env = oldEnv;
|
this.env = oldEnv;
|
||||||
@@ -471,6 +457,16 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value visit(EnumDefinition e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value visit(EnumValue e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(StructDefinition e) {
|
public Value visit(StructDefinition e) {
|
||||||
// We get these from a previous visitor
|
// We get these from a previous visitor
|
||||||
@@ -484,7 +480,7 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(StructFieldAccessExpression e) {
|
public Value visit(MemberAccessExpression e) {
|
||||||
Value var = this.env.get(e.varName);
|
Value var = this.env.get(e.varName);
|
||||||
Map<String, Value> struct = var.asStruct();
|
Map<String, Value> struct = var.asStruct();
|
||||||
|
|
||||||
@@ -496,6 +492,11 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
return currentValue;
|
return currentValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value visit(EnumAccessExpression e) {
|
||||||
|
return new Value(e.enumValueName, e.type);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(ConstructorCall e) {
|
public Value visit(ConstructorCall e) {
|
||||||
StructDefinition structDef = this.structs.get(e.structName);
|
StructDefinition structDef = this.structs.get(e.structName);
|
||||||
@@ -506,12 +507,17 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
struct.put(structDef.fields[i].name, arg);
|
struct.put(structDef.fields[i].name, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Value(struct);
|
var structValue = new Value(struct);
|
||||||
|
structValue.type = structDef.type;
|
||||||
|
|
||||||
|
return structValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(NullExpression e) {
|
public Value visit(NullExpression e) {
|
||||||
return null;
|
var nullValue = new Value(null);
|
||||||
|
nullValue.type = new NullType();
|
||||||
|
return nullValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -194,7 +194,9 @@ class GetVars implements Visitor<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(ReturnStatement e) {
|
public Void visit(ReturnStatement e) {
|
||||||
e.expression.welcome(this);
|
if (e.expression != null) {
|
||||||
|
e.expression.welcome(this);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,6 +236,16 @@ class GetVars implements Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(EnumDefinition e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(EnumValue e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(StructDefinition e) {
|
public Void visit(StructDefinition e) {
|
||||||
return null;
|
return null;
|
||||||
@@ -245,7 +257,12 @@ class GetVars implements Visitor<Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(StructFieldAccessExpression e) {
|
public Void visit(MemberAccessExpression e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(EnumAccessExpression e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,12 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
ex.nl();
|
ex.nl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var enumDef: e.enums.values()) {
|
||||||
|
enumDef.welcome(this);
|
||||||
|
ex.nl();
|
||||||
|
ex.nl();
|
||||||
|
}
|
||||||
|
|
||||||
e.expression.welcome(this);
|
e.expression.welcome(this);
|
||||||
ex.write(";");
|
ex.write(";");
|
||||||
return null;
|
return null;
|
||||||
@@ -304,8 +310,11 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(ReturnStatement e) {
|
public Void visit(ReturnStatement e) {
|
||||||
ex.write("return ");
|
ex.write("return");
|
||||||
e.expression.welcome(this);
|
if (e.expression != null) {
|
||||||
|
ex.write(" ");
|
||||||
|
e.expression.welcome(this);
|
||||||
|
}
|
||||||
ex.write(";");
|
ex.write(";");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -377,6 +386,28 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
return null;
|
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
|
@Override
|
||||||
public Void visit(StructDefinition e) {
|
public Void visit(StructDefinition e) {
|
||||||
ex.write("struct " + e.name + " {");
|
ex.write("struct " + e.name + " {");
|
||||||
@@ -398,7 +429,7 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(StructFieldAccessExpression e) {
|
public Void visit(MemberAccessExpression e) {
|
||||||
ex.write(e.varName);
|
ex.write(e.varName);
|
||||||
for (int i = 0; i < e.path.length; i++) {
|
for (int i = 0; i < e.path.length; i++) {
|
||||||
ex.write(".");
|
ex.write(".");
|
||||||
@@ -407,6 +438,15 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(EnumAccessExpression e) {
|
||||||
|
ex.write(e.enumName);
|
||||||
|
ex.write(".");
|
||||||
|
ex.write(e.enumValueName);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(ConstructorCall e) {
|
public Void visit(ConstructorCall e) {
|
||||||
ex.write("create " + e.structName + "(");
|
ex.write("create " + e.structName + "(");
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
package de.hsrm.compiler.Klang.visitors;
|
package de.hsrm.compiler.Klang.visitors;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.nodes.Block;
|
import de.hsrm.compiler.Klang.nodes.*;
|
||||||
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.expressions.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.*;
|
import de.hsrm.compiler.Klang.nodes.loops.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.statements.*;
|
import de.hsrm.compiler.Klang.nodes.statements.*;
|
||||||
@@ -42,9 +37,12 @@ public interface Visitor<R> {
|
|||||||
R visit(FunctionCall e);
|
R visit(FunctionCall e);
|
||||||
R visit(Program e);
|
R visit(Program e);
|
||||||
R visit(Parameter e);
|
R visit(Parameter e);
|
||||||
|
R visit(EnumDefinition e);
|
||||||
|
R visit(EnumValue e);
|
||||||
R visit(StructDefinition e);
|
R visit(StructDefinition e);
|
||||||
R visit(StructField e);
|
R visit(StructField e);
|
||||||
R visit(StructFieldAccessExpression e);
|
R visit(MemberAccessExpression e);
|
||||||
|
R visit(EnumAccessExpression e);
|
||||||
R visit(ConstructorCall e);
|
R visit(ConstructorCall e);
|
||||||
R visit(NullExpression e);
|
R visit(NullExpression e);
|
||||||
R visit(DestructorCall e);
|
R visit(DestructorCall e);
|
||||||
|
|||||||
@@ -92,4 +92,28 @@ int runFunctionCallTests () {
|
|||||||
argumentTest("fgetMix8(...args)", 8, fgetMix8());
|
argumentTest("fgetMix8(...args)", 8, fgetMix8());
|
||||||
argumentTest_f("fgetMix9(...args)", 9.0, fgetMix9());
|
argumentTest_f("fgetMix9(...args)", 9.0, fgetMix9());
|
||||||
argumentTest("fgetMix10(...args)", 10, fgetMix10());
|
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));
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,28 @@ long get8();
|
|||||||
long get9();
|
long get9();
|
||||||
long get10();
|
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 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 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);
|
double farg3(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j);
|
||||||
|
|||||||
21
src/test/java/AndTest.java
Normal file
21
src/test/java/AndTest.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/test/java/ConstructorCallTest.java
Normal file
46
src/test/java/ConstructorCallTest.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/test/java/DestroyStatementTest.java
Normal file
22
src/test/java/DestroyStatementTest.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/test/java/FieldAssignmentTest.java
Normal file
34
src/test/java/FieldAssignmentTest.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/test/java/FunctionCallTest.java
Normal file
46
src/test/java/FunctionCallTest.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
119
src/test/java/FunctionDefinitionTest.java
Normal file
119
src/test/java/FunctionDefinitionTest.java
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/test/java/Helper.java
Normal file
36
src/test/java/Helper.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.*;
|
||||||
|
import org.antlr.v4.runtime.tree.*;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.*;
|
||||||
|
import de.hsrm.compiler.Klang.helper.*;
|
||||||
|
import de.hsrm.compiler.Klang.nodes.*;
|
||||||
|
|
||||||
|
public class Helper {
|
||||||
|
public static ParseTree prepareParser(String input) {
|
||||||
|
CharStream inStream = CharStreams.fromString(input);
|
||||||
|
KlangLexer lexer = new KlangLexer(inStream);
|
||||||
|
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||||
|
KlangParser parser = new KlangParser(tokens);
|
||||||
|
return parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, FunctionDefinition> getFuncs(ParseTree tree) {
|
||||||
|
var functionDefinitions = new HashMap<String, FunctionDefinition>();
|
||||||
|
new GetDefinitions(functionDefinitions, new HashMap<>(), new HashMap<>()).visit(tree);
|
||||||
|
return functionDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, StructDefinition> getStructs(ParseTree tree) {
|
||||||
|
var structs = new HashMap<String, StructDefinition>();
|
||||||
|
new GetDefinitions(new HashMap<>(), structs, new HashMap<>()).visit(tree);
|
||||||
|
return structs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, EnumDefinition> getEnums(ParseTree tree) {
|
||||||
|
var enums = new HashMap<String, EnumDefinition>();
|
||||||
|
new GetDefinitions(new HashMap<>(), new HashMap<>(), enums).visit(tree);
|
||||||
|
return enums;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/test/java/ModuloTest.java
Normal file
21
src/test/java/ModuloTest.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.ContextAnalysis;
|
||||||
|
|
||||||
|
public class ModuloTest {
|
||||||
|
@Test
|
||||||
|
void onlyForInt() {
|
||||||
|
ParseTree tree = Helper.prepareParser("function foo(): float { return 1.0 % 2.3; } foo();");
|
||||||
|
var funcs = Helper.getFuncs(tree);
|
||||||
|
var structs = Helper.getStructs(tree);
|
||||||
|
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:31 Only integers are allowed for modulo.", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/test/java/NaughtTest.java
Normal file
72
src/test/java/NaughtTest.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import de.hsrm.compiler.Klang.ContextAnalysis;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class NaughtTest {
|
||||||
|
@Test
|
||||||
|
void shouldBeComparableToStruct() {
|
||||||
|
ParseTree tree = Helper.prepareParser("""
|
||||||
|
struct bar { a: int; }
|
||||||
|
|
||||||
|
function foo(): int {
|
||||||
|
let a: bar = create bar(1);
|
||||||
|
if (a == naught) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
foo();
|
||||||
|
""");
|
||||||
|
var ctxAnal = new ContextAnalysis(
|
||||||
|
Helper.getFuncs(tree),
|
||||||
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> ctxAnal.visit(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldBeAssignableToStruct() {
|
||||||
|
ParseTree tree = Helper.prepareParser("""
|
||||||
|
struct bar { a: int; }
|
||||||
|
|
||||||
|
function foo(): int {
|
||||||
|
let a: bar = naught;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
foo();
|
||||||
|
""");
|
||||||
|
var ctxAnal = new ContextAnalysis(
|
||||||
|
Helper.getFuncs(tree),
|
||||||
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> ctxAnal.visit(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldBeReturnable() {
|
||||||
|
ParseTree tree = Helper.prepareParser("""
|
||||||
|
struct bar { a: int; }
|
||||||
|
|
||||||
|
function foo(): bar {
|
||||||
|
return naught;
|
||||||
|
}
|
||||||
|
|
||||||
|
foo();
|
||||||
|
""");
|
||||||
|
var ctxAnal = new ContextAnalysis(
|
||||||
|
Helper.getFuncs(tree),
|
||||||
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> ctxAnal.visit(tree));
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/test/java/OrTest.java
Normal file
22
src/test/java/OrTest.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.ContextAnalysis;
|
||||||
|
|
||||||
|
public class OrTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void onlyForBool() {
|
||||||
|
ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 || 2; } foo();");
|
||||||
|
var funcs = Helper.getFuncs(tree);
|
||||||
|
var structs = Helper.getStructs(tree);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user