package org.shedlang.compiler.parser; import org.junit.jupiter.api.Test; import org.shedlang.compiler.parser.errors.PositionalParamAfterNamedParamError; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.shedlang.compiler.ast.untyped.UntypedNodeMatchers.*; import static org.shedlang.compiler.ast.untyped.UntypedNodeMatchers.isUntypedTypeLevelReferenceNode; import static org.shedlang.compiler.parser.ParserTesting.parseString; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; public class ParserFunctionSignatureTests { @Test public void canParseEmptyParams() { var source = "() -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode() .withParams(isUntypedParamsNode(isSequence(), isSequence())) ); } @Test public void canParseSinglePositionalParam() { var source = "(a: Int) -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withParams(isUntypedParamsNode( isSequence( isUntypedParamNode("a", isOptionalOf(isUntypedTypeLevelReferenceNode("Int"))) ), isSequence() )) ); } @Test public void canParseManyPositionalParams() { var source = "(a: Int, b: String, c: Bool) -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withParams(isUntypedParamsNode( isSequence( isUntypedParamNode("a", isOptionalOf(isUntypedTypeLevelReferenceNode("Int"))), isUntypedParamNode("b", isOptionalOf(isUntypedTypeLevelReferenceNode("String"))), isUntypedParamNode("c", isOptionalOf(isUntypedTypeLevelReferenceNode("Bool"))) ), isSequence() )) ); } @Test public void canParseSingleNamedParam() { var source = "(.a: Int) -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withParams(isUntypedParamsNode( isSequence(), isSequence( isUntypedParamNode("a", isOptionalOf(isUntypedTypeLevelReferenceNode("Int"))) ) )) ); } @Test public void canParseManyNamedParams() { var source = "(.a: Int, .b: String, .c: Bool) -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withParams(isUntypedParamsNode( isSequence(), isSequence( isUntypedParamNode("a", isOptionalOf(isUntypedTypeLevelReferenceNode("Int"))), isUntypedParamNode("b", isOptionalOf(isUntypedTypeLevelReferenceNode("String"))), isUntypedParamNode("c", isOptionalOf(isUntypedTypeLevelReferenceNode("Bool"))) ) )) ); } @Test public void canParseNamedParamAfterPositionalParam() { var source = "(a: Int, .b: String) -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withParams(isUntypedParamsNode( isSequence( isUntypedParamNode("a", isOptionalOf(isUntypedTypeLevelReferenceNode("Int"))) ), isSequence( isUntypedParamNode("b", isOptionalOf(isUntypedTypeLevelReferenceNode("String"))) ) )) ); } @Test public void cannotParsePositionalParamAfterNamedParam() { var source = "(.a: Int, b: String) -> Unit"; assertThrows( PositionalParamAfterNamedParamError.class, () -> parseString(source, Parser::parseFunctionSignature) ); } @Test public void canParseParamWithoutType() { var source = "(a) -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withParams(isUntypedParamsNode( isSequence( isUntypedParamNode("a", isOptionalEmpty()) ), isSequence() )) ); } @Test public void paramsCanHaveTrailingComma() { var source = "(a: Int, ) -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withParams(isUntypedParamsNode( isSequence( isUntypedParamNode("a", isOptionalOf(isUntypedTypeLevelReferenceNode("Int"))) ), isSequence() )) ); } @Test public void canParseSignatureWithoutEffect() { var source = "() -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode() .withEffect(isOptionalEmpty()) ); } @Test public void canParseSignatureWithEffect() { var source = "() ! Io -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode() .withEffect(isOptionalOf(isUntypedTypeLevelReferenceNode("Io"))) ); } @Test public void canParseReturnType() { var source = "() -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode() .withReturnType(isUntypedTypeLevelReferenceNode("Unit")) ); } @Test public void canParseSignatureWithoutReturnType() { var source = "()"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode() .withoutReturnType() ); } @Test public void canParseAbsentTypeLevelParams() { var source = "() -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withTypeLevelParams(isOptionalEmpty()) ); } @Test public void canParseSingleTypeLevelParam() { var source = "[A]() -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withTypeLevelParams(isOptionalOf(isSequence( isUntypedTypeParamNode("A") ))) ); } @Test public void canParseManyTypeLevelParams() { var source = "[A, B, C]() -> Unit"; var node = parseString(source, Parser::parseFunctionSignature); assertThat( node, isUntypedFunctionSignatureNode().withTypeLevelParams(isOptionalOf(isSequence( isUntypedTypeParamNode("A"), isUntypedTypeParamNode("B"), isUntypedTypeParamNode("C") ))) ); } }