From 00a97675f5f6cef672495371a6b7e36a372586ab Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 2 Jul 2026 16:28:03 +0100 Subject: Parse sum variant boxing --- .../untyped/UntypedSumVariantDefinitionNode.java | 18 ++++--- .../generators/java/JavaGeneratorConfig.java | 1 + .../compiler/output/lang/java/ast/JavaTypeRef.java | 2 + .../zwobble/hobgoblin/compiler/parser/Parser.java | 3 ++ .../UntypedSumVariantDefinitionNodeMatcher.java | 6 +++ .../lang/java/ast/JavaBooleanLiteralMatcher.java | 2 +- .../compiler/parser/ParserEnumDefinitionTests.java | 25 +--------- .../compiler/parser/ParserSumDefinitionTests.java | 57 ++++++++++++++++++++++ 8 files changed, 83 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNode.java index 082a3b6..30eaf13 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNode.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNode.java @@ -5,26 +5,30 @@ package org.zwobble.hobgoblin.compiler.ast.untyped; // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode imports // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode imports -public record UntypedSumVariantDefinitionNode(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode type, org.zwobble.hobgoblin.compiler.sources.Source source) { +public record UntypedSumVariantDefinitionNode(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode type, boolean isBox, org.zwobble.hobgoblin.compiler.sources.Source source) { public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder arbitrary() { - return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode.arbitrary().build(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode.arbitrary().build(), false, org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); } - public record Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode type, org.zwobble.hobgoblin.compiler.sources.Source source) { + public record Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode type, boolean isBox, org.zwobble.hobgoblin.compiler.sources.Source source) { public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode build() { - return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode(type, source); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode(type, isBox, source); } public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder withType(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode type) { - return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(type, source); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(type, isBox, source); } public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder withType(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode.Builder type) { - return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(type.build(), source); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(type.build(), isBox, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder withIsBox(boolean isBox) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(type, isBox, source); } public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { - return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(type, source); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder(type, isBox, source); } // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode.Builder body diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGeneratorConfig.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGeneratorConfig.java index f22e8ff..37262fe 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGeneratorConfig.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGeneratorConfig.java @@ -78,6 +78,7 @@ public record JavaGeneratorConfig( } private static final Map DEFAULT_NATIVE_TYPE_CONFIGS = Map.ofEntries( + Map.entry(NativeTypes.BOOL, JavaNativeTypeConfig.primitive(JavaTypeRef.BOOLEAN, JavaTypeRef.BOOLEAN_BOXED, new JavaBooleanLiteral(false))), Map.entry(NativeTypes.INT_32, JavaNativeTypeConfig.primitive(JavaTypeRef.INT, JavaTypeRef.INT_BOXED, new JavaIntegerLiteral(0))), Map.entry(NativeTypes.INT_64, JavaNativeTypeConfig.primitive(JavaTypeRef.LONG, JavaTypeRef.LONG_BOXED, new JavaIntegerLiteral(0))), Map.entry(NativeTypes.STRING, JavaNativeTypeConfig.of(JavaTypeRef.STRING, new JavaStringLiteral(""))), diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaTypeRef.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaTypeRef.java index 6db0c5c..099518c 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaTypeRef.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaTypeRef.java @@ -38,6 +38,8 @@ public record JavaTypeRef(JavaPackageName packageName, List type private static final JavaPackageName JAVA_UTIL_STREAM = JavaPackageName.of("java", "util", "stream"); } + public static final JavaTypeRef BOOLEAN = primitive("boolean"); + public static final JavaTypeRef BOOLEAN_BOXED = topLevel(JavaPackages.JAVA_LANG, JavaIdentifier.of("Boolean")); public static final JavaTypeRef INT = primitive("int"); public static final JavaTypeRef INT_BOXED = topLevel(JavaPackages.JAVA_LANG, JavaIdentifier.of("Integer")); public static final JavaTypeRef LONG = primitive("long"); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java index a9e5947..47a8095 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java @@ -150,6 +150,7 @@ public class Parser { tokens.skip(TokenType.KEYWORD_VARIANT); var name = parseIdentifier(tokens); + tokens.skip(TokenType.SYMBOL_SEMICOLON); var end = tokens.endPosition(); @@ -292,6 +293,7 @@ public class Parser { tokens.skip(TokenType.KEYWORD_VARIANT); var type = parseTypeLevelExpression(tokens); + var isBox = tokens.trySkip(TokenType.KEYWORD_BOX); tokens.skip(TokenType.SYMBOL_SEMICOLON); var end = tokens.endPosition(); @@ -299,6 +301,7 @@ public class Parser { return new UntypedSumVariantDefinitionNode( type, + isBox, source ); } diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNodeMatcher.java index 9f28f26..721a7ea 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNodeMatcher.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedSumVariantDefinitionNodeMatcher.java @@ -34,6 +34,12 @@ public class UntypedSumVariantDefinitionNodeMatcher implements org.zwobble.preci return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNodeMatcher(submatchers); } + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNodeMatcher withIsBox(org.zwobble.precisely.Matcher isBox) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("isBox", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode::isBox, isBox)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNodeMatcher(submatchers); + } + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNodeMatcher withSource(org.zwobble.precisely.Matcher source) { var submatchers = new java.util.ArrayList>(this.submatchers); submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumVariantDefinitionNode::source, source)); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaBooleanLiteralMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaBooleanLiteralMatcher.java index 56308d4..80583b0 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaBooleanLiteralMatcher.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/java/ast/JavaBooleanLiteralMatcher.java @@ -28,7 +28,7 @@ public class JavaBooleanLiteralMatcher implements org.zwobble.precisely.Matcher< return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.output.lang.java.ast.JavaBooleanLiteral.class, this.submatchers); } - public org.zwobble.hobgoblin.compiler.output.lang.java.ast.JavaBooleanLiteralMatcher withValue(org.zwobble.precisely.Matcher value) { + public org.zwobble.hobgoblin.compiler.output.lang.java.ast.JavaBooleanLiteralMatcher withValue(org.zwobble.precisely.Matcher value) { var submatchers = new java.util.ArrayList>(this.submatchers); submatchers.add(org.zwobble.precisely.Matchers.has("value", org.zwobble.hobgoblin.compiler.output.lang.java.ast.JavaBooleanLiteral::value, value)); return new org.zwobble.hobgoblin.compiler.output.lang.java.ast.JavaBooleanLiteralMatcher(submatchers); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserEnumDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserEnumDefinitionTests.java index 925077f..b7b03fa 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserEnumDefinitionTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserEnumDefinitionTests.java @@ -1,17 +1,13 @@ 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 static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher.isUntypedEnumDefinitionNode; import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher.isUntypedEnumVariantDefinitionNode; -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.UntypedTypeLevelReferenceNodeMatcher.isUntypedTypeLevelReferenceNode; import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; import static org.zwobble.precisely.AssertThat.assertThat; -import static org.zwobble.precisely.Matchers.*; +import static org.zwobble.precisely.Matchers.equalTo; +import static org.zwobble.precisely.Matchers.isSequence; public class ParserEnumDefinitionTests { @Test @@ -66,21 +62,4 @@ public class ParserEnumDefinitionTests { )) ); } - - @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"))) - ); - } } diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserSumDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserSumDefinitionTests.java index 20de7ba..1a13bff 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserSumDefinitionTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserSumDefinitionTests.java @@ -6,8 +6,10 @@ 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; @@ -84,6 +86,61 @@ public class ParserSumDefinitionTests { )); } + @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 = """ -- cgit v1.2.3