From 765357e0d65c3f869344d8e86321fbed72edbcd6 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 8 Aug 2026 23:06:06 +0100 Subject: Support nested constructed native types in java-transient-0 --- .../javatransient0/JavaTransient0Generator.java | 205 ++++++++++++++++----- .../hobgoblin/compiler/types/ConstructedType.java | 2 +- .../org/zwobble/hobgoblin/compiler/types/Type.java | 2 +- 3 files changed, 158 insertions(+), 51 deletions(-) (limited to 'src/main/java/org/zwobble') diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java index b0f63e7..c9c4ea0 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java @@ -60,12 +60,11 @@ public class JavaTransient0Generator implements Generator { List namespaces, TypesInfo typesInfo ) throws IOException { - var context = new Context(typesInfo); - write(generateSupportTypes()); - write(generateBuiltins(context)); + write(generateBuiltins(new Context(NamespaceName.of(), typesInfo))); for (var namespace : namespaces) { + var context = new Context(namespace.namespaceName(), typesInfo); var javaCompilationUnits = generateNamespace(namespace, context); write(javaCompilationUnits); @@ -268,7 +267,7 @@ public class JavaTransient0Generator implements Generator { generateEncodeInt64Method(), generateDecodeInt64Method(), generateEncodeStringMethod(context), - generateDecodeStringMethod() + generateDecodeStringMethod(context) ), DocComment.EMPTY ) @@ -446,7 +445,7 @@ public class JavaTransient0Generator implements Generator { ); } - private JavaMethodDeclaration generateDecodeStringMethod() { + private JavaMethodDeclaration generateDecodeStringMethod(Context context) { var bytesLengthVariableName = JavaIdentifier.of("bytesLength"); var bytesVariableName = JavaIdentifier.of("bytes"); @@ -454,7 +453,8 @@ public class JavaTransient0Generator implements Generator { statements.addAll(generateDecode( bytesLengthVariableName, - NativeTypes.INT_64 + NativeTypes.INT_64, + context )); statements.add(generateAssertLessThanIntMax( @@ -506,14 +506,36 @@ public class JavaTransient0Generator implements Generator { transient0JavaName ); + var body = new ArrayList(); + + for (var statement : namespace.body()) { + body.addAll(generateNamespaceStatement(statement, context)); + } + + for ( + var constructedTypeIndex = 0; + constructedTypeIndex < context.constructedTypes.size(); + constructedTypeIndex++ + ) { + var constructedType = context.constructedTypes.get(constructedTypeIndex); + body.add(generateEncodeConstructedTypeMethod( + constructedType, + constructedTypeIndex, + context + )); + body.add(generateDecodeConstructedTypeMethod( + constructedType, + constructedTypeIndex, + context + )); + } + return List.of( new JavaCompilationUnit( new JavaClassDeclaration( transient0JavaTypeRef, List.of(), - namespace.body().stream() - .flatMap(statement -> generateNamespaceStatement(statement, context)) - .toList(), + body, DocComment.EMPTY ) ) @@ -537,33 +559,33 @@ public class JavaTransient0Generator implements Generator { ); } - private Stream generateNamespaceStatement( + private List generateNamespaceStatement( TypedNamespaceStatementNode statement, Context context ) { return switch (statement) { case TypedEnumDefinitionNode enumDefinition -> { - yield Stream.concat( - Stream.of(generateEncodeEnumMethod(enumDefinition, context)), - generateDecodeEnumMethod(enumDefinition) - ); + yield Lists.concat(List.of( + List.of(generateEncodeEnumMethod(enumDefinition, context)), + generateDecodeEnumMethod(enumDefinition, context) + )); } case TypedNativeTypeDefinitionNode _ -> { - yield Stream.of(); + yield List.of(); } case TypedStructDefinitionNode structDefinition -> { - yield Stream.of( + yield List.of( generateEncodeStructMethod(structDefinition, context), - generateDecodeStructMethod(structDefinition) + generateDecodeStructMethod(structDefinition, context) ); } case TypedSumDefinitionNode sumDefinition -> { - yield Stream.of( + yield List.of( generateEncodeSumMethod(sumDefinition, context), - generateDecodeSumMethod(sumDefinition, context.typesInfo) + generateDecodeSumMethod(sumDefinition, context) ); } }; @@ -587,12 +609,15 @@ public class JavaTransient0Generator implements Generator { ); } - private Stream generateDecodeEnumMethod(TypedEnumDefinitionNode enumDefinition) { + private List generateDecodeEnumMethod( + TypedEnumDefinitionNode enumDefinition, + Context context + ) { var enumJavaTypeRef = this.javaGenerator.generateTypeRef(enumDefinition.type()); var ordinal = JavaIdentifier.of("ordinal"); var enumValues = JavaIdentifier.of(this.javaGenerator.generateTypeName(enumDefinition.type().name()).value() + "$values"); - return Stream.of( + return List.of( new JavaFieldDeclaration( JavaVisibility.PRIVATE, JavaMemberKind.STATIC, @@ -608,7 +633,7 @@ public class JavaTransient0Generator implements Generator { generateDecodeMethod( enumDefinition.type(), new JavaBlock(Stream.concat( - generateDecode(ordinal, NativeTypes.INT_32).stream(), + generateDecode(ordinal, NativeTypes.INT_32, context).stream(), Stream.of(new JavaReturn(new JavaArrayAccess( new JavaRef(enumValues), new JavaRef(ordinal) @@ -640,7 +665,10 @@ public class JavaTransient0Generator implements Generator { ); } - private JavaMethodDeclaration generateDecodeStructMethod(TypedStructDefinitionNode structDefinition) { + private JavaMethodDeclaration generateDecodeStructMethod( + TypedStructDefinitionNode structDefinition, + Context context + ) { var structJavaTypeRef = this.javaGenerator.generateTypeRef(structDefinition.type()); return generateDecodeMethod( @@ -654,7 +682,8 @@ public class JavaTransient0Generator implements Generator { structDefinition.fields().get().stream() .flatMap(field -> generateDecode( this.javaGenerator.generateFieldName(field.name()), - field.type().value() + field.type().value(), + context ).stream()), Stream.of(new JavaReturn(new JavaNewExpression( structJavaTypeRef, @@ -710,22 +739,22 @@ public class JavaTransient0Generator implements Generator { private JavaMethodDeclaration generateDecodeSumMethod( TypedSumDefinitionNode sumDefinition, - TypesInfo typesInfo + Context context ) { var decodeStatements = new ArrayList(); var variantTag = JavaIdentifier.of("variantTag"); - decodeStatements.addAll(generateDecode(variantTag, NativeTypes.INT_32)); + decodeStatements.addAll(generateDecode(variantTag, NativeTypes.INT_32, context)); var cases = new ArrayList(); - for (var variant : typesInfo.sumVariants(sumDefinition.type())) { + for (var variant : context.typesInfo.sumVariants(sumDefinition.type())) { var variantVariable = JavaIdentifier.of(VALUE_NAME.value() + "$" + variant.tag()); var statements = new ArrayList(); - statements.addAll(generateDecode(variantVariable, variant.containerType())); + statements.addAll(generateDecode(variantVariable, variant.containerType(), context)); statements.add(new JavaReturn(new JavaRef(variantVariable))); @@ -850,8 +879,13 @@ public class JavaTransient0Generator implements Generator { private JavaMethodRef generateEncodeMethodRef(Type type, Context context) { return switch (type) { - case ConstructedNativeType _ -> { - throw new UnsupportedOperationException("TODO"); + case ConstructedType constructedType -> { + var constructedTypeIndex = context.constructedTypeIndex(constructedType); + var methodName = generateEncodeConstructedTypeMethodName(constructedTypeIndex); + yield new JavaMethodRef( + generateTransient0TypeRef(context.namespaceName), + methodName + ); } case SimpleType simpleType -> { @@ -871,6 +905,28 @@ public class JavaTransient0Generator implements Generator { }; } + private static JavaIdentifier generateEncodeConstructedTypeMethodName(int constructedTypeIndex) { + return JavaIdentifier.of("encode$constructedType$" + constructedTypeIndex); + } + + private JavaMethodDeclaration generateEncodeConstructedTypeMethod( + ConstructedType constructedType, + int constructedTypeIndex, + Context context + ) { + return new JavaMethodDeclaration( + generateEncodeConstructedTypeMethodName(constructedTypeIndex), + JavaVisibility.PRIVATE, + JavaMemberKind.STATIC, + JavaTypeRef.VOID, + generateEncodeParams(this.javaGenerator.generateTypeRef(constructedType)), + List.of(JavaTypeRef.IO_EXCEPTION), + Optional.of(new JavaBlock( + generateEncode(new JavaRef(VALUE_NAME), constructedType, context) + )) + ); + } + private JavaBlockStatement generateEncode( JavaExpression value, NamespaceName typeNamespaceName, @@ -893,17 +949,21 @@ public class JavaTransient0Generator implements Generator { ); } - private List generateDecode(JavaIdentifier target, Type type) { + private List generateDecode( + JavaIdentifier target, + Type type, + Context context + ) { return switch (type) { case ConstructedNativeType constructedNativeType -> { if (constructedNativeType.constructor().equals(NativeTypes.BOX)) { - yield generateDecode(target, constructedNativeType.args().getFirst()); + yield generateDecode(target, constructedNativeType.args().getFirst(), context); } else if (constructedNativeType.constructor().equals(NativeTypes.LIST)) { - yield generateDecodeList(target, constructedNativeType.args().getFirst()); + yield generateDecodeList(target, constructedNativeType.args().getFirst(), context); } else if (constructedNativeType.constructor().equals(NativeTypes.OPTION)) { - yield generateDecodeOption(target, constructedNativeType.args().getFirst()); + yield generateDecodeOption(target, constructedNativeType.args().getFirst(), context); } else if (constructedNativeType.constructor().equals(NativeTypes.SHARED)) { - yield generateDecodeShared(target, constructedNativeType.args().getFirst()); + yield generateDecodeShared(target, constructedNativeType.args().getFirst(), context); } else { yield List.of( generateDecode( @@ -911,7 +971,7 @@ public class JavaTransient0Generator implements Generator { constructedNativeType.namespaceName(), constructedNativeType, constructedNativeType.args().stream() - .map(typeArg -> generateDecodeMethodRef(typeArg)) + .map(typeArg -> generateDecodeMethodRef(typeArg, context)) .toList() ) ); @@ -938,10 +998,15 @@ public class JavaTransient0Generator implements Generator { }; } - private JavaMethodRef generateDecodeMethodRef(Type type) { + private JavaMethodRef generateDecodeMethodRef(Type type, Context context) { return switch (type) { - case ConstructedNativeType _ -> { - throw new UnsupportedOperationException("TODO"); + case ConstructedType constructedType -> { + var constructedTypeIndex = context.constructedTypeIndex(constructedType); + var methodName = generateDecodeConstructedTypeMethodName(constructedTypeIndex); + yield new JavaMethodRef( + generateTransient0TypeRef(context.namespaceName), + methodName + ); } case SimpleType simpleType -> { @@ -961,6 +1026,29 @@ public class JavaTransient0Generator implements Generator { }; } + private static JavaIdentifier generateDecodeConstructedTypeMethodName(int constructedTypeIndex) { + return JavaIdentifier.of("decode$constructedType$" + constructedTypeIndex); + } + + private JavaMethodDeclaration generateDecodeConstructedTypeMethod( + ConstructedType constructedType, + int constructedTypeIndex, + Context context + ) { + return new JavaMethodDeclaration( + generateDecodeConstructedTypeMethodName(constructedTypeIndex), + JavaVisibility.PRIVATE, + JavaMemberKind.STATIC, + this.javaGenerator.generateTypeRef(constructedType), + generateDecodeParams(), + List.of(JavaTypeRef.IO_EXCEPTION), + Optional.of(new JavaBlock(Lists.concat(List.of( + generateDecode(VALUE_NAME, constructedType, context), + List.of(new JavaReturn(new JavaRef(VALUE_NAME))) + )))) + ); + } + private JavaBlockStatement generateDecode( JavaIdentifier target, NamespaceName typeNamespaceName, @@ -1019,7 +1107,8 @@ public class JavaTransient0Generator implements Generator { private List generateDecodeList( JavaIdentifier target, - Type elementType + Type elementType, + Context context ) { var size = JavaIdentifier.of(target.value() + "$size"); var index = JavaIdentifier.of(target.value() + "$index"); @@ -1027,7 +1116,7 @@ public class JavaTransient0Generator implements Generator { var statements = new ArrayList(); - statements.addAll(generateDecode(size, NativeTypes.INT_64)); + statements.addAll(generateDecode(size, NativeTypes.INT_64, context)); statements.add(generateAssertLessThanIntMax( size, @@ -1049,7 +1138,7 @@ public class JavaTransient0Generator implements Generator { var forBodyStatements = new ArrayList(); - forBodyStatements.addAll(generateDecode(element, elementType)); + forBodyStatements.addAll(generateDecode(element, elementType, context)); forBodyStatements.add(new JavaExpressionStatement(new JavaMethodCall( new JavaRef(target), @@ -1115,14 +1204,15 @@ public class JavaTransient0Generator implements Generator { private List generateDecodeOption( JavaIdentifier target, - Type elementType + Type elementType, + Context context ) { var isPresent = JavaIdentifier.of(target.value() + "$isPresent"); var element = JavaIdentifier.of(target.value() + "$element"); var statements = new ArrayList(); - statements.addAll(generateDecode(isPresent, NativeTypes.BOOL)); + statements.addAll(generateDecode(isPresent, NativeTypes.BOOL, context)); statements.add(new JavaLocalVariableDeclaration( Optional.of(JavaTypeRef.optional(this.javaGenerator.generateReferenceTypeRef(elementType))), @@ -1132,7 +1222,7 @@ public class JavaTransient0Generator implements Generator { var ifTrue = new ArrayList(); - ifTrue.addAll(generateDecode(element, elementType)); + ifTrue.addAll(generateDecode(element, elementType, context)); ifTrue.add(new JavaExpressionStatement(new JavaBinaryOperation( JavaBinaryOperator.ASSIGN, @@ -1229,13 +1319,14 @@ public class JavaTransient0Generator implements Generator { private List generateDecodeShared( JavaIdentifier target, - Type type + Type type, + Context context ) { var id = JavaIdentifier.of(target.value() + "$id"); var decodedValue = JavaIdentifier.of(target.value() + "$decodedValue"); return Lists.concat(List.of( - generateDecode(id, NativeTypes.INT_64), + generateDecode(id, NativeTypes.INT_64, context), List.of( generateAssertLessThanIntMax( id, @@ -1291,7 +1382,7 @@ public class JavaTransient0Generator implements Generator { )) )), new JavaBlock(Lists.concat(List.of( - generateDecode(decodedValue, type), + generateDecode(decodedValue, type, context), List.of( new JavaExpressionStatement(new JavaMethodCall( new JavaRef(SHARED_VALUES_NAME), @@ -1398,10 +1489,26 @@ public class JavaTransient0Generator implements Generator { } private static class Context { + private final NamespaceName namespaceName; private final TypesInfo typesInfo; + private final Map constructedTypeIndexes = new HashMap<>(); + private final List constructedTypes = new ArrayList<>(); - public Context(TypesInfo typesInfo) { + public Context(NamespaceName namespaceName, TypesInfo typesInfo) { + this.namespaceName = namespaceName; this.typesInfo = typesInfo; } + + public int constructedTypeIndex(ConstructedType type) { + var existingIndex = constructedTypeIndexes.get(type); + if (existingIndex != null) { + return existingIndex; + } + + var index = constructedTypes.size(); + constructedTypeIndexes.put(type, index); + constructedTypes.add(type); + return index; + } } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/ConstructedType.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/ConstructedType.java index 03d4718..6224331 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/types/ConstructedType.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/ConstructedType.java @@ -1,4 +1,4 @@ package org.zwobble.hobgoblin.compiler.types; -public sealed interface ConstructedType permits ConstructedNativeType { +public sealed interface ConstructedType extends Type permits ConstructedNativeType { } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java index b483209..90dcc9b 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Type.java @@ -1,5 +1,5 @@ package org.zwobble.hobgoblin.compiler.types; -public sealed interface Type extends TypeLevelValue permits ConstructedNativeType, ConstructibleType, EnumType, SimpleNativeType, SimpleType, StructType, SumType, TypeLevelValueType, TypeParam { +public sealed interface Type extends TypeLevelValue permits ConstructedNativeType, ConstructedType, ConstructibleType, EnumType, SimpleNativeType, SimpleType, StructType, SumType, TypeLevelValueType, TypeParam { String name(); } -- cgit v1.2.3