Refactor FunctionDefinition and Parameter context analysis and extend the type check to include enums.

This commit is contained in:
2023-03-15 17:47:58 +01:00
parent 9a58afb550
commit 3b928d621b
2 changed files with 106 additions and 55 deletions

View File

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

View File

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