summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java15
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java70
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java80
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTesting.java17
4 files changed, 164 insertions, 18 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java
index 54796e2..c638763 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java
@@ -11,6 +11,8 @@ import org.zwobble.hobgoblin.compiler.output.java.ast.JavaRecordComponent;
import org.zwobble.hobgoblin.compiler.output.java.ast.JavaRecordDeclaration;
import org.zwobble.hobgoblin.compiler.output.java.ast.JavaCompilationUnit;
import org.zwobble.hobgoblin.compiler.output.java.ast.JavaTypeRef;
+import org.zwobble.hobgoblin.compiler.types.NamespaceName;
+import org.zwobble.hobgoblin.compiler.types.StructType;
import org.zwobble.hobgoblin.compiler.types.Type;
import java.io.File;
@@ -36,8 +38,7 @@ public class JavaTypesGenerator implements Generator {
}
private void generateNamespace(TypedNamespaceNode namespace) throws IOException {
- var packageParts = new ArrayList<>(this.packageName);
- packageParts.addAll(namespace.namespaceName().parts());
+ var packageParts = namespaceToJavaPackageParts(namespace.namespaceName());
for (var statement : namespace.body()) {
switch (statement) {
@@ -69,10 +70,18 @@ public class JavaTypesGenerator implements Generator {
}
}
+ private ArrayList<String> namespaceToJavaPackageParts(NamespaceName namespaceName) {
+ var packageParts = new ArrayList<>(this.packageName);
+ packageParts.addAll(namespaceName.parts());
+ return packageParts;
+ }
+
private JavaTypeRef generateTypeRef(TypedTypeLevelExpressionNode<Type> typeNode) {
var type = typeNode.value();
- if (type.equals(BuiltinTypes.INT_32)) {
+ if (type instanceof StructType structType) {
+ return new JavaTypeRef(namespaceToJavaPackageParts(structType.namespaceName()), structType.name());
+ } else if (type.equals(BuiltinTypes.INT_32)) {
return new JavaTypeRef(List.of(), "int");
} else {
throw new UnsupportedOperationException("TODO");
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
index d9a37e2..7678f94 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -2,6 +2,7 @@ package org.zwobble.hobgoblin.compiler.typechecker;
import org.zwobble.hobgoblin.compiler.ast.typed.*;
import org.zwobble.hobgoblin.compiler.ast.untyped.*;
+import org.zwobble.hobgoblin.compiler.sources.Source;
import org.zwobble.hobgoblin.compiler.types.*;
import java.util.ArrayList;
@@ -16,9 +17,13 @@ public class TypeChecker {
) {
var namespaceContext = context.enterNamespace(untyped.namespaceName());
+ for (var untypedStatement : untyped.body()) {
+ declareNamespaceStatement(untypedStatement, namespaceContext);
+ }
+
var typedBody = new ArrayList<TypedNamespaceStatementNode>();
for (var untypedStatement : untyped.body()) {
- var typedStatement = typeCheckNamespaceStatement(untypedStatement, namespaceContext);
+ var typedStatement = defineNamespaceStatement(untypedStatement, namespaceContext);
typedBody.add(typedStatement);
}
@@ -29,21 +34,41 @@ public class TypeChecker {
);
}
- static TypedNamespaceStatementNode typeCheckNamespaceStatement(
+ static void declareNamespaceStatement(
+ UntypedNamespaceStatementNode untyped,
+ TypeCheckerNamespaceContext context
+ ) {
+ switch (untyped) {
+ case UntypedStructDefinitionNode untypedStructDefinition ->
+ declareStructDefinition(untypedStructDefinition, context);
+ }
+ }
+
+ static TypedNamespaceStatementNode defineNamespaceStatement(
UntypedNamespaceStatementNode untyped,
TypeCheckerNamespaceContext context
) {
return switch (untyped) {
case UntypedStructDefinitionNode untypedStructDefinition ->
- typeCheckStructDefinition(untypedStructDefinition, context);
+ defineStructDefinition(untypedStructDefinition, context);
};
}
- private static TypedStructDefinitionNode typeCheckStructDefinition(
+ private static void declareStructDefinition(
UntypedStructDefinitionNode untyped,
TypeCheckerNamespaceContext context
) {
var structType = new StructType(context.namespaceName(), untyped.name());
+ // TODO: tidy up declare vs define
+ context.declare(untyped.name());
+ context.define(untyped.name(), new TypeLevelValueType(structType));
+ }
+
+ private static TypedStructDefinitionNode defineStructDefinition(
+ UntypedStructDefinitionNode untyped,
+ TypeCheckerNamespaceContext context
+ ) {
+ var structType = (StructType) lookupMetaType(untyped.name(), untyped.source(), context);
var typedFields = new ArrayList<TypedStructFieldDefinitionNode>();
for (var untypedField : untyped.fields()) {
@@ -89,12 +114,35 @@ public class TypeChecker {
UntypedTypeLevelReferenceNode untyped,
TypeCheckerNamespaceContext context
) {
- var variable = context.lookup(untyped.name())
- .orElseThrow(() -> new UndeclaredVariableError(untyped.name(), untyped.source()));
+ var value = lookupTypeLevelValue(untyped.name(), untyped.source(), context);
+
+ return new TypedTypeLevelReferenceNode<>(
+ untyped.name(),
+ value,
+ untyped.source()
+ );
+ }
+
+ private static Type lookupMetaType(
+ String name,
+ Source source,
+ TypeCheckerNamespaceContext context
+ ) {
+ // TODO: handle not a type properly
+ return (Type) lookupTypeLevelValue(name, source, context);
+ }
+
+ private static TypeLevelValue lookupTypeLevelValue(
+ String name,
+ Source source,
+ TypeCheckerNamespaceContext context
+ ) {
+ var variable = context.lookup(name)
+ .orElseThrow(() -> new UndeclaredVariableError(name, source));
var type = switch (variable) {
case Variable.Declared declared ->
- throw new UndefinedVariableError(untyped.name(), untyped.source());
+ throw new UndefinedVariableError(name, source);
case Variable.Defined defined ->
defined.type();
@@ -102,13 +150,9 @@ public class TypeChecker {
// TODO: handle not a type-level value
if (!(type instanceof TypeLevelValueType(TypeLevelValue value))) {
- throw new UnexpectedTypeError(new TypeSet.MetaType(), type, untyped.source());
+ throw new UnexpectedTypeError(new TypeSet.MetaType(), type, source);
}
- return new TypedTypeLevelReferenceNode<>(
- untyped.name(),
- value,
- untyped.source()
- );
+ return value;
}
}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
index f13731f..aec039b 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
@@ -1,10 +1,12 @@
package org.zwobble.hobgoblin.compiler.typechecker;
import org.junit.jupiter.api.Test;
+import org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedStructDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedStructFieldDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedTypeLevelExpressionNode;
import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedArb;
+import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceNode;
import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode;
import org.zwobble.hobgoblin.compiler.sources.NullSource;
@@ -15,6 +17,7 @@ import org.zwobble.hobgoblin.compiler.types.TypeLevelValueType;
import java.util.List;
+import static org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement;
import static org.zwobble.precisely.AssertThat.assertThat;
import static org.zwobble.precisely.Matchers.*;
@@ -28,7 +31,7 @@ public class TypeCheckerStructDefinitionTests {
);
var context = TypeCheckerContextArb.namespaceContext(NamespaceName.of("a", "b"));
- var typed = TypeChecker.typeCheckNamespaceStatement(untyped, context);
+ var typed = typeCheckNamespaceStatement(untyped, context);
assertThat(typed, instanceOf(
TypedStructDefinitionNode.class,
@@ -58,7 +61,7 @@ public class TypeCheckerStructDefinitionTests {
context.declare(int64Type.name());
context.define(int64Type.name(), new TypeLevelValueType(int64Type));
- var typed = TypeChecker.typeCheckNamespaceStatement(untyped, context);
+ var typed = typeCheckNamespaceStatement(untyped, context);
assertThat(typed, instanceOf(
TypedStructDefinitionNode.class,
@@ -86,4 +89,77 @@ public class TypeCheckerStructDefinitionTests {
)
));
}
+
+ @Test
+ public void structCanUseTypeDefinedLater() {
+ var int32Type = new ScalarType("Int32");
+ var namespaceName = NamespaceName.of("a", "b");
+ var untyped = new UntypedNamespaceNode(
+ namespaceName,
+ List.of(
+ new UntypedStructDefinitionNode(
+ "X",
+ List.of(
+ new UntypedStructFieldDefinitionNode("value", UntypedArb.typeLevelReference("Y"), NullSource.INSTANCE)
+ ),
+ NullSource.INSTANCE
+ ),
+ new UntypedStructDefinitionNode(
+ "Y",
+ List.of(
+ new UntypedStructFieldDefinitionNode("value", UntypedArb.typeLevelReference("Int32"), NullSource.INSTANCE)
+ ),
+ NullSource.INSTANCE
+ )
+ ),
+ NullSource.INSTANCE
+ );
+ var context = TypeCheckerContextArb.globalContext();
+ context.addBuiltinScalarType(int32Type);
+
+ var typed = TypeChecker.typeCheckNamespace(untyped, context);
+
+ assertThat(typed, has(
+ "body",
+ TypedNamespaceNode::body,
+ isSequence(
+ instanceOf(
+ TypedStructDefinitionNode.class,
+ has("name", TypedStructDefinitionNode::name, equalTo("X")),
+ has(
+ "fields",
+ TypedStructDefinitionNode::fields,
+ isSequence(
+ allOf(
+ has("name", TypedStructFieldDefinitionNode::name, equalTo("value")),
+ has("type", TypedStructFieldDefinitionNode::type, has(
+ "value",
+ TypedTypeLevelExpressionNode::value,
+ equalTo(new StructType(namespaceName, "Y"))
+ ))
+ )
+ )
+ )
+ ),
+ instanceOf(
+ TypedStructDefinitionNode.class,
+ has("name", TypedStructDefinitionNode::name, equalTo("Y")),
+ has(
+ "fields",
+ TypedStructDefinitionNode::fields,
+ isSequence(
+ allOf(
+ has("name", TypedStructFieldDefinitionNode::name, equalTo("value")),
+ has("type", TypedStructFieldDefinitionNode::type, has(
+ "value",
+ TypedTypeLevelExpressionNode::value,
+ equalTo(int32Type)
+ ))
+ )
+ )
+ )
+ )
+ )
+ ));
+ }
}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTesting.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTesting.java
new file mode 100644
index 0000000..b0c21ec
--- /dev/null
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTesting.java
@@ -0,0 +1,17 @@
+package org.zwobble.hobgoblin.compiler.typechecker;
+
+import org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceStatementNode;
+import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceStatementNode;
+
+public class TypeCheckerTesting {
+ private TypeCheckerTesting() {
+ }
+
+ public static TypedNamespaceStatementNode typeCheckNamespaceStatement(
+ UntypedNamespaceStatementNode untyped,
+ TypeCheckerNamespaceContext context
+ ) {
+ TypeChecker.declareNamespaceStatement(untyped, context);
+ return TypeChecker.defineNamespaceStatement(untyped, context);
+ }
+}