Implement StructField type checking in ContextAnalysis.
This commit is contained in:
@@ -39,18 +39,23 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitProgram(KlangParser.ProgramContext ctx) {
|
||||
FunctionDefinition[] funcs = new FunctionDefinition[ctx.functionDef().size()];
|
||||
var typeCheckedFunctionDefs = new FunctionDefinition[ctx.functionDef().size()];
|
||||
var typeCheckedStructDefs = new HashMap<String, StructDefinition>();
|
||||
|
||||
for (int i = 0; i < ctx.functionDef().size(); i++) {
|
||||
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
|
||||
typeCheckedFunctionDefs[i] = (FunctionDefinition) visit(ctx.functionDef(i));
|
||||
}
|
||||
|
||||
Expression expression = (Expression) this.visit(ctx.expression());
|
||||
Program result = new Program(funcs, structDefs, enumDefs, 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
|
||||
@@ -727,6 +732,46 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
|
||||
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 super.visitStructDef(ctx);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
var structField = new StructField(structFieldName);
|
||||
structField.type = structFieldType;
|
||||
structField.line = line;
|
||||
structField.col = col;
|
||||
|
||||
return structField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
||||
String name = ctx.funcName.getText();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
62
src/test/java/StructDefinitionTest.java
Normal file
62
src/test/java/StructDefinitionTest.java
Normal file
@@ -0,0 +1,62 @@
|
||||
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 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user