From 776fcb476f229030cb5d34f4b948603f05c01a79 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 24 Apr 2026 12:43:29 +0100 Subject: Type check type level reference --- .../hobgoblin/compiler/ast/typed/TypedNode.java | 7 +++ .../ast/typed/TypedTypeLevelExpressionNode.java | 7 +++ .../ast/typed/TypedTypeLevelReferenceNode.java | 11 +++++ .../compiler/typechecker/TypeChecker.java | 51 ++++++++++++++++++++++ .../compiler/typechecker/TypeCheckerContext.java | 25 +++++++++++ .../typechecker/UndeclaredVariableError.java | 17 ++++++++ .../typechecker/UndefinedVariableError.java | 17 ++++++++ .../compiler/typechecker/UnexpectedTypeError.java | 25 +++++++++++ .../hobgoblin/compiler/typechecker/Variable.java | 11 +++++ .../hobgoblin/compiler/types/ScalarType.java | 8 ++++ .../org/zwobble/hobgoblin/compiler/types/Type.java | 4 ++ .../hobgoblin/compiler/types/TypeLevelValue.java | 5 +++ .../compiler/types/TypeLevelValueType.java | 8 ++++ .../zwobble/hobgoblin/compiler/types/TypeSet.java | 19 ++++++++ 14 files changed, 215 insertions(+) create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNode.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelExpressionNode.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelReferenceNode.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerContext.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndeclaredVariableError.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndefinedVariableError.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UnexpectedTypeError.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/types/ScalarType.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValueType.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/types/TypeSet.java (limited to 'src/main') diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNode.java new file mode 100644 index 0000000..6baa8d6 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNode.java @@ -0,0 +1,7 @@ +package org.zwobble.hobgoblin.compiler.ast.typed; + +import org.zwobble.hobgoblin.compiler.sources.Source; + +public interface TypedNode { + Source source(); +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelExpressionNode.java new file mode 100644 index 0000000..c588f95 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelExpressionNode.java @@ -0,0 +1,7 @@ +package org.zwobble.hobgoblin.compiler.ast.typed; + +import org.zwobble.hobgoblin.compiler.types.TypeLevelValue; + +public sealed interface TypedTypeLevelExpressionNode extends TypedNode permits TypedTypeLevelReferenceNode { + T value(); +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelReferenceNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelReferenceNode.java new file mode 100644 index 0000000..bc2c50d --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedTypeLevelReferenceNode.java @@ -0,0 +1,11 @@ +package org.zwobble.hobgoblin.compiler.ast.typed; + +import org.zwobble.hobgoblin.compiler.sources.Source; +import org.zwobble.hobgoblin.compiler.types.TypeLevelValue; + +public record TypedTypeLevelReferenceNode( + String name, + T value, + Source source +) implements TypedTypeLevelExpressionNode { +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java new file mode 100644 index 0000000..56d4645 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java @@ -0,0 +1,51 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.ast.typed.TypedTypeLevelExpressionNode; +import org.zwobble.hobgoblin.compiler.ast.typed.TypedTypeLevelReferenceNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode; +import org.zwobble.hobgoblin.compiler.types.TypeLevelValue; +import org.zwobble.hobgoblin.compiler.types.TypeLevelValueType; +import org.zwobble.hobgoblin.compiler.types.TypeSet; + +public class TypeChecker { + private TypeChecker() { + } + + public static TypedTypeLevelExpressionNode typeCheckTypeLevelExpression( + UntypedTypeLevelExpressionNode untyped, + TypeCheckerContext context + ) { + return switch (untyped) { + case UntypedTypeLevelReferenceNode untypedTypeLevelReference -> + typeCheckTypeLevelReference(untypedTypeLevelReference, context); + }; + } + + private static TypedTypeLevelExpressionNode typeCheckTypeLevelReference( + UntypedTypeLevelReferenceNode untyped, + TypeCheckerContext context + ) { + var variable = context.lookup(untyped.name()) + .orElseThrow(() -> new UndeclaredVariableError(untyped.name(), untyped.source())); + + var type = switch (variable) { + case Variable.Declared declared -> + throw new UndefinedVariableError(untyped.name(), untyped.source()); + + case Variable.Defined defined -> + defined.type(); + }; + + // TODO: handle not a type-level value + if (!(type instanceof TypeLevelValueType(TypeLevelValue value))) { + throw new UnexpectedTypeError(new TypeSet.MetaType(), type, untyped.source()); + } + + return new TypedTypeLevelReferenceNode<>( + untyped.name(), + value, + untyped.source() + ); + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerContext.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerContext.java new file mode 100644 index 0000000..e861c30 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerContext.java @@ -0,0 +1,25 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.types.Type; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class TypeCheckerContext { + private final Map variables = new HashMap<>(); + + public void declare(String name) { + // TODO: Check variable is not already declared. + this.variables.put(name, new Variable.Declared()); + } + + public void define(String name, Type type) { + // TODO: Check variable is not already defined. + this.variables.put(name, new Variable.Defined(type)); + } + + public Optional lookup(String name) { + return Optional.ofNullable(this.variables.get(name)); + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndeclaredVariableError.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndeclaredVariableError.java new file mode 100644 index 0000000..c6130fa --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndeclaredVariableError.java @@ -0,0 +1,17 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.errors.SourceError; +import org.zwobble.hobgoblin.compiler.sources.Source; + +public class UndeclaredVariableError extends SourceError { + private final String variableName; + + public UndeclaredVariableError(String variableName, Source source) { + super("Undeclared variable: " + variableName, source); + this.variableName = variableName; + } + + public String variableName() { + return variableName; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndefinedVariableError.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndefinedVariableError.java new file mode 100644 index 0000000..95be5a6 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UndefinedVariableError.java @@ -0,0 +1,17 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.errors.SourceError; +import org.zwobble.hobgoblin.compiler.sources.Source; + +public class UndefinedVariableError extends SourceError { + private final String variableName; + + public UndefinedVariableError(String variableName, Source source) { + super("Variable has been declared but not defined: " + variableName, source); + this.variableName = variableName; + } + + public String variableName() { + return variableName; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UnexpectedTypeError.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UnexpectedTypeError.java new file mode 100644 index 0000000..c2ca4b6 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UnexpectedTypeError.java @@ -0,0 +1,25 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.errors.SourceError; +import org.zwobble.hobgoblin.compiler.sources.Source; +import org.zwobble.hobgoblin.compiler.types.Type; +import org.zwobble.hobgoblin.compiler.types.TypeSet; + +public class UnexpectedTypeError extends SourceError { + private final TypeSet expected; + private final Type actual; + + public UnexpectedTypeError(TypeSet expected, Type actual, Source source) { + super("Expected " + expected.describe() + " but was " + actual.describe(), source); + this.expected = expected; + this.actual = actual; + } + + public TypeSet expected() { + return expected; + } + + public Type actual() { + return actual; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java new file mode 100644 index 0000000..1d3a4b6 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/Variable.java @@ -0,0 +1,11 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.types.Type; + +public sealed interface Variable { + record Declared() implements Variable { + } + + record Defined(Type type) implements Variable { + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/ScalarType.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/ScalarType.java new file mode 100644 index 0000000..22032a1 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/ScalarType.java @@ -0,0 +1,8 @@ +package org.zwobble.hobgoblin.compiler.types; + +public record ScalarType(String name) implements Type { + @Override + public String describe() { + return name; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java new file mode 100644 index 0000000..2692261 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java @@ -0,0 +1,4 @@ +package org.zwobble.hobgoblin.compiler.types; + +public sealed interface Type extends TypeLevelValue permits ScalarType, TypeLevelValueType { +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java new file mode 100644 index 0000000..9740cf9 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java @@ -0,0 +1,5 @@ +package org.zwobble.hobgoblin.compiler.types; + +public sealed interface TypeLevelValue permits Type { + String describe(); +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValueType.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValueType.java new file mode 100644 index 0000000..b885c25 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValueType.java @@ -0,0 +1,8 @@ +package org.zwobble.hobgoblin.compiler.types; + +public record TypeLevelValueType(TypeLevelValue value) implements Type { + @Override + public String describe() { + return "TypeLevelValue[" + value.describe() + "]"; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeSet.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeSet.java new file mode 100644 index 0000000..f587897 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeSet.java @@ -0,0 +1,19 @@ +package org.zwobble.hobgoblin.compiler.types; + +public sealed interface TypeSet { + String describe(); + + record SingleType(Type type) implements TypeSet { + @Override + public String describe() { + return type.describe(); + } + } + + record MetaType() implements TypeSet { + @Override + public String describe() { + return "metatype"; + } + } +} -- cgit v1.2.3