Compare commits

...

19 Commits

Author SHA1 Message Date
e835bd0f06 GenASM: Make GenASM quietly rewrite a user's function if it's called main.
We generate our own main function that executes the user's specified expression at the end of his file. This auto generated function has to be called "main" in order for it to be executed on startup. If the user chooses to call one of his own functions "main" our auto generated function would collide with the user's function. That's why we quietly rewrite the user's function to "main_by_user". This way we prevent a collision.

Note for the future: We should change our Syntax to allow for no expression as the last definition of a file. This way the user can choose if a particular source file needs to contain a main function or not (just like c does it). This is also one of the requirements for modules to work.
2023-03-20 21:05:24 +01:00
ea1c04ae0a Build: Add a main manifest attribute to the generated jar.
This makes it possible to directly execute the packaged jar.
2023-03-20 19:55:37 +01:00
198bd74a47 Enums: Make the EnumAccessExpression save a reference to the EnumValue it is referencing.
This can be used during assembler generation to easily find the correct EnumValue for a given EnumAccessExpression.
2023-03-20 19:54:48 +01:00
0594542167 Enums: Make EnumDefinition use EnumValues instead of Strings as children.
This allows us to store the index of the enum value along the name. The index can be used to compare two enum values in assembler.

Later on this might be used to enable users of KLang to set arbitrary values as the index of an enum value.
2023-03-20 19:30:07 +01:00
77fe360ffa Evaluate: Implement evaluation for enums. 2023-03-20 19:10:40 +01:00
55a5b8f54a Make sure that a variable that references an enum has to be initialized. 2023-03-16 00:01:31 +01:00
2768b4429c Check that a struct field name of a struct declaration does not shadow an enum or a struct definition. 2023-03-15 23:48:57 +01:00
30dfbbbbba Check that a variable name of variable declaration does not shadow an enum definition. 2023-03-15 23:33:12 +01:00
f77d6a002d Check that a parameter name of a function definition does not shadow an enum definition. 2023-03-15 23:17:43 +01:00
22634c9652 Use LinkedHashMaps and LinkedHashSets to preserve the order of parameters and struct fields. 2023-03-15 23:08:38 +01:00
6fd3f5a2e6 Make it possible to use an enum in an expression (i.e. selecting one of the enum values: Foo.A) 2023-03-15 19:14:04 +01:00
3b928d621b Refactor FunctionDefinition and Parameter context analysis and extend the type check to include enums. 2023-03-15 17:47:58 +01:00
9a58afb550 Implement StructField type checking in ContextAnalysis. 2023-03-15 17:21:35 +01:00
6e4431652c Remove FunctionInformation and replace it with FunctionDefinition. 2023-03-15 16:19:42 +01:00
7af815042b WIP: Add enum support 2023-03-15 15:56:42 +01:00
7c40a50196 add intellij config files 2023-03-15 05:23:09 +01:00
Dennis Kaiser
8529e24a37 Merge branch '32-create-asm-class-structure' into 'master'
32: Create Meta ASM Structure

Closes #32

See merge request mkais001/klang!23
2020-03-17 16:08:54 +01:00
Marvin Kaiser
49b024b95f 32: Require data type for all asm functions 2020-03-17 16:02:03 +01:00
Marvin Kaiser
982fc6417d 32: Create Meta ASM Structure 2020-03-14 14:14:19 +01:00
92 changed files with 2213 additions and 742 deletions

8
.idea/.gitignore generated vendored Normal file
View 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
View 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="11" />
</bytecodeTargetLevel>
</component>
</project>

8
.idea/encodings.xml generated Normal file
View 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
View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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
View 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
View 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
View File

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

22
klang.iml Normal file
View 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_11">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/antlr4" isTestSource="false" generated="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.antlr:antlr4-runtime:4.7.2" level="project" />
<orderEntry type="library" name="Maven: org.junit.jupiter:junit-jupiter-api:5.6.0" level="project" />
<orderEntry type="library" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
<orderEntry type="library" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
<orderEntry type="library" name="Maven: org.junit.platform:junit-platform-commons:1.6.0" level="project" />
<orderEntry type="library" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.6.0" level="project" />
<orderEntry type="library" name="Maven: org.junit.platform:junit-platform-engine:1.6.0" level="project" />
</component>
</module>

View File

@@ -72,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>

View File

@@ -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
@@ -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
@@ -143,6 +147,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';

View File

@@ -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.*;
@@ -13,10 +9,13 @@ 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 java.util.*;
public class ContextAnalysis extends KlangBaseVisitor<Node> { public class ContextAnalysis extends KlangBaseVisitor<Node> {
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; String currentFunctionDefinitionName;
@@ -27,25 +26,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
@@ -67,7 +76,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
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
@@ -89,9 +98,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
// 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]; Statement[] 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;
} }
@@ -172,43 +179,59 @@ 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.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
String error = "Type " + declaredType.getName() + " not defined."; var error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
if (this.vars.get(name) != null) { if (structDefs.containsKey(variableName)) {
String error = "Redeclaration of variable with name \"" + name + "\"."; 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) {
var error = "Redeclaration of variable 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
@@ -291,7 +314,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
String structName = variableDef.type.getName(); String structName = variableDef.type.getName();
Type fieldType; Type fieldType;
try { try {
fieldType = Helper.drillType(this.structs, structName, path, 0); fieldType = Helper.drillType(this.structDefs, structName, path, 0);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage()); throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
} }
@@ -311,43 +334,71 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
} }
@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];
// Create a list of member names. This excludes
// the first entry as it is the base name.
var path = new ArrayList<String>();
for (int i = 1; i < ctx.IDENT().size(); i++) { for (int i = 1; i < ctx.IDENT().size(); i++) {
path[i - 1] = ctx.IDENT(i).getText(); path.add(ctx.IDENT(i).getText());
}
// Determine if the base name points to an enum or a variable
var enumDef = enumDefs.get(baseName);
if (enumDef != null) {
if (path.size() != 1) {
var error = "Illegal access to enum " + enumDef.name + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var enumValueName = path.get(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 // Get the referenced variable, make sure it is defined
var variableDef = this.vars.get(varName); var variableDef = vars.get(baseName);
if (variableDef == null) { if (variableDef == null) {
String error = "Variable with name " + varName + " not defined."; var error = "Variable with name " + baseName + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
// Make sure it references a struct // Make sure it references a struct
if (variableDef.type.isPrimitiveType()) { if (variableDef.type.isPrimitiveType()) {
String error = "Variable must reference a struct but references " + variableDef.type.getName() + "."; var error = "Variable must reference a struct but references " + variableDef.type.getName() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
// Get the type of the result of this expression // Get the type of the result of this expression
String structName = variableDef.type.getName(); var structName = variableDef.type.getName();
Type resultType; Type resultType;
try { try {
resultType = Helper.drillType(this.structs, structName, path, 0); resultType = Helper.drillType(structDefs, structName, path.toArray(new String[0]), 0);
} 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 StructFieldAccessExpression(varName, structName, path); var memberAccessExpression = new MemberAccessExpression(baseName, structName, path.toArray(new String[0]));
result.type = resultType; memberAccessExpression.type = resultType;
result.line = line; memberAccessExpression.line = line;
result.col = col; memberAccessExpression.col = col;
return result;
return memberAccessExpression;
} }
@Override @Override
@@ -708,7 +759,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();
@@ -725,70 +776,137 @@ 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();
this.currentFunctionDefinitionName = name;
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.isPrimitiveType() && !structDefs.containsKey(structFieldType.getName()) && !enumDefs.containsKey(structFieldType.getName())) {
var error = "Type " + structFieldType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (structDefs.containsKey(structFieldName)) {
var error = "Struct field name " + structFieldName + " shadows a struct of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (enumDefs.containsKey(structFieldName)) {
var error = "Struct field name " + structFieldName + " shadows an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var structField = new StructField(structFieldName);
structField.type = structFieldType;
structField.line = line;
structField.col = col;
return structField;
}
@Override
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
var name = ctx.funcName.getText();
var returnType = Type.getByName(ctx.returnType.type().getText());
currentDeclaredReturnType = returnType;
currentFunctionDefinitionName = name;
if (!returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName())) {
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); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
// Create a new set for the variables of the current function // Create a new set for the variables of the current function
// this will be filled in the variable declaration visitor aswell // this will be filled in the variable declaration visitor as well
this.vars = new HashMap<>(); vars = new HashMap<>();
// Process the paremter list by visiting every paremter in it // Process the parameter list by visiting every parameter in it
int paramCount = ctx.params.parameter().size(); var paramCount = ctx.params.parameter().size();
Parameter[] params = new Parameter[paramCount]; var params = new Parameter[paramCount];
for (int i = 0; i < paramCount; i++) { for (int i = 0; i < paramCount; i++) {
// Add the parameter to the list of parameters // Add the parameter to the list of parameters
Parameter param = (Parameter) this.visit(ctx.params.parameter(i)); var param = (Parameter) visit(ctx.params.parameter(i));
params[i] = param; params[i] = param;
// add the param as a variable // add the param as a variable
VariableDeclaration var = new VariableDeclaration(param.name); var var = new VariableDeclaration(param.name);
var.initialized = true; // parameters can always be considered initialized var.initialized = true; // parameters can always be considered initialized
var.type = param.type; var.type = param.type;
this.vars.put(param.name, var); vars.put(param.name, var);
} }
// 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) {
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, params, (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();
Type type = Type.getByName(ctx.type_annotation().type().getText());
if (!type.isPrimitiveType() && this.structs.get(type.getName()) == null) { if (structDefs.containsKey(parameterName)) {
String error = "Type " + type.getName() + " not defined."; var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var error = "Parameter name " + parameterName + " duplicates a struct of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
Parameter result = new Parameter(name); if (enumDefs.containsKey(parameterName)) {
result.type = type; var line = ctx.start.getLine();
result.line = line; var col = ctx.start.getCharPositionInLine();
result.col = col; var error = "Parameter name " + parameterName + " duplicates an enum of the same name.";
return result; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) {
var line = ctx.type_annotation().start.getLine();
var col = ctx.type_annotation().start.getCharPositionInLine();
var error = "Type " + parameterType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var parameter = new Parameter(parameterName);
parameter.type = parameterType;
parameter.line = ctx.start.getLine();
parameter.col = ctx.start.getCharPositionInLine();
return parameter;
} }
@Override @Override
@@ -797,15 +915,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);
@@ -815,14 +933,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;
@@ -835,7 +953,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);

View 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);
functionDef.type = Type.getByName(ctx.returnType.type().getText());
functionDef.line = line;
functionDef.col = col;
functionDefs.put(funcName, functionDef);
return null;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,19 +1,22 @@
package de.hsrm.compiler.Klang; package de.hsrm.compiler.Klang;
// import ANTLR's runtime libraries import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import org.antlr.v4.runtime.*; import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import org.antlr.v4.runtime.tree.*;
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import de.hsrm.compiler.Klang.nodes.Node; import de.hsrm.compiler.Klang.nodes.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.visitors.EvalVisitor;
import de.hsrm.compiler.Klang.helper.*; 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 {
@@ -87,22 +90,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());
@@ -122,18 +118,15 @@ public class Klang {
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()); 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());
} }
} }

View File

@@ -0,0 +1,218 @@
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 ret() {
mnemonics.add(new Ret());
}
public void xor(String dataType, String src, String dst) {
mnemonics.add(new Xor(dataType, src, dst));
}
public void neg(String operand) {
mnemonics.add(new Neg(operand));
}
public void functionHead(String functionName) {
mnemonics.add(new FunctionHead(functionName));
}
public void call(String operand) {
mnemonics.add(new Call(operand));
}
public void text(String text) {
mnemonics.add(new Text(text));
}
public void newline() {
mnemonics.add(new Newline());
}
public void cvtsi2sd(String src, String dst) {
mnemonics.add(new Cvtsi2sd(src, dst));
}
public String toAsm() {
StringBuilder sb = new StringBuilder();
mnemonics.stream().forEach(x -> {
for (int i = 0; i < x.indentation; i++) {
sb.append("\t");
}
sb.append(x.toAsm());
sb.append("\n");
});
return sb.toString();
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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;
}
}

View File

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

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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();
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}

View File

@@ -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 + ":";
}
}

View File

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

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

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

View File

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

View File

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

View 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;
}
}

View 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;
}
}

View File

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

View 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;
}
}

View 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;
}
}

View File

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

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View 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);
}
}

View File

@@ -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;
} }

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
@@ -33,4 +35,8 @@ public class BooleanType extends PrimitiveType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
} }
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asBoolean() == b.asBoolean();
}
} }

View File

@@ -1,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;
@@ -37,4 +39,8 @@ public class FloatType extends NumericType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
} }
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asFloat() == b.asFloat();
}
} }

View File

@@ -1,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;
@@ -37,4 +39,9 @@ public class IntegerType extends NumericType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
} }
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asInteger() == b.asInteger();
}
} }

View File

@@ -0,0 +1,59 @@
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 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)) {
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) {
var thatType = (NamedType) that;
return getName().equals(thatType.getName());
}
return false;
}
}

View File

@@ -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;
@@ -28,6 +30,11 @@ public class NullType extends Type {
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;

View File

@@ -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;
}
}

View File

@@ -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
@@ -26,12 +33,13 @@ public abstract class Type {
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); 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();
} }

View File

@@ -4,12 +4,7 @@ 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;
@@ -51,25 +46,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
@@ -471,6 +452,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 +475,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 +487,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);

View File

@@ -1,12 +1,6 @@
package de.hsrm.compiler.Klang.visitors; package de.hsrm.compiler.Klang.visitors;
import java.io.*; import de.hsrm.compiler.Klang.asm.ASM;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
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.*;
@@ -16,48 +10,9 @@ 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 java.util.*;
public class GenASM implements Visitor<Void> { public class GenASM implements Visitor<Void> {
public static class ExWriter {
Writer w;
String indent = "";
void addIndent() {
indent = indent + " ";
}
void subIndent() {
indent = indent.substring(2);
}
void nl() {
write("\n" + indent);
}
int lbl = 0;
int next() {
return lbl++;
}
public ExWriter(Writer w) {
this.w = w;
}
void lnwrite(Object o) {
nl();
write(o);
}
void write(Object o) {
try {
w.write(o + "");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private class FloatWriter { private class FloatWriter {
private StringBuilder sb = new StringBuilder(); private StringBuilder sb = new StringBuilder();
private int id = -1; private int id = -1;
@@ -104,7 +59,7 @@ public class GenASM implements Visitor<Void> {
} }
} }
public ExWriter ex; private ASM asm;
private FloatWriter fw = new FloatWriter(); private FloatWriter fw = new FloatWriter();
private String mainName; private String mainName;
Map<String, Integer> env = new HashMap<>(); Map<String, Integer> env = new HashMap<>();
@@ -112,81 +67,81 @@ public class GenASM implements Visitor<Void> {
Set<String> vars; Set<String> vars;
String[] registers = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" }; String[] registers = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" };
String[] floatRegisters = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" }; String[] floatRegisters = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" };
private int lCount = 0; // Invariante: lCount ist benutzt private int lCount = 0; // Invariant: lCount is used
private int currentFunctionStartLabel = 0; private int currentFunctionStartLabel = 0;
private Parameter[] currentFunctionParams; private Parameter[] currentFunctionParams;
private void intToFloat(String src, String dst) {
this.ex.write(" cvtsi2sd " + src + ", " + dst + "\n");
}
private boolean prepareRegisters(Expression lhs, Expression rhs) { private boolean prepareRegisters(Expression lhs, Expression rhs) {
boolean lhsIsFloat = lhs.type.equals(Type.getFloatType()); boolean lhsIsFloat = lhs.type.equals(Type.getFloatType());
boolean rhsIsFloat = rhs.type.equals(Type.getFloatType()); boolean rhsIsFloat = rhs.type.equals(Type.getFloatType());
if (lhsIsFloat && rhsIsFloat) { if (lhsIsFloat && rhsIsFloat) {
lhs.welcome(this); lhs.welcome(this);
this.ex.write(" movsd %xmm0, %xmm2\n"); asm.mov("sd", "%xmm0", "%xmm2");
rhs.welcome(this); rhs.welcome(this);
this.ex.write(" movsd %xmm0, %xmm1\n"); asm.mov("sd", "%xmm2", "%xmm0");
this.ex.write(" movsd %xmm2, %xmm0\n"); asm.mov("sd", "%xmm2", "%xmm0");
return true; return true;
} else if (lhsIsFloat && !rhsIsFloat) { } else if (lhsIsFloat && !rhsIsFloat) {
lhs.welcome(this); lhs.welcome(this);
rhs.welcome(this); rhs.welcome(this);
this.intToFloat("%rax", "%xmm1"); asm.cvtsi2sd("%rax", "%xmm1");
return true; return true;
} else if (!lhsIsFloat && rhsIsFloat) { } else if (!lhsIsFloat && rhsIsFloat) {
lhs.welcome(this); lhs.welcome(this);
this.intToFloat("%rax", "%xmm2"); asm.cvtsi2sd("%rax", "%xmm2");
rhs.welcome(this); rhs.welcome(this);
this.ex.write(" movsd %xmm0, %xmm1\n"); asm.mov("sd", "%xmm0", "%xmm1");
this.ex.write(" movsd %xmm2, %xmm0\n"); asm.mov("sd", "%xmm2", "%xmm0");
return true; return true;
} else { } else {
lhs.welcome(this); lhs.welcome(this);
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
rhs.welcome(this); rhs.welcome(this);
this.ex.write(" movq %rax, %rbx\n"); asm.mov("q", "%rax", "%rbx");
this.ex.write(" popq %rax\n"); asm.pop("q", "%rax");
return false; return false;
} }
} }
public GenASM(ExWriter ex, String mainName, Map<String, StructDefinition> structs) { public GenASM(String mainName, Map<String, StructDefinition> structs) {
this.ex = ex;
this.mainName = mainName; this.mainName = mainName;
this.structs = structs; this.structs = structs;
this.asm = new ASM();
} }
public GenASM(ExWriter ex, Map<String, StructDefinition> structs) { public GenASM(Map<String, StructDefinition> structs) {
this(ex, "main", structs); this("main", structs);
}
public String toAsm() {
return asm.toAsm();
} }
@Override @Override
public Void visit(IntegerExpression e) { public Void visit(IntegerExpression e) {
this.ex.write(" movq $" + e.value + ", %rax\n"); asm.mov("q", e.value, "%rax");
return null; return null;
} }
@Override @Override
public Void visit(FloatExpression e) { public Void visit(FloatExpression e) {
String floatLabel = fw.getFloat(e.value); String floatLabel = fw.getFloat(e.value);
this.ex.write(" movsd " + floatLabel + "(%rip), %xmm0\n"); asm.mov("sd", floatLabel, "%rip", "%xmm0");
return null; return null;
} }
@Override @Override
public Void visit(BooleanExpression e) { public Void visit(BooleanExpression e) {
this.ex.write(" movq $" + (e.value ? 1 : 0) + ", %rax\n"); asm.mov("q", e.value ? 1 : 0, "%rax");
return null; return null;
} }
@Override @Override
public Void visit(Variable e) { public Void visit(Variable e) {
if (e.type.equals(Type.getFloatType())) { if (e.type.equals(Type.getFloatType())) {
this.ex.write(" movsd " + this.env.get(e.name) + "(%rbp), %xmm0\n"); asm.mov("sd", this.env.get(e.name), "%rbp", "%xmm0");
} else { } else {
this.ex.write(" movq " + this.env.get(e.name) + "(%rbp), %rax\n"); asm.mov("q", this.env.get(e.name), "%rbp", "%rax");
} }
return null; return null;
} }
@@ -198,18 +153,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n"); asm.ucomi("sd", "%xmm1", "xmm0");
} else { } else {
this.ex.write(" cmp %rbx, %rax\n"); asm.cmp("q", "%rbx", "%rax");
} }
this.ex.write(" je .L" + lblTrue + "\n"); asm.je(lblTrue);
// false // false
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
// true // true
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -220,18 +175,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" ucomisd %xmm0, %xmm1\n"); asm.ucomi("sd", "%xmm0", "%xmm1");
} else { } else {
this.ex.write(" cmp %rax, %rbx\n"); asm.cmp("q", "%rax", "%rbx");
} }
this.ex.write(" jne .L" + lblTrue + "\n"); asm.jne(lblTrue);
// false // false
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
// true // true
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -242,18 +197,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n"); asm.ucomi("sd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" cmp %rbx, %rax\n"); asm.cmp("q", "%rbx", "%rax");
} }
this.ex.write(" jg .L" + lblTrue + "\n"); asm.jg(lblTrue);
// false // false
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
// true // true
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -264,18 +219,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n"); asm.ucomi("sd", "%xmm1", "xmm0");
} else { } else {
this.ex.write(" cmp %rbx, %rax\n"); asm.cmp("q", "%rbx", "%rax");
} }
this.ex.write(" jge .L" + lblTrue + "\n"); asm.jge(lblTrue);
// false // false
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
// true // true
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -286,18 +241,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n"); asm.ucomi("sd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" cmp %rbx, %rax\n"); asm.cmp("q", "%rbx", "%rax");
} }
this.ex.write(" jl .L" + lblTrue + "\n"); asm.jl(lblTrue);
// false // false
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
// true // true
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -308,18 +263,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n"); asm.ucomi("sd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" cmp %rbx, %rax\n"); asm.cmp("q", "%rbx", "%rax");
} }
this.ex.write(" jle .L" + lblTrue + "\n"); asm.jle(lblTrue);
// false // false
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
// true // true
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -327,9 +282,9 @@ public class GenASM implements Visitor<Void> {
public Void visit(AdditionExpression e) { public Void visit(AdditionExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" addsd %xmm1, %xmm0\n"); asm.add("sd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" addq %rbx, %rax\n"); asm.add("q", "%rbx", "%rax");
} }
return null; return null;
} }
@@ -338,9 +293,9 @@ public class GenASM implements Visitor<Void> {
public Void visit(SubstractionExpression e) { public Void visit(SubstractionExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" subsd %xmm1, %xmm0\n"); asm.sub("sd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" subq %rbx, %rax\n"); asm.sub("q", "%rbx", "%rax");
} }
return null; return null;
} }
@@ -349,9 +304,9 @@ public class GenASM implements Visitor<Void> {
public Void visit(MultiplicationExpression e) { public Void visit(MultiplicationExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" mulsd %xmm1, %xmm0\n"); asm.mul("sd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" imulq %rbx, %rax\n"); asm.imul("q", "%rbx", "%rax");
} }
return null; return null;
} }
@@ -360,10 +315,10 @@ public class GenASM implements Visitor<Void> {
public Void visit(DivisionExpression e) { public Void visit(DivisionExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs); boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) { if (isFloatOperation) {
this.ex.write(" divsd %xmm1, %xmm0\n"); asm.div("sd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" cqto\n"); // sign extend rax into rdx since we're dealing with signed values asm.cqto();
this.ex.write(" idiv %rbx\n"); // %rax/%rbx, quotient now in %rax asm.idiv("q", "%rbx");
} }
return null; return null;
} }
@@ -371,13 +326,13 @@ public class GenASM implements Visitor<Void> {
@Override @Override
public Void visit(ModuloExpression e) { public Void visit(ModuloExpression e) {
e.lhs.welcome(this); e.lhs.welcome(this);
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
e.rhs.welcome(this); e.rhs.welcome(this);
this.ex.write(" movq %rax, %rbx\n"); asm.mov("q", "%rax", "%rbx");
this.ex.write(" popq %rax\n"); asm.pop("q", "%rax");
this.ex.write(" cqto\n"); // sign extend rax into rdx since we're dealing with signed values asm.cqto();
this.ex.write(" idiv %rbx\n"); // %rax/%rbx, remainder now in %rdx asm.idiv("q", "%rbx");
this.ex.write(" movq %rdx, %rax\n"); asm.mov("q", "%rdx", "%rax");
return null; return null;
} }
@@ -386,10 +341,10 @@ public class GenASM implements Visitor<Void> {
e.lhs.welcome(this); e.lhs.welcome(this);
if (e.lhs.type.equals(Type.getFloatType())) { if (e.lhs.type.equals(Type.getFloatType())) {
String floatLabel = fw.getNegateFloat(); String floatLabel = fw.getNegateFloat();
this.ex.write(" movsd " + floatLabel + "(%rip), %xmm1\n"); asm.mov("sd", floatLabel, "%rip", "%xmm1");
this.ex.write(" xorpd %xmm1, %xmm0\n"); asm.xor("pd", "%xmm1", "%xmm0");
} else { } else {
this.ex.write(" neg %rax\n"); asm.neg("%rax");
} }
return null; return null;
} }
@@ -404,28 +359,28 @@ public class GenASM implements Visitor<Void> {
// Wenn LHS != 0 bedeutet das true // Wenn LHS != 0 bedeutet das true
// also können wir direkt sagen dass das Ergebnis true ist // also können wir direkt sagen dass das Ergebnis true ist
e.lhs.welcome(this); e.lhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n"); asm.cmp("q", 0, "%rax");
this.ex.write(" jne .L" + lblTrue + "\n"); asm.jne(lblTrue);
// LHS war false, also werte RHS aus // LHS war false, also werte RHS aus
// Wenn RHS == 0 bedeutet das false, // Wenn RHS == 0 bedeutet das false,
// also ist das Gesamtergebnis false // also ist das Gesamtergebnis false
e.rhs.welcome(this); e.rhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n"); asm.cmp("q", 0, "%rax");
this.ex.write(" je .L" + lblFalse + "\n"); asm.je(lblFalse);
// Die Expression wertet zu true aus // Die Expression wertet zu true aus
// Springe am false Teil vorbei // Springe am false Teil vorbei
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
// Die Expressoin wertet zu false aus // Die Expressoin wertet zu false aus
this.ex.write(".L" + lblFalse + ":\n"); asm.label(lblFalse);
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
// Das hier ist das ende // Das hier ist das ende
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -439,28 +394,28 @@ public class GenASM implements Visitor<Void> {
// Wenn LHS == 0, bedeutet das false // Wenn LHS == 0, bedeutet das false
// also können wir direkt sagen dass das Ergebnis false ist // also können wir direkt sagen dass das Ergebnis false ist
e.lhs.welcome(this); e.lhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n"); asm.cmp("q", 0, "%rax");
this.ex.write(" je .L" + lblFalse + "\n"); asm.je(lblFalse);
// LHS war true, also werte RHS aus. // LHS war true, also werte RHS aus.
// Wenn RHS == 0, bedeutet das false // Wenn RHS == 0, bedeutet das false
// also ist das Gesamtergebnis false // also ist das Gesamtergebnis false
e.rhs.welcome(this); e.rhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n"); asm.cmp("q", 0, "%rax");
this.ex.write(" je .L" + lblFalse + "\n"); asm.je(lblFalse);
// Die Expression wertet zu true aus // Die Expression wertet zu true aus
// Springe am false Teil vorbei // Springe am false Teil vorbei
this.ex.write(".L" + lblTrue + ":\n"); asm.label(lblTrue);
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
// Die Expressoin wertet zu false aus // Die Expressoin wertet zu false aus
this.ex.write(".L" + lblFalse + ":\n"); asm.label(lblFalse);
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0, "%rax");
// Das hier ist das ende // Das hier ist das ende
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -473,21 +428,21 @@ public class GenASM implements Visitor<Void> {
// Wenn LHS != 0 bedeutet das true, also jumpe zum false Teil // Wenn LHS != 0 bedeutet das true, also jumpe zum false Teil
// Wenn nicht, falle durch zum true Teil // Wenn nicht, falle durch zum true Teil
e.lhs.welcome(this); e.lhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n"); asm.cmp("q", 0, "%rax");
this.ex.write(" jne .L" + lblFalse + "\n"); asm.jne(lblFalse);
// Hier ist das Ergebnis true // Hier ist das Ergebnis true
// Springe am false Teil vorbei // Springe am false Teil vorbei
this.ex.write(" movq $1, %rax\n"); asm.mov("q", 1, "%rax");
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
// Hier ist das Ergebnis false // Hier ist das Ergebnis false
// Falle zum Ende durch // Falle zum Ende durch
this.ex.write(".L" + lblFalse + ":\n"); asm.label(lblFalse);
this.ex.write("movq $0, %rax\n"); asm.mov("q", 0, "%rax");
// Hier ist das Ende // Hier ist das Ende
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -497,25 +452,25 @@ public class GenASM implements Visitor<Void> {
int lblEnd = ++lCount; int lblEnd = ++lCount;
boolean hasElse = e.alt != null || e.elif != null; boolean hasElse = e.alt != null || e.elif != null;
e.cond.welcome(this); e.cond.welcome(this);
this.ex.write(" cmp $0, %rax\n"); asm.cmp("", 0, "%rax");
// in case of cond evaluating to false, jump to else/elif // in case of cond evaluating to false, jump to else/elif
// Jump to end if there is no else part, this saves a label declaration // Jump to end if there is no else part, this saves a label declaration
if (hasElse) { if (hasElse) {
this.ex.write(" jz .L" + lblElse + "\n"); asm.jz(lblElse);
} else { } else {
this.ex.write(" jz .L" + lblEnd + "\n"); asm.jz(lblEnd);
} }
e.then.welcome(this); e.then.welcome(this);
if (hasElse) { if (hasElse) {
this.ex.write(" jmp .L" + lblEnd + "\n"); asm.jmp(lblEnd);
this.ex.write(".L" + lblElse + ":\n"); asm.label(lblElse);
if (e.alt != null) { if (e.alt != null) {
e.alt.welcome(this); e.alt.welcome(this);
} else { } else {
e.elif.welcome(this); e.elif.welcome(this);
} }
} }
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -523,24 +478,24 @@ public class GenASM implements Visitor<Void> {
public Void visit(WhileLoop e) { public Void visit(WhileLoop e) {
int lblCond = ++lCount; int lblCond = ++lCount;
int lblEnd = ++lCount; int lblEnd = ++lCount;
this.ex.write(".L" + lblCond + ":\n"); asm.label(lblCond);
e.cond.welcome(this); e.cond.welcome(this);
this.ex.write(" cmp $0, %rax\n"); asm.cmp("", 0, "%rax");
this.ex.write(" jz .L" + lblEnd + "\n"); asm.jz(lblEnd);
e.block.welcome(this); e.block.welcome(this);
this.ex.write(" jmp .L" + lblCond + "\n"); asm.jmp(lblCond);
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@Override @Override
public Void visit(DoWhileLoop e) { public Void visit(DoWhileLoop e) {
int lblStart = ++lCount; int lblStart = ++lCount;
this.ex.write(".L" + lblStart + ":\n"); asm.label(lblStart);
e.block.welcome(this); e.block.welcome(this);
e.cond.welcome(this); e.cond.welcome(this);
this.ex.write(" cmp $0, %rax\n"); asm.cmp("", 0, "%rax");
this.ex.write(" jnz .L" + lblStart + "\n"); asm.jnz(lblStart);
return null; return null;
} }
@@ -549,15 +504,14 @@ public class GenASM implements Visitor<Void> {
int lblStart = ++lCount; int lblStart = ++lCount;
int lblEnd = ++lCount; int lblEnd = ++lCount;
e.init.welcome(this); e.init.welcome(this);
this.ex.write(".L" + lblStart + ":\n"); asm.label(lblStart);
e.condition.welcome(this); e.condition.welcome(this);
this.ex.write(" cmp $0, %rax\n"); asm.cmp("", 0, "%rax");
this.ex.write(" jz .L" + lblEnd + "\n"); asm.jz(lblEnd);
e.block.welcome(this); e.block.welcome(this);
e.step.welcome(this); e.step.welcome(this);
this.ex.write(" jmp .L" + lblStart + "\n"); asm.jmp(lblStart);
this.ex.write(".L" + lblEnd + ":\n"); asm.label(lblEnd);
return null; return null;
} }
@@ -568,12 +522,12 @@ public class GenASM implements Visitor<Void> {
if (e.expression != null) { if (e.expression != null) {
e.expression.welcome(this); e.expression.welcome(this);
int offset = this.env.get(e.name); int offset = this.env.get(e.name);
if (e.expression.type.equals(Type.getFloatType())) { if (e.expression.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n"); asm.mov("q", "%xmm0", "%rax");
} }
this.ex.write(" movq %rax, " + offset + "(%rbp)\n"); asm.mov("q", "%rax", offset, "%rbp");
} }
return null; return null;
} }
@@ -586,20 +540,20 @@ public class GenASM implements Visitor<Void> {
// Determine where the result of this expression was placed into // Determine where the result of this expression was placed into
// and move it onto the stack from there // and move it onto the stack from there
if (e.expression.type.equals(Type.getFloatType())) { if (e.expression.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, " + offset + "(%rbp)\n"); asm.mov("q", "%xmm0", offset, "%rbp");
} else { } else {
this.ex.write(" movq %rax, " + offset + "(%rbp)\n"); asm.mov("q", "%rax", offset, "%rbp");
} }
return null; return null;
} }
@Override @Override
public Void visit(ReturnStatement e) { public Void visit(ReturnStatement e) {
e.expression.welcome(this); e.expression.welcome(this);
this.ex.write(" movq %rbp, %rsp\n"); asm.mov("q", "%rbp", "%rsp");
this.ex.write(" popq %rbp\n"); asm.pop("q", "%rbp");
this.ex.write(" ret\n"); asm.ret();
return null; return null;
} }
@@ -613,15 +567,20 @@ public class GenASM implements Visitor<Void> {
@Override @Override
public Void visit(FunctionDefinition e) { public Void visit(FunctionDefinition e) {
// If the user chooses "main" as one of his function names then
// rename it and hope that they didn't use the renamed function name
// as well :D
if (e.name.equals("main")) {
e.name = "main_by_user";
}
int lblStart = ++lCount; int lblStart = ++lCount;
this.currentFunctionStartLabel = lblStart; this.currentFunctionStartLabel = lblStart;
this.currentFunctionParams = e.parameters; this.currentFunctionParams = e.parameters;
this.ex.write(".globl " + e.name + "\n"); asm.functionHead(e.name);
this.ex.write(".type " + e.name + ", @function\n"); asm.push("q", "%rbp");
this.ex.write(e.name + ":\n"); asm.mov("q", "%rsp", "%rbp");
this.ex.write(" pushq %rbp\n"); asm.label(lblStart);
this.ex.write(" movq %rsp, %rbp\n");
this.ex.write(".L" + lblStart + ":\n");
// hole die anzahl der lokalen variablen // hole die anzahl der lokalen variablen
this.vars = new TreeSet<String>(); this.vars = new TreeSet<String>();
@@ -661,19 +620,19 @@ public class GenASM implements Visitor<Void> {
} }
} }
} }
offset = 0; offset = 0;
ri = 0; ri = 0;
fi = 0; fi = 0;
for (var param: registerParameters) { for (var param: registerParameters) {
if (param.type.equals(Type.getFloatType())) { if (param.type.equals(Type.getFloatType())) {
this.ex.write(" movq "+ this.floatRegisters[fi] + ", %rax\n"); asm.mov("q", this.floatRegisters[fi], "%rax");
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
offset -= 8; offset -= 8;
this.env.put(param.name, offset); // negative, liegt unter aktuellem BP this.env.put(param.name, offset); // negative, liegt unter aktuellem BP
fi++; fi++;
} else { } else {
this.ex.write(" pushq " + this.registers[ri] + "\n"); asm.push("q", this.registers[ri]);
offset -= 8; offset -= 8;
this.env.put(param.name, offset); // negative, liegt unter aktuellem BP this.env.put(param.name, offset); // negative, liegt unter aktuellem BP
ri++; ri++;
@@ -683,7 +642,7 @@ public class GenASM implements Visitor<Void> {
// Reserviere Platz auf dem Stack für jede lokale variable // Reserviere Platz auf dem Stack für jede lokale variable
for (String lok_var : vars) { for (String lok_var : vars) {
offset -= 8; offset -= 8;
this.ex.write(" pushq $0\n"); asm.push("q", 0);
this.env.put(lok_var, offset); this.env.put(lok_var, offset);
} }
@@ -700,18 +659,18 @@ public class GenASM implements Visitor<Void> {
e.arguments[i].welcome(this); e.arguments[i].welcome(this);
if (e.arguments[i].type.equals(Type.getFloatType())) { if (e.arguments[i].type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n"); asm.mov("q", "%xmm0", "%rax0");
} }
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
} }
// push args into local var locations, last arg is ontop of the stack // push args into local var locations, last arg is ontop of the stack
for (int i = e.arguments.length - 1; i >= 0; i--) { for (int i = e.arguments.length - 1; i >= 0; i--) {
this.ex.write(" popq " + this.env.get(this.currentFunctionParams[i].name) + "(%rbp)\n"); asm.pop("q", this.env.get(this.currentFunctionParams[i].name) + "(%rbp)");
} }
this.ex.write(" jmp .L" + this.currentFunctionStartLabel + "\n"); asm.jmp(this.currentFunctionStartLabel);
return null; return null;
} }
@@ -724,12 +683,12 @@ public class GenASM implements Visitor<Void> {
// Mapping arguments index -> all purpose registers index // Mapping arguments index -> all purpose registers index
int[] rIdxs = new int[this.registers.length]; int[] rIdxs = new int[this.registers.length];
int ri = -1; int ri = -1;
// Mapping arguments index -> stack // Mapping arguments index -> stack
ArrayList<Integer> stackIdxs = new ArrayList<Integer>(); ArrayList<Integer> stackIdxs = new ArrayList<Integer>();
// Go through arguments // Go through arguments
// sort them into the memory regions they go when being passed to functions // sort them into the memory regions they go when being passed to functions
for (int i = 0; i < e.arguments.length; i++) { for (int i = 0; i < e.arguments.length; i++) {
var arg = e.arguments[i]; var arg = e.arguments[i];
if (arg.type.equals(Type.getFloatType())) { if (arg.type.equals(Type.getFloatType())) {
@@ -757,10 +716,10 @@ public class GenASM implements Visitor<Void> {
for (var arg : e.arguments) { for (var arg : e.arguments) {
arg.welcome(this); arg.welcome(this);
if (arg.type.equals(Type.getFloatType())) { if (arg.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n"); asm.mov("q", "%xmm0", "%rax");
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
} else { } else {
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
} }
} }
@@ -768,14 +727,14 @@ public class GenASM implements Visitor<Void> {
for (int i = 0; i <= fi; i++) { for (int i = 0; i <= fi; i++) {
int indexInArguments = xmmIdxs[i]; int indexInArguments = xmmIdxs[i];
int rspOffset = (((e.arguments.length - indexInArguments) - 1) * 8); int rspOffset = (((e.arguments.length - indexInArguments) - 1) * 8);
this.ex.write(" movsd " + rspOffset + "(%rsp), " + this.floatRegisters[i] + "\n"); asm.mov("sd", rspOffset, "%rsp", this.floatRegisters[i]);
} }
// Move primitives from stack to all purpose registers // Move primitives from stack to all purpose registers
for (int i = 0; i <= ri; i++) { for (int i = 0; i <= ri; i++) {
int indexInArguments = rIdxs[i]; int indexInArguments = rIdxs[i];
int rspOffset = (((e.arguments.length - indexInArguments) - 1) * 8); int rspOffset = (((e.arguments.length - indexInArguments) - 1) * 8);
this.ex.write(" movq " + rspOffset + "(%rsp), " + this.registers[i] + "\n"); asm.mov("q", rspOffset, "%rsp", this.registers[i]);
} }
// Move everything else from a higher stack position to our stack frame start // Move everything else from a higher stack position to our stack frame start
@@ -784,49 +743,55 @@ public class GenASM implements Visitor<Void> {
stackStartOffset -= 8; stackStartOffset -= 8;
int indexInArguments = stackIdxs.get(i); int indexInArguments = stackIdxs.get(i);
int rspOffset = (((e.arguments.length - indexInArguments) - 1) * 8); int rspOffset = (((e.arguments.length - indexInArguments) - 1) * 8);
this.ex.write(" movq " + rspOffset + "(%rsp), %rax\n"); asm.mov("q", rspOffset, "%rsp", "%rax");
this.ex.write(" movq %rax, " + stackStartOffset + "(%rsp)\n"); asm.mov("q", "%rax", stackStartOffset, "%rsp");
} }
// Rescue RSP // Rescue RSP
this.ex.write(" addq $" + stackStartOffset + ", %rsp\n"); asm.add("q", stackStartOffset, "%rsp");
} }
this.ex.write(" call " + e.name + "\n"); // We rename a function name if it is "main"
asm.call(e.name.equals("main") ? "main_by_user": e.name);
return null; return null;
} }
@Override @Override
public Void visit(Program e) { public Void visit(Program e) {
this.ex.write(".text\n"); asm.text(".text");
for (var func : e.funcs) { for (var func : e.funcs) {
func.welcome(this); func.welcome(this);
this.ex.write("\n"); asm.newline();
} }
this.ex.write(".globl " + mainName + "\n"); asm.functionHead(mainName);
this.ex.write(".type " + mainName + ", @function\n"); asm.push("q", "%rbp");
this.ex.write(mainName + ":\n"); asm.mov("q", "%rsp", "%rbp");
this.ex.write(" pushq %rbp\n");
this.ex.write(" movq %rsp, %rbp\n");
e.expression.welcome(this); e.expression.welcome(this);
asm.mov("q", "%rbp", "%rsp");
asm.pop("q", "%rbp");
asm.ret();
this.ex.write(" movq %rbp, %rsp\n"); asm.text(fw.getFloatSection());
this.ex.write(" popq %rbp\n");
this.ex.write(" ret\n");
// PRINT FLOATS HERE
this.ex.write("\n");
this.ex.write(fw.getFloatSection());
return null; return null;
} }
@Override @Override
public Void visit(Parameter e) { public Void visit(Parameter e) {
// The work for a paremeter node is implement // The work for a parameter node is implement
// in the function definition visitor // in the function definition visitor
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) {
// We get these from a previous visitor // We get these from a previous visitor
@@ -840,31 +805,39 @@ public class GenASM implements Visitor<Void> {
} }
@Override @Override
public Void visit(StructFieldAccessExpression e) { public Void visit(MemberAccessExpression e) {
var structDef = this.structs.get(e.structName); var structDef = this.structs.get(e.structName);
int offset = this.env.get(e.varName); int offset = this.env.get(e.varName);
// move struct address into rax // move struct address into rax
this.ex.write(" movq " + offset + "(%rbp), %rax\n"); asm.mov("q", offset, "%rbp", "%rax");
// "follow" the first path element by moving the referenced value into rax // "follow" the first path element by moving the referenced value into rax
this.ex.write(" movq " + Helper.getFieldOffset(structDef, e.path[0]) + "(%rax), %rax\n"); asm.mov("q", Helper.getFieldOffset(structDef, e.path[0]), "%rax", "%rax");
for (int i = 1; i < e.path.length; i++) { for (int i = 1; i < e.path.length; i++) {
// "follow" the current path element // "follow" the current path element
structDef = this.structs.get(structDef.fields[Helper.getFieldIndex(structDef, e.path[i - 1])].type.getName()); structDef = this.structs.get(structDef.fields[Helper.getFieldIndex(structDef, e.path[i - 1])].type.getName());
this.ex.write(" movq " + Helper.getFieldOffset(structDef, e.path[i]) + "(%rax), %rax\n"); asm.mov("q", Helper.getFieldOffset(structDef, e.path[i]), "%rax", "%rax");
} }
// desired value now in rax // desired value now in rax
// push rax to xmm0 if the result type is a float // push rax to xmm0 if the result type is a float
if (e.type.equals(Type.getFloatType())) { if (e.type.equals(Type.getFloatType())) {
this.ex.write(" movq %rax, %xmm0\n"); asm.mov("q", "%rax", "%xmm0");
} }
return null; return null;
} }
@Override
public Void visit(EnumAccessExpression e) {
// Since the access to an enum simply results in an integer (i.e. the index
// of the enum value), we can just let IntegerExpression handle the expected behaviour.
new IntegerExpression(e.enumValue.index).welcome(this);
return null;
}
@Override @Override
public Void visit(ConstructorCall e) { public Void visit(ConstructorCall e) {
// push arguments onto the stack // push arguments onto the stack
@@ -873,20 +846,20 @@ public class GenASM implements Visitor<Void> {
// move float values from xmm0 to rax first // move float values from xmm0 to rax first
if (arg.type.equals(Type.getFloatType())) { if (arg.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n"); asm.mov("q", "%xmm0", "%rax");
} }
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
} }
// allocate heap memory by calling malloc // allocate heap memory by calling malloc
var structDef = this.structs.get(e.structName); var structDef = this.structs.get(e.structName);
this.ex.write(" movl $" + Helper.getFieldSizeBytes(structDef) + ", %edi\n"); asm.mov("l", Helper.getFieldSizeBytes(structDef), "%edi");
this.ex.write(" call malloc@PLT\n"); // struct address now in rax asm.call("malloc@PLT");
// push args into struct memory, last arg is ontop of the stack // push args into struct memory, last arg is ontop of the stack
for (int i = e.args.length - 1; i >= 0; i--) { for (int i = e.args.length - 1; i >= 0; i--) {
this.ex.write(" popq " + Helper.getFieldOffset(structDef, i) + "(%rax)\n"); asm.pop("q", Helper.getFieldOffset(structDef, i) + "(%rax)");
} }
return null; return null;
@@ -894,14 +867,14 @@ public class GenASM implements Visitor<Void> {
@Override @Override
public Void visit(NullExpression e) { public Void visit(NullExpression e) {
this.ex.write(" movq $0, %rax\n"); asm.mov("q", 0 , "%rax");
return null; return null;
} }
@Override @Override
public Void visit(DestructorCall e) { public Void visit(DestructorCall e) {
this.ex.write(" movq " +this.env.get(e.name) + "(%rbp), %rdi\n"); asm.mov("q", this.env.get(e.name), "%rbp", "%rdi");
this.ex.write(" call free@PLT\n"); asm.call("free@PLT");
return null; return null;
} }
@@ -915,25 +888,25 @@ public class GenASM implements Visitor<Void> {
// Move it from xmm0 rax if its a flaot // Move it from xmm0 rax if its a flaot
if (e.expression.type.equals(Type.getFloatType())) { if (e.expression.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n"); asm.mov("q", "%xmm0", "%rax");
} }
// Push the expression onto the stack // Push the expression onto the stack
this.ex.write(" pushq %rax\n"); asm.push("q", "%rax");
// move struct address into rax // move struct address into rax
this.ex.write(" movq " + offset + "(%rbp), %rax\n"); asm.mov("q", offset, "%rbp", "%rax");
// If there are at least two elements in the path, // If there are at least two elements in the path,
// move the address of the next referenced struct into rax // move the address of the next referenced struct into rax
for (int i = 1; i < e.path.length - 1; i++) { for (int i = 1; i < e.path.length - 1; i++) {
structDef = this.structs.get(structDef.fields[Helper.getFieldIndex(structDef, e.path[i - 1])].type.getName()); structDef = this.structs.get(structDef.fields[Helper.getFieldIndex(structDef, e.path[i - 1])].type.getName());
this.ex.write(" movq " + Helper.getFieldOffset(structDef, e.path[i]) + "(%rax), %rax\n"); asm.mov("q", Helper.getFieldOffset(structDef, e.path[i]), "%rax", "%rax");
} }
// pop the expression that is ontop of the stack into the field of the struct that has to be updated // pop the expression that is ontop of the stack into the field of the struct that has to be updated
this.ex.write(" popq " + Helper.getFieldOffset(structDef, fieldNameToUpdate) + "(%rax)\n"); asm.pop("q", Helper.getFieldOffset(structDef, fieldNameToUpdate) + "(%rax)");
this.ex.write(" movq $0, %rax\n"); // clear rax sind an assignment has no result asm.mov("q", 0 , "%rax"); // clear rax since an assignment has no result
return null; return null;
} }

View File

@@ -234,6 +234,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 +255,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;
} }

View File

@@ -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;
@@ -377,6 +383,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 +426,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 +435,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 + "(");

View File

@@ -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);

View File

@@ -12,7 +12,8 @@ public class AndTest {
ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 && 2; } foo();"); ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 && 2; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:30 && is only defined for bool.", e.getMessage()); assertEquals("Error in line 1:30 && is only defined for bool.", e.getMessage());

View File

@@ -13,7 +13,8 @@ public class ConstructorCallTest {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create schwurbel(1); } foo();"); ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create schwurbel(1); } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:52 Struct with name \"schwurbel\" not defined.", e.getMessage()); assertEquals("Error in line 1:52 Struct with name \"schwurbel\" not defined.", e.getMessage());
@@ -24,7 +25,8 @@ public class ConstructorCallTest {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();"); ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); 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()); assertEquals("Error in line 1:52 Struct \"bar\" defined 1 fields, but got 2 constructor parameters.", e.getMessage());
@@ -35,7 +37,8 @@ public class ConstructorCallTest {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(false); } foo();"); ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(false); } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:63 argument 0 Type missmatch: cannot combine bool and int", e.getMessage()); assertEquals("Error in line 1:63 argument 0 Type missmatch: cannot combine bool and int", e.getMessage());

View File

@@ -13,7 +13,8 @@ public class DestroyStatementTest {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { destroy x; return 1; } foo();"); ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { destroy x; return 1; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:45 Variable with name \"x\" not defined.", e.getMessage()); assertEquals("Error in line 1:45 Variable with name \"x\" not defined.", e.getMessage());

View File

@@ -13,7 +13,8 @@ public class FieldAssignmentTest {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { str.a = 1; return 1; } foo();"); ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { str.a = 1; return 1; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:46 Variable with name str not defined.", e.getMessage()); assertEquals("Error in line 1:46 Variable with name str not defined.", e.getMessage());
@@ -24,7 +25,8 @@ public class FieldAssignmentTest {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { let x: int = 0; x.a = 0; return 1; } foo();"); 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 funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:62 Variable must reference a struct but references int.", e.getMessage()); assertEquals("Error in line 1:62 Variable must reference a struct but references int.", e.getMessage());

View File

@@ -13,7 +13,8 @@ public class FunctionCallTest {
ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } bar();"); ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } bar();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:34 Function with name \"bar\" not defined.", e.getMessage()); assertEquals("Error in line 1:34 Function with name \"bar\" not defined.", e.getMessage());
@@ -24,7 +25,8 @@ public class FunctionCallTest {
ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } foo(5);"); ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } foo(5);");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:34 Function \"foo\" expects 0 parameters, but got 1.", e.getMessage()); assertEquals("Error in line 1:34 Function \"foo\" expects 0 parameters, but got 1.", e.getMessage());
@@ -35,7 +37,8 @@ public class FunctionCallTest {
ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);"); ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:40 argument 0 Expected int but got: bool", e.getMessage()); assertEquals("Error in line 1:40 argument 0 Expected int but got: bool", e.getMessage());

View File

@@ -1,32 +1,95 @@
import static org.junit.jupiter.api.Assertions.assertEquals; import de.hsrm.compiler.Klang.ContextAnalysis;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis; import static org.junit.jupiter.api.Assertions.*;
public class FunctionDefinitionTest { public class FunctionDefinitionTest {
@Test @Test
void typeNotDefined() { void shouldNotThrowIfReturnTypeIsReferringToAnEnum() {
ParseTree tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();"); // given
var funcs = Helper.getFuncs(tree); var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo(1);");
var structs = Helper.getStructs(tree); var ctxAnal = new ContextAnalysis(
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); Helper.getFuncs(tree),
Helper.getStructs(tree),
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Helper.getEnums(tree)
assertEquals("Error in line 1:0 Type schwurbel not defined.", e.getMessage()); );
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
} }
@Test @Test
void noReturnExpression() { void shouldNotThrowIfParameterTypeIsReferringToAnEnum() {
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();"); // given
var funcs = Helper.getFuncs(tree); var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: bar): int { return 1; } foo(bar.A);");
var structs = Helper.getStructs(tree); var ctxAnal = new ContextAnalysis(
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); Helper.getFuncs(tree),
Helper.getStructs(tree),
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Helper.getEnums(tree)
assertEquals("Error in line 1:0 Function foo has to return something of type int.", e.getMessage()); );
// 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());
} }
} }

View File

@@ -16,21 +16,21 @@ public class Helper {
return parser.parse(); return parser.parse();
} }
public static Map<String, FunctionInformation> getFuncs(ParseTree tree) { public static Map<String, FunctionDefinition> getFuncs(ParseTree tree) {
var functionDefinitions = new HashMap<String, FunctionInformation>(); var functionDefinitions = new HashMap<String, FunctionDefinition>();
new GetFunctions(functionDefinitions).visit(tree); new GetDefinitions(functionDefinitions, new HashMap<>(), new HashMap<>()).visit(tree);
return functionDefinitions; return functionDefinitions;
} }
public static Set<String> getStructNames(ParseTree tree) {
var structNames = new HashSet<String>();
new GetStructNames(structNames).visit(tree);
return structNames;
}
public static Map<String, StructDefinition> getStructs(ParseTree tree) { public static Map<String, StructDefinition> getStructs(ParseTree tree) {
var structs = new HashMap<String, StructDefinition>(); var structs = new HashMap<String, StructDefinition>();
new GetStructs(getStructNames(tree), structs).visit(tree); new GetDefinitions(new HashMap<>(), structs, new HashMap<>()).visit(tree);
return structs; 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;
}
} }

View File

@@ -12,7 +12,8 @@ public class ModuloTest {
ParseTree tree = Helper.prepareParser("function foo(): float { return 1.0 % 2.3; } foo();"); ParseTree tree = Helper.prepareParser("function foo(): float { return 1.0 % 2.3; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:31 Only integers are allowed for modulo.", e.getMessage()); assertEquals("Error in line 1:31 Only integers are allowed for modulo.", e.getMessage());

View File

@@ -13,7 +13,8 @@ public class OrTest {
ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 || 2; } foo();"); ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 || 2; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:30 || is only defined for bool.", e.getMessage()); assertEquals("Error in line 1:30 || is only defined for bool.", e.getMessage());

View File

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

View File

@@ -0,0 +1,94 @@
import de.hsrm.compiler.Klang.ContextAnalysis;
import de.hsrm.compiler.Klang.GetDefinitions;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import static org.junit.jupiter.api.Assertions.*;
public class StructDefinitionTest {
@Test
void shouldNotThrowIfStructIsWellDefined() {
// given
var tree = Helper.prepareParser("struct test { a: int; } function foo(): int { return 1; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfStructFieldTypeIsReferringToEnum() {
// given
var tree = Helper.prepareParser("struct test { a: bar; } enum bar {A,B,C} function foo(): int { return 1; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfStructFieldTypeIsReferringToStruct() {
// given
var tree = Helper.prepareParser("struct a { hello: int; } struct b { world: a; } function foo(): int { return 1; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldThrowExceptionIfStructFieldNameShadowsAStruct() {
// given
var tree = Helper.prepareParser("struct bar { a: int; } struct baz { bar: int; } function foo(): 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:36 Struct field name bar shadows a struct of the same name.", e.getMessage());
}
@Test
void shouldThrowExceptionIfStructFieldNameShadowsAnEnum() {
// given
var tree = Helper.prepareParser("enum bar { A, B } struct baz { bar: int; } function foo(): 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:31 Struct field name bar shadows an enum of the same name.", e.getMessage());
}
@Test
void shouldThrowExceptionIfStructFieldTypeIsNotDefined() {
// given
var tree = Helper.prepareParser("struct test { a: schwurbel; } function foo(): 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 shouldThrowExceptionIfStructFieldTypeIsReferringToAFunction() {
// given
var tree = Helper.prepareParser("struct test { a: foo; } function foo(): 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 foo not defined.", e.getMessage());
}
@Test
void shouldThrowExceptionIfStructFieldNameIsDuplicated() {
// given
var tree = Helper.prepareParser("struct test { a: int; a: bool; } function foo(): int { return 1; } foo();");
var getDefs = new GetDefinitions(new HashMap<>(), new HashMap<>(), new HashMap<>());
// when / then
var e = assertThrows(RuntimeException.class, () -> getDefs.visit(tree));
assertEquals("Error in line 1:22 Duplicate struct field a in struct test.", e.getMessage());
}
}

View File

@@ -12,7 +12,8 @@ public class StructFieldAccessTest {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { return str.a; } foo();"); ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { return str.a; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:53 Variable with name str not defined.", e.getMessage()); assertEquals("Error in line 1:53 Variable with name str not defined.", e.getMessage());
@@ -23,7 +24,8 @@ public class StructFieldAccessTest {
ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { let x: int = 0; return x.a; } foo();"); ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { let x: int = 0; return x.a; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:69 Variable must reference a struct but references int.", e.getMessage()); assertEquals("Error in line 1:69 Variable must reference a struct but references int.", e.getMessage());

View File

@@ -13,7 +13,8 @@ public class VariableAssignmentTest {
ParseTree tree = Helper.prepareParser("function foo(): int { x = 1; return 1; } foo();"); ParseTree tree = Helper.prepareParser("function foo(): int { x = 1; return 1; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:22 Variable with name \"x\" not defined.", e.getMessage()); assertEquals("Error in line 1:22 Variable with name \"x\" not defined.", e.getMessage());

View File

@@ -1,32 +1,109 @@
import static org.junit.jupiter.api.Assertions.assertEquals; import de.hsrm.compiler.Klang.ContextAnalysis;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis; import static org.junit.jupiter.api.Assertions.*;
public class VariableDeclarationTest { public class VariableDeclarationTest {
@Test @Test
void typeNotDefined() { void shouldNotThrowIfDeclaredTypeIsAStruct() {
ParseTree tree = Helper.prepareParser("function foo(): int { let X: unk; return 1; } foo();"); // given
var funcs = Helper.getFuncs(tree); var tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { let a: bar; return 1; } foo();");
var structs = Helper.getStructs(tree); var ctxAnal = new ContextAnalysis(
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); Helper.getFuncs(tree),
Helper.getStructs(tree),
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Helper.getEnums(tree)
);
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfDeclaredTypeIsAnEnum() {
// given
var tree = Helper.prepareParser("enum bar { A, B } function foo(): int { let a: bar = bar.A; return 1; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldThrowExceptionIfDeclaredNameShadowsEnumName() {
// given
var tree = Helper.prepareParser("enum bar { A, B } function foo(): int { let bar: 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:40 Variable name bar shadows an enum of the same name.", e.getMessage());
}
@Test
void shouldThrowExceptionIfDeclaredNameShadowsStruct() {
// given
var tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { let bar: 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:45 Variable name bar shadows a struct of the same name.", e.getMessage());
}
@Test
void shouldThrowExceptionIfDeclaredTypeIsNotDefined() {
// given
var tree = Helper.prepareParser("function foo(): int { let X: unk; 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:22 Type unk not defined.", e.getMessage()); assertEquals("Error in line 1:22 Type unk not defined.", e.getMessage());
} }
@Test @Test
void variableRedeclaration() void shouldThrowExceptionIfVariableIsRedeclared() {
{ // given
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();"); var tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();");
var funcs = Helper.getFuncs(tree); var ctxAnal = new ContextAnalysis(
var structs = Helper.getStructs(tree); Helper.getFuncs(tree),
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); Helper.getStructs(tree),
Helper.getEnums(tree)
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); );
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:34 Redeclaration of variable with name \"x\".", e.getMessage()); assertEquals("Error in line 1:34 Redeclaration of variable with name \"x\".", e.getMessage());
} }
@Test
void shouldThrowExceptionIfVariableReferencesEnumButIsNotInitialized() {
// given
var tree = Helper.prepareParser("enum bar { A, B } function foo(): int { let x: bar; 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:40 Variable x references an enum but is not initialized.", e.getMessage());
}
} }

View File

@@ -13,7 +13,8 @@ public class VariableTest {
ParseTree tree = Helper.prepareParser("function foo(): int { return x; } foo();"); ParseTree tree = Helper.prepareParser("function foo(): int { return x; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:29 Variable with name \"x\" not defined.", e.getMessage()); assertEquals("Error in line 1:29 Variable with name \"x\" not defined.", e.getMessage());
@@ -24,7 +25,8 @@ public class VariableTest {
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; return x; } foo();"); ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; return x; } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); var enums = Helper.getEnums(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:41 Variable with name \"x\" has not been initialized.", e.getMessage()); assertEquals("Error in line 1:41 Variable with name \"x\" has not been initialized.", e.getMessage());