diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -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
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..5b530e0
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..923da22
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_antlr_antlr4_runtime_4_7_2.xml b/.idea/libraries/Maven__org_antlr_antlr4_runtime_4_7_2.xml
new file mode 100644
index 0000000..d0c6a38
--- /dev/null
+++ b/.idea/libraries/Maven__org_antlr_antlr4_runtime_4_7_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml b/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml
new file mode 100644
index 0000000..f854ab0
--- /dev/null
+++ b/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_0.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_0.xml
new file mode 100644
index 0000000..be380a5
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_0.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_0.xml
new file mode 100644
index 0000000..227bc44
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_0.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_0.xml
new file mode 100644
index 0000000..8e5d3f2
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_0.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_0.xml
new file mode 100644
index 0000000..2f36d10
--- /dev/null
+++ b/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml b/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml
new file mode 100644
index 0000000..fbc1b16
--- /dev/null
+++ b/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..1f08112
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..eb129d2
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/klang.iml b/klang.iml
new file mode 100644
index 0000000..d75274d
--- /dev/null
+++ b/klang.iml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index dd5fd96..956661e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,6 +72,11 @@
jar-with-dependencies
+
+
+ de.hsrm.compiler.Klang.Klang
+
+
diff --git a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4
index a1a176a..efe054d 100644
--- a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4
+++ b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4
@@ -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';
diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java
index ee49476..f752bce 100644
--- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java
+++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java
@@ -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 {
Map vars = new HashMap<>();
- Map funcs;
- Map structs;
+ Map functionDefs;
+ Map structDefs;
+ Map enumDefs;
Type currentDeclaredReturnType;
String currentFunctionDefinitionName;
@@ -27,25 +26,35 @@ public class ContextAnalysis extends KlangBaseVisitor {
}
}
- public ContextAnalysis(Map funcs, Map structs) {
- this.funcs = funcs;
- this.structs = structs;
+ public ContextAnalysis(
+ Map functionDefs,
+ Map structDefs,
+ Map 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();
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 {
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 {
// 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 {
@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 {
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,71 @@ public class ContextAnalysis extends KlangBaseVisitor {
}
@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();
for (int i = 1; i < ctx.IDENT().size(); i++) {
- path[i - 1] = ctx.IDENT(i).getText();
+ path.add(ctx.IDENT(i).getText());
+ }
+
+ // Determine if the base name points to an enum or a variable
+ var enumDef = enumDefs.get(baseName);
+ if (enumDef != null) {
+ if (path.size() != 1) {
+ var error = "Illegal access to enum " + enumDef.name + ".";
+ throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
+ }
+
+ var enumValueName = path.get(0);
+ var enumValue = Arrays.stream(enumDef.enums)
+ .filter(e -> e.value.equals(enumValueName))
+ .findFirst()
+ .orElseThrow(() -> {
+ var error = "Unknown enum value " + enumValueName + " of enum " + enumDef.name + ".";
+ throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
+ });
+
+ var enumAccessExpression = new EnumAccessExpression(baseName, enumValueName, enumValue);
+ enumAccessExpression.type = enumDef.type;
+ enumAccessExpression.line = line;
+ enumAccessExpression.col = col;
+
+ return enumAccessExpression;
}
// Get the referenced variable, make sure it is defined
- 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 +759,7 @@ public class ContextAnalysis extends KlangBaseVisitor {
@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 +776,137 @@ public class ContextAnalysis extends KlangBaseVisitor {
}
@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 aswell
- this.vars = new HashMap<>();
+ // this will be filled in the variable declaration visitor as well
+ 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 +915,15 @@ public class ContextAnalysis extends KlangBaseVisitor {
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 +933,14 @@ public class ContextAnalysis extends KlangBaseVisitor {
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 +953,7 @@ public class ContextAnalysis extends KlangBaseVisitor {
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);
diff --git a/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java b/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java
new file mode 100644
index 0000000..d5cf9e4
--- /dev/null
+++ b/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java
@@ -0,0 +1,220 @@
+package de.hsrm.compiler.Klang;
+
+import de.hsrm.compiler.Klang.helper.Helper;
+import de.hsrm.compiler.Klang.nodes.*;
+import de.hsrm.compiler.Klang.types.NamedType;
+import de.hsrm.compiler.Klang.types.Type;
+
+import java.util.*;
+
+public class GetDefinitions extends KlangBaseVisitor {
+ private final Map functionDefs;
+ private final Map structDefs;
+ private final Map enumDefs;
+
+ private Set functionNames;
+ private Set structNames;
+ private Set enumNames;
+
+ public GetDefinitions(
+ Map functionDefs,
+ Map structDefs,
+ Map enumDefs
+ ) {
+ this.functionDefs = functionDefs;
+ this.structDefs = structDefs;
+ this.enumDefs = enumDefs;
+ }
+
+ private Set collectFunctionNames(KlangParser.ProgramContext ctx) {
+ var result = new HashSet();
+ 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 collectStructNames(KlangParser.ProgramContext ctx) {
+ var result = new HashSet();
+ 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 collectEnumNames(KlangParser.ProgramContext ctx) {
+ var result = new HashSet();
+ for (int i = 0; i < ctx.enumDef().size(); i++) {
+ var currentEnumDef = ctx.enumDef(i);
+ var enumName = currentEnumDef.enumName.getText();
+ if (result.contains(enumName)) {
+ var line = currentEnumDef.enumName.getLine();
+ var col = currentEnumDef.enumName.getCharPositionInLine();
+ var error = "Enum " + enumName + " defined multiple times.";
+ throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
+ }
+ result.add(enumName);
+ }
+ return result;
+ }
+
+ @Override
+ public Node visitProgram(KlangParser.ProgramContext ctx) {
+ functionNames = collectFunctionNames(ctx);
+ structNames = collectStructNames(ctx);
+ enumNames = collectEnumNames(ctx);
+
+ for (int i = 0; i < ctx.functionDef().size(); i++) {
+ visit(ctx.functionDef(i));
+ }
+
+ for (int i = 0; i < ctx.structDef().size(); i++) {
+ visit(ctx.structDef(i));
+ }
+
+ for (int i = 0; i < ctx.enumDef().size(); i++) {
+ visit(ctx.enumDef(i));
+ }
+
+ return null;
+ }
+
+ @Override
+ public Node visitEnumDef(KlangParser.EnumDefContext ctx) {
+ // Check that there isn't a function or struct with the same name
+ var enumName = ctx.enumName.getText();
+ if (functionNames.contains(enumName) || structNames.contains(enumName)) {
+ var line = ctx.start.getLine();
+ var col = ctx.start.getCharPositionInLine();
+ var error = "Duplicate use of name " + enumName + ".";
+ throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
+ }
+
+ // IDENT() includes the enumName as the first entry, which we skip
+ var enumValues = new LinkedHashMap();
+ for (int i = 1; i < ctx.IDENT().size(); i++) {
+ var currentEnumField = ctx.IDENT(i);
+ var currentEnumFieldName = currentEnumField.getText();
+ var line = currentEnumField.getSymbol().getLine();
+ var col = currentEnumField.getSymbol().getCharPositionInLine();
+
+ if (enumValues.containsKey(currentEnumFieldName)) {
+ var error = " Duplicate enum value " + currentEnumFieldName + " in enum " + enumName + ".";
+ throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
+ }
+
+ var enumValue = new EnumValue(currentEnumFieldName, i - 1);
+ enumValue.line = line;
+ enumValue.col = col;
+
+ enumValues.put(currentEnumFieldName, enumValue);
+ }
+
+ var enumDef = new EnumDefinition(enumName, enumValues.values().toArray(new EnumValue[0]));
+ enumDef.line = ctx.start.getLine();
+ enumDef.col = ctx.start.getCharPositionInLine();
+ enumDef.type = new NamedType(enumName);
+ enumDefs.put(enumName, enumDef);
+
+ return null;
+ }
+
+ @Override
+ public Node visitStructDef(KlangParser.StructDefContext ctx) {
+ var structName = ctx.structName.getText();
+ var structFieldCount = ctx.structField().size();
+ var structFields = new LinkedHashMap();
+ 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();
+ 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;
+ }
+}
diff --git a/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java b/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java
deleted file mode 100644
index f9faa5c..0000000
--- a/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package de.hsrm.compiler.Klang;
-
-import java.util.Map;
-import java.util.TreeMap;
-
-import de.hsrm.compiler.Klang.types.*;
-import de.hsrm.compiler.Klang.helper.*;
-
-public class GetFunctions extends KlangBaseVisitor {
-
- private Map funcs;
-
- public GetFunctions(Map 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 parameters = new TreeMap();
-
- // 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;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/GetStructNames.java b/src/main/java/de/hsrm/compiler/Klang/GetStructNames.java
deleted file mode 100644
index 0e5fcb3..0000000
--- a/src/main/java/de/hsrm/compiler/Klang/GetStructNames.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package de.hsrm.compiler.Klang;
-
-import java.util.Set;
-
-import de.hsrm.compiler.Klang.helper.Helper;
-
-public class GetStructNames extends KlangBaseVisitor {
- private Set structNames;
-
- public GetStructNames(Set 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;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/GetStructs.java b/src/main/java/de/hsrm/compiler/Klang/GetStructs.java
deleted file mode 100644
index ca1dc2f..0000000
--- a/src/main/java/de/hsrm/compiler/Klang/GetStructs.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package de.hsrm.compiler.Klang;
-
-import java.util.Map;
-import java.util.Set;
-
-import de.hsrm.compiler.Klang.helper.Helper;
-import de.hsrm.compiler.Klang.nodes.Node;
-import de.hsrm.compiler.Klang.nodes.StructDefinition;
-import de.hsrm.compiler.Klang.nodes.StructField;
-import de.hsrm.compiler.Klang.types.StructType;
-import de.hsrm.compiler.Klang.types.Type;
-
-public class GetStructs extends KlangBaseVisitor {
-
- private Set structNames;
- private Map structs;
-
- public GetStructs(Set structNames, Map 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;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/Klang.java b/src/main/java/de/hsrm/compiler/Klang/Klang.java
index 029fdd6..ca8094c 100644
--- a/src/main/java/de/hsrm/compiler/Klang/Klang.java
+++ b/src/main/java/de/hsrm/compiler/Klang/Klang.java
@@ -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 structs;
+ var functionDefs = new HashMap();
+ var structDefs = new HashMap();
+ var enumDefs = new HashMap();
try {
- // Extract information about all functions
- var functionDefinitions = new HashMap();
- new GetFunctions(functionDefinitions).visit(tree);
-
- // Extract names of all structs
- var structNames = new HashSet();
- new GetStructNames(structNames).visit(tree);
-
- // Extract information about all structs
- structs = new HashMap();
- 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,14 +118,14 @@ 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
- GenASM genasm = new GenASM(mainName, structs);
+ GenASM genasm = new GenASM(mainName, structDefs);
root.welcome(genasm);
generateOutput(out, genasm.toAsm());
}
diff --git a/src/main/java/de/hsrm/compiler/Klang/helper/FunctionInformation.java b/src/main/java/de/hsrm/compiler/Klang/helper/FunctionInformation.java
deleted file mode 100644
index 97508f0..0000000
--- a/src/main/java/de/hsrm/compiler/Klang/helper/FunctionInformation.java
+++ /dev/null
@@ -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 parameters;
- public Type[] signature;
-
- public FunctionInformation(String name, Type returnType, Map parameters, Type[] signature) {
- this.name = name;
- this.returnType = returnType;
- this.parameters = parameters;
- this.signature = signature;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/EnumDefinition.java b/src/main/java/de/hsrm/compiler/Klang/nodes/EnumDefinition.java
new file mode 100644
index 0000000..4971b2d
--- /dev/null
+++ b/src/main/java/de/hsrm/compiler/Klang/nodes/EnumDefinition.java
@@ -0,0 +1,19 @@
+package de.hsrm.compiler.Klang.nodes;
+
+import de.hsrm.compiler.Klang.visitors.Visitor;
+
+public class EnumDefinition extends Node {
+
+ public String name;
+ public EnumValue[] enums;
+
+ public EnumDefinition(String name, EnumValue[] enums) {
+ this.name = name;
+ this.enums = enums;
+ }
+
+ @Override
+ public R welcome(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/EnumValue.java b/src/main/java/de/hsrm/compiler/Klang/nodes/EnumValue.java
new file mode 100644
index 0000000..2a20bc7
--- /dev/null
+++ b/src/main/java/de/hsrm/compiler/Klang/nodes/EnumValue.java
@@ -0,0 +1,18 @@
+package de.hsrm.compiler.Klang.nodes;
+
+import de.hsrm.compiler.Klang.visitors.Visitor;
+
+public class EnumValue extends Node {
+ public String value;
+ public int index;
+
+ public EnumValue(String value, int index) {
+ this.value = value;
+ this.index = index;
+ }
+
+ @Override
+ public R welcome(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java b/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java
index f56d749..35e0c62 100644
--- a/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java
+++ b/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java
@@ -9,11 +9,18 @@ public class Program extends Node {
public FunctionDefinition[] funcs;
public Map structs;
+ public Map enums;
public Expression expression;
- public Program(FunctionDefinition[] funcs, Map structs, Expression expression) {
+ public Program(
+ FunctionDefinition[] funcs,
+ Map structs,
+ Map enums,
+ Expression expression
+ ) {
this.funcs = funcs;
this.structs = structs;
+ this.enums = enums;
this.expression = expression;
}
diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/EnumAccessExpression.java b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/EnumAccessExpression.java
new file mode 100644
index 0000000..d46c715
--- /dev/null
+++ b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/EnumAccessExpression.java
@@ -0,0 +1,26 @@
+package de.hsrm.compiler.Klang.nodes.expressions;
+
+import de.hsrm.compiler.Klang.nodes.EnumDefinition;
+import de.hsrm.compiler.Klang.nodes.EnumValue;
+import de.hsrm.compiler.Klang.visitors.Visitor;
+
+public class EnumAccessExpression extends Expression {
+ public String enumName;
+ public String enumValueName;
+ public EnumValue enumValue;
+
+ public EnumAccessExpression(
+ String enumName,
+ String enumValueName,
+ EnumValue enumValue
+ ) {
+ this.enumName = enumName;
+ this.enumValueName = enumValueName;
+ this.enumValue = enumValue;
+ }
+
+ @Override
+ public R welcome(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/MemberAccessExpression.java
similarity index 69%
rename from src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java
rename to src/main/java/de/hsrm/compiler/Klang/nodes/expressions/MemberAccessExpression.java
index 73b9ac8..3a71418 100644
--- a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java
+++ b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/MemberAccessExpression.java
@@ -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;
diff --git a/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java b/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java
index 78bcb6b..8e3f27a 100644
--- a/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java
+++ b/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java
@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
+import de.hsrm.compiler.Klang.Value;
+
public class BooleanType extends PrimitiveType {
private static BooleanType instance = null;
@@ -33,4 +35,8 @@ public class BooleanType extends PrimitiveType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
+ @Override
+ public boolean valuesEqual(Value a, Value b) {
+ return a.asBoolean() == b.asBoolean();
+ }
}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/types/FloatType.java b/src/main/java/de/hsrm/compiler/Klang/types/FloatType.java
index 14f3fd7..cb383d5 100644
--- a/src/main/java/de/hsrm/compiler/Klang/types/FloatType.java
+++ b/src/main/java/de/hsrm/compiler/Klang/types/FloatType.java
@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
+import de.hsrm.compiler.Klang.Value;
+
public class FloatType extends NumericType {
private static FloatType instance = null;
@@ -37,4 +39,8 @@ public class FloatType extends NumericType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
+ @Override
+ public boolean valuesEqual(Value a, Value b) {
+ return a.asFloat() == b.asFloat();
+ }
}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java b/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java
index 41064d1..7eccf14 100644
--- a/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java
+++ b/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java
@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
+import de.hsrm.compiler.Klang.Value;
+
public class IntegerType extends NumericType {
private static IntegerType instance = null;
@@ -37,4 +39,9 @@ public class IntegerType extends NumericType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
+ @Override
+ public boolean valuesEqual(Value a, Value b) {
+ return a.asInteger() == b.asInteger();
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/types/NamedType.java b/src/main/java/de/hsrm/compiler/Klang/types/NamedType.java
new file mode 100644
index 0000000..8ea4bdf
--- /dev/null
+++ b/src/main/java/de/hsrm/compiler/Klang/types/NamedType.java
@@ -0,0 +1,59 @@
+package de.hsrm.compiler.Klang.types;
+
+import de.hsrm.compiler.Klang.Value;
+import de.hsrm.compiler.Klang.nodes.EnumDefinition;
+import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
+import de.hsrm.compiler.Klang.nodes.StructDefinition;
+
+import java.util.Map;
+
+public class NamedType extends Type {
+ public String name;
+
+ public NamedType(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Type combine(Type that) {
+ if(this.equals(that)) {
+ return this;
+ }
+
+ throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + that.getName());
+ }
+
+ @Override
+ public boolean valuesEqual(Value a, Value b) {
+ return a.asObject().equals(b.asObject());
+ }
+
+ @Override
+ public boolean isPrimitiveType() {
+ return false;
+ }
+
+ @Override
+ public boolean isNumericType() {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ if (this == that) {
+ return true;
+ }
+
+ if (that instanceof NamedType) {
+ var thatType = (NamedType) that;
+ return getName().equals(thatType.getName());
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/de/hsrm/compiler/Klang/types/NullType.java b/src/main/java/de/hsrm/compiler/Klang/types/NullType.java
index 452efd8..0e2cf62 100644
--- a/src/main/java/de/hsrm/compiler/Klang/types/NullType.java
+++ b/src/main/java/de/hsrm/compiler/Klang/types/NullType.java
@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
+import de.hsrm.compiler.Klang.Value;
+
public class NullType extends Type {
private static NullType instance = null;
@@ -28,6 +30,11 @@ public class NullType extends Type {
return that;
}
+ @Override
+ public boolean valuesEqual(Value a, Value b) {
+ return a.asObject() == b.asObject();
+ }
+
@Override
public boolean isPrimitiveType() {
return false;
diff --git a/src/main/java/de/hsrm/compiler/Klang/types/StructType.java b/src/main/java/de/hsrm/compiler/Klang/types/StructType.java
deleted file mode 100644
index 23cac1c..0000000
--- a/src/main/java/de/hsrm/compiler/Klang/types/StructType.java
+++ /dev/null
@@ -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;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/types/Type.java b/src/main/java/de/hsrm/compiler/Klang/types/Type.java
index 15b2810..4ce91ca 100644
--- a/src/main/java/de/hsrm/compiler/Klang/types/Type.java
+++ b/src/main/java/de/hsrm/compiler/Klang/types/Type.java
@@ -1,5 +1,12 @@
package de.hsrm.compiler.Klang.types;
+import de.hsrm.compiler.Klang.Value;
+import de.hsrm.compiler.Klang.nodes.EnumDefinition;
+import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
+import de.hsrm.compiler.Klang.nodes.StructDefinition;
+
+import java.util.Map;
+
public abstract class Type {
// Returns an instance of IntegerType
@@ -26,12 +33,13 @@ 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);
}
}
public abstract String getName();
public abstract Type combine(Type that);
+ public abstract boolean valuesEqual(Value a, Value b);
public abstract boolean isPrimitiveType();
public abstract boolean isNumericType();
}
\ No newline at end of file
diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java
index dad982e..ed24dbc 100644
--- a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java
+++ b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java
@@ -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;
@@ -51,25 +46,11 @@ public class EvalVisitor implements Visitor {
@Override
public Value visit(EqualityExpression e) {
- Value lhs = e.lhs.welcome(this);
- Value rhs = e.rhs.welcome(this);
- Type resultType = Type.getBooleanType();
- Type combineType = lhs.type.combine(rhs.type);
+ var lhs = e.lhs.welcome(this);
+ var rhs = e.rhs.welcome(this);
+ var combinedType = lhs.type.combine(rhs.type);
- switch(combineType.getName()) {
- case "bool": {
- return new Value(lhs.asBoolean() == rhs.asBoolean(), resultType);
- }
- case "int": {
- return new Value(lhs.asInteger() == rhs.asInteger(), resultType);
- }
- case "float": {
- return new Value(lhs.asFloat() == rhs.asFloat(), resultType);
- }
- default: {
- return new Value(lhs.asObject() == rhs.asObject(), resultType);
- }
- }
+ return new Value(combinedType.valuesEqual(lhs, rhs), Type.getBooleanType());
}
@Override
@@ -471,6 +452,16 @@ public class EvalVisitor implements Visitor {
return null;
}
+ @Override
+ public Value visit(EnumDefinition e) {
+ return null;
+ }
+
+ @Override
+ public Value visit(EnumValue e) {
+ return null;
+ }
+
@Override
public Value visit(StructDefinition e) {
// We get these from a previous visitor
@@ -484,7 +475,7 @@ public class EvalVisitor implements Visitor {
}
@Override
- public Value visit(StructFieldAccessExpression e) {
+ public Value visit(MemberAccessExpression e) {
Value var = this.env.get(e.varName);
Map struct = var.asStruct();
@@ -496,6 +487,11 @@ public class EvalVisitor implements Visitor {
return currentValue;
}
+ @Override
+ public Value visit(EnumAccessExpression e) {
+ return new Value(e.enumValueName, e.type);
+ }
+
@Override
public Value visit(ConstructorCall e) {
StructDefinition structDef = this.structs.get(e.structName);
diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java
index aad1f62..5bc32f2 100644
--- a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java
+++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java
@@ -1,11 +1,5 @@
package de.hsrm.compiler.Klang.visitors;
-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.*;
@@ -16,6 +10,8 @@ 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 {
private class FloatWriter {
private StringBuilder sb = new StringBuilder();
@@ -571,6 +567,13 @@ public class GenASM implements Visitor {
@Override
public Void visit(FunctionDefinition e) {
+ // If the user chooses "main" as one of his function names then
+ // rename it and hope that they didn't use the renamed function name
+ // as well :D
+ if (e.name.equals("main")) {
+ e.name = "main_by_user";
+ }
+
int lblStart = ++lCount;
this.currentFunctionStartLabel = lblStart;
this.currentFunctionParams = e.parameters;
@@ -748,7 +751,8 @@ public class GenASM implements Visitor {
asm.add("q", stackStartOffset, "%rsp");
}
- asm.call(e.name);
+ // We rename a function name if it is "main"
+ asm.call(e.name.equals("main") ? "main_by_user": e.name);
return null;
}
@@ -773,11 +777,21 @@ public class GenASM implements Visitor {
@Override
public Void visit(Parameter e) {
- // The work for a paremeter node is implement
+ // The work for a parameter node is implement
// in the function definition visitor
return null;
}
+ @Override
+ public Void visit(EnumDefinition e) {
+ return null;
+ }
+
+ @Override
+ public Void visit(EnumValue e) {
+ return null;
+ }
+
@Override
public Void visit(StructDefinition e) {
// We get these from a previous visitor
@@ -791,7 +805,7 @@ public class GenASM implements Visitor {
}
@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);
@@ -816,6 +830,14 @@ public class GenASM implements Visitor {
return null;
}
+ @Override
+ public Void visit(EnumAccessExpression e) {
+ // Since the access to an enum simply results in an integer (i.e. the index
+ // of the enum value), we can just let IntegerExpression handle the expected behaviour.
+ new IntegerExpression(e.enumValue.index).welcome(this);
+ return null;
+ }
+
@Override
public Void visit(ConstructorCall e) {
// push arguments onto the stack
diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java
index 50d807d..33e6b6b 100644
--- a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java
+++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java
@@ -234,6 +234,16 @@ class GetVars implements Visitor {
return null;
}
+ @Override
+ public Void visit(EnumDefinition e) {
+ return null;
+ }
+
+ @Override
+ public Void visit(EnumValue e) {
+ return null;
+ }
+
@Override
public Void visit(StructDefinition e) {
return null;
@@ -245,7 +255,12 @@ class GetVars implements Visitor {
}
@Override
- public Void visit(StructFieldAccessExpression e) {
+ public Void visit(MemberAccessExpression e) {
+ return null;
+ }
+
+ @Override
+ public Void visit(EnumAccessExpression e) {
return null;
}
diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java
index 563779b..252797b 100644
--- a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java
+++ b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java
@@ -68,6 +68,12 @@ public class PrettyPrintVisitor implements Visitor {
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,28 @@ public class PrettyPrintVisitor implements Visitor {
return null;
}
+ @Override
+ public Void visit(EnumDefinition e) {
+ ex.write("enum " + e.name + " { ");
+ var first = true;
+ for(var enumValue: e.enums) {
+ if (!first) {
+ ex.write(", ");
+ } else {
+ first = false;
+ }
+ enumValue.welcome(this);
+ }
+ ex.write(" }");
+ return null;
+ }
+
+ @Override
+ public Void visit(EnumValue e) {
+ ex.write(e.value);
+ return null;
+ }
+
@Override
public Void visit(StructDefinition e) {
ex.write("struct " + e.name + " {");
@@ -398,7 +426,7 @@ public class PrettyPrintVisitor implements Visitor {
}
@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 +435,15 @@ public class PrettyPrintVisitor implements Visitor {
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 + "(");
diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java
index cfef604..f56e8b6 100644
--- a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java
+++ b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java
@@ -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,12 @@ public interface Visitor {
R visit(FunctionCall e);
R visit(Program e);
R visit(Parameter e);
+ R visit(EnumDefinition e);
+ R visit(EnumValue e);
R visit(StructDefinition e);
R visit(StructField e);
- R visit(StructFieldAccessExpression e);
+ R visit(MemberAccessExpression e);
+ R visit(EnumAccessExpression e);
R visit(ConstructorCall e);
R visit(NullExpression e);
R visit(DestructorCall e);
diff --git a/src/test/java/AndTest.java b/src/test/java/AndTest.java
index 03f4f33..a4fc6d5 100644
--- a/src/test/java/AndTest.java
+++ b/src/test/java/AndTest.java
@@ -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());
diff --git a/src/test/java/ConstructorCallTest.java b/src/test/java/ConstructorCallTest.java
index 99010bc..9d21d29 100644
--- a/src/test/java/ConstructorCallTest.java
+++ b/src/test/java/ConstructorCallTest.java
@@ -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());
diff --git a/src/test/java/DestroyStatementTest.java b/src/test/java/DestroyStatementTest.java
index f157d2b..523c4b5 100644
--- a/src/test/java/DestroyStatementTest.java
+++ b/src/test/java/DestroyStatementTest.java
@@ -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());
diff --git a/src/test/java/FieldAssignmentTest.java b/src/test/java/FieldAssignmentTest.java
index 879e4c8..32eeacb 100644
--- a/src/test/java/FieldAssignmentTest.java
+++ b/src/test/java/FieldAssignmentTest.java
@@ -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());
diff --git a/src/test/java/FunctionCallTest.java b/src/test/java/FunctionCallTest.java
index d0e5126..ce3876f 100644
--- a/src/test/java/FunctionCallTest.java
+++ b/src/test/java/FunctionCallTest.java
@@ -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());
diff --git a/src/test/java/FunctionDefinitionTest.java b/src/test/java/FunctionDefinitionTest.java
index 442e792..b6086fc 100644
--- a/src/test/java/FunctionDefinitionTest.java
+++ b/src/test/java/FunctionDefinitionTest.java
@@ -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);
-
- Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
- assertEquals("Error in line 1:0 Type schwurbel not defined.", e.getMessage());
+ void shouldNotThrowIfReturnTypeIsReferringToAnEnum() {
+ // given
+ var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo(1);");
+ var ctxAnal = new ContextAnalysis(
+ Helper.getFuncs(tree),
+ Helper.getStructs(tree),
+ Helper.getEnums(tree)
+ );
+
+ // when / then
+ assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
- void noReturnExpression() {
- ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();");
- var funcs = Helper.getFuncs(tree);
- var structs = Helper.getStructs(tree);
- ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs);
-
- Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
- assertEquals("Error in line 1:0 Function foo has to return something of type int.", e.getMessage());
+ void shouldNotThrowIfParameterTypeIsReferringToAnEnum() {
+ // given
+ var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: bar): int { return 1; } foo(bar.A);");
+ var ctxAnal = new ContextAnalysis(
+ Helper.getFuncs(tree),
+ Helper.getStructs(tree),
+ Helper.getEnums(tree)
+ );
+
+ // when / then
+ assertDoesNotThrow(() -> ctxAnal.visit(tree));
+ }
+
+ @Test
+ void shouldThrowExceptionIfParameterTypeIsNotDefined() {
+ // given
+ var tree = Helper.prepareParser("function foo(a: schwurbel): int { return 1; } foo();");
+ var ctxAnal = new ContextAnalysis(
+ Helper.getFuncs(tree),
+ Helper.getStructs(tree),
+ Helper.getEnums(tree)
+ );
+
+ // when / then
+ var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
+ assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage());
+ }
+
+ @Test
+ void shouldThrowExceptionIfReturnTypeIsNotDefined() {
+ // given
+ var tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();");
+ var ctxAnal = new ContextAnalysis(
+ Helper.getFuncs(tree),
+ Helper.getStructs(tree),
+ Helper.getEnums(tree)
+ );
+
+ // when / then
+ var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
+ assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage());
+ }
+
+ @Test
+ void shouldThrowExceptionIfReturnStatementIsMissing() {
+ // given
+ var tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();");
+ var ctxAnal = new ContextAnalysis(
+ Helper.getFuncs(tree),
+ Helper.getStructs(tree),
+ Helper.getEnums(tree)
+ );
+
+ // when / then
+ var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
+ assertEquals("Error in line 1:20 Function foo has to return something of type int.", e.getMessage());
+ }
+
+ @Test
+ void shouldThrowExceptionIfParameterNameMatchesEnumName() {
+ // given
+ var tree = Helper.prepareParser("enum Bar { A, B } function foo(Bar: int): int { return 1; } foo(1);");
+ var ctxAnal = new ContextAnalysis(
+ Helper.getFuncs(tree),
+ Helper.getStructs(tree),
+ Helper.getEnums(tree)
+ );
+
+ // when / then
+ var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
+ assertEquals("Error in line 1:31 Parameter name Bar duplicates an enum of the same name.", e.getMessage());
}
}
\ No newline at end of file
diff --git a/src/test/java/Helper.java b/src/test/java/Helper.java
index 68b0ff6..eef2a8c 100644
--- a/src/test/java/Helper.java
+++ b/src/test/java/Helper.java
@@ -16,21 +16,21 @@ public class Helper {
return parser.parse();
}
- public static Map getFuncs(ParseTree tree) {
- var functionDefinitions = new HashMap();
- new GetFunctions(functionDefinitions).visit(tree);
+ public static Map getFuncs(ParseTree tree) {
+ var functionDefinitions = new HashMap();
+ new GetDefinitions(functionDefinitions, new HashMap<>(), new HashMap<>()).visit(tree);
return functionDefinitions;
}
- public static Set getStructNames(ParseTree tree) {
- var structNames = new HashSet();
- new GetStructNames(structNames).visit(tree);
- return structNames;
- }
-
public static Map getStructs(ParseTree tree) {
var structs = new HashMap();
- new GetStructs(getStructNames(tree), structs).visit(tree);
+ new GetDefinitions(new HashMap<>(), structs, new HashMap<>()).visit(tree);
return structs;
}
+
+ public static Map getEnums(ParseTree tree) {
+ var enums = new HashMap();
+ new GetDefinitions(new HashMap<>(), new HashMap<>(), enums).visit(tree);
+ return enums;
+ }
}
\ No newline at end of file
diff --git a/src/test/java/ModuloTest.java b/src/test/java/ModuloTest.java
index c11a87f..57b2f8b 100644
--- a/src/test/java/ModuloTest.java
+++ b/src/test/java/ModuloTest.java
@@ -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());
diff --git a/src/test/java/OrTest.java b/src/test/java/OrTest.java
index 07dfc33..c5511d5 100644
--- a/src/test/java/OrTest.java
+++ b/src/test/java/OrTest.java
@@ -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());
diff --git a/src/test/java/ParameterTest.java b/src/test/java/ParameterTest.java
deleted file mode 100644
index 9342ccc..0000000
--- a/src/test/java/ParameterTest.java
+++ /dev/null
@@ -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());
- }
-}
\ No newline at end of file
diff --git a/src/test/java/StructDefinitionTest.java b/src/test/java/StructDefinitionTest.java
new file mode 100644
index 0000000..11dce9c
--- /dev/null
+++ b/src/test/java/StructDefinitionTest.java
@@ -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());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/StructFieldAccessTest.java b/src/test/java/StructFieldAccessTest.java
index 79b9766..9bda8f3 100644
--- a/src/test/java/StructFieldAccessTest.java
+++ b/src/test/java/StructFieldAccessTest.java
@@ -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());
diff --git a/src/test/java/VariableAssignmentTest.java b/src/test/java/VariableAssignmentTest.java
index faa338d..292e5f4 100644
--- a/src/test/java/VariableAssignmentTest.java
+++ b/src/test/java/VariableAssignmentTest.java
@@ -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());
diff --git a/src/test/java/VariableDeclarationTest.java b/src/test/java/VariableDeclarationTest.java
index a52ea2e..7727159 100644
--- a/src/test/java/VariableDeclarationTest.java
+++ b/src/test/java/VariableDeclarationTest.java
@@ -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);
-
- Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
+ 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)
+ );
+
+ // 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);
-
- Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
+ 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)
+ );
+
+ // 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());
+ }
}
\ No newline at end of file
diff --git a/src/test/java/VariableTest.java b/src/test/java/VariableTest.java
index 763226c..c9d2d39 100644
--- a/src/test/java/VariableTest.java
+++ b/src/test/java/VariableTest.java
@@ -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());