aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2023-09-28 10:48:27 +0100
committerMichael Williamson <mike@zwobble.org>2023-09-28 10:52:03 +0100
commit48f686951979f8fa8bd3b424a5139dd9150efec6 (patch)
tree5de5c0c7789079ef0836b8edb978fabe421b305c /src
parent34b02197132f215f651bb3ee3d54b16cc2fc0836 (diff)
Add struct type to context
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java8
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java22
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/TypeCheckerContext.java23
-rw-r--r--src/main/java/org/shedlang/compiler/types/Types.java4
-rw-r--r--src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java11
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java19
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerContextTests.java13
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java7
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java3
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerIfTests.java3
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java78
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java26
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java5
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java5
-rw-r--r--src/test/java/org/shedlang/compiler/types/TypeMatchers.java18
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))
+ );
+ }
+}