package org.zwobble.hobgoblin.compiler.parser; import org.junit.jupiter.api.Test; import org.zwobble.hobgoblin.compiler.ast.DocComment; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumDefinitionNode; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher.isUntypedEnumDefinitionNode; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNodeMatcher.isUntypedStructFieldDefinitionNode; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumDefinitionNodeMatcher.isUntypedSumDefinitionNode; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNodeMatcher.isUntypedSumVariantDefinitionNode; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNodeMatcher.isUntypedTypeLevelReferenceNode; import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; public class ParserSumDefinitionTests { @Test public void nameIsParsed() { var source = """ sum Shape { }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, instanceOf( UntypedSumDefinitionNode.class, has("name", UntypedSumDefinitionNode::name, equalTo("Shape")) )); } @Test public void emptySumHasNoVariants() { var source = """ sum Shape { }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, instanceOf( UntypedSumDefinitionNode.class, has("variants", UntypedSumDefinitionNode::variants, isSequence()) )); } @Test public void sumWithVariants() { var source = """ sum Shape { variant Rectangle; variant Triangle; }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, instanceOf( UntypedSumDefinitionNode.class, has("variants", UntypedSumDefinitionNode::variants, isSequence( has( "type", UntypedSumVariantDefinitionNode::type, instanceOf( UntypedTypeLevelReferenceNode.class, has("name", UntypedTypeLevelReferenceNode::name, equalTo("Rectangle")) ) ), has( "type", UntypedSumVariantDefinitionNode::type, instanceOf( UntypedTypeLevelReferenceNode.class, has("name", UntypedTypeLevelReferenceNode::name, equalTo("Triangle")) ) ) )) )); } @Test public void variantIsNotBoxedByDefault() { var source = """ sum Shape { variant Rectangle; }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, isUntypedSumDefinitionNode() .withVariants(isSequence( isUntypedSumVariantDefinitionNode().withIsBox(equalTo(false)) )) ); } @Test public void variantIsBoxedWhenAnnotatedWithBox() { var source = """ sum Shape { variant Rectangle box; }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, isUntypedSumDefinitionNode() .withVariants(isSequence( isUntypedSumVariantDefinitionNode().withIsBox(equalTo(true)) )) ); } @Test public void enumWithDocComment() { var source = """ /// A small number. enum SmallNumber { }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, isUntypedEnumDefinitionNode() .withDocComment(equalTo(new DocComment("A small number.\n"))) ); } @Test public void emptySumHasNoFields() { var source = """ sum Shape { }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, instanceOf( UntypedSumDefinitionNode.class, has("fields", UntypedSumDefinitionNode::fields, isSequence()) )); } @Test public void sumWithFields() { var source = """ sum Shape { field height: Int32; field width: Int64; }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, isUntypedSumDefinitionNode() .withFields(isSequence( isUntypedStructFieldDefinitionNode() .withName(equalTo("height")) .withType(isUntypedTypeLevelReferenceNode().withName(equalTo("Int32"))), isUntypedStructFieldDefinitionNode() .withName(equalTo("width")) .withType(isUntypedTypeLevelReferenceNode().withName(equalTo("Int64"))) )) ); } @Test public void sumWithDocComment() { var source = """ /// A 2D shape. sum Shape { }"""; var node = parseString( source, Parser::parseNamespaceStatement ); assertThat(node, instanceOf( UntypedSumDefinitionNode.class, has( "docComment", UntypedSumDefinitionNode::docComment, equalTo(new DocComment("A 2D shape.\n")) ) )); } }