From 85be56d890f100b72dde8e011d7421e597888363 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 9 Jul 2023 11:59:27 +0100 Subject: Add explicit function return statement --- .../org/shedlang/compiler/ast/typed/Typed.java | 8 +-- .../compiler/ast/typed/TypedNodeMatchers.java | 2 +- .../org/shedlang/compiler/ast/untyped/Untyped.java | 8 +-- .../compiler/ast/untyped/UntypedNodeMatchers.java | 18 +++--- .../parser/ParserFunctionDefinitionTests.java | 2 +- .../compiler/parser/ParserFunctionReturnTests.java | 18 ++++++ .../compiler/stackir/StackCompilerReturnTests.java | 2 +- .../TypeCheckerFunctionDefinitionTests.java | 2 +- .../TypeCheckerFunctionReturnTests.java | 66 ++++++++++++++++++++++ .../typechecker/TypeCheckerReturnTests.java | 66 ---------------------- 10 files changed, 105 insertions(+), 87 deletions(-) create mode 100644 src/test/java/org/shedlang/compiler/parser/ParserFunctionReturnTests.java create mode 100644 src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java delete mode 100644 src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReturnTests.java (limited to 'src/test/java') diff --git a/src/test/java/org/shedlang/compiler/ast/typed/Typed.java b/src/test/java/org/shedlang/compiler/ast/typed/Typed.java index f7dfb097..ab575632 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/Typed.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/Typed.java @@ -35,6 +35,10 @@ public class Typed { return new TypedExpressionStatementNode(expression, Sources.NULL); } + public static TypedFunctionReturnNode functionReturn(TypedExpressionNode expression) { + return new TypedFunctionReturnNode(expression, Sources.NULL); + } + public static TypedBinaryOperationNode intAdd( TypedExpressionNode left, TypedExpressionNode right @@ -122,10 +126,6 @@ public class Typed { return new TypedReferenceNode(name, type, Sources.NULL); } - public static TypedReturnNode return_(TypedExpressionNode expression) { - return new TypedReturnNode(expression, Sources.NULL); - } - public static TypedBinaryOperationNode stringAdd( TypedExpressionNode left, TypedExpressionNode right 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 d32e0d23..befeb326 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java @@ -148,7 +148,7 @@ public class TypedNodeMatchers { Matcher expression ) { return instanceOf( - TypedReturnNode.class, + TypedFunctionReturnNode.class, has("expression", x -> x.expression(), expression) ); } 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 6465f27a..db7037ce 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java @@ -62,6 +62,10 @@ public class Untyped { return new UntypedExpressionStatementNode(expression, Sources.NULL); } + public static UntypedFunctionReturnNode functionReturn_(UntypedExpressionNode expression) { + return new UntypedFunctionReturnNode(expression, Sources.NULL); + } + public static UntypedIntLiteralNode intLiteral(int value) { return new UntypedIntLiteralNode(BigInteger.valueOf(value), Sources.NULL); } @@ -101,10 +105,6 @@ public class Untyped { return new UntypedReferenceNode(name, Sources.NULL); } - public static UntypedReturnNode return_(UntypedExpressionNode expression) { - return new UntypedReturnNode(expression, Sources.NULL); - } - public static UntypedStringLiteralNode stringLiteral(String value) { return new UntypedStringLiteralNode(value, Sources.NULL); } 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 17675e35..54edfc9d 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java @@ -69,6 +69,15 @@ public class UntypedNodeMatchers { return UntypedFunctionDefinitionNodeMatcher.empty(); } + public static Matcher isUntypedFunctionReturnNode( + Matcher expression + ) { + return instanceOf( + UntypedFunctionReturnNode.class, + has("expression", x -> x.expression(), expression) + ); + } + public static Matcher isUntypedIfNode( Matcher> conditionalBranches, Matcher> elseBody @@ -135,15 +144,6 @@ public class UntypedNodeMatchers { ); } - public static Matcher isUntypedReturnNode( - Matcher expression - ) { - return instanceOf( - UntypedReturnNode.class, - has("expression", x -> x.expression(), expression) - ); - } - public static Matcher isUntypedStringLiteralNode(String value) { return instanceOf( UntypedStringLiteralNode.class, diff --git a/src/test/java/org/shedlang/compiler/parser/ParserFunctionDefinitionTests.java b/src/test/java/org/shedlang/compiler/parser/ParserFunctionDefinitionTests.java index 0f96b59b..88bde61d 100644 --- a/src/test/java/org/shedlang/compiler/parser/ParserFunctionDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/parser/ParserFunctionDefinitionTests.java @@ -221,7 +221,7 @@ public class ParserFunctionDefinitionTests { .withBody(isSequence( isUntypedExpressionStatementNode(isUntypedReferenceNode("a")), isUntypedExpressionStatementNode(isUntypedReferenceNode("b")), - isUntypedReturnNode(isUntypedReferenceNode("c")) + isUntypedFunctionReturnNode(isUntypedReferenceNode("c")) )) ); } diff --git a/src/test/java/org/shedlang/compiler/parser/ParserFunctionReturnTests.java b/src/test/java/org/shedlang/compiler/parser/ParserFunctionReturnTests.java new file mode 100644 index 00000000..6e7f5cc3 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/parser/ParserFunctionReturnTests.java @@ -0,0 +1,18 @@ +package org.shedlang.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.shedlang.compiler.ast.untyped.UntypedNodeMatchers.*; +import static org.shedlang.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; + +public class ParserFunctionReturnTests { + @Test + public void canParseFunctionReturn() { + var source = "return f;"; + + var node = parseString(source, Parser::parseFunctionStatement); + + assertThat(node, isUntypedFunctionReturnNode(isUntypedReferenceNode("f"))); + } +} diff --git a/src/test/java/org/shedlang/compiler/stackir/StackCompilerReturnTests.java b/src/test/java/org/shedlang/compiler/stackir/StackCompilerReturnTests.java index 69a3f735..dcb1af47 100644 --- a/src/test/java/org/shedlang/compiler/stackir/StackCompilerReturnTests.java +++ b/src/test/java/org/shedlang/compiler/stackir/StackCompilerReturnTests.java @@ -10,7 +10,7 @@ import static org.zwobble.precisely.Matchers.isSequence; public class StackCompilerReturnTests { @Test public void expressionIsEvaluatedAndTheResultDiscarded() { - var node = Typed.return_(Typed.stringLiteral("hello")); + var node = Typed.functionReturn(Typed.stringLiteral("hello")); var instructions = StackCompiler.compileFunctionStatement(node); diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java index 7bb490a8..2ff53f74 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java @@ -205,7 +205,7 @@ public class TypeCheckerFunctionDefinitionTests { public void bodyHasReturnTypeAsDefinedInSignature() { var untyped = UntypedFunctionDefinitionNode.builder() .withReturnType(Untyped.typeLevelReference("String")) - .addStatement(Untyped.return_(Untyped.intLiteral(42))) + .addStatement(Untyped.functionReturn_(Untyped.intLiteral(42))) .build(); var context = TypeCheckerContext.EMPTY .enterNamespaceWithDefinedVariables(P.map( diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java new file mode 100644 index 00000000..bea2d4d9 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java @@ -0,0 +1,66 @@ +package org.shedlang.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.ast.untyped.Untyped; +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.Types; +import org.shedlang.compiler.util.P; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; + +public class TypeCheckerFunctionReturnTests { + @Test + public void expressionIsTypeChecked() { + var untyped = Untyped.functionReturn_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, Types.STRING); + + var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); + + assertThat(typed.value(), isTypedReturnNode(isTypedStringLiteralNode("hello"))); + } + + @Test + public void whenNotInFunctionThenReturnIsForbidden() { + var untyped = Untyped.functionReturn_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterNamespace(P.map()); + + assertThrows( + CannotReturnOutsideOfFunction.class, + () -> TypeChecker.typeCheckFunctionStatement(untyped, context) + ); + } + + @Test + public void canReturnSubtypeOfReturnType() { + var untyped = Untyped.functionReturn_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, Types.TOP); + + var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); + + assertThat(typed.value(), isTypedReturnNode(isTypedStringLiteralNode("hello"))); + } + + @Test + public void cannotReturnNonSubtypeOfReturnType() { + var untyped = Untyped.functionReturn_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, Types.INT); + + var error = assertThrows( + UnexpectedTypeError.class, + () -> TypeChecker.typeCheckFunctionStatement(untyped, context) + ); + + assertThat(error.expected(), equalTo(TypeSets.type(Types.INT))); + assertThat(error.actual(), equalTo(Types.STRING)); + } +} diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReturnTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReturnTests.java deleted file mode 100644 index 08fb61fd..00000000 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReturnTests.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.shedlang.compiler.typechecker; - -import org.junit.jupiter.api.Test; -import org.shedlang.compiler.ast.untyped.Untyped; -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.Types; -import org.shedlang.compiler.util.P; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; -import static org.zwobble.precisely.AssertThat.assertThat; -import static org.zwobble.precisely.Matchers.equalTo; - -public class TypeCheckerReturnTests { - @Test - public void expressionIsTypeChecked() { - var untyped = Untyped.return_(Untyped.stringLiteral("hello")); - var context = TypeCheckerContext.EMPTY - .enterFunction(P.map(), Effects.NONE, Types.STRING); - - var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); - - assertThat(typed.value(), isTypedReturnNode(isTypedStringLiteralNode("hello"))); - } - - @Test - public void whenNotInFunctionThenReturnIsForbidden() { - var untyped = Untyped.return_(Untyped.stringLiteral("hello")); - var context = TypeCheckerContext.EMPTY - .enterNamespace(P.map()); - - assertThrows( - CannotReturnOutsideOfFunction.class, - () -> TypeChecker.typeCheckFunctionStatement(untyped, context) - ); - } - - @Test - public void canReturnSubtypeOfReturnType() { - var untyped = Untyped.return_(Untyped.stringLiteral("hello")); - var context = TypeCheckerContext.EMPTY - .enterFunction(P.map(), Effects.NONE, Types.TOP); - - var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); - - assertThat(typed.value(), isTypedReturnNode(isTypedStringLiteralNode("hello"))); - } - - @Test - public void cannotReturnNonSubtypeOfReturnType() { - var untyped = Untyped.return_(Untyped.stringLiteral("hello")); - var context = TypeCheckerContext.EMPTY - .enterFunction(P.map(), Effects.NONE, Types.INT); - - var error = assertThrows( - UnexpectedTypeError.class, - () -> TypeChecker.typeCheckFunctionStatement(untyped, context) - ); - - assertThat(error.expected(), equalTo(TypeSets.type(Types.INT))); - assertThat(error.actual(), equalTo(Types.STRING)); - } -} -- cgit v1.2.3