Implement StructField type checking in ContextAnalysis.
This commit is contained in:
@@ -39,18 +39,23 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitProgram(KlangParser.ProgramContext ctx) {
|
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++) {
|
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());
|
for (int i = 0; i < ctx.structDef().size(); i++) {
|
||||||
Program result = new Program(funcs, structDefs, enumDefs, expression);
|
typeCheckedStructDefs.put(ctx.structDef(i).structName.getText(), (StructDefinition) visit(ctx.structDef(i)));
|
||||||
result.type = expression.type;
|
}
|
||||||
result.line = ctx.start.getLine();
|
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
var expression = (Expression) visit(ctx.expression());
|
||||||
return result;
|
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
|
@Override
|
||||||
@@ -727,6 +732,46 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
return n;
|
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
|
@Override
|
||||||
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
||||||
String name = ctx.funcName.getText();
|
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