summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-04-25 10:56:55 +0100
committerMichael Williamson <mike@zwobble.org>2026-04-25 10:56:55 +0100
commit72c84ea17a7e4b27cf9428ef23f713e32f5339d3 (patch)
tree05f6d962ef88e92f292c834adb1225086824d749 /src
parent39ef07c887e33aff56c087388980a92e750b547c (diff)
Type check struct fields
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java3
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java31
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java14
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java15
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java4
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java53
6 files changed, 110 insertions, 10 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java b/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java
index 4f56fb1..d9f7d21 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java
@@ -4,6 +4,7 @@ import org.zwobble.hobgoblin.compiler.parser.Parser;
import org.zwobble.hobgoblin.compiler.typechecker.TypeChecker;
import org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerGlobalContext;
import org.zwobble.hobgoblin.compiler.types.NamespaceName;
+import org.zwobble.hobgoblin.compiler.types.ScalarType;
import org.zwobble.sourcetext.SourceText;
import java.io.IOException;
@@ -29,6 +30,8 @@ public class HobgoblinCompiler {
var untypedNamespaceNode = Parser.parseNamespace(sourceText, namespaceName);
var typeCheckerContext = TypeCheckerGlobalContext.initial();
+ typeCheckerContext.addBuiltinScalarType(new ScalarType("Int32"));
+
var typedNamespaceNode = TypeChecker.typeCheckNamespace(untypedNamespaceNode, typeCheckerContext);
}
}
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 b8bdd7f..d9a37e2 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -2,13 +2,9 @@ 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.types.StructType;
-import org.zwobble.hobgoblin.compiler.types.TypeLevelValue;
-import org.zwobble.hobgoblin.compiler.types.TypeLevelValueType;
-import org.zwobble.hobgoblin.compiler.types.TypeSet;
+import org.zwobble.hobgoblin.compiler.types.*;
import java.util.ArrayList;
-import java.util.List;
public class TypeChecker {
private TypeChecker() {
@@ -49,13 +45,36 @@ public class TypeChecker {
) {
var structType = new StructType(context.namespaceName(), untyped.name());
+ var typedFields = new ArrayList<TypedStructFieldDefinitionNode>();
+ for (var untypedField : untyped.fields()) {
+ var typedField = new TypedStructFieldDefinitionNode(
+ untypedField.name(),
+ typeCheckMetaType(untypedField.type(), context),
+ untyped.source()
+ );
+ typedFields.add(typedField);
+ }
+
return new TypedStructDefinitionNode(
structType,
- List.of(),
+ typedFields,
untyped.source()
);
}
+ private static TypedTypeLevelExpressionNode<Type> typeCheckMetaType(
+ UntypedTypeLevelExpressionNode untyped,
+ TypeCheckerNamespaceContext context
+ ) {
+ var typed = typeCheckTypeLevelExpression(untyped, context);
+ if (typed.value() instanceof Type) {
+ return (TypedTypeLevelExpressionNode<Type>) typed;
+ } else {
+ // TODO: better error
+ throw new RuntimeException("not a metatype");
+ }
+ }
+
static TypedTypeLevelExpressionNode<? extends TypeLevelValue> typeCheckTypeLevelExpression(
UntypedTypeLevelExpressionNode untyped,
TypeCheckerNamespaceContext context
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java
index 3915767..849da2f 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerGlobalContext.java
@@ -1,8 +1,16 @@
package org.zwobble.hobgoblin.compiler.typechecker;
import org.zwobble.hobgoblin.compiler.types.NamespaceName;
+import org.zwobble.hobgoblin.compiler.types.ScalarType;
+import org.zwobble.hobgoblin.compiler.types.Type;
+import org.zwobble.hobgoblin.compiler.types.TypeLevelValueType;
+
+import java.util.HashMap;
+import java.util.Map;
public class TypeCheckerGlobalContext {
+ private final Map<String, Type> builtins = new HashMap<>();
+
public static TypeCheckerGlobalContext initial() {
return new TypeCheckerGlobalContext();
}
@@ -10,7 +18,11 @@ public class TypeCheckerGlobalContext {
private TypeCheckerGlobalContext() {
}
+ public void addBuiltinScalarType(ScalarType type) {
+ this.builtins.put(type.name(), new TypeLevelValueType(type));
+ }
+
public TypeCheckerNamespaceContext enterNamespace(NamespaceName namespaceName) {
- return TypeCheckerNamespaceContext.initial(namespaceName);
+ return TypeCheckerNamespaceContext.initial(namespaceName, this.builtins);
}
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java
index c1a23fd..353209b 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java
@@ -3,13 +3,22 @@ package org.zwobble.hobgoblin.compiler.typechecker;
import org.zwobble.hobgoblin.compiler.types.NamespaceName;
import org.zwobble.hobgoblin.compiler.types.Type;
-import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
public class TypeCheckerNamespaceContext {
- public static TypeCheckerNamespaceContext initial(NamespaceName namespaceName) {
- return new TypeCheckerNamespaceContext(namespaceName, new HashMap<>());
+ public static TypeCheckerNamespaceContext initial(
+ NamespaceName namespaceName,
+ Map<String, Type> builtins
+ ) {
+ var variables = builtins.entrySet().stream()
+ .collect(Collectors.toMap(
+ entry -> entry.getKey(),
+ entry -> Variable.defined(entry.getValue())
+ ));
+
+ return new TypeCheckerNamespaceContext(namespaceName, variables);
}
private final NamespaceName namespaceName;
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java
index 1d3a4b6..e80283f 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java
@@ -3,6 +3,10 @@ package org.zwobble.hobgoblin.compiler.typechecker;
import org.zwobble.hobgoblin.compiler.types.Type;
public sealed interface Variable {
+ public static Variable defined(Type type) {
+ return new Defined(type);
+ }
+
record Declared() implements Variable {
}
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 b6fddba..f13731f 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
@@ -2,10 +2,16 @@ package org.zwobble.hobgoblin.compiler.typechecker;
import org.junit.jupiter.api.Test;
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.UntypedStructDefinitionNode;
+import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode;
import org.zwobble.hobgoblin.compiler.sources.NullSource;
import org.zwobble.hobgoblin.compiler.types.NamespaceName;
+import org.zwobble.hobgoblin.compiler.types.ScalarType;
import org.zwobble.hobgoblin.compiler.types.StructType;
+import org.zwobble.hobgoblin.compiler.types.TypeLevelValueType;
import java.util.List;
@@ -33,4 +39,51 @@ public class TypeCheckerStructDefinitionTests {
)
));
}
+
+ @Test
+ public void fieldsAreTypeChecked() {
+ var int32Type = new ScalarType("Int32");
+ var int64Type = new ScalarType("Int64");
+ var untyped = new UntypedStructDefinitionNode(
+ "X",
+ List.of(
+ new UntypedStructFieldDefinitionNode("a", UntypedArb.typeLevelReference("Int32"), NullSource.INSTANCE),
+ new UntypedStructFieldDefinitionNode("b", UntypedArb.typeLevelReference("Int64"), NullSource.INSTANCE)
+ ),
+ NullSource.INSTANCE
+ );
+ var context = TypeCheckerContextArb.namespaceContext(NamespaceName.of("a", "b"));
+ context.declare(int32Type.name());
+ context.define(int32Type.name(), new TypeLevelValueType(int32Type));
+ context.declare(int64Type.name());
+ context.define(int64Type.name(), new TypeLevelValueType(int64Type));
+
+ var typed = TypeChecker.typeCheckNamespaceStatement(untyped, context);
+
+ assertThat(typed, instanceOf(
+ TypedStructDefinitionNode.class,
+ has(
+ "fields",
+ TypedStructDefinitionNode::fields,
+ isSequence(
+ allOf(
+ has("name", TypedStructFieldDefinitionNode::name, equalTo("a")),
+ has("type", TypedStructFieldDefinitionNode::type, has(
+ "value",
+ TypedTypeLevelExpressionNode::value,
+ equalTo(int32Type)
+ ))
+ ),
+ allOf(
+ has("name", TypedStructFieldDefinitionNode::name, equalTo("b")),
+ has("type", TypedStructFieldDefinitionNode::type, has(
+ "value",
+ TypedTypeLevelExpressionNode::value,
+ equalTo(int64Type)
+ ))
+ )
+ )
+ )
+ ));
+ }
}