diff options
| author | Michael Williamson <mike@zwobble.org> | 2025-02-07 20:53:02 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2025-02-07 21:11:55 +0000 |
| commit | bd1515f9b219cef789cc31b2f8efbb217bfc2c3c (patch) | |
| tree | 13f18d9336f4702dcab5587664c8e67f8289bd3b /src/test/java | |
| parent | 9b56f880febd39cb705d658a50512ef3ae1a9162 (diff) | |
Add basic type checking for native type declarations
Diffstat (limited to 'src/test/java')
4 files changed, 211 insertions, 0 deletions
diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java new file mode 100644 index 00000000..16d3f36c --- /dev/null +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java @@ -0,0 +1,62 @@ +package org.shedlang.compiler.ast.typed; + +import org.pcollections.PVector; +import org.shedlang.compiler.testing.MatcherBuilder; +import org.shedlang.compiler.types.SimpleNativeType; +import org.shedlang.compiler.types.TypeConstructor; +import org.shedlang.compiler.util.P; +import org.zwobble.precisely.Matcher; + +import static org.shedlang.compiler.types.TypeMatchers.isConstructorNotType; +import static org.shedlang.compiler.types.TypeMatchers.isTypeNotConstructor; +import static org.zwobble.precisely.Matchers.has; + +public class TypedNativeTypeDeclarationNodeMatcher extends MatcherBuilder< + TypedNamespaceStatementNode, + TypedNativeTypeDeclarationNode, + TypedNativeTypeDeclarationNodeMatcher +> { + public static TypedNativeTypeDeclarationNodeMatcher empty() { + return new TypedNativeTypeDeclarationNodeMatcher(P.vector()); + } + + private TypedNativeTypeDeclarationNodeMatcher( + PVector<Matcher<? super TypedNativeTypeDeclarationNode>> submatchers + ) { + super(submatchers); + } + + @Override + protected Class<TypedNativeTypeDeclarationNode> expectedType() { + return TypedNativeTypeDeclarationNode.class; + } + + @Override + protected TypedNativeTypeDeclarationNodeMatcher create( + PVector<Matcher<? super TypedNativeTypeDeclarationNode>> submatchers + ) { + return new TypedNativeTypeDeclarationNodeMatcher(submatchers); + } + + public TypedNativeTypeDeclarationNodeMatcher withType(Matcher<? super SimpleNativeType> type) { + return with(has( + "typeOrConstructor", + x -> x.typeOrConstructor(), + isTypeNotConstructor(type) + )); + } + + public TypedNativeTypeDeclarationNodeMatcher withTypeConstructor(Matcher<? super TypeConstructor<?>> type) { + return with(has( + "typeOrConstructor", + x -> x.typeOrConstructor(), + isConstructorNotType(type) + )); + } + + public TypedNativeTypeDeclarationNodeMatcher withMethods( + Matcher<Iterable<? extends TypedNativeFunctionDeclarationNode>> methods + ) { + return with(has("methods", x -> x.methods(), methods)); + } +} diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java index 7c0c4396..5d81d0f3 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java @@ -283,6 +283,10 @@ public class TypedNodeMatchers { return TypedNativeFunctionDeclarationNodeMatcher.empty(); } + public static TypedNativeTypeDeclarationNodeMatcher isTypedNativeTypeDeclarationNode() { + return TypedNativeTypeDeclarationNodeMatcher.empty(); + } + public static Matcher<TypedParamNode> isTypedParamNode( String name, Matcher<Optional<? extends TypedTypeLevelExpressionNode<?>>> type diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java new file mode 100644 index 00000000..c9dca725 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java @@ -0,0 +1,122 @@ +package org.shedlang.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.ast.untyped.UntypedNativeTypeDeclarationNode; +import org.shedlang.compiler.typechecker.errors.CannotDefineNativeTypeOutsideOfNamespaceError; +import org.shedlang.compiler.types.NamespaceName; +import org.shedlang.compiler.types.Types; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.isTypedNativeTypeDeclarationNode; +import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; +import static org.shedlang.compiler.types.TypeMatchers.*; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.*; + +public class TypeCheckerNativeTypeDeclarationTests { + private static final NamespaceName NAMESPACE_NAME = NamespaceName.of("Example"); + + @Test + public void whenOutsideOfNamespaceThenErrorIsThrown() { + var untyped = UntypedNativeTypeDeclarationNode.builder() + .withName("ExampleType") + .build(); + var context = TypeCheckerContext.EMPTY; + + assertThrows( + CannotDefineNativeTypeOutsideOfNamespaceError.class, + () -> TypeChecker.typeCheckStepsNamespaceStatement(untyped).typeCheckAllPhases(context) + ); + } + + @Test + public void whenDeclarationHasNoTypeLevelParamsThenTypedNodeHasSimpleNativeType() { + var untyped = UntypedNativeTypeDeclarationNode.builder() + .withName("ExampleType") + .build(); + var context = TypeCheckerContext.EMPTY; + + var typed = typeCheckNamespaceStatement(untyped, context); + + assertThat( + typed.value(), + isTypedNativeTypeDeclarationNode() + .withType(isNativeType(NAMESPACE_NAME, "ExampleType")) + ); + } + + @Test + public void whenDeclarationHasTypeLevelParamsThenTypedNodeHasTypeConstructor() { + var untyped = UntypedNativeTypeDeclarationNode.builder() + .withName("ExampleType") + .withTypeLevelParams(List.of( + Untyped.typeParam("T1"), + Untyped.typeParam("T2") + )) + .build(); + var context = TypeCheckerContext.EMPTY; + + var typed = typeCheckNamespaceStatement(untyped, context); + + assertThat( + typed.value(), + isTypedNativeTypeDeclarationNode() + .withTypeConstructor(isTypeConstructor( + isSequence( + isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T1"), + isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T2") + ), + isNativeType(NAMESPACE_NAME, "ExampleType") + )) + ); + } + + @Test + public void whenDeclarationHasNoTypeParamsThenTypeIsAddedToEnvironment() { + var untyped = UntypedNativeTypeDeclarationNode.builder() + .withName("ExampleType") + .build(); + var context = TypeCheckerContext.EMPTY; + + var typed = typeCheckNamespaceStatement(untyped, context); + + assertThat( + typed.context().compileTimeLookup("ExampleType"), + isOptionalOf(equalTo(Variable.defined( + Types.metaType(Types.nativeType(NAMESPACE_NAME, "ExampleType")), + VariableType.CONSTANT + ))) + ); + } + + @Test + public void whenDeclarationHasTypeParamsThenTypeConstructorIsAddedToEnvironment() { + var untyped = UntypedNativeTypeDeclarationNode.builder() + .withName("ExampleType") + .withTypeLevelParams(List.of( + Untyped.typeParam("T1"), + Untyped.typeParam("T2") + )) + .build(); + var context = TypeCheckerContext.EMPTY; + + var typed = typeCheckNamespaceStatement(untyped, context); + + assertThat( + typed.context().compileTimeLookup("ExampleType"), + isOptionalOf(allOf( + has("type", x -> x.type(), isOptionalOf(isTypeLevelValueType(isTypeConstructor( + isSequence( + isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T1"), + isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T2") + ), + isNativeType(NAMESPACE_NAME, "ExampleType") + )))), + has("variableType", x -> x.variableType(), equalTo(VariableType.CONSTANT)) + )) + ); + } +} diff --git a/src/test/java/org/shedlang/compiler/types/TypeMatchers.java b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java index 2c777c76..c6480342 100644 --- a/src/test/java/org/shedlang/compiler/types/TypeMatchers.java +++ b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java @@ -10,6 +10,14 @@ public class TypeMatchers { private TypeMatchers() { } + public static Matcher<TypeLevelValue> isNativeType(NamespaceName namespaceName, String name) { + return instanceOf( + SimpleNativeType.class, + has("namespaceName", x -> x.namespaceName(), equalTo(namespaceName)), + has("name", x -> x.name(), equalTo(name)) + ); + } + public static Matcher<TypeLevelValue> isStructType(NamespaceName namespaceName, String name) { return instanceOf( SimpleStructType.class, @@ -55,6 +63,21 @@ public class TypeMatchers { ); } + public static Matcher<Type> isNativeTypeTypeLevelParam( + NamespaceName namespaceName, + String typeName, + String paramName + ) { + return instanceOf( + TypeParam.class, + has("scope", x -> x.scope(), instanceOf( + Scope.NativeType.class, + has("type", x -> x.type(), isNativeType(namespaceName, typeName)) + )), + has("name", x -> x.name(), equalTo(paramName)) + ); + } + public static Matcher<Type> isStructTypeLevelParam( NamespaceName namespaceName, String structName, |
