diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index ced5fc1..4ba5b6f 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -39,18 +39,23 @@ public class ContextAnalysis extends KlangBaseVisitor { @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(); 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 { 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(); diff --git a/src/test/java/ParameterTest.java b/src/test/java/ParameterTest.java deleted file mode 100644 index 9342ccc..0000000 --- a/src/test/java/ParameterTest.java +++ /dev/null @@ -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()); - } -} \ No newline at end of file diff --git a/src/test/java/StructDefinitionTest.java b/src/test/java/StructDefinitionTest.java new file mode 100644 index 0000000..b3150f3 --- /dev/null +++ b/src/test/java/StructDefinitionTest.java @@ -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()); + } +} \ No newline at end of file