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
|
@Override
|
||||||
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
||||||
String name = ctx.funcName.getText();
|
var name = ctx.funcName.getText();
|
||||||
int line = ctx.start.getLine();
|
var returnType = Type.getByName(ctx.returnType.type().getText());
|
||||||
int col = ctx.start.getCharPositionInLine();
|
currentDeclaredReturnType = returnType;
|
||||||
Type returnType = Type.getByName(ctx.returnType.type().getText());
|
currentFunctionDefinitionName = name;
|
||||||
this.currentDeclaredReturnType = returnType;
|
|
||||||
this.currentFunctionDefinitionName = name;
|
|
||||||
|
|
||||||
if (!returnType.isPrimitiveType() && this.structDefs.get(returnType.getName()) == null) {
|
if (!returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName())) {
|
||||||
String error = "Type " + returnType.getName() + " not defined.";
|
var line = ctx.returnType.start.getLine();
|
||||||
|
var col = ctx.returnType.start.getCharPositionInLine();
|
||||||
|
var error = "Type " + returnType.getName() + " not defined.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new set for the variables of the current function
|
// Create a new set for the variables of the current function
|
||||||
// this will be filled in the variable declaration visitor aswell
|
// this will be filled in the variable declaration visitor as well
|
||||||
this.vars = new HashMap<>();
|
vars = new HashMap<>();
|
||||||
|
|
||||||
// Process the paremter list by visiting every paremter in it
|
// Process the parameter list by visiting every parameter in it
|
||||||
int paramCount = ctx.params.parameter().size();
|
var paramCount = ctx.params.parameter().size();
|
||||||
Parameter[] params = new Parameter[paramCount];
|
var params = new Parameter[paramCount];
|
||||||
for (int i = 0; i < paramCount; i++) {
|
for (int i = 0; i < paramCount; i++) {
|
||||||
// Add the parameter to the list of parameters
|
// Add the parameter to the list of parameters
|
||||||
Parameter param = (Parameter) this.visit(ctx.params.parameter(i));
|
var param = (Parameter) visit(ctx.params.parameter(i));
|
||||||
params[i] = param;
|
params[i] = param;
|
||||||
|
|
||||||
// add the param as a variable
|
// add the param as a variable
|
||||||
VariableDeclaration var = new VariableDeclaration(param.name);
|
var var = new VariableDeclaration(param.name);
|
||||||
var.initialized = true; // parameters can always be considered initialized
|
var.initialized = true; // parameters can always be considered initialized
|
||||||
var.type = param.type;
|
var.type = param.type;
|
||||||
this.vars.put(param.name, var);
|
vars.put(param.name, var);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visit the block, make sure that a return value is guaranteed
|
// Visit the block, make sure that a return value is guaranteed
|
||||||
Node block = this.visit(ctx.braced_block());
|
var block = visit(ctx.braced_block());
|
||||||
if (block.type == null) {
|
if (block.type == null) {
|
||||||
String error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
|
var line = ctx.braced_block().start.getLine();
|
||||||
|
var col = ctx.braced_block().start.getCharPositionInLine();
|
||||||
|
var error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionDefinition result = new FunctionDefinition(name, params, (Block) block);
|
var functionDef = new FunctionDefinition(name, params, (Block) block);
|
||||||
result.type = returnType;
|
functionDef.type = returnType;
|
||||||
|
functionDef.line = ctx.start.getLine();
|
||||||
|
functionDef.col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
result.line = ctx.start.getLine();
|
return functionDef;
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
var parameterName = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var parameterType = Type.getByName(ctx.type_annotation().type().getText());
|
||||||
int col = ctx.start.getCharPositionInLine();
|
|
||||||
Type type = Type.getByName(ctx.type_annotation().type().getText());
|
|
||||||
|
|
||||||
if (!type.isPrimitiveType() && this.structDefs.get(type.getName()) == null) {
|
if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) {
|
||||||
String error = "Type " + type.getName() + " not defined.";
|
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);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
Parameter result = new Parameter(name);
|
var parameter = new Parameter(parameterName);
|
||||||
result.type = type;
|
parameter.type = parameterType;
|
||||||
result.line = line;
|
parameter.line = ctx.start.getLine();
|
||||||
result.col = col;
|
parameter.col = ctx.start.getCharPositionInLine();
|
||||||
return result;
|
|
||||||
|
return parameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.antlr.v4.runtime.tree.ParseTree;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.ContextAnalysis;
|
import de.hsrm.compiler.Klang.ContextAnalysis;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
public class FunctionDefinitionTest {
|
public class FunctionDefinitionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void typeNotDefined() {
|
void shouldNotThrowIfReturnTypeIsReferringToAnEnum() {
|
||||||
ParseTree tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();");
|
// given
|
||||||
var funcs = Helper.getFuncs(tree);
|
var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo();");
|
||||||
var structs = Helper.getStructs(tree);
|
var ctxAnal = new ContextAnalysis(
|
||||||
var enums = Helper.getEnums(tree);
|
Helper.getFuncs(tree),
|
||||||
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
);
|
||||||
assertEquals("Error in line 1:0 Type schwurbel not defined.", e.getMessage());
|
|
||||||
|
// when / then
|
||||||
|
assertDoesNotThrow(() -> ctxAnal.visit(tree));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void noReturnExpression() {
|
void shouldNotThrowIfParameterTypeIsReferringToAnEnum() {
|
||||||
ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();");
|
// given
|
||||||
var funcs = Helper.getFuncs(tree);
|
var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: bar): int { return 1; } foo(bar.A);");
|
||||||
var structs = Helper.getStructs(tree);
|
var ctxAnal = new ContextAnalysis(
|
||||||
var enums = Helper.getEnums(tree);
|
Helper.getFuncs(tree),
|
||||||
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
);
|
||||||
assertEquals("Error in line 1:0 Function foo has to return something of type int.", e.getMessage());
|
|
||||||
|
// when / then
|
||||||
|
assertDoesNotThrow(() -> ctxAnal.visit(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionIfParameterTypeIsNotDefined() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("function foo(a: schwurbel): int { return 1; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(
|
||||||
|
Helper.getFuncs(tree),
|
||||||
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
|
);
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionIfReturnTypeIsNotDefined() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(
|
||||||
|
Helper.getFuncs(tree),
|
||||||
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
|
);
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 1:14 Type schwurbel not defined.", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionIfReturnStatementIsMissing() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(
|
||||||
|
Helper.getFuncs(tree),
|
||||||
|
Helper.getStructs(tree),
|
||||||
|
Helper.getEnums(tree)
|
||||||
|
);
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 1:20 Function foo has to return something of type int.", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user