package scotch.compiler.parser; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static scotch.compiler.parser.Tree.leaf; import static scotch.compiler.parser.Tree.node; import static scotch.compiler.parser.Tree.tree; import org.junit.Test; public class ScotchParserTest extends ScotchParserBaseTest { @Test public void shouldParseDottedName() { assertThat(parse(ScotchParser::qualifiedName, "scotch.test.name"), is( tree("qualifiedName", asList( node("moduleName", asList( node("idVar", asList(leaf("scotch"))), leaf("."), node("idVar", asList(leaf("test"))))), leaf("."), node("name", asList( node("idVar", asList(leaf("name"))))))))); } @Test public void shouldParseDottedNameWithOperator() { assertThat(parse(ScotchParser::qualifiedName, "scotch.test.(+)"), is( tree("qualifiedName", asList( node("moduleName", asList( node("idVar", asList(leaf("scotch"))), leaf("."), node("idVar", asList(leaf("test"))))), leaf("."), node("name", asList( node("operatorName", asList( leaf("("), leaf("+"), leaf(")"))))))))); } @Test public void shouldParseDestructuringPatternArgument() { assertThat(parse(ScotchParser::patternArgument, "Toast { burnLevel = 4, kind = _ }"), is( tree("patternArgument", asList( node("destructuringArgument", asList( node("idType", asList(leaf("Toast"))), leaf("{"), node("destructuringFields", asList( node("destructuringField", asList( leaf("burnLevel"), leaf("="), node("unshuffledArgument", asList( node("patternArgument", asList( node("literalArgument", asList( node("literal", asList( leaf("4"))))))))))), leaf(","), node("destructuringField", asList( leaf("kind"), leaf("="), node("unshuffledArgument", asList( node("patternArgument", asList( node("ignoreArgument", asList( leaf("_"))))))))))), leaf("}"))))))); } @Test public void shouldParseTuplePatternArgument() { assertThat(parse(ScotchParser::patternArgument, "(1, 2)"), is( tree("patternArgument", asList( node("tupleArgument", asList( leaf("("), node("tupleArgumentFields", asList( node("unshuffledArgument", asList( node("patternArgument", asList( node("literalArgument", asList( node("literal", asList( leaf("1"))))))))), leaf(","), node("unshuffledArgument", asList( node("patternArgument", asList( node("literalArgument", asList( node("literal", asList( leaf("2"))))))))))), leaf(")"))))))); } @Test public void shouldParseUnshuffledPatternArgument() { assertThat(parse(ScotchParser::patternArgument, "(Map a b)"), is( tree("patternArgument", asList( node("parenthesizedArgument", asList( leaf("("), node("unshuffledArgument", asList( node("patternArgument", asList( node("typeArgument", asList( node("qualifiedType", asList( node("idType", asList(leaf("Map"))))))))), node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))), node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("b"))))))))), leaf(")"))))))); } @Test public void shouldDestructureAndCaptureArgument() { assertThat(parse(ScotchParser::patternArgument, "Toast { burnLevel = 4, kind = _ } as toast"), is( tree("patternArgument", asList( node("destructuringArgument", asList( node("idType", asList(leaf("Toast"))), leaf("{"), node("destructuringFields", asList( node("destructuringField", asList( leaf("burnLevel"), leaf("="), node("unshuffledArgument", asList( node("patternArgument", asList( node("literalArgument", asList( node("literal", asList( leaf("4"))))))))))), leaf(","), node("destructuringField", asList( leaf("kind"), leaf("="), node("unshuffledArgument", asList( node("patternArgument", asList( node("ignoreArgument", asList( leaf("_"))))))))))), leaf("}"))), node("asCapture", asList( leaf("as"), node("idVar", asList(leaf("toast"))))))))); } @Test public void shouldParseInitializer() { assertThat(parse(ScotchParser::expression, "Node { left = Left, right = Right }"), is( tree("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedType", asList( node("idType", asList(leaf("Node"))))), node("structFields", asList( leaf("{"), node("structField", asList( node("idVar", asList(leaf("left"))), leaf("="), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedType", asList( node("idType", asList(leaf("Left"))))))))))))), leaf(","), node("structField", asList( node("idVar", asList(leaf("right"))), leaf("="), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedType", asList( node("idType", asList(leaf("Right"))))))))))))), leaf("}"))))))), leaf(";"))))); } @Test public void shouldParsePartialOperator() { assertThat(parse(ScotchParser::expression, "(+2)"), is( tree("expression", asList( node("primaryExpression", asList( node("partialOperator", asList( leaf("("), leaf("+"), node("primaryExpression", asList( node("literal", asList( leaf("2"))))), leaf(")"))))), leaf(";"))))); } @Test public void shouldParseModules() { String result = parse(ScotchParser::modules, "module a.b.c", "x = y", "module x.y.z", "a = b" ); assertThat(result, is( tree("modules", asList( node("module", asList( leaf("module"), node("moduleName", asList( node("idVar", asList(leaf("a"))), leaf("."), node("idVar", asList(leaf("b"))), leaf("."), node("idVar", asList(leaf("c"))))), leaf(";"), node("moduleMembers", asList( node("moduleMember", asList( node("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("y"))))))))))))))))))), leaf(";"))))), node("module", asList( leaf("module"), node("moduleName", asList( node("idVar", asList(leaf("x"))), leaf("."), node("idVar", asList(leaf("y"))), leaf("."), node("idVar", asList(leaf("z"))))), leaf(";"), node("moduleMembers", asList( node("moduleMember", asList( node("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))))))))))), leaf(";"))))), leaf("<EOF>"))))); } @Test public void shouldParseModuleImports() { String result = parse(ScotchParser::module, "module a.b.c", "import x.y.z" ); assertThat(result, is( tree("module", asList( leaf("module"), node("moduleName", asList( node("idVar", asList(leaf("a"))), leaf("."), node("idVar", asList(leaf("b"))), leaf("."), node("idVar", asList(leaf("c"))))), leaf(";"), node("moduleImports", asList( node("moduleImport", asList( leaf("import"), node("moduleName", asList( node("idVar", asList(leaf("x"))), leaf("."), node("idVar", asList(leaf("y"))), leaf("."), node("idVar", asList(leaf("z"))))))), leaf(";"))))))); } @Test public void shouldParseFunctionSignature() { String result = parse(ScotchParser::moduleMember, "(+) :: a -> a -> a"); assertThat(result, is( tree("moduleMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("operatorName", asList( leaf("("), leaf("+"), leaf(")"))))))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))), leaf(";"))))))))))))))); } @Test public void shouldParseSumSignature() { String result = parse(ScotchParser::moduleMember, "thing :: Node a b -> c"); assertThat(result, is( tree("moduleMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("idVar", asList( leaf("thing"))))))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("qualifiedType", asList( node("idType", asList(leaf("Node"))))), node("typeParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), node("typeParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("c"))))), leaf(";"))))))))))))); } @Test public void shouldParseParenthesizedSignature() { String result = parse(ScotchParser::moduleMember, "thing :: (a -> b) -> c"); assertThat(result, is( tree("moduleMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("idVar", asList(leaf("thing"))))))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("parenthesizedTypeSignature", asList( leaf("("), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))))))), leaf(")"))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("c"))))), leaf(";"))))))))))))); } @Test public void shouldParseMultiNameSignature() { String result = parse(ScotchParser::moduleMember, "one, two, (+) :: b"); assertThat(result, is( tree("moduleMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("idVar", asList(leaf("one"))))), leaf(","), node("name", asList( node("idVar", asList(leaf("two"))))), leaf(","), node("name", asList( node("operatorName", asList( leaf("("), leaf("+"), leaf(")"))))))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))), leaf(";"))))))))))); } @Test public void shouldParseLeftInfixOperator() { String result = parse(ScotchParser::moduleMember, "left infix 0 (|>)"); assertThat(result, is( tree("moduleMember", asList( node("operatorDefinition", asList( node("fixity", asList( leaf("left"), leaf("infix"))), leaf("0"), node("operatorNames", asList( node("operatorName", asList( leaf("("), leaf("|>"), leaf(")"))), leaf(";"))))))))); } @Test public void shouldParseRightInfixOperator() { String result = parse(ScotchParser::moduleMember, "right infix 0 ($)"); assertThat(result, is( tree("moduleMember", asList( node("operatorDefinition", asList( node("fixity", asList( leaf("right"), leaf("infix"))), leaf("0"), node("operatorNames", asList( node("operatorName", asList( leaf("("), leaf("$"), leaf(")"))), leaf(";"))))))))); } @Test public void shouldParsePrefixOperator() { String result = parse(ScotchParser::moduleMember, "prefix 9 (!)"); assertThat(result, is( tree("moduleMember", asList( node("operatorDefinition", asList( node("fixity", asList( leaf("prefix") )), leaf("9"), node("operatorNames", asList( node("operatorName", asList( leaf("("), leaf("!"), leaf(")"))), leaf(";"))))))))); } @Test public void shouldParseOperatorList() { String result = parse(ScotchParser::moduleMember, "left infix 6 (+), (-)"); assertThat(result, is( tree("moduleMember", asList( node("operatorDefinition", asList( node("fixity", asList( leaf("left"), leaf("infix"))), leaf("6"), node("operatorNames", asList( node("operatorName", asList( leaf("("), leaf("+"), leaf(")"))), leaf(","), node("operatorName", asList( leaf("("), leaf("-"), leaf(")"))), leaf(";"))))))))); } @Test public void shouldParseDefaultOperator() { String result = parse(ScotchParser::moduleMember, "left infix 7 `mod`"); assertThat(result, is( tree("moduleMember", asList( node("operatorDefinition", asList( node("fixity", asList( leaf("left"), leaf("infix"))), leaf("7"), node("operatorNames", asList( node("operatorName", asList( leaf("`"), node("idVar", asList(leaf("mod"))), leaf("`"))), leaf(";"))))))))); } @Test public void shouldParseExpression() { String result = parse(ScotchParser::expression, "2 + 2"); assertThat(result, is( tree("expression", asList( node("primaryExpression", asList( node("literal", asList(leaf("2"))))), node("primaryExpression", asList( node("reference", asList( node("operatorReference", asList( leaf("+"))))))), node("primaryExpression", asList( node("literal", asList( leaf("2"))))), leaf(";"))))); } @Test public void shouldParseParenthesizedExpression() { String result = parse(ScotchParser::expression, "(2 + 2) * 2"); assertThat(result, is( tree("expression", asList( node("primaryExpression", asList( node("parenthesizedExpression", asList( leaf("("), node("expression", asList( node("primaryExpression", asList( node("literal", asList( leaf("2"))))), node("primaryExpression", asList( node("reference", asList( node("operatorReference", asList( leaf("+"))))))), node("primaryExpression", asList( node("literal", asList( leaf("2"))))))), leaf(")"))))), node("primaryExpression", asList( node("reference", asList( node("operatorReference", asList( leaf("*"))))))), node("primaryExpression", asList( node("literal", asList( leaf("2"))))), leaf(";"))))); } @Test public void shouldParseTypeSignatureWithSingleContextParameter() { String result = parse(ScotchParser::moduleMember, "(+) :: Num a => a -> a -> a"); assertThat(result, is( tree("moduleMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("operatorName", asList( leaf("("), leaf("+"), leaf(")"))))))), leaf("::"), node("typeContext", asList( node("contextParameter", asList( node("qualifiedType", asList( node("idType", asList(leaf("Num"))))), node("idVar", asList(leaf("a"))))), leaf("=>"))), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))), leaf(";"))))))))))))))); } @Test public void shouldParseTypeWithMultipleContextParameters() { String result = parse(ScotchParser::moduleMember, "convert :: (Num a, Num b) => a -> b"); assertThat(result, is( tree("moduleMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("idVar", asList(leaf("convert"))))))), leaf("::"), node("typeContext", asList( leaf("("), node("contextParameters", asList( node("contextParameter", asList( node("qualifiedType", asList( node("idType", asList(leaf("Num"))))), node("idVar", asList(leaf("a"))))), leaf(","), node("contextParameter", asList( node("qualifiedType", asList( node("idType", asList(leaf("Num"))))), node("idVar", asList(leaf("b"))))))), leaf(")"), leaf("=>"))), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))), leaf(";"))))))))))))); } @Test public void shouldParseFunctionLiteral() { String result = parse(ScotchParser::expression, "\\x -> x"); assertThat(result, is( tree("expression", asList( node("primaryExpression", asList( node("functionLiteral", asList( leaf("\\"), node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))))))))), leaf("->"), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("x"))))))))))), leaf(";"))))))))))); } @Test public void shouldParseFunctionLiteralWithMultipleArguments() { String result = parse(ScotchParser::expression, "\\x y -> x"); assertThat(result, is( tree("expression", asList( node("primaryExpression", asList( node("functionLiteral", asList( leaf("\\"), node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))))))), node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("y"))))))))), leaf("->"), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("x"))))))))))), leaf(";"))))))))))); } @Test public void shouldParseConditional() { String result = parse(ScotchParser::expression, "if True then \"a\" else \"b\""); assertThat(result, is( tree("expression", asList( node("primaryExpression", asList( node("conditional", asList( leaf("if"), node("expression", asList( node("primaryExpression", asList( node("literal", asList(leaf("True"))))))), leaf("then"), node("expression", asList( node("primaryExpression", asList( node("literal", asList( leaf("\"a\""))))))), leaf("else"), node("expression", asList( node("primaryExpression", asList( node("literal", asList( leaf("\"b\""))))), leaf(";"))))))))))); } @Test public void shouldParseGuardCase() { String result = parse(ScotchParser::pattern, "guardCase z | a = b", " | x = y" ); assertThat(result, is( tree("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("guardCase"))))))), node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("z"))))))))), node("guardCases", asList( node("guardCase", asList( leaf("|"), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("a"))))))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))))))))), node("guardCase", asList( leaf("|"), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("x"))))))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("y"))))))))))), leaf(";"))))))))))))); } @Test public void shouldParseListInTypeSignature() { String result = parse(ScotchParser::typeSignature, "[a] -> a"); assertThat(result, is( tree("typeSignature", asList( node("primarySignature", asList( node("listSignature", asList( leaf("["), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))))), leaf("]"))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))), leaf(";"))))))))); } @Test public void shouldParseListLiteral() { String result = parse(ScotchParser::expression, "[1, 2, 3]"); assertThat(result, is( tree("expression", asList( node("primaryExpression", asList( node("listLiteral", asList( leaf("["), node("listElements", asList( node("expression", asList( node("primaryExpression", asList( node("literal", asList( leaf("1"))))))), leaf(","), node("expression", asList( node("primaryExpression", asList( node("literal", asList( leaf("2"))))))), leaf(","), node("expression", asList( node("primaryExpression", asList( node("literal", asList( leaf("3"))))))))), leaf("]"))))), leaf(";"))))); } @Test public void shouldParseTupleInTypeSignature() { String result = parse(ScotchParser::typeSignature, "(a, b) -> a"); assertThat(result, is( tree("typeSignature", asList( node("primarySignature", asList( node("tupleSignature", asList( leaf("("), node("tupleSignatureElements", asList( node("tupleSignatureElement", asList( node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))))))), leaf(","), node("tupleSignatureElement", asList( node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))))))))), leaf(")"))))), leaf("->"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))), leaf(";"))))))))); } @Test public void shouldParseTupleLiteral() { String result = parse(ScotchParser::expression, "(1, 2, 3)"); assertThat(result, is( tree("expression", asList( node("primaryExpression", asList( node("tupleLiteral", asList( leaf("("), node("tupleElements", asList( node("tupleElement", asList( node("expression", asList( node("primaryExpression", asList( node("literal", asList(leaf("1"))))))))), leaf(","), node("tupleElement", asList( node("expression", asList( node("primaryExpression", asList( node("literal", asList(leaf("2"))))))))), leaf(","), node("tupleElement", asList( node("expression", asList( node("primaryExpression", asList( node("literal", asList(leaf("3"))))))))))), leaf(")"))))), leaf(";"))))); } @Test public void shouldParseListPatternArgument() { String result = parse(ScotchParser::patternArgument, "[a, b]"); assertThat(result, is( tree("patternArgument", asList( node("listArgument", asList( leaf("["), node("listArgumentElements", asList( node("listArgumentElement", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))))), leaf(","), node("listArgumentElement", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("b"))))))))))), leaf("]"))))))); } @Test public void shouldParseClassDefinition() { String result = parse(ScotchParser::moduleMember, "class Eq a where", " a :: b", " a = b" ); assertThat(result, is( tree("moduleMember", asList( node("classDefinition", asList( leaf("class"), node("idType", asList(leaf("Eq"))), node("classParameters", asList( node("classParameter", asList( node("idVar", asList(leaf("a"))))))), leaf("where"), leaf("{"), node("classMembers", asList( node("classMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("idVar", asList(leaf("a"))))))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))))))))), leaf(";"), node("classMember", asList( node("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))))))))))), leaf(";"), leaf(";"))), leaf("}"))))))); } @Test public void shouldParseClassDefinitionWithContext() { String result = parse(ScotchParser::moduleMember, "class Eq a => Ord a where", " a :: b" ); assertThat(result, is( tree("moduleMember", asList( node("classDefinition", asList( leaf("class"), node("typeContext", asList( node("contextParameter", asList( node("qualifiedType", asList( node("idType", asList(leaf("Eq"))))), node("idVar", asList(leaf("a"))))), leaf("=>"))), node("idType", asList(leaf("Ord"))), node("classParameters", asList( node("classParameter", asList( node("idVar", asList(leaf("a"))))))), leaf("where"), leaf("{"), node("classMembers", asList( node("classMember", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("idVar", asList(leaf("a"))))))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))))))))), leaf(";"), leaf(";"))), leaf("}"))))))); } @Test public void shouldParseInstanceDefinition() { String result = parse(ScotchParser::moduleMember, "instance Eq Int where", " a = b" ); assertThat(result, is( tree("moduleMember", asList( node("instanceDefinition", asList( leaf("instance"), node("idType", asList(leaf("Eq"))), node("instanceParameters", asList( node("instanceParameter", asList( node("qualifiedType", asList( node("idType", asList(leaf("Int"))))))))), leaf("where"), leaf("{"), node("instanceMembers", asList( node("instanceMember", asList( node("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))))))))))), leaf(";"), leaf(";"))), leaf("}"))))))); } @Test public void shouldParseInstanceDefinitionWithTypeContext() { String result = parse(ScotchParser::moduleMember, "instance Eq a => Eq [a] where", " a = b" ); assertThat(result, is( tree("moduleMember", asList( node("instanceDefinition", asList( leaf("instance"), node("typeContext", asList( node("contextParameter", asList( node("qualifiedType", asList( node("idType", asList(leaf("Eq"))))), node("idVar", asList(leaf("a"))))), leaf("=>"))), node("idType", asList(leaf("Eq"))), node("instanceParameters", asList( node("instanceParameter", asList( node("listParameter", asList( leaf("["), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))))), leaf("]"))))))), leaf("where"), leaf("{"), node("instanceMembers", asList( node("instanceMember", asList( node("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))))))))))), leaf(";"), leaf(";"))), leaf("}"))))))); } @Test public void shouldParseParenthesizedInstanceParameterWithTypeSum() { assertThat(parse(ScotchParser::instanceParameter, "(Map a b)"), is( tree("instanceParameter", asList( node("parenthesizedParameter", asList( leaf("("), node("typeSignature", asList( node("primarySignature", asList( node("qualifiedType", asList( node("idType", asList(leaf("Map"))))), node("typeParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), node("typeParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))))))), leaf(")"))))))); } @Test public void shouldParseTupleInstanceParameter() { assertThat(parse(ScotchParser::instanceParameter, "(a, b)"), is( tree("instanceParameter", asList( node("tupleParameter", asList( leaf("("), node("tupleParameterElements", asList( node("tupleParameterElement", asList( node("instanceParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))))), leaf(","), node("tupleParameterElement", asList( node("instanceParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))))))), leaf(")"))))))); } @Test public void shouldParseDoNotation() { String result = parse(ScotchParser::patternBody, "do x <- y", " x" ); assertThat(result, is( tree("patternBody", asList( node("doNotation", asList( leaf("do"), leaf("{"), node("doStatements", asList( node("doStatement", asList( node("drawFrom", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))))))), leaf("<-"), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("y"))))))))))))))))), leaf(";"), node("doStatement", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("x"))))))))))))))), leaf(";"), leaf(";"))), leaf("}"))))))); } @Test public void shouldParseWhereStatement() { String result = parse(ScotchParser::pattern, "a x = b y", " where y = x" ); assertThat(result, is( tree("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))), node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))), node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("y"))))))))))))))), node("whereStatement", asList( leaf("where"), leaf("{"), node("wherePatterns", asList( node("wherePattern", asList( node("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("y"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("x"))))))))))))))))))), leaf(";"), leaf(";"))), leaf("}"))))))); } @Test public void shouldParseLetDeclaration() { String result = parse(ScotchParser::expression, "let x = y", "x" ); assertThat(result, is( tree("expression", asList( node("letDeclaration", asList( leaf("let"), leaf("{"), node("letPatterns", asList( node("letPattern", asList( node("pattern", asList( node("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))))))))), leaf("="), node("patternBody", asList( node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("y"))))))))))))))))))), leaf(";"))), leaf("}"), leaf("in"), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("x"))))))))))), leaf(";"))))))))); } @Test public void shouldParseSignatureInLetPattern() { assertThat(parse(ScotchParser::letPattern, "a :: b"), is( tree("letPattern", asList( node("patternSignature", asList( node("patternNames", asList( node("name", asList( node("idVar", asList(leaf("a"))))))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))), leaf(";"))))))))))); } @Test public void shouldParseParallelPatternInLetPattern() { assertThat(parse(ScotchParser::letPattern, "(a, _) = b"), is( tree("letPattern", asList( node("parallelPattern", asList( node("parallelDeclaration", asList( leaf("("), node("parallelArguments", asList( node("parallelArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))), leaf(","), node("parallelArgument", asList( node("ignoreArgument", asList(leaf("_"))))))), leaf(")"))), leaf("="), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))), leaf(";") )))))))); } @Test public void shouldParseParallelPatternInWherePattern() { assertThat(parse(ScotchParser::wherePattern, "(a, _) = b"), is( tree("wherePattern", asList( node("parallelPattern", asList( node("parallelDeclaration", asList( leaf("("), node("parallelArguments", asList( node("parallelArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("a"))))))), leaf(","), node("parallelArgument", asList( node("ignoreArgument", asList(leaf("_"))))))), leaf(")"))), leaf("="), node("expression", asList( node("primaryExpression", asList( node("reference", asList( node("qualifiedName", asList( node("name", asList( node("idVar", asList(leaf("b"))))))))))), leaf(";") )))))))); } @Test public void shouldParseConsListPattern() { assertThat(parse(ScotchParser::patternArgument, "(x:xs)"), is( tree("patternArgument", asList( node("parenthesizedArgument", asList( leaf("("), node("unshuffledArgument", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))))))), node("patternArgument", asList( node("typeArgument", asList( node("qualifiedType", asList( node("idType", asList(leaf(":"))))))))), node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("xs"))))))))), leaf(")"))))))); } @Test public void shouldParseNilListPattern() { assertThat(parse(ScotchParser::patternArgument, "[]"), is( tree("patternArgument", asList( node("typeArgument", asList( node("qualifiedType", asList( node("idType", asList(leaf("[]"))))))))))); } @Test public void shouldParseSingletonDataDefinition() { String result = parse(ScotchParser::moduleMember, "data Box a {", " value :: a,", " }" ); assertThat(result, is( tree("moduleMember", asList( node("singletonDataDefinition", asList( leaf("data"), node("idType", asList(leaf("Box"))), node("dataParameters", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), leaf("{"), node("dataFields", asList( node("dataField", asList( node("idVar", asList(leaf("value"))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))))))), leaf(","))), leaf("}"))))))); } @Test public void shouldParseDataDefinitionWithConstants() { String result = parse(ScotchParser::moduleMember, "data Colors = Red | Green | Blue" ); assertThat(result, is( tree("moduleMember", asList( node("dataDefinition", asList( leaf("data"), node("idType", asList(leaf("Colors"))), leaf("="), node("dataConstructors", asList( node("dataConstructor", asList( node("dataConstant", asList( node("idType", asList(leaf("Red"))))))), leaf("|"), node("dataConstructor", asList( node("dataConstant", asList( node("idType", asList(leaf("Green"))))))), leaf("|"), node("dataConstructor", asList( node("dataConstant", asList( node("idType", asList(leaf("Blue"))))))), leaf(";"))))))))); } @Test public void shouldParseDataTuple() { String result = parse(ScotchParser::dataConstructor, "Node a b"); assertThat(result, is( tree("dataConstructor", asList( node("dataTuple", asList( node("idType", asList(leaf("Node"))), node("dataTupleParameters", asList( node("dataTupleParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("a"))))))), node("dataTupleParameter", asList( node("typeVariable", asList( node("idVar", asList(leaf("b"))))))), leaf(";"))))))))); } @Test public void shouldParseDataRecord() { String result = parse(ScotchParser::dataConstructor, "Node { left :: Tree, right :: Tree }" ); assertThat(result, is( tree("dataConstructor", asList( node("dataRecord", asList( node("idType", asList(leaf("Node"))), leaf("{"), node("dataFields", asList( node("dataField", asList( node("idVar", asList(leaf("left"))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("qualifiedType", asList( node("idType", asList(leaf("Tree"))))))))))), leaf(","), node("dataField", asList( node("idVar", asList(leaf("right"))), leaf("::"), node("typeSignature", asList( node("primarySignature", asList( node("qualifiedType", asList( node("idType", asList(leaf("Tree"))))))))))))), leaf("}"))))))); } @Test public void shouldParsePatternMatchWithOperator() { String result = parse(ScotchParser::patternArguments, "x == y"); assertThat(result, is( tree("patternArguments", asList( node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("x"))) )) )), node("patternArgument", asList( node("operatorArgument", asList(leaf("=="))) )), node("patternArgument", asList( node("captureArgument", asList( node("idVar", asList(leaf("y"))) )) )), leaf(";") )) )); } @Test public void shouldParseDoubleLiteral() { String result = parse(ScotchParser::literal, ".23"); assertThat(result, is( tree("literal", asList(leaf(".23"))) )); } @Test public void shouldParseBigIntLiteral() { String result = parse(ScotchParser::literal, "2131215b"); assertThat(result, is( tree("literal", asList(leaf("2131215b"))) )); } }