diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-09-28 10:48:27 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-09-28 10:52:03 +0100 |
| commit | 48f686951979f8fa8bd3b424a5139dd9150efec6 (patch) | |
| tree | 5de5c0c7789079ef0836b8edb978fabe421b305c /src | |
| parent | 34b02197132f215f651bb3ee3d54b16cc2fc0836 (diff) | |
Add struct type to context
Diffstat (limited to 'src')
15 files changed, 202 insertions, 43 deletions
diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java b/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java index 4e1e46ec..be94fb24 100644 --- a/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java +++ b/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java @@ -1,13 +1,17 @@ package org.shedlang.compiler.typechecker; public interface TypeCheckSteps<T> { + default TypeCheckerContext declareTypes(TypeCheckerContext context) { + return context; + } + default TypeCheckerContext declareFunctions(TypeCheckerContext context) { return context; } - TypeCheckerResult<T> define(TypeCheckerContext context); + TypeCheckerResult<T> finish(TypeCheckerContext context); default TypeCheckerResult<T> typeCheckAllPhases(TypeCheckerContext context) { - return define(declareFunctions(context)); + return finish(declareFunctions(declareTypes(context))); } } diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java index 1681ac25..1c8bbd54 100644 --- a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java @@ -130,7 +130,7 @@ public class TypeChecker { ) { return new TypeCheckSteps<>() { @Override - public TypeCheckerResult<TypedAssignNode> define(TypeCheckerContext context) { + public TypeCheckerResult<TypedAssignNode> finish(TypeCheckerContext context) { return typeCheckAssign(untyped, context); } }; @@ -402,7 +402,7 @@ public class TypeChecker { } @Override - public TypeCheckerResult<TypedFunctionDefinitionNode> define(TypeCheckerContext context) { + public TypeCheckerResult<TypedFunctionDefinitionNode> finish(TypeCheckerContext context) { return defineFunctionDefinition(untyped, context); } }; @@ -626,7 +626,7 @@ public class TypeChecker { namespaceEnvironment = namespaceEnvironment.addUndefined(declaredVariables); } - context = context.enterNamespace(namespaceEnvironment.build()); + context = context.enterNamespace(untyped.namespaceName(), namespaceEnvironment.build()); var typedBody = new ArrayList<TypedNamespaceStatementNode>(); @@ -635,6 +635,10 @@ public class TypeChecker { .toList(); for (var typeCheckSteps : typeCheckStepSets) { + context = typeCheckSteps.declareTypes(context); + } + + for (var typeCheckSteps : typeCheckStepSets) { context = typeCheckSteps.declareFunctions(context); } @@ -651,7 +655,7 @@ public class TypeChecker { context = context.enterFreeVariableCapture(); - var typedStatementResult = typeCheckSteps.define(context); + var typedStatementResult = typeCheckSteps.finish(context); var typedStatement = typedStatementResult.value(); context = typedStatementResult.context(); @@ -682,7 +686,6 @@ public class TypeChecker { } case UntypedStructDefinitionNode untypedStruct -> { - throw new UnsupportedOperationException("TODO"); } } } @@ -786,7 +789,14 @@ public class TypeChecker { ) { return new TypeCheckSteps<TypedStructDefinitionNode>() { @Override - public TypeCheckerResult<TypedStructDefinitionNode> define(TypeCheckerContext context) { + public TypeCheckerContext declareTypes(TypeCheckerContext context) { + // TODO: better error + var type = new StructType(context.namespaceName().orElseThrow(), untyped.name()); + return context.define(untyped.name(), Types.metaType(type), untyped.source()); + } + + @Override + public TypeCheckerResult<TypedStructDefinitionNode> finish(TypeCheckerContext context) { var fieldsResult = context.mapWithContext(untyped.fields(), (untypedField, fieldContext) -> { return fieldContext.toResult(new TypedStructFieldDefinitionNode( untypedField.name(), diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeCheckerContext.java b/src/main/java/org/shedlang/compiler/typechecker/TypeCheckerContext.java index 1f50d24b..044f5041 100644 --- a/src/main/java/org/shedlang/compiler/typechecker/TypeCheckerContext.java +++ b/src/main/java/org/shedlang/compiler/typechecker/TypeCheckerContext.java @@ -21,6 +21,7 @@ public class TypeCheckerContext { public static final TypeCheckerContext EMPTY = new TypeCheckerContext( Empty.map(), P.stack(new LexicalContext( + Optional.empty(), Empty.map(), Effects.NONE, Optional.empty(), @@ -56,6 +57,7 @@ public class TypeCheckerContext { } private static final class LexicalContext implements StackElement { + private final Optional<NamespaceName> namespaceName; private final PMap<String, Variable> environment; private final Effect effect; private final Optional<Type> returnType; @@ -64,6 +66,7 @@ public class TypeCheckerContext { private final PMap<String, Object> state; public LexicalContext( + Optional<NamespaceName> namespaceName, PMap<String, Variable> environment, Effect effect, Optional<Type> returnType, @@ -71,6 +74,7 @@ public class TypeCheckerContext { FreeVariables.Builder freeVariables, PMap<String, Object> state ) { + this.namespaceName = namespaceName; this.environment = environment; this.effect = effect; this.returnType = returnType; @@ -87,6 +91,7 @@ public class TypeCheckerContext { @Override public LexicalContext withFreeVariable(String name, Source referenceSource) { return new LexicalContext( + this.namespaceName, this.environment, this.effect, this.returnType, @@ -142,11 +147,11 @@ public class TypeCheckerContext { .map(x -> Map.entry(x.getKey(), Variable.defined(x.getValue(), VariableType.VAL))) .collect(PCollectors.toMap()); - return enterNamespace(environment); + return enter(Optional.empty(), environment, Effects.NONE, Optional.empty()); } - public TypeCheckerContext enterNamespace(PMap<String, Variable> environment) { - return enter(environment, Effects.NONE, Optional.empty()); + public TypeCheckerContext enterNamespace(NamespaceName namespaceName, PMap<String, Variable> environment) { + return enter(Optional.of(namespaceName), environment, Effects.NONE, Optional.empty()); } public TypeCheckerContext enterFunction( @@ -154,14 +159,15 @@ public class TypeCheckerContext { Effect effect, Type returnType ) { - return enter(environment, effect, Optional.of(returnType)); + return enter(this.namespaceName(), environment, effect, Optional.of(returnType)); } public TypeCheckerContext enterBlock(PMap<String, Variable> environment) { - return enter(environment, this.effect(), this.returnType()); + return enter(this.namespaceName(), environment, this.effect(), this.returnType()); } private TypeCheckerContext enter( + Optional<NamespaceName> newNamespaceName, PMap<String, Variable> newEnvironment, Effect newEffect, Optional<Type> newReturnType @@ -169,6 +175,7 @@ public class TypeCheckerContext { return new TypeCheckerContext( this.namespaceTypeInfo, this.stack.plus(new LexicalContext( + newNamespaceName, this.environment().plusAll(newEnvironment), newEffect, newReturnType, @@ -235,6 +242,7 @@ public class TypeCheckerContext { public TypeCheckerContext addLexicalState(String name, Object state) { return this.updateLexicalContext( lexicalContext -> new LexicalContext( + lexicalContext.namespaceName, lexicalContext.environment, lexicalContext.effect, lexicalContext.returnType, @@ -275,6 +283,7 @@ public class TypeCheckerContext { return this.updateLexicalContext( lexicalContext -> new LexicalContext( + lexicalContext.namespaceName, lexicalContext.environment.plus(name, new Variable( Optional.of(type), variable.variableType() @@ -288,6 +297,10 @@ public class TypeCheckerContext { ); } + public Optional<NamespaceName> namespaceName() { + return currentLexicalContext().namespaceName; + } + private PMap<String, Variable> environment() { return currentLexicalContext().environment; } diff --git a/src/main/java/org/shedlang/compiler/types/Types.java b/src/main/java/org/shedlang/compiler/types/Types.java index 2f07133a..f56f0ca7 100644 --- a/src/main/java/org/shedlang/compiler/types/Types.java +++ b/src/main/java/org/shedlang/compiler/types/Types.java @@ -14,6 +14,10 @@ public class Types { public static final Type TOP = TopType.INSTANCE; public static final Type UNIT = UnitType.INSTANCE; + public static Type struct(NamespaceName namespaceName, String name) { + return new StructType(namespaceName, name); + } + public static TupleType tuple(List<Type> elements) { return new TupleType(elements); } 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 82221c08..e5b13e6c 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java @@ -283,15 +283,22 @@ public class TypedNodeMatchers { public static Matcher<TypedTypeLevelExpressionNode<?>> isTypedTypeLevelReferenceNode( String name, - TypeLevelValue value + Matcher<TypeLevelValue> value ) { return instanceOf( TypedTypeLevelReferenceNode.class, has("name", x -> x.name(), equalTo(name)), - has("value", x -> x.value(), equalTo(value)) + has("value", x -> x.value(), value) ); } + public static Matcher<TypedTypeLevelExpressionNode<?>> isTypedTypeLevelReferenceNode( + String name, + TypeLevelValue value + ) { + return isTypedTypeLevelReferenceNode(name, equalTo(value)); + } + public static Matcher<TypedNode> isTypedValNode( String targetName, Matcher<? super TypedExpressionNode> expression diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java index 9bd07ffd..cc161b56 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java @@ -10,6 +10,7 @@ import org.shedlang.compiler.typechecker.errors.CanOnlyAssignToVarError; import org.shedlang.compiler.typechecker.errors.TargetTupleHasWrongElementCount; import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError; import org.shedlang.compiler.typechecker.typesets.TypeSets; +import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.TupleType; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -27,7 +28,7 @@ public class TypeCheckerAssignTests { public void canTypeCheckTargetIgnore() { var untyped = Untyped.assign(Untyped.targetIgnore(), Untyped.stringLiteral("hello")); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(Types.STRING, VariableType.VAR)) )); @@ -43,7 +44,7 @@ public class TypeCheckerAssignTests { public void canTypeCheckReassign() { var untyped = Untyped.assign(Untyped.targetReassign("x"), Untyped.stringLiteral("hello")); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(Types.STRING, VariableType.VAR)) )); @@ -59,7 +60,7 @@ public class TypeCheckerAssignTests { public void whenExpressionIsNotSubTypeOfReassignTargetVariableThenErrorIsThrown() { var untyped = Untyped.assign(Untyped.targetReassign("x"), Untyped.stringLiteral("hello")); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(Types.INT, VariableType.VAR)) )); @@ -76,7 +77,7 @@ public class TypeCheckerAssignTests { public void whenReassignTargetIsNotVarThenErrorIsThrown() { var untyped = Untyped.assign(Untyped.targetReassign("x"), Untyped.stringLiteral("hello")); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(Types.STRING, VariableType.VAL)) )); @@ -102,7 +103,7 @@ public class TypeCheckerAssignTests { Types.BOOL )); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(tupleType, VariableType.VAL)) )); @@ -128,7 +129,7 @@ public class TypeCheckerAssignTests { Untyped.reference("x") ); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(Types.INT, VariableType.VAL)) )); @@ -152,7 +153,7 @@ public class TypeCheckerAssignTests { ); var tupleType = new TupleType(List.of(Types.STRING)); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(tupleType, VariableType.VAL)) )); @@ -172,7 +173,7 @@ public class TypeCheckerAssignTests { var declaredVariables = DeclaredVariables.forFunctionStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); return TypeChecker.typeCheckFunctionStatement(untyped, context); } @@ -184,7 +185,7 @@ public class TypeCheckerAssignTests { var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerContextTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerContextTests.java index c3791df8..aa06e499 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerContextTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerContextTests.java @@ -5,6 +5,7 @@ import org.shedlang.compiler.sources.Sources; import org.shedlang.compiler.typechecker.errors.CannotDefineVariableDeclaredInOuterScopeError; import org.shedlang.compiler.typechecker.errors.UnknownVariableError; import org.shedlang.compiler.typechecker.errors.VariableIsAlreadyDefinedError; +import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -18,7 +19,7 @@ public class TypeCheckerContextTests { @Test public void givenVariableIsUndefinedWhenVariableIsDefinedThenVariableHasTypeSet() { var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.undefined(VariableType.VAL)), Map.entry("y", Variable.undefined(VariableType.VAL)) )); @@ -32,7 +33,7 @@ public class TypeCheckerContextTests { @Test public void givenVariableIsUnknownWhenVariableIsDefinedThenErrorIsThrown() { var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("y", Variable.undefined(VariableType.VAL)) )); @@ -47,7 +48,7 @@ public class TypeCheckerContextTests { @Test public void givenVariableIsDefinedWhenVariableIsDefinedThenErrorIsThrown() { var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(Types.STRING, VariableType.VAL)) )); @@ -62,10 +63,10 @@ public class TypeCheckerContextTests { @Test public void givenVariableIsBoundInOuterScopeWhenVariableIsDefinedThenErrorIsThrown() { var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.undefined(VariableType.VAL)) )) - .enterNamespace(P.map()); + .enterNamespace(NamespaceName.of("Example"), P.map()); var error = assertThrows( CannotDefineVariableDeclaredInOuterScopeError.class, @@ -78,7 +79,7 @@ public class TypeCheckerContextTests { @Test public void definingVariableOnlyAffectsTopMostLexicalContext() { var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.undefined(VariableType.VAL)), Map.entry("y", Variable.undefined(VariableType.VAL)) )); diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java index 136dce36..d6a600bc 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java @@ -7,10 +7,7 @@ import org.shedlang.compiler.ast.untyped.UntypedCallNode; import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode; import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError; import org.shedlang.compiler.typechecker.typesets.TypeSets; -import org.shedlang.compiler.types.Effects; -import org.shedlang.compiler.types.FunctionType; -import org.shedlang.compiler.types.NamedParam; -import org.shedlang.compiler.types.Types; +import org.shedlang.compiler.types.*; import org.shedlang.compiler.util.P; import java.util.List; @@ -394,7 +391,7 @@ public class TypeCheckerFunctionDefinitionTests { var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java index 2e4b4206..2a8ae446 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java @@ -6,6 +6,7 @@ import org.shedlang.compiler.typechecker.errors.CannotReturnOutsideOfFunction; import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError; import org.shedlang.compiler.typechecker.typesets.TypeSets; import org.shedlang.compiler.types.Effects; +import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -30,7 +31,7 @@ public class TypeCheckerFunctionReturnTests { public void whenNotInFunctionThenReturnIsForbidden() { var untyped = Untyped.functionReturn(Untyped.stringLiteral("hello")); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map()); + .enterNamespace(NamespaceName.of("Example"), P.map()); assertThrows( CannotReturnOutsideOfFunction.class, diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIfTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIfTests.java index adb5968c..c619409b 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIfTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIfTests.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError; import org.shedlang.compiler.typechecker.typesets.TypeSets; +import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -128,7 +129,7 @@ public class TypeCheckerIfTests { )) ); var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map( + .enterNamespace(NamespaceName.of("Example"), P.map( Map.entry("x", Variable.defined(Types.STRING, VariableType.VAL)) )); diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java index 36e1ee5d..d8fdc3ee 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java @@ -3,6 +3,7 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode; +import org.shedlang.compiler.ast.untyped.UntypedStructDefinitionNode; import org.shedlang.compiler.typechecker.errors.ReferenceBeforeDefinitionError; import org.shedlang.compiler.types.*; import org.shedlang.compiler.util.P; @@ -12,6 +13,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.shedlang.compiler.types.TypeMatchers.isStructType; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; @@ -284,6 +286,82 @@ public class TypeCheckerNamespaceTests { } @Test + public void functionCanUseTypeDefinedEarlier() { + var untyped = Untyped.namespace(NamespaceName.of("Example", "Project"), List.of( + UntypedStructDefinitionNode.builder() + .withName("User") + .build(), + UntypedFunctionDefinitionNode.builder() + .withName("printUser") + .addPositionalParam("user", Untyped.typeLevelReference("User")) + .withReturnType(Untyped.typeLevelReference("Unit")) + .build() + )); + var context = TypeCheckerContext.EMPTY + .enterGlobal(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var result = TypeChecker.typeCheckNamespace(untyped, context); + + assertThat( + result.value(), + isTypedNamespaceNode() + .withBody(isSequence( + isTypedStructDefinitionNode().withName("User"), + isTypedFunctionDefinitionNode() + .withName("printUser") + .withParams(isTypedParamsNode( + isSequence(isTypedParamNode( + "user", + isTypedTypeLevelReferenceNode( + "User", + isStructType(NamespaceName.of("Example", "Project"), "User") + ) + )), + isSequence() + )) + )) + ); + } + + @Test + public void functionCanUseTypeDefinedLater() { + var untyped = Untyped.namespace(NamespaceName.of("Example", "Project"), List.of( + UntypedFunctionDefinitionNode.builder() + .withName("printUser") + .addPositionalParam("user", Untyped.typeLevelReference("User")) + .withReturnType(Untyped.typeLevelReference("Unit")) + .build(), + UntypedStructDefinitionNode.builder() + .withName("User") + .build() + )); + var context = TypeCheckerContext.EMPTY + .enterGlobal(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var result = TypeChecker.typeCheckNamespace(untyped, context); + + assertThat( + result.value(), + isTypedNamespaceNode() + .withBody(isSequence( + isTypedFunctionDefinitionNode() + .withName("printUser") + .withParams(isTypedParamsNode( + isSequence(isTypedParamNode( + "user", + isTypedTypeLevelReferenceNode( + "User", + isStructType(NamespaceName.of("Example", "Project"), "User") + ) + )), + isSequence() + )), + isTypedStructDefinitionNode().withName("User") + )) + ); + } + + @Test public void typesOfFieldsAreStored() { var namespaceName = NamespaceName.of("Example"); var untyped = Untyped.namespace( diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java index c8bfd742..dc23fa1c 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java @@ -5,6 +5,8 @@ import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.ast.untyped.UntypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.UntypedStructDefinitionNode; +import org.shedlang.compiler.types.NamespaceName; +import org.shedlang.compiler.types.StructType; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -12,9 +14,11 @@ import java.util.Map; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; import static org.zwobble.precisely.AssertThat.assertThat; -import static org.zwobble.precisely.Matchers.isSequence; +import static org.zwobble.precisely.Matchers.*; public class TypeCheckerStructDefinitionTests { + private static final NamespaceName NAMESPACE_NAME = NamespaceName.of("Example"); + @Test public void canTypeCheckStructDefinitionWithNoFields() { var untyped = UntypedStructDefinitionNode.builder() @@ -33,6 +37,24 @@ public class TypeCheckerStructDefinitionTests { } @Test + public void typeIsAddedToEnvironment() { + var untyped = UntypedStructDefinitionNode.builder() + .withName("ExampleStruct") + .build(); + var context = TypeCheckerContext.EMPTY; + + var typed = typeCheckNamespaceStatement(untyped, context); + + assertThat( + typed.context().compileTimeLookup("ExampleStruct"), + isOptionalOf(equalTo(Variable.defined( + Types.metaType(Types.struct(NAMESPACE_NAME, "ExampleStruct")), + VariableType.CONSTANT + ))) + ); + } + + @Test public void canTypeCheckStructDefinitionWithFields() { var untyped = UntypedStructDefinitionNode.builder() .withName("ExampleStruct") @@ -70,7 +92,7 @@ public class TypeCheckerStructDefinitionTests { var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NAMESPACE_NAME, environment.build()); return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java index 6cb02e1b..9fd948ba 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java @@ -6,6 +6,7 @@ import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.*; import org.shedlang.compiler.typechecker.errors.DuplicateVariableName; import org.shedlang.compiler.typechecker.errors.ReferenceBeforeDefinitionError; +import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -152,7 +153,7 @@ public class TypeCheckerValTests { var declaredVariables = DeclaredVariables.forFunctionStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); return TypeChecker.typeCheckFunctionStatement(untyped, context); } @@ -164,7 +165,7 @@ public class TypeCheckerValTests { var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java index 21f5502e..466ab16c 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java @@ -7,6 +7,7 @@ import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode; import org.shedlang.compiler.ast.untyped.UntypedFunctionStatementNode; import org.shedlang.compiler.ast.untyped.UntypedNamespaceStatementNode; +import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -60,7 +61,7 @@ public class TypeCheckerVarTests { var declaredVariables = DeclaredVariables.forFunctionStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); return TypeChecker.typeCheckFunctionStatement(untyped, context); } @@ -72,7 +73,7 @@ public class TypeCheckerVarTests { var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - context = context.enterNamespace(environment.build()); + context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); } diff --git a/src/test/java/org/shedlang/compiler/types/TypeMatchers.java b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java new file mode 100644 index 00000000..429caec7 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java @@ -0,0 +1,18 @@ +package org.shedlang.compiler.types; + +import org.zwobble.precisely.Matcher; + +import static org.zwobble.precisely.Matchers.*; + +public class TypeMatchers { + private TypeMatchers() { + } + + public static Matcher<TypeLevelValue> isStructType(NamespaceName namespaceName, String name) { + return instanceOf( + StructType.class, + has("namespaceName", x -> x.namespaceName(), equalTo(namespaceName)), + has("name", x -> x.name(), equalTo(name)) + ); + } +} |
