From 74b2aa135e2bf1af1ef65476aa03406eaaddd2ab Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 10 Jun 2023 11:00:22 +0100 Subject: Add incomplete type checking for calls --- .../compiler/ast/typed/TypedArgsNodeMatcher.java | 36 ++++++++++++ .../compiler/ast/typed/TypedCallNodeMatcher.java | 46 +++++++++++++++ .../compiler/ast/typed/TypedNodeMatchers.java | 18 ++++++ .../org/shedlang/compiler/ast/untyped/Untyped.java | 25 ++++++++ .../ast/untyped/UntypedArgsNodeMatcher.java | 7 ++- .../ast/untyped/UntypedCallNodeMatcher.java | 7 ++- .../UntypedFunctionDefinitionNodeMatcher.java | 7 ++- .../compiler/ast/untyped/UntypedNodeMatchers.java | 6 +- .../shedlang/compiler/testing/MatcherBuilder.java | 5 -- .../compiler/typechecker/TypeCheckerCallTests.java | 68 ++++++++++++++++++++++ 10 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 src/test/java/org/shedlang/compiler/ast/typed/TypedArgsNodeMatcher.java create mode 100644 src/test/java/org/shedlang/compiler/ast/typed/TypedCallNodeMatcher.java create mode 100644 src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java (limited to 'src/test/java') diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedArgsNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedArgsNodeMatcher.java new file mode 100644 index 00000000..05037526 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedArgsNodeMatcher.java @@ -0,0 +1,36 @@ +package org.shedlang.compiler.ast.typed; + +import org.pcollections.Empty; +import org.pcollections.PVector; +import org.shedlang.compiler.testing.MatcherBuilder; +import org.zwobble.precisely.Matcher; + +import static org.zwobble.precisely.Matchers.has; + +public class TypedArgsNodeMatcher extends MatcherBuilder { + public static TypedArgsNodeMatcher empty() { + return new TypedArgsNodeMatcher(Empty.vector()); + } + + public TypedArgsNodeMatcher(PVector> submatchers) { + super(submatchers); + } + + @Override + protected Class expectedType() { + return TypedArgsNode.class; + } + + @Override + protected TypedArgsNodeMatcher create(PVector> submatchers) { + return new TypedArgsNodeMatcher(submatchers); + } + + public TypedArgsNodeMatcher withPositional(Matcher> positional) { + return with(has("positional", x -> x.positional(), positional)); + } + + public TypedArgsNodeMatcher withNamed(Matcher> named) { + return with(has("named", x -> x.named(), named)); + } +} diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedCallNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedCallNodeMatcher.java new file mode 100644 index 00000000..5512d9c8 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedCallNodeMatcher.java @@ -0,0 +1,46 @@ +package org.shedlang.compiler.ast.typed; + +import org.pcollections.Empty; +import org.pcollections.PVector; +import org.shedlang.compiler.testing.MatcherBuilder; +import org.shedlang.compiler.types.Type; +import org.zwobble.precisely.Matcher; + +import static org.zwobble.precisely.Matchers.equalTo; +import static org.zwobble.precisely.Matchers.has; + +public class TypedCallNodeMatcher extends MatcherBuilder { + public static TypedCallNodeMatcher empty() { + return new TypedCallNodeMatcher(Empty.vector()); + } + + public TypedCallNodeMatcher(PVector> submatchers) { + super(submatchers); + } + + @Override + protected Class expectedType() { + return TypedCallNode.class; + } + + @Override + protected TypedCallNodeMatcher create(PVector> submatchers) { + return new TypedCallNodeMatcher(submatchers); + } + + public TypedCallNodeMatcher withArgs(Matcher args) { + return with(has("args", x -> x.args(), args)); + } + + public TypedCallNodeMatcher withHasEffects(boolean hasEffects) { + return with(has("hasEffects", x -> x.hasEffects(), equalTo(hasEffects))); + } + + public TypedCallNodeMatcher withReceiver(Matcher receiver) { + return with(has("receiver", x -> x.receiver(), receiver)); + } + + public TypedCallNodeMatcher withType(Type type) { + return with(has("type", x -> x.type(), equalTo(type))); + } +} 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 7716b76a..acf61900 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java @@ -11,6 +11,10 @@ public class TypedNodeMatchers { private TypedNodeMatchers() { } + public static TypedArgsNodeMatcher isTypedArgsNode() { + return TypedArgsNodeMatcher.empty(); + } + public static Matcher isTypedBoolLiteralNode(boolean value) { return instanceOf( TypedBoolLiteralNode.class, @@ -18,6 +22,10 @@ public class TypedNodeMatchers { ); } + public static TypedCallNodeMatcher isTypedCallNode() { + return TypedCallNodeMatcher.empty(); + } + public static Matcher isTypedIntLiteralNode(int value) { return isTypedIntLiteralNode(BigInteger.valueOf(value)); } @@ -29,6 +37,16 @@ public class TypedNodeMatchers { ); } + public static Matcher isTypedNamedArgNode( + String name, + Matcher expression + ) { + return allOf( + has("name", x -> x.name(), equalTo(name)), + has("expression", x -> x.expression(), expression) + ); + } + public static Matcher isTypedReferenceNode(String name, Type type) { return instanceOf( TypedReferenceNode.class, diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java index 37778968..11e7b38f 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java @@ -3,6 +3,7 @@ package org.shedlang.compiler.ast.untyped; import org.shedlang.compiler.sources.Sources; import java.math.BigInteger; +import java.util.List; public class Untyped { private Untyped() { @@ -20,6 +21,10 @@ public class Untyped { return new UntypedIntLiteralNode(value, Sources.NULL); } + public static UntypedNamedArgNode namedArg(String name, UntypedExpressionNode expression) { + return new UntypedNamedArgNode(name, expression, Sources.NULL); + } + public static UntypedReferenceNode reference(String name) { return new UntypedReferenceNode(name, Sources.NULL); } @@ -27,4 +32,24 @@ public class Untyped { public static UntypedStringLiteralNode stringLiteral(String value) { return new UntypedStringLiteralNode(value, Sources.NULL); } + + public static UntypedCallNode call( + UntypedExpressionNode receiver, + List positionalArgs + ) { + return call(receiver, positionalArgs, List.of()); + } + + public static UntypedCallNode call( + UntypedExpressionNode receiver, + List positionalArgs, + List namedArgs + ) { + return new UntypedCallNode( + receiver, + new UntypedArgsNode(positionalArgs, namedArgs, Sources.NULL), + false, + Sources.NULL + ); + } } diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedArgsNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedArgsNodeMatcher.java index ce782c0d..da40138d 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedArgsNodeMatcher.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedArgsNodeMatcher.java @@ -1,5 +1,6 @@ package org.shedlang.compiler.ast.untyped; +import org.pcollections.Empty; import org.pcollections.PVector; import org.shedlang.compiler.testing.MatcherBuilder; import org.zwobble.precisely.Matcher; @@ -7,8 +8,8 @@ import org.zwobble.precisely.Matcher; import static org.zwobble.precisely.Matchers.has; public class UntypedArgsNodeMatcher extends MatcherBuilder { - public UntypedArgsNodeMatcher() { - super(); + public static UntypedArgsNodeMatcher empty() { + return new UntypedArgsNodeMatcher(Empty.vector()); } public UntypedArgsNodeMatcher(PVector> submatchers) { @@ -22,7 +23,7 @@ public class UntypedArgsNodeMatcher extends MatcherBuilder> submatchers) { - return new UntypedArgsNodeMatcher(); + return new UntypedArgsNodeMatcher(submatchers); } public UntypedArgsNodeMatcher withPositional(Matcher> positional) { diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedCallNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedCallNodeMatcher.java index b9411fec..aeb020a3 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedCallNodeMatcher.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedCallNodeMatcher.java @@ -1,5 +1,6 @@ package org.shedlang.compiler.ast.untyped; +import org.pcollections.Empty; import org.pcollections.PVector; import org.shedlang.compiler.testing.MatcherBuilder; import org.zwobble.precisely.Matcher; @@ -8,8 +9,8 @@ import static org.zwobble.precisely.Matchers.equalTo; import static org.zwobble.precisely.Matchers.has; public class UntypedCallNodeMatcher extends MatcherBuilder { - public UntypedCallNodeMatcher() { - super(); + public static UntypedCallNodeMatcher empty() { + return new UntypedCallNodeMatcher(Empty.vector()); } public UntypedCallNodeMatcher(PVector> submatchers) { @@ -23,7 +24,7 @@ public class UntypedCallNodeMatcher extends MatcherBuilder> submatchers) { - return new UntypedCallNodeMatcher(); + return new UntypedCallNodeMatcher(submatchers); } public UntypedCallNodeMatcher withArgs(Matcher args) { diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedFunctionDefinitionNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedFunctionDefinitionNodeMatcher.java index e8a227c3..c88111df 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedFunctionDefinitionNodeMatcher.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedFunctionDefinitionNodeMatcher.java @@ -1,5 +1,6 @@ package org.shedlang.compiler.ast.untyped; +import org.pcollections.Empty; import org.pcollections.PVector; import org.shedlang.compiler.testing.MatcherBuilder; import org.zwobble.precisely.Matcher; @@ -10,8 +11,8 @@ import static org.zwobble.precisely.Matchers.equalTo; import static org.zwobble.precisely.Matchers.has; public class UntypedFunctionDefinitionNodeMatcher extends MatcherBuilder { - public UntypedFunctionDefinitionNodeMatcher() { - super(); + public static UntypedFunctionDefinitionNodeMatcher empty() { + return new UntypedFunctionDefinitionNodeMatcher(Empty.vector()); } public UntypedFunctionDefinitionNodeMatcher(PVector> submatchers) { @@ -25,7 +26,7 @@ public class UntypedFunctionDefinitionNodeMatcher extends MatcherBuilder> submatchers) { - return new UntypedFunctionDefinitionNodeMatcher(); + return new UntypedFunctionDefinitionNodeMatcher(submatchers); } public UntypedFunctionDefinitionNodeMatcher withBody(Matcher> body) { diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java index 04c56443..6c06e52a 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java @@ -9,11 +9,11 @@ public class UntypedNodeMatchers { } public static UntypedArgsNodeMatcher isUntypedArgsNode() { - return new UntypedArgsNodeMatcher(); + return UntypedArgsNodeMatcher.empty(); } public static UntypedCallNodeMatcher isUntypedCallNode() { - return new UntypedCallNodeMatcher(); + return UntypedCallNodeMatcher.empty(); } public static Matcher isUntypedExpressionStatementNode( @@ -26,7 +26,7 @@ public class UntypedNodeMatchers { } public static UntypedFunctionDefinitionNodeMatcher isUntypedFunctionDefinitionNode() { - return new UntypedFunctionDefinitionNodeMatcher(); + return UntypedFunctionDefinitionNodeMatcher.empty(); } public static Matcher isUntypedNamedArgNode( diff --git a/src/test/java/org/shedlang/compiler/testing/MatcherBuilder.java b/src/test/java/org/shedlang/compiler/testing/MatcherBuilder.java index 6736659f..984ba3da 100644 --- a/src/test/java/org/shedlang/compiler/testing/MatcherBuilder.java +++ b/src/test/java/org/shedlang/compiler/testing/MatcherBuilder.java @@ -1,6 +1,5 @@ package org.shedlang.compiler.testing; -import org.pcollections.Empty; import org.pcollections.PVector; import org.zwobble.precisely.MatchResult; import org.zwobble.precisely.Matcher; @@ -11,10 +10,6 @@ import static org.zwobble.precisely.Matchers.instanceOf; public abstract class MatcherBuilder implements Matcher { private final PVector> submatchers; - public MatcherBuilder() { - this(Empty.vector()); - } - public MatcherBuilder(PVector> submatchers) { this.submatchers = submatchers; } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java new file mode 100644 index 00000000..379368a1 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java @@ -0,0 +1,68 @@ +package org.shedlang.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.types.FunctionType; +import org.shedlang.compiler.types.Types; +import org.shedlang.compiler.util.P; + +import java.util.List; +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; + +public class TypeCheckerCallTests { + @Test + public void canCallFunctionWithNoArgs() { + var untyped = Untyped.call( + Untyped.reference("f"), + List.of() + ); + var functionType = FunctionType.builder() + .withReturnType(Types.STRING) + .build(); + var context = TypeCheckerContext.EMPTY + .enter(P.map(Map.entry("f", functionType))); + + var typed = TypeChecker.typeCheckExpression(untyped, context); + + assertThat( + typed, + isTypedCallNode() + .withReceiver(isTypedReferenceNode("f", functionType)) + .withArgs(isTypedArgsNode().withPositional(isSequence()).withNamed(isSequence())) + .withHasEffects(false) + .withType(Types.STRING) + ); + } + + @Test + public void positionalArgsAreTypedChecked() { + var untyped = Untyped.call( + Untyped.reference("f"), + List.of( + Untyped.stringLiteral("hello"), + Untyped.intLiteral(42) + ) + ); + var functionType = FunctionType.builder() + .addPositionalParam(Types.STRING) + .addPositionalParam(Types.INT) + .build(); + var context = TypeCheckerContext.EMPTY + .enter(P.map(Map.entry("f", functionType))); + + var typed = TypeChecker.typeCheckExpression(untyped, context); + + assertThat( + typed, + isTypedCallNode() + .withArgs(isTypedArgsNode().withPositional(isSequence( + isTypedStringLiteralNode("hello"), + isTypedIntLiteralNode(42) + ))) + ); + } +} -- cgit v1.2.3