Compare commits

...

14 Commits

Author SHA1 Message Date
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
86 changed files with 2091 additions and 723 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

@@ -5,7 +5,11 @@ parse
;
program
: (functionDef | structDef)* expression SCOL
: (functionDef | structDef | enumDef)* expression SCOL
;
enumDef
: ENUM enumName=IDENT OBRK (IDENT (COMMA IDENT)*)+ CBRK
;
structDef
@@ -78,7 +82,7 @@ destroy_statement
expression
: atom #atomExpression
| IDENT (PERIOD IDENT)+ #structFieldAccessExpression
| IDENT (PERIOD IDENT)+ #memberAccessExpression
| OPAR expression CPAR #parenthesisExpression
| lhs=expression MUL rhs=expression #multiplicationExpression
| lhs=expression DIV rhs=expression #divisionExpression
@@ -143,6 +147,7 @@ forLoop
IF: 'if';
ELSE: 'else';
FUNC: 'function';
ENUM: 'enum';
STRUCT: 'struct';
RETURN: 'return';
LET: 'let';

View File

@@ -1,9 +1,5 @@
package de.hsrm.compiler.Klang;
import java.util.Map;
import java.util.HashMap;
import de.hsrm.compiler.Klang.helper.FunctionInformation;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*;
@@ -13,10 +9,13 @@ import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
import de.hsrm.compiler.Klang.nodes.statements.*;
import de.hsrm.compiler.Klang.types.Type;
import java.util.*;
public class ContextAnalysis extends KlangBaseVisitor<Node> {
Map<String, VariableDeclaration> vars = new HashMap<>();
Map<String, FunctionInformation> funcs;
Map<String, StructDefinition> structs;
Map<String, FunctionDefinition> functionDefs;
Map<String, StructDefinition> structDefs;
Map<String, EnumDefinition> enumDefs;
Type currentDeclaredReturnType;
String currentFunctionDefinitionName;
@@ -27,25 +26,35 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
}
}
public ContextAnalysis(Map<String, FunctionInformation> funcs, Map<String, StructDefinition> structs) {
this.funcs = funcs;
this.structs = structs;
public ContextAnalysis(
Map<String, FunctionDefinition> functionDefs,
Map<String, StructDefinition> structDefs,
Map<String, EnumDefinition> enumDefs
) {
this.functionDefs = functionDefs;
this.structDefs = structDefs;
this.enumDefs = enumDefs;
}
@Override
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++) {
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
typeCheckedFunctionDefs[i] = (FunctionDefinition) visit(ctx.functionDef(i));
}
Expression expression = (Expression) this.visit(ctx.expression());
Program result = new Program(funcs, this.structs, expression);
result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
for (int i = 0; i < ctx.structDef().size(); i++) {
typeCheckedStructDefs.put(ctx.structDef(i).structName.getText(), (StructDefinition) visit(ctx.structDef(i)));
}
var expression = (Expression) visit(ctx.expression());
var program = new Program(typeCheckedFunctionDefs, typeCheckedStructDefs, enumDefs, expression);
program.type = expression.type;
program.line = ctx.start.getLine();
program.col = ctx.start.getCharPositionInLine();
return program;
}
@Override
@@ -67,7 +76,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
statements[i] = (Statement) currentStatement;
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
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
// 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
if (actualStatementCount < declaredStatementCount) {
Statement[] newStatements = new Statement[actualStatementCount];
for (int i = 0; i < actualStatementCount; i++) {
newStatements[i] = statements[i];
}
System.arraycopy(statements, 0, newStatements, 0, actualStatementCount);
statements = newStatements;
}
@@ -172,43 +179,59 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
var variableName = ctx.IDENT().getText();
var declaredType = Type.getByName(ctx.type_annotation().type().getText());
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
if (!declaredType.isPrimitiveType() && this.structs.get(declaredType.getName()) == null) {
String error = "Type " + declaredType.getName() + " not defined.";
if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
var error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (this.vars.get(name) != null) {
String error = "Redeclaration of variable with name \"" + name + "\".";
if (structDefs.containsKey(variableName)) {
var error = "Variable name " + variableName + " shadows a struct of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (enumDefs.containsKey(variableName)) {
var error = "Variable name " + variableName + " shadows an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (vars.get(variableName) != null) {
var error = "Redeclaration of variable with name \"" + variableName + "\".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Create the appropriate instance
VariableDeclaration result;
VariableDeclaration variableDeclaration;
if (ctx.expression() != null) {
Node expression = this.visit(ctx.expression());
var expression = visit(ctx.expression());
try {
declaredType.combine(expression.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result = new VariableDeclaration(name, (Expression) expression);
result.initialized = true;
variableDeclaration = new VariableDeclaration(variableName, (Expression) expression);
variableDeclaration.initialized = true;
} 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
this.vars.put(name, result);
vars.put(variableName, variableDeclaration);
result.line = line;
result.col = col;
result.type = declaredType;
return result;
variableDeclaration.line = line;
variableDeclaration.col = col;
variableDeclaration.type = declaredType;
return variableDeclaration;
}
@Override
@@ -291,7 +314,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
String structName = variableDef.type.getName();
Type fieldType;
try {
fieldType = Helper.drillType(this.structs, structName, path, 0);
fieldType = Helper.drillType(this.structDefs, structName, path, 0);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
@@ -311,43 +334,68 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
}
@Override
public Node visitStructFieldAccessExpression(KlangParser.StructFieldAccessExpressionContext ctx) {
String varName = ctx.IDENT(0).getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
String[] path = new String[ctx.IDENT().size() - 1];
public Node visitMemberAccessExpression(KlangParser.MemberAccessExpressionContext ctx) {
var baseName = ctx.IDENT(0).getText();
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
// 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++) {
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);
if (Arrays.stream(enumDef.enums).noneMatch(e -> e.equals(enumValueName))) {
var error = "Unknown enum value " + enumValueName + " of enum " + enumDef.name + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
var enumAccessExpression = new EnumAccessExpression(baseName, enumValueName);
enumAccessExpression.type = enumDef.type;
enumAccessExpression.line = line;
enumAccessExpression.col = col;
return enumAccessExpression;
}
// Get the referenced variable, make sure it is defined
var variableDef = this.vars.get(varName);
var variableDef = vars.get(baseName);
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);
}
// Make sure it references a struct
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);
}
// Get the type of the result of this expression
String structName = variableDef.type.getName();
var structName = variableDef.type.getName();
Type resultType;
try {
resultType = Helper.drillType(this.structs, structName, path, 0);
resultType = Helper.drillType(structDefs, structName, path.toArray(new String[0]), 0);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
Node result = new StructFieldAccessExpression(varName, structName, path);
result.type = resultType;
result.line = line;
result.col = col;
return result;
var memberAccessExpression = new MemberAccessExpression(baseName, structName, path.toArray(new String[0]));
memberAccessExpression.type = resultType;
memberAccessExpression.line = line;
memberAccessExpression.col = col;
return memberAccessExpression;
}
@Override
@@ -708,7 +756,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
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.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
@@ -725,70 +773,137 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
}
@Override
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
String name = ctx.funcName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type returnType = Type.getByName(ctx.returnType.type().getText());
this.currentDeclaredReturnType = returnType;
this.currentFunctionDefinitionName = name;
public Node visitStructDef(KlangParser.StructDefContext ctx) {
var structName = ctx.structName.getText();
var structFieldCount = ctx.structField().size();
var structFields = new StructField[structFieldCount];
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
if (!returnType.isPrimitiveType() && this.structs.get(returnType.getName()) == null) {
String error = "Type " + returnType.getName() + " not defined.";
for (int i = 0; i < structFieldCount; i++) {
structFields[i] = (StructField) visit(ctx.structField(i));
}
var structDef = new StructDefinition(structName, structFields);
structDef.type = Type.getByName(structName);
structDef.line = line;
structDef.col = col;
return structDef;
}
@Override
public Node visitStructField(KlangParser.StructFieldContext ctx) {
var structFieldName = ctx.IDENT().getText();
var structFieldType = Type.getByName(ctx.type_annotation().type().getText());
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
if (!structFieldType.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);
}
// Create a new set for the variables of the current function
// 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
int paramCount = ctx.params.parameter().size();
Parameter[] params = new Parameter[paramCount];
// Process the parameter list by visiting every parameter in it
var paramCount = ctx.params.parameter().size();
var params = new Parameter[paramCount];
for (int i = 0; i < paramCount; i++) {
// 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;
// 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.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
Node block = this.visit(ctx.braced_block());
var block = visit(ctx.braced_block());
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);
}
FunctionDefinition result = new FunctionDefinition(name, params, (Block) block);
result.type = returnType;
var functionDef = new FunctionDefinition(name, params, (Block) block);
functionDef.type = returnType;
functionDef.line = ctx.start.getLine();
functionDef.col = ctx.start.getCharPositionInLine();
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
return functionDef;
}
@Override
public Node visitParameter(KlangParser.ParameterContext 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());
var parameterName = ctx.IDENT().getText();
var parameterType = Type.getByName(ctx.type_annotation().type().getText());
if (!type.isPrimitiveType() && this.structs.get(type.getName()) == null) {
String error = "Type " + type.getName() + " not defined.";
if (structDefs.containsKey(parameterName)) {
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);
}
Parameter result = new Parameter(name);
result.type = type;
result.line = line;
result.col = col;
return result;
if (enumDefs.containsKey(parameterName)) {
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var error = "Parameter name " + parameterName + " duplicates an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) {
var 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
@@ -797,15 +912,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
FunctionInformation func = this.funcs.get(name);
if (func == null) {
var functionDef = this.functionDefs.get(name);
if (functionDef == null) {
String error = "Function with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Make sure the number of arguments matches the number of parameters
int argCount = ctx.functionCall().arguments().expression().size();
int paramCount = func.parameters.size();
int paramCount = functionDef.parameters.length;
if (argCount != paramCount) {
String error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
@@ -815,14 +930,14 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Expression[] args = new Expression[argCount];
for (int i = 0; i < argCount; i++) {
Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
if (!expression.type.equals(func.signature[i])) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + func.signature[i].getName() + " but got: " + expression.type.getName());
if (!expression.type.equals(functionDef.parameters[i].type)) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + functionDef.parameters[i].type.getName() + " but got: " + expression.type.getName());
}
args[i] = expression;
}
FunctionCall result = new FunctionCall(name, args);
result.type = func.returnType;
result.type = functionDef.type;
result.line = line;
result.col = col;
return result;
@@ -835,7 +950,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
int col = ctx.start.getCharPositionInLine();
// Get the corresponding struct definition
var struct = this.structs.get(name);
var struct = this.structDefs.get(name);
if (struct == null) {
String error = "Struct with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);

View File

@@ -0,0 +1,214 @@
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 enumFields = new LinkedHashSet<String>();
for (int i = 1; i < ctx.IDENT().size(); i++) {
var currentEnumField = ctx.IDENT(i);
var currentEnumFieldName = currentEnumField.getText();
if (enumFields.contains(currentEnumFieldName)) {
var line = currentEnumField.getSymbol().getLine();
var col = currentEnumField.getSymbol().getCharPositionInLine();
var error = " Duplicate enum value " + currentEnumFieldName + " in enum " + enumName + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
enumFields.add(currentEnumFieldName);
}
var enumDef = new EnumDefinition(enumName, enumFields.toArray(new String[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;
// import ANTLR's runtime libraries
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.Node;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.visitors.*;
import de.hsrm.compiler.Klang.helper.*;
import de.hsrm.compiler.Klang.visitors.EvalVisitor;
import de.hsrm.compiler.Klang.visitors.GenASM;
import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import java.io.FileWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class Klang {
@@ -87,22 +90,15 @@ public class Klang {
// Context Analysis and DAST generation
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 {
// Extract information about all functions
var functionDefinitions = new HashMap<String, FunctionInformation>();
new GetFunctions(functionDefinitions).visit(tree);
// Extract names of all structs
var structNames = new HashSet<String>();
new GetStructNames(structNames).visit(tree);
// Extract information about all structs
structs = new HashMap<String, StructDefinition>();
new GetStructs(structNames, structs).visit(tree);
// Extract information about all definitions
new GetDefinitions(functionDefs, structDefs, enumDefs).visit(tree);
// Create the DAST
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions, structs);
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefs, structDefs, enumDefs);
root = ctxAnal.visit(tree);
} catch (Exception e) {
System.err.println(e.getMessage());
@@ -122,18 +118,15 @@ public class Klang {
if (evaluate) {
// Evaluate the sourcecode and print the result
System.out.println("\nEvaluating the source code:");
EvalVisitor evalVisitor = new EvalVisitor(structs);
EvalVisitor evalVisitor = new EvalVisitor(structDefs);
Value result = root.welcome(evalVisitor);
generateOutput(out, "Result was: " + result.asObject().toString());
return;
}
// Generate assembler code
// System.out.println("\nPrinting the assembler code");
StringWriter wAsm = new StringWriter();
GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm);
GenASM genasm = new GenASM(exAsm, mainName, structs);
GenASM genasm = new GenASM(mainName, structDefs);
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 String[] enums;
public EnumDefinition(String name, String[] enums) {
this.name = name;
this.enums = enums;
}
@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 Map<String, StructDefinition> structs;
public Map<String, EnumDefinition> enums;
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.structs = structs;
this.enums = enums;
this.expression = expression;
}

View File

@@ -0,0 +1,18 @@
package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class EnumAccessExpression extends Expression {
public String enumName;
public String enumValueName;
public EnumAccessExpression(String enumName, String enumValueName) {
this.enumName = enumName;
this.enumValueName = enumValueName;
}
@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;
public class StructFieldAccessExpression extends Expression {
public class MemberAccessExpression extends Expression {
public String varName;
public String structName;
public String[] path;
public StructFieldAccessExpression(String varName, String structName, String[] path) {
public MemberAccessExpression(String varName, String structName, String[] path) {
this.varName = varName;
this.structName = structName;
this.path = path;

View File

@@ -0,0 +1,47 @@
package de.hsrm.compiler.Klang.types;
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 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,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,7 @@
package de.hsrm.compiler.Klang.types;
import java.util.Set;
public abstract class Type {
// Returns an instance of IntegerType
@@ -26,7 +28,7 @@ public abstract class Type {
case "int": return getIntegerType();
case "float": return getFloatType();
case "null": return getNullType();
default: return new StructType(name);
default: return new NamedType(name);
}
}

View File

@@ -4,12 +4,7 @@ import java.util.HashMap;
import java.util.Map;
import de.hsrm.compiler.Klang.Value;
import de.hsrm.compiler.Klang.nodes.Block;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.Parameter;
import de.hsrm.compiler.Klang.nodes.Program;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
@@ -471,6 +466,11 @@ public class EvalVisitor implements Visitor<Value> {
return null;
}
@Override
public Value visit(EnumDefinition e) {
return null;
}
@Override
public Value visit(StructDefinition e) {
// We get these from a previous visitor
@@ -484,7 +484,7 @@ public class EvalVisitor implements Visitor<Value> {
}
@Override
public Value visit(StructFieldAccessExpression e) {
public Value visit(MemberAccessExpression e) {
Value var = this.env.get(e.varName);
Map<String, Value> struct = var.asStruct();
@@ -496,6 +496,11 @@ public class EvalVisitor implements Visitor<Value> {
return currentValue;
}
@Override
public Value visit(EnumAccessExpression e) {
return null;
}
@Override
public Value visit(ConstructorCall e) {
StructDefinition structDef = this.structs.get(e.structName);

View File

@@ -1,12 +1,6 @@
package de.hsrm.compiler.Klang.visitors;
import java.io.*;
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.asm.ASM;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.*;
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.types.Type;
import java.util.*;
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 StringBuilder sb = new StringBuilder();
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 String mainName;
Map<String, Integer> env = new HashMap<>();
@@ -112,81 +67,81 @@ public class GenASM implements Visitor<Void> {
Set<String> vars;
String[] registers = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" };
String[] floatRegisters = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" };
private int lCount = 0; // Invariante: lCount ist benutzt
private int lCount = 0; // Invariant: lCount is used
private int currentFunctionStartLabel = 0;
private Parameter[] currentFunctionParams;
private void intToFloat(String src, String dst) {
this.ex.write(" cvtsi2sd " + src + ", " + dst + "\n");
}
private boolean prepareRegisters(Expression lhs, Expression rhs) {
boolean lhsIsFloat = lhs.type.equals(Type.getFloatType());
boolean rhsIsFloat = rhs.type.equals(Type.getFloatType());
if (lhsIsFloat && rhsIsFloat) {
lhs.welcome(this);
this.ex.write(" movsd %xmm0, %xmm2\n");
asm.mov("sd", "%xmm0", "%xmm2");
rhs.welcome(this);
this.ex.write(" movsd %xmm0, %xmm1\n");
this.ex.write(" movsd %xmm2, %xmm0\n");
asm.mov("sd", "%xmm2", "%xmm0");
asm.mov("sd", "%xmm2", "%xmm0");
return true;
} else if (lhsIsFloat && !rhsIsFloat) {
lhs.welcome(this);
rhs.welcome(this);
this.intToFloat("%rax", "%xmm1");
asm.cvtsi2sd("%rax", "%xmm1");
return true;
} else if (!lhsIsFloat && rhsIsFloat) {
lhs.welcome(this);
this.intToFloat("%rax", "%xmm2");
asm.cvtsi2sd("%rax", "%xmm2");
rhs.welcome(this);
this.ex.write(" movsd %xmm0, %xmm1\n");
this.ex.write(" movsd %xmm2, %xmm0\n");
asm.mov("sd", "%xmm0", "%xmm1");
asm.mov("sd", "%xmm2", "%xmm0");
return true;
} else {
lhs.welcome(this);
this.ex.write(" pushq %rax\n");
asm.push("q", "%rax");
rhs.welcome(this);
this.ex.write(" movq %rax, %rbx\n");
this.ex.write(" popq %rax\n");
asm.mov("q", "%rax", "%rbx");
asm.pop("q", "%rax");
return false;
}
}
public GenASM(ExWriter ex, String mainName, Map<String, StructDefinition> structs) {
this.ex = ex;
public GenASM(String mainName, Map<String, StructDefinition> structs) {
this.mainName = mainName;
this.structs = structs;
this.asm = new ASM();
}
public GenASM(ExWriter ex, Map<String, StructDefinition> structs) {
this(ex, "main", structs);
public GenASM(Map<String, StructDefinition> structs) {
this("main", structs);
}
public String toAsm() {
return asm.toAsm();
}
@Override
public Void visit(IntegerExpression e) {
this.ex.write(" movq $" + e.value + ", %rax\n");
asm.mov("q", e.value, "%rax");
return null;
}
@Override
public Void visit(FloatExpression e) {
String floatLabel = fw.getFloat(e.value);
this.ex.write(" movsd " + floatLabel + "(%rip), %xmm0\n");
asm.mov("sd", floatLabel, "%rip", "%xmm0");
return null;
}
@Override
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;
}
@Override
public Void visit(Variable e) {
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 {
this.ex.write(" movq " + this.env.get(e.name) + "(%rbp), %rax\n");
asm.mov("q", this.env.get(e.name), "%rbp", "%rax");
}
return null;
}
@@ -198,18 +153,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n");
asm.ucomi("sd", "%xmm1", "xmm0");
} else {
this.ex.write(" cmp %rbx, %rax\n");
asm.cmp("q", "%rbx", "%rax");
}
this.ex.write(" je .L" + lblTrue + "\n");
asm.je(lblTrue);
// false
this.ex.write(" movq $0, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
this.ex.write(".L" + lblTrue + ":\n");
asm.mov("q", 0, "%rax");
asm.jmp(lblEnd);
asm.label(lblTrue);
// true
this.ex.write(" movq $1, %rax\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.mov("q", 1, "%rax");
asm.label(lblEnd);
return null;
}
@@ -220,18 +175,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" ucomisd %xmm0, %xmm1\n");
asm.ucomi("sd", "%xmm0", "%xmm1");
} else {
this.ex.write(" cmp %rax, %rbx\n");
asm.cmp("q", "%rax", "%rbx");
}
this.ex.write(" jne .L" + lblTrue + "\n");
asm.jne(lblTrue);
// false
this.ex.write(" movq $0, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
this.ex.write(".L" + lblTrue + ":\n");
asm.mov("q", 0, "%rax");
asm.jmp(lblEnd);
asm.label(lblTrue);
// true
this.ex.write(" movq $1, %rax\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.mov("q", 1, "%rax");
asm.label(lblEnd);
return null;
}
@@ -242,18 +197,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n");
asm.ucomi("sd", "%xmm1", "%xmm0");
} else {
this.ex.write(" cmp %rbx, %rax\n");
asm.cmp("q", "%rbx", "%rax");
}
this.ex.write(" jg .L" + lblTrue + "\n");
asm.jg(lblTrue);
// false
this.ex.write(" movq $0, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
this.ex.write(".L" + lblTrue + ":\n");
asm.mov("q", 0, "%rax");
asm.jmp(lblEnd);
asm.label(lblTrue);
// true
this.ex.write(" movq $1, %rax\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.mov("q", 1, "%rax");
asm.label(lblEnd);
return null;
}
@@ -264,18 +219,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n");
asm.ucomi("sd", "%xmm1", "xmm0");
} else {
this.ex.write(" cmp %rbx, %rax\n");
asm.cmp("q", "%rbx", "%rax");
}
this.ex.write(" jge .L" + lblTrue + "\n");
asm.jge(lblTrue);
// false
this.ex.write(" movq $0, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
this.ex.write(".L" + lblTrue + ":\n");
asm.mov("q", 0, "%rax");
asm.jmp(lblEnd);
asm.label(lblTrue);
// true
this.ex.write(" movq $1, %rax\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.mov("q", 1, "%rax");
asm.label(lblEnd);
return null;
}
@@ -286,18 +241,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n");
asm.ucomi("sd", "%xmm1", "%xmm0");
} else {
this.ex.write(" cmp %rbx, %rax\n");
asm.cmp("q", "%rbx", "%rax");
}
this.ex.write(" jl .L" + lblTrue + "\n");
asm.jl(lblTrue);
// false
this.ex.write(" movq $0, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
this.ex.write(".L" + lblTrue + ":\n");
asm.mov("q", 0, "%rax");
asm.jmp(lblEnd);
asm.label(lblTrue);
// true
this.ex.write(" movq $1, %rax\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.mov("q", 1, "%rax");
asm.label(lblEnd);
return null;
}
@@ -308,18 +263,18 @@ public class GenASM implements Visitor<Void> {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" ucomisd %xmm1, %xmm0\n");
asm.ucomi("sd", "%xmm1", "%xmm0");
} else {
this.ex.write(" cmp %rbx, %rax\n");
asm.cmp("q", "%rbx", "%rax");
}
this.ex.write(" jle .L" + lblTrue + "\n");
asm.jle(lblTrue);
// false
this.ex.write(" movq $0, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
this.ex.write(".L" + lblTrue + ":\n");
asm.mov("q", 0, "%rax");
asm.jmp(lblEnd);
asm.label(lblTrue);
// true
this.ex.write(" movq $1, %rax\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.mov("q", 1, "%rax");
asm.label(lblEnd);
return null;
}
@@ -327,9 +282,9 @@ public class GenASM implements Visitor<Void> {
public Void visit(AdditionExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" addsd %xmm1, %xmm0\n");
asm.add("sd", "%xmm1", "%xmm0");
} else {
this.ex.write(" addq %rbx, %rax\n");
asm.add("q", "%rbx", "%rax");
}
return null;
}
@@ -338,9 +293,9 @@ public class GenASM implements Visitor<Void> {
public Void visit(SubstractionExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" subsd %xmm1, %xmm0\n");
asm.sub("sd", "%xmm1", "%xmm0");
} else {
this.ex.write(" subq %rbx, %rax\n");
asm.sub("q", "%rbx", "%rax");
}
return null;
}
@@ -349,9 +304,9 @@ public class GenASM implements Visitor<Void> {
public Void visit(MultiplicationExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" mulsd %xmm1, %xmm0\n");
asm.mul("sd", "%xmm1", "%xmm0");
} else {
this.ex.write(" imulq %rbx, %rax\n");
asm.imul("q", "%rbx", "%rax");
}
return null;
}
@@ -360,10 +315,10 @@ public class GenASM implements Visitor<Void> {
public Void visit(DivisionExpression e) {
boolean isFloatOperation = this.prepareRegisters(e.lhs, e.rhs);
if (isFloatOperation) {
this.ex.write(" divsd %xmm1, %xmm0\n");
asm.div("sd", "%xmm1", "%xmm0");
} else {
this.ex.write(" cqto\n"); // sign extend rax into rdx since we're dealing with signed values
this.ex.write(" idiv %rbx\n"); // %rax/%rbx, quotient now in %rax
asm.cqto();
asm.idiv("q", "%rbx");
}
return null;
}
@@ -371,13 +326,13 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(ModuloExpression e) {
e.lhs.welcome(this);
this.ex.write(" pushq %rax\n");
asm.push("q", "%rax");
e.rhs.welcome(this);
this.ex.write(" movq %rax, %rbx\n");
this.ex.write(" popq %rax\n");
this.ex.write(" cqto\n"); // sign extend rax into rdx since we're dealing with signed values
this.ex.write(" idiv %rbx\n"); // %rax/%rbx, remainder now in %rdx
this.ex.write(" movq %rdx, %rax\n");
asm.mov("q", "%rax", "%rbx");
asm.pop("q", "%rax");
asm.cqto();
asm.idiv("q", "%rbx");
asm.mov("q", "%rdx", "%rax");
return null;
}
@@ -386,10 +341,10 @@ public class GenASM implements Visitor<Void> {
e.lhs.welcome(this);
if (e.lhs.type.equals(Type.getFloatType())) {
String floatLabel = fw.getNegateFloat();
this.ex.write(" movsd " + floatLabel + "(%rip), %xmm1\n");
this.ex.write(" xorpd %xmm1, %xmm0\n");
asm.mov("sd", floatLabel, "%rip", "%xmm1");
asm.xor("pd", "%xmm1", "%xmm0");
} else {
this.ex.write(" neg %rax\n");
asm.neg("%rax");
}
return null;
}
@@ -404,28 +359,28 @@ public class GenASM implements Visitor<Void> {
// Wenn LHS != 0 bedeutet das true
// also können wir direkt sagen dass das Ergebnis true ist
e.lhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n");
this.ex.write(" jne .L" + lblTrue + "\n");
asm.cmp("q", 0, "%rax");
asm.jne(lblTrue);
// LHS war false, also werte RHS aus
// Wenn RHS == 0 bedeutet das false,
// also ist das Gesamtergebnis false
e.rhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n");
this.ex.write(" je .L" + lblFalse + "\n");
asm.cmp("q", 0, "%rax");
asm.je(lblFalse);
// Die Expression wertet zu true aus
// Springe am false Teil vorbei
this.ex.write(".L" + lblTrue + ":\n");
this.ex.write(" movq $1, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
asm.label(lblTrue);
asm.mov("q", 1, "%rax");
asm.jmp(lblEnd);
// Die Expressoin wertet zu false aus
this.ex.write(".L" + lblFalse + ":\n");
this.ex.write(" movq $0, %rax\n");
asm.label(lblFalse);
asm.mov("q", 0, "%rax");
// Das hier ist das ende
this.ex.write(".L" + lblEnd + ":\n");
asm.label(lblEnd);
return null;
}
@@ -439,28 +394,28 @@ public class GenASM implements Visitor<Void> {
// Wenn LHS == 0, bedeutet das false
// also können wir direkt sagen dass das Ergebnis false ist
e.lhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n");
this.ex.write(" je .L" + lblFalse + "\n");
asm.cmp("q", 0, "%rax");
asm.je(lblFalse);
// LHS war true, also werte RHS aus.
// Wenn RHS == 0, bedeutet das false
// also ist das Gesamtergebnis false
e.rhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n");
this.ex.write(" je .L" + lblFalse + "\n");
asm.cmp("q", 0, "%rax");
asm.je(lblFalse);
// Die Expression wertet zu true aus
// Springe am false Teil vorbei
this.ex.write(".L" + lblTrue + ":\n");
this.ex.write(" movq $1, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
asm.label(lblTrue);
asm.mov("q", 1, "%rax");
asm.jmp(lblEnd);
// Die Expressoin wertet zu false aus
this.ex.write(".L" + lblFalse + ":\n");
this.ex.write(" movq $0, %rax\n");
asm.label(lblFalse);
asm.mov("q", 0, "%rax");
// Das hier ist das ende
this.ex.write(".L" + lblEnd + ":\n");
asm.label(lblEnd);
return null;
}
@@ -473,21 +428,21 @@ public class GenASM implements Visitor<Void> {
// Wenn LHS != 0 bedeutet das true, also jumpe zum false Teil
// Wenn nicht, falle durch zum true Teil
e.lhs.welcome(this);
this.ex.write(" cmpq $0, %rax\n");
this.ex.write(" jne .L" + lblFalse + "\n");
asm.cmp("q", 0, "%rax");
asm.jne(lblFalse);
// Hier ist das Ergebnis true
// Springe am false Teil vorbei
this.ex.write(" movq $1, %rax\n");
this.ex.write(" jmp .L" + lblEnd + "\n");
asm.mov("q", 1, "%rax");
asm.jmp(lblEnd);
// Hier ist das Ergebnis false
// Falle zum Ende durch
this.ex.write(".L" + lblFalse + ":\n");
this.ex.write("movq $0, %rax\n");
asm.label(lblFalse);
asm.mov("q", 0, "%rax");
// Hier ist das Ende
this.ex.write(".L" + lblEnd + ":\n");
asm.label(lblEnd);
return null;
}
@@ -497,25 +452,25 @@ public class GenASM implements Visitor<Void> {
int lblEnd = ++lCount;
boolean hasElse = e.alt != null || e.elif != null;
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
// Jump to end if there is no else part, this saves a label declaration
if (hasElse) {
this.ex.write(" jz .L" + lblElse + "\n");
asm.jz(lblElse);
} else {
this.ex.write(" jz .L" + lblEnd + "\n");
asm.jz(lblEnd);
}
e.then.welcome(this);
if (hasElse) {
this.ex.write(" jmp .L" + lblEnd + "\n");
this.ex.write(".L" + lblElse + ":\n");
asm.jmp(lblEnd);
asm.label(lblElse);
if (e.alt != null) {
e.alt.welcome(this);
} else {
e.elif.welcome(this);
}
}
this.ex.write(".L" + lblEnd + ":\n");
asm.label(lblEnd);
return null;
}
@@ -523,24 +478,24 @@ public class GenASM implements Visitor<Void> {
public Void visit(WhileLoop e) {
int lblCond = ++lCount;
int lblEnd = ++lCount;
this.ex.write(".L" + lblCond + ":\n");
asm.label(lblCond);
e.cond.welcome(this);
this.ex.write(" cmp $0, %rax\n");
this.ex.write(" jz .L" + lblEnd + "\n");
asm.cmp("", 0, "%rax");
asm.jz(lblEnd);
e.block.welcome(this);
this.ex.write(" jmp .L" + lblCond + "\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.jmp(lblCond);
asm.label(lblEnd);
return null;
}
@Override
public Void visit(DoWhileLoop e) {
int lblStart = ++lCount;
this.ex.write(".L" + lblStart + ":\n");
asm.label(lblStart);
e.block.welcome(this);
e.cond.welcome(this);
this.ex.write(" cmp $0, %rax\n");
this.ex.write(" jnz .L" + lblStart + "\n");
asm.cmp("", 0, "%rax");
asm.jnz(lblStart);
return null;
}
@@ -549,15 +504,14 @@ public class GenASM implements Visitor<Void> {
int lblStart = ++lCount;
int lblEnd = ++lCount;
e.init.welcome(this);
this.ex.write(".L" + lblStart + ":\n");
asm.label(lblStart);
e.condition.welcome(this);
this.ex.write(" cmp $0, %rax\n");
this.ex.write(" jz .L" + lblEnd + "\n");
asm.cmp("", 0, "%rax");
asm.jz(lblEnd);
e.block.welcome(this);
e.step.welcome(this);
this.ex.write(" jmp .L" + lblStart + "\n");
this.ex.write(".L" + lblEnd + ":\n");
asm.jmp(lblStart);
asm.label(lblEnd);
return null;
}
@@ -570,10 +524,10 @@ public class GenASM implements Visitor<Void> {
int offset = this.env.get(e.name);
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;
}
@@ -586,9 +540,9 @@ public class GenASM implements Visitor<Void> {
// Determine where the result of this expression was placed into
// and move it onto the stack from there
if (e.expression.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, " + offset + "(%rbp)\n");
asm.mov("q", "%xmm0", offset, "%rbp");
} else {
this.ex.write(" movq %rax, " + offset + "(%rbp)\n");
asm.mov("q", "%rax", offset, "%rbp");
}
return null;
@@ -597,9 +551,9 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(ReturnStatement e) {
e.expression.welcome(this);
this.ex.write(" movq %rbp, %rsp\n");
this.ex.write(" popq %rbp\n");
this.ex.write(" ret\n");
asm.mov("q", "%rbp", "%rsp");
asm.pop("q", "%rbp");
asm.ret();
return null;
}
@@ -616,12 +570,10 @@ public class GenASM implements Visitor<Void> {
int lblStart = ++lCount;
this.currentFunctionStartLabel = lblStart;
this.currentFunctionParams = e.parameters;
this.ex.write(".globl " + e.name + "\n");
this.ex.write(".type " + e.name + ", @function\n");
this.ex.write(e.name + ":\n");
this.ex.write(" pushq %rbp\n");
this.ex.write(" movq %rsp, %rbp\n");
this.ex.write(".L" + lblStart + ":\n");
asm.functionHead(e.name);
asm.push("q", "%rbp");
asm.mov("q", "%rsp", "%rbp");
asm.label(lblStart);
// hole die anzahl der lokalen variablen
this.vars = new TreeSet<String>();
@@ -667,13 +619,13 @@ public class GenASM implements Visitor<Void> {
fi = 0;
for (var param: registerParameters) {
if (param.type.equals(Type.getFloatType())) {
this.ex.write(" movq "+ this.floatRegisters[fi] + ", %rax\n");
this.ex.write(" pushq %rax\n");
asm.mov("q", this.floatRegisters[fi], "%rax");
asm.push("q", "%rax");
offset -= 8;
this.env.put(param.name, offset); // negative, liegt unter aktuellem BP
fi++;
} else {
this.ex.write(" pushq " + this.registers[ri] + "\n");
asm.push("q", this.registers[ri]);
offset -= 8;
this.env.put(param.name, offset); // negative, liegt unter aktuellem BP
ri++;
@@ -683,7 +635,7 @@ public class GenASM implements Visitor<Void> {
// Reserviere Platz auf dem Stack für jede lokale variable
for (String lok_var : vars) {
offset -= 8;
this.ex.write(" pushq $0\n");
asm.push("q", 0);
this.env.put(lok_var, offset);
}
@@ -700,18 +652,18 @@ public class GenASM implements Visitor<Void> {
e.arguments[i].welcome(this);
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
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;
}
@@ -757,10 +709,10 @@ public class GenASM implements Visitor<Void> {
for (var arg : e.arguments) {
arg.welcome(this);
if (arg.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n");
this.ex.write(" pushq %rax\n");
asm.mov("q", "%xmm0", "%rax");
asm.push("q", "%rax");
} else {
this.ex.write(" pushq %rax\n");
asm.push("q", "%rax");
}
}
@@ -768,14 +720,14 @@ public class GenASM implements Visitor<Void> {
for (int i = 0; i <= fi; i++) {
int indexInArguments = xmmIdxs[i];
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
for (int i = 0; i <= ri; i++) {
int indexInArguments = rIdxs[i];
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
@@ -784,39 +736,34 @@ public class GenASM implements Visitor<Void> {
stackStartOffset -= 8;
int indexInArguments = stackIdxs.get(i);
int rspOffset = (((e.arguments.length - indexInArguments) - 1) * 8);
this.ex.write(" movq " + rspOffset + "(%rsp), %rax\n");
this.ex.write(" movq %rax, " + stackStartOffset + "(%rsp)\n");
asm.mov("q", rspOffset, "%rsp", "%rax");
asm.mov("q", "%rax", stackStartOffset, "%rsp");
}
// Rescue RSP
this.ex.write(" addq $" + stackStartOffset + ", %rsp\n");
asm.add("q", stackStartOffset, "%rsp");
}
this.ex.write(" call " + e.name + "\n");
asm.call(e.name);
return null;
}
@Override
public Void visit(Program e) {
this.ex.write(".text\n");
asm.text(".text");
for (var func : e.funcs) {
func.welcome(this);
this.ex.write("\n");
asm.newline();
}
this.ex.write(".globl " + mainName + "\n");
this.ex.write(".type " + mainName + ", @function\n");
this.ex.write(mainName + ":\n");
this.ex.write(" pushq %rbp\n");
this.ex.write(" movq %rsp, %rbp\n");
asm.functionHead(mainName);
asm.push("q", "%rbp");
asm.mov("q", "%rsp", "%rbp");
e.expression.welcome(this);
asm.mov("q", "%rbp", "%rsp");
asm.pop("q", "%rbp");
asm.ret();
this.ex.write(" movq %rbp, %rsp\n");
this.ex.write(" popq %rbp\n");
this.ex.write(" ret\n");
// PRINT FLOATS HERE
this.ex.write("\n");
this.ex.write(fw.getFloatSection());
asm.text(fw.getFloatSection());
return null;
}
@@ -827,6 +774,11 @@ public class GenASM implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumDefinition e) {
return null;
}
@Override
public Void visit(StructDefinition e) {
// We get these from a previous visitor
@@ -840,31 +792,36 @@ public class GenASM implements Visitor<Void> {
}
@Override
public Void visit(StructFieldAccessExpression e) {
public Void visit(MemberAccessExpression e) {
var structDef = this.structs.get(e.structName);
int offset = this.env.get(e.varName);
// 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
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++) {
// "follow" the current path element
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
// push rax to xmm0 if the result type is a float
if (e.type.equals(Type.getFloatType())) {
this.ex.write(" movq %rax, %xmm0\n");
asm.mov("q", "%rax", "%xmm0");
}
return null;
}
@Override
public Void visit(EnumAccessExpression e) {
return null;
}
@Override
public Void visit(ConstructorCall e) {
// push arguments onto the stack
@@ -873,20 +830,20 @@ public class GenASM implements Visitor<Void> {
// move float values from xmm0 to rax first
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
var structDef = this.structs.get(e.structName);
this.ex.write(" movl $" + Helper.getFieldSizeBytes(structDef) + ", %edi\n");
this.ex.write(" call malloc@PLT\n"); // struct address now in rax
asm.mov("l", Helper.getFieldSizeBytes(structDef), "%edi");
asm.call("malloc@PLT");
// push args into struct memory, last arg is ontop of the stack
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;
@@ -894,14 +851,14 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(NullExpression e) {
this.ex.write(" movq $0, %rax\n");
asm.mov("q", 0 , "%rax");
return null;
}
@Override
public Void visit(DestructorCall e) {
this.ex.write(" movq " +this.env.get(e.name) + "(%rbp), %rdi\n");
this.ex.write(" call free@PLT\n");
asm.mov("q", this.env.get(e.name), "%rbp", "%rdi");
asm.call("free@PLT");
return null;
}
@@ -915,25 +872,25 @@ public class GenASM implements Visitor<Void> {
// Move it from xmm0 rax if its a flaot
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
this.ex.write(" pushq %rax\n");
asm.push("q", "%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,
// move the address of the next referenced struct into rax
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());
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
this.ex.write(" popq " + Helper.getFieldOffset(structDef, fieldNameToUpdate) + "(%rax)\n");
this.ex.write(" movq $0, %rax\n"); // clear rax sind an assignment has no result
asm.pop("q", Helper.getFieldOffset(structDef, fieldNameToUpdate) + "(%rax)");
asm.mov("q", 0 , "%rax"); // clear rax since an assignment has no result
return null;
}

View File

@@ -234,6 +234,11 @@ class GetVars implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumDefinition e) {
return null;
}
@Override
public Void visit(StructDefinition e) {
return null;
@@ -245,7 +250,12 @@ class GetVars implements Visitor<Void> {
}
@Override
public Void visit(StructFieldAccessExpression e) {
public Void visit(MemberAccessExpression e) {
return null;
}
@Override
public Void visit(EnumAccessExpression e) {
return null;
}

View File

@@ -68,6 +68,12 @@ public class PrettyPrintVisitor implements Visitor<Void> {
ex.nl();
}
for (var enumDef: e.enums.values()) {
enumDef.welcome(this);
ex.nl();
ex.nl();
}
e.expression.welcome(this);
ex.write(";");
return null;
@@ -377,6 +383,22 @@ public class PrettyPrintVisitor implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumDefinition e) {
ex.write("enum " + e.name + " { ");
var first = true;
for(var enumName: e.enums) {
if (!first) {
ex.write(", ");
} else {
first = false;
}
ex.write(enumName);
}
ex.write(" }");
return null;
}
@Override
public Void visit(StructDefinition e) {
ex.write("struct " + e.name + " {");
@@ -398,7 +420,7 @@ public class PrettyPrintVisitor implements Visitor<Void> {
}
@Override
public Void visit(StructFieldAccessExpression e) {
public Void visit(MemberAccessExpression e) {
ex.write(e.varName);
for (int i = 0; i < e.path.length; i++) {
ex.write(".");
@@ -407,6 +429,15 @@ public class PrettyPrintVisitor implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumAccessExpression e) {
ex.write(e.enumName);
ex.write(".");
ex.write(e.enumValueName);
return null;
}
@Override
public Void visit(ConstructorCall e) {
ex.write("create " + e.structName + "(");

View File

@@ -1,11 +1,6 @@
package de.hsrm.compiler.Klang.visitors;
import de.hsrm.compiler.Klang.nodes.Block;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.Parameter;
import de.hsrm.compiler.Klang.nodes.Program;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.*;
import de.hsrm.compiler.Klang.nodes.statements.*;
@@ -42,9 +37,11 @@ public interface Visitor<R> {
R visit(FunctionCall e);
R visit(Program e);
R visit(Parameter e);
R visit(EnumDefinition e);
R visit(StructDefinition e);
R visit(StructField e);
R visit(StructFieldAccessExpression e);
R visit(MemberAccessExpression e);
R visit(EnumAccessExpression e);
R visit(ConstructorCall e);
R visit(NullExpression 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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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);");
var funcs = Helper.getFuncs(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));
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);");
var funcs = Helper.getFuncs(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));
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 static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import de.hsrm.compiler.Klang.ContextAnalysis;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
import static org.junit.jupiter.api.Assertions.*;
public class FunctionDefinitionTest {
@Test
void typeNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
void shouldNotThrowIfReturnTypeIsReferringToAnEnum() {
// given
var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo(1);");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:0 Type schwurbel not defined.", e.getMessage());
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void noReturnExpression() {
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
void shouldNotThrowIfParameterTypeIsReferringToAnEnum() {
// given
var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: bar): int { return 1; } foo(bar.A);");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:0 Function foo has to return something of type int.", e.getMessage());
// 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();
}
public static Map<String, FunctionInformation> getFuncs(ParseTree tree) {
var functionDefinitions = new HashMap<String, FunctionInformation>();
new GetFunctions(functionDefinitions).visit(tree);
public static Map<String, FunctionDefinition> getFuncs(ParseTree tree) {
var functionDefinitions = new HashMap<String, FunctionDefinition>();
new GetDefinitions(functionDefinitions, new HashMap<>(), new HashMap<>()).visit(tree);
return functionDefinitions;
}
public static Set<String> getStructNames(ParseTree tree) {
var structNames = new HashSet<String>();
new GetStructNames(structNames).visit(tree);
return structNames;
}
public static Map<String, StructDefinition> getStructs(ParseTree tree) {
var structs = new HashMap<String, StructDefinition>();
new GetStructs(getStructNames(tree), structs).visit(tree);
new GetDefinitions(new HashMap<>(), structs, new HashMap<>()).visit(tree);
return structs;
}
public static Map<String, EnumDefinition> getEnums(ParseTree tree) {
var enums = new HashMap<String, EnumDefinition>();
new GetDefinitions(new HashMap<>(), new HashMap<>(), enums).visit(tree);
return enums;
}
}

View File

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

View File

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

View File

@@ -1,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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
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 static org.junit.jupiter.api.Assertions.assertThrows;
import org.antlr.v4.runtime.tree.ParseTree;
import de.hsrm.compiler.Klang.ContextAnalysis;
import org.junit.jupiter.api.Test;
import de.hsrm.compiler.Klang.ContextAnalysis;
import static org.junit.jupiter.api.Assertions.*;
public class VariableDeclarationTest {
@Test
void typeNotDefined() {
ParseTree tree = Helper.prepareParser("function foo(): int { let X: unk; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
void shouldNotThrowIfDeclaredTypeIsAStruct() {
// given
var tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { let a: bar; return 1; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
Helper.getStructs(tree),
Helper.getEnums(tree)
);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(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());
}
@Test
void variableRedeclaration()
{
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();");
var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree);
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
void shouldThrowExceptionIfVariableIsRedeclared() {
// given
var tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();");
var ctxAnal = new ContextAnalysis(
Helper.getFuncs(tree),
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());
}
@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();");
var funcs = Helper.getFuncs(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));
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();");
var funcs = Helper.getFuncs(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));
assertEquals("Error in line 1:41 Variable with name \"x\" has not been initialized.", e.getMessage());