diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-10-24 21:02:07 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-10-24 21:02:07 +0100 |
| commit | 0dccc0cdf7813c3c5571a79186a73bfd239a66d6 (patch) | |
| tree | bc313d4336b83fe774ea1d8f14064bef2d09a97b /src/test/java/org/shedlang/compiler/parser | |
| parent | 192cb27a8d8bc78211897c9f9325eb898390dbc4 (diff) | |
Parse function expressions
Diffstat (limited to 'src/test/java/org/shedlang/compiler/parser')
| -rw-r--r-- | src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java b/src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java new file mode 100644 index 00000000..d86776be --- /dev/null +++ b/src/test/java/org/shedlang/compiler/parser/ParserFunctionExpressionTests.java @@ -0,0 +1,246 @@ +package org.shedlang.compiler.parser; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.parser.errors.PositionalParamAfterNamedParamError; +import org.shedlang.compiler.tokeniser.UnexpectedTokenException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.shedlang.compiler.ast.untyped.UntypedNodeMatchers.*; +import static org.shedlang.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.*; + +public class ParserFunctionExpressionTests { + @Test + public void canParseMinimalFunction() { + var source = """ + fn () -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withParams(isUntypedParamsNode(isSequence(), isSequence())) + .withEffect(isOptionalEmpty()) + .withReturnType(isUntypedTypeLevelReferenceNode("Unit")) + .withBody(isUntypedBlockNode(isSequence())) + ); + } + + @Test + public void canParseFunctionWithSinglePositionalParam() { + var source = """ + fn (a: Int) -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withParams(isUntypedParamsNode( + isSequence( + isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")) + ), + isSequence() + )) + ); + } + + @Test + public void canParseFunctionWithManyPositionalParams() { + var source = """ + fn (a: Int, b: String, c: Bool) -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withParams(isUntypedParamsNode( + isSequence( + isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")), + isUntypedParamNode("b", isUntypedTypeLevelReferenceNode("String")), + isUntypedParamNode("c", isUntypedTypeLevelReferenceNode("Bool")) + ), + isSequence() + )) + ); + } + + @Test + public void canParseFunctionWithSingleNamedParam() { + var source = """ + fn (.a: Int) -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withParams(isUntypedParamsNode( + isSequence(), + isSequence( + isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")) + ) + )) + ); + } + + @Test + public void canParseFunctionWithManyNamedParams() { + var source = """ + fn (.a: Int, .b: String, .c: Bool) -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withParams(isUntypedParamsNode( + isSequence(), + isSequence( + isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")), + isUntypedParamNode("b", isUntypedTypeLevelReferenceNode("String")), + isUntypedParamNode("c", isUntypedTypeLevelReferenceNode("Bool")) + ) + )) + ); + } + + @Test + public void canParseNamedParamAfterPositionalParam() { + var source = """ + fn (a: Int, .b: String) -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withParams(isUntypedParamsNode( + isSequence( + isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")) + ), + isSequence( + isUntypedParamNode("b", isUntypedTypeLevelReferenceNode("String")) + ) + )) + ); + } + + @Test + public void cannotParsePositionalParamAfterNamedParam() { + var source = """ + fn (.a: Int, b: String) -> Unit { + }"""; + + assertThrows( + PositionalParamAfterNamedParamError.class, + () -> parseString(source, Parser::parseTopLevelExpression) + ); + } + + @Test + public void paramsCanHaveTrailingComma() { + var source = """ + fn (a: Int, ) -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withParams(isUntypedParamsNode( + isSequence( + isUntypedParamNode("a", isUntypedTypeLevelReferenceNode("Int")) + ), + isSequence() + )) + ); + } + + @Test + public void canParseFunctionWithEffect() { + var source = """ + fn () ! Io -> Unit { + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withEffect(isOptionalOf(isUntypedTypeLevelReferenceNode("Io"))) + ); + } + + @Test + public void canParseFunctionWithBodyWithoutReturn() { + var source = """ + fn () -> Unit { + a; + b; + c; + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withBody(isUntypedBlockNode(isSequence( + isUntypedExpressionStatementNode(isUntypedReferenceNode("a")), + isUntypedExpressionStatementNode(isUntypedReferenceNode("b")), + isUntypedExpressionStatementNode(isUntypedReferenceNode("c")) + ))) + ); + } + + @Test + public void canParseFunctionWithBodyWithBlockReturn() { + var source = """ + fn () -> Unit { + a; + b; + c + }"""; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat( + node, + isUntypedFunctionExpressionNode() + .withBody(isUntypedBlockNode(isSequence( + isUntypedExpressionStatementNode(isUntypedReferenceNode("a")), + isUntypedExpressionStatementNode(isUntypedReferenceNode("b")), + isUntypedBlockReturnNode(isUntypedReferenceNode("c")) + ))) + ); + } + + @Test + public void blockReturnEndsFunction() { + var source = """ + fn () -> Unit { + a; + b + c + }"""; + + var error = assertThrows( + UnexpectedTokenException.class, + () -> parseString(source, Parser::parseTopLevelExpression) + ); + + assertThat(error.getActual().value(), equalTo("c")); + assertThat(error.getExpected(), equalTo(TokenType.SYMBOL_BRACE_CLOSE.toString())); + } +} |
