From 608e7b2be13c1f79c0ea3fc9b38fdf217507227b Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 10 Aug 2026 10:37:05 +0100 Subject: Add basic support for generic structs in java-transient-0 --- examples/03-struct-generic/hobgoblin.json5 | 8 +- examples/03-struct-generic/output/java/.gitignore | 2 + examples/03-struct-generic/output/java/pom.xml | 1 + .../test/java/org/zwobble/example/PointTests.java | 18 +++++ .../output/generators/java/JavaGenerator.java | 2 +- .../generators/javatypes/JavaTypesGenerator.java | 86 +++++++++++++--------- 6 files changed, 79 insertions(+), 38 deletions(-) create mode 100644 examples/03-struct-generic/output/java/.gitignore create mode 120000 examples/03-struct-generic/output/java/pom.xml create mode 100644 examples/03-struct-generic/output/java/src/test/java/org/zwobble/example/PointTests.java diff --git a/examples/03-struct-generic/hobgoblin.json5 b/examples/03-struct-generic/hobgoblin.json5 index b5ea8a6..744e6d6 100644 --- a/examples/03-struct-generic/hobgoblin.json5 +++ b/examples/03-struct-generic/hobgoblin.json5 @@ -6,10 +6,10 @@ }, }, generators: [ -// { -// generator: "java-types", -// path: "output/java/src/gen/java", -// }, + { + generator: "java-types", + path: "output/java/src/gen/java", + }, // { // generator: "java-precisely-matchers", // path: "output/java/src/gen/java", diff --git a/examples/03-struct-generic/output/java/.gitignore b/examples/03-struct-generic/output/java/.gitignore new file mode 100644 index 0000000..ff511d4 --- /dev/null +++ b/examples/03-struct-generic/output/java/.gitignore @@ -0,0 +1,2 @@ +/src/gen/ +/target/ diff --git a/examples/03-struct-generic/output/java/pom.xml b/examples/03-struct-generic/output/java/pom.xml new file mode 120000 index 0000000..445fa65 --- /dev/null +++ b/examples/03-struct-generic/output/java/pom.xml @@ -0,0 +1 @@ +../../../templates/java-junit/pom.xml \ No newline at end of file diff --git a/examples/03-struct-generic/output/java/src/test/java/org/zwobble/example/PointTests.java b/examples/03-struct-generic/output/java/src/test/java/org/zwobble/example/PointTests.java new file mode 100644 index 0000000..36810d4 --- /dev/null +++ b/examples/03-struct-generic/output/java/src/test/java/org/zwobble/example/PointTests.java @@ -0,0 +1,18 @@ +package org.zwobble.example; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.zwobble.example.types.point.Line; +import org.zwobble.example.types.point.Point; + +public class PointTests { + @Test + public void line() { + var line = new Line<>( + new Point(10, 25), + new Point(42, 47) + ); + assertEquals(line.start().x(), 10); + assertEquals(line.end().y(), 47); + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java index 1bb256c..901171c 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java @@ -201,7 +201,7 @@ public class JavaGenerator { throw new UnsupportedOperationException("TODO"); case TypeParam typeParam -> - throw new UnsupportedOperationException("TODO"); + JavaTypeRef.local(this.generateTypeName(typeParam.name())); }; } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java index 62f24b6..270683d 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java @@ -123,7 +123,17 @@ public class JavaTypesGenerator implements Generator { TypedStructDefinitionNode structDefinition, Context context ) { - var javaTypeRef = generateTypeRef(structDefinition.typeOrThrow(), context); + var javaRawTypeRef = generateTypeRef(structDefinition.typeOrConstructor().unboundType(), context); + + var javaTypeParams = switch (structDefinition.typeOrConstructor()) { + case TypeOrConstructor.Constructor constructor -> + constructor.value().params().stream() + .map(param -> new JavaTypeParameter(this.javaGenerator.generateTypeName(param.name()))) + .toList(); + + case TypeOrConstructor.Type _ -> + List.of(); + }; var components = structDefinition.fields().orElse(List.of()).stream() .map(field -> new JavaRecordComponent( @@ -132,48 +142,58 @@ public class JavaTypesGenerator implements Generator { )) .toList(); - var implementsTypes = context.variantOf(structDefinition.typeOrThrow()).stream() - .map(sumType -> this.generateTypeRef(sumType, context)) - .toList(); - - var builderJavaTypeRef = generateBuilderTypeRef(structDefinition.typeOrThrow()); + // TODO: handle generic types + var implementsTypes = switch (structDefinition.typeOrConstructor()) { + case TypeOrConstructor.Constructor _ -> + List.of(); - var arbitraryValueExpression = new JavaNewExpression( - builderJavaTypeRef, - structDefinition.fields().orElse(List.of()).stream() - .map(field -> arbitraryValue(field.type(), context)) - .toList() - ); + case TypeOrConstructor.Type type -> + context.variantOf(type.value()).stream() + .map(sumType -> this.generateTypeRef(sumType, context)) + .toList(); + }; var body = new ArrayList(); if (structDefinition.fields().isEmpty()) { - body.add(generateStructSingletonInstanceField(javaTypeRef)); - body.add(generateStructSingletonConstructor(javaTypeRef)); + body.add(generateStructSingletonInstanceField(javaRawTypeRef)); + body.add(generateStructSingletonConstructor(javaRawTypeRef)); } - body.add(new JavaMethodDeclaration( - ARBITRARY_METHOD_NAME, - JavaVisibility.PUBLIC, - JavaMemberKind.STATIC, - builderJavaTypeRef, - List.of(), - List.of(), - new JavaBlock(List.of( - new JavaReturn(arbitraryValueExpression) - )) - )); + // TODO: handle generic types + if (structDefinition.typeOrConstructor() instanceof TypeOrConstructor.Type) { + var builderJavaTypeRef = generateBuilderTypeRef(structDefinition.typeOrThrow()); - body.add(generateStructDefinitionBuilder( - structDefinition, - components, - context - )); + var arbitraryValueExpression = new JavaNewExpression( + builderJavaTypeRef, + structDefinition.fields().orElse(List.of()).stream() + .map(field -> arbitraryValue(field.type(), context)) + .toList() + ); + + body.add(new JavaMethodDeclaration( + ARBITRARY_METHOD_NAME, + JavaVisibility.PUBLIC, + JavaMemberKind.STATIC, + builderJavaTypeRef, + List.of(), + List.of(), + new JavaBlock(List.of( + new JavaReturn(arbitraryValueExpression) + )) + )); + + body.add(generateStructDefinitionBuilder( + structDefinition, + components, + context + )); + } if (structDefinition.fields().isPresent()) { return new JavaRecordDeclaration( - javaTypeRef, - List.of(), + javaRawTypeRef, + javaTypeParams, components, implementsTypes, body, @@ -181,7 +201,7 @@ public class JavaTypesGenerator implements Generator { ); } else { return new JavaClassDeclaration( - javaTypeRef, + javaRawTypeRef, implementsTypes, body, structDefinition.docComment() -- cgit v1.2.3