Refactor FunctionDefinition and Parameter context analysis and extend the type check to include enums.
This commit is contained in:
@@ -774,69 +774,72 @@ 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;
|
||||
var name = ctx.funcName.getText();
|
||||
var returnType = Type.getByName(ctx.returnType.type().getText());
|
||||
currentDeclaredReturnType = returnType;
|
||||
currentFunctionDefinitionName = name;
|
||||
|
||||
if (!returnType.isPrimitiveType() && this.structDefs.get(returnType.getName()) == null) {
|
||||
String error = "Type " + returnType.getName() + " not defined.";
|
||||
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.structDefs.get(type.getName()) == null) {
|
||||
String error = "Type " + type.getName() + " not defined.";
|
||||
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();
|
||||
String error = "Type " + parameterType.getName() + " not defined.";
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||
}
|
||||
|
||||
Parameter result = new Parameter(name);
|
||||
result.type = type;
|
||||
result.line = line;
|
||||
result.col = col;
|
||||
return result;
|
||||
var parameter = new Parameter(parameterName);
|
||||
parameter.type = parameterType;
|
||||
parameter.line = ctx.start.getLine();
|
||||
parameter.col = ctx.start.getCharPositionInLine();
|
||||
|
||||
return parameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,34 +1,82 @@
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import de.hsrm.compiler.Klang.ContextAnalysis;
|
||||
|
||||
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);
|
||||
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: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();");
|
||||
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);
|
||||
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: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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user