diff options
Diffstat (limited to 'src')
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); + } +} |
