diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-07-04 23:31:53 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-07-04 23:31:53 +0100 |
| commit | 06ae500619b2c2401548f6eac9e97fce09fceb58 (patch) | |
| tree | 8db4968d3db0d33a888285a07051cf7737cf1121 | |
| parent | 5d15eac0f3fd7656f9b78c5796d936182572c297 (diff) | |
Support encode/decode of Int32 and structs
7 files changed, 261 insertions, 16 deletions
diff --git a/examples/10-transient-0/hobgoblin.json5 b/examples/10-transient-0/hobgoblin.json5 new file mode 100644 index 0000000..f3e455d --- /dev/null +++ b/examples/10-transient-0/hobgoblin.json5 @@ -0,0 +1,19 @@ +{ + outputs: { + langs: { + java: { + packageName: "org.zwobble.example.types", + }, + }, + generators: [ + { + generator: "java-types", + path: "output/java/src/gen/java", + }, + { + generator: "java-transient-0", + path: "output/java/src/gen/java", + }, + ], + } +} diff --git a/examples/10-transient-0/output/java/.gitignore b/examples/10-transient-0/output/java/.gitignore new file mode 100644 index 0000000..ff511d4 --- /dev/null +++ b/examples/10-transient-0/output/java/.gitignore @@ -0,0 +1,2 @@ +/src/gen/ +/target/ diff --git a/examples/10-transient-0/output/java/pom.xml b/examples/10-transient-0/output/java/pom.xml new file mode 100644 index 0000000..7f892e1 --- /dev/null +++ b/examples/10-transient-0/output/java/pom.xml @@ -0,0 +1,48 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.zwobble.hobgoblin.examples</groupId> + <artifactId>example</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.source>25</maven.compiler.source> + <maven.compiler.target>25</maven.compiler.target> + <exec.mainClass>org.zwobble.example.Main</exec.mainClass> + </properties> + + <dependencies> + <dependency> + <groupId>org.zwobble.precisely</groupId> + <artifactId>precisely</artifactId> + <version>0.1.5</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>3.6.1</version> + <executions> + <execution> + <id>add-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>src/gen/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/examples/10-transient-0/output/java/src/main/java/org/zwobble/example/Main.java b/examples/10-transient-0/output/java/src/main/java/org/zwobble/example/Main.java new file mode 100644 index 0000000..155b2a0 --- /dev/null +++ b/examples/10-transient-0/output/java/src/main/java/org/zwobble/example/Main.java @@ -0,0 +1,27 @@ +package org.zwobble.example; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import org.zwobble.example.types.point.Point; +import org.zwobble.example.types.point.HobgoblinTransient0Point; + +public class Main { + public static void main() throws java.io.IOException { + var point = new Point(10, 25); + + var outputStream = new ByteArrayOutputStream(); + HobgoblinTransient0Point.encode(point, outputStream); + var bytes = outputStream.toByteArray(); + + var inputStream = new ByteArrayInputStream(bytes); + var decodedPoint = HobgoblinTransient0Point.decode(inputStream); + + if (!point.equals(decodedPoint)) { + throw new RuntimeException(String.format( + "encode-decode did not produce identical data\nexpected: %s\nbut was: %s", + point, + decodedPoint + )); + } + } +} diff --git a/examples/10-transient-0/src/point.hob b/examples/10-transient-0/src/point.hob new file mode 100644 index 0000000..82b55cc --- /dev/null +++ b/examples/10-transient-0/src/point.hob @@ -0,0 +1,4 @@ +struct Point { + field x: Int32; + field y: Int32; +} 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 5f91c1f..0837855 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 @@ -2,17 +2,20 @@ package org.zwobble.hobgoblin.compiler.output.generators.javatransient0; import org.zwobble.hobgoblin.compiler.ast.DocComment; import org.zwobble.hobgoblin.compiler.ast.typed.*; +import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGenerator; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGeneratorConfig; import org.zwobble.hobgoblin.compiler.output.lang.java.ast.*; import org.zwobble.hobgoblin.compiler.typechecker.TypesInfo; +import org.zwobble.hobgoblin.compiler.types.*; import org.zwobble.hobgoblin.compiler.util.Casing; import org.zwobble.json5.reader.Json5ObjectReader; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Stream; @@ -22,6 +25,9 @@ public class JavaTransient0Generator implements Generator { private static final JavaIdentifier ENCODE_METHOD_NAME = JavaIdentifier.of("encode"); private static final JavaIdentifier DECODE_METHOD_NAME = JavaIdentifier.of("decode"); + private static final JavaIdentifier VALUE_NAME = JavaIdentifier.of("value"); + private static final JavaIdentifier OUTPUT_STREAM_NAME = JavaIdentifier.of("outputStream"); + private static final JavaIdentifier INPUT_STREAM_NAME = JavaIdentifier.of("inputStream"); public static OutputConfig parseGeneratorConfig( Path projectRoot, @@ -107,10 +113,6 @@ public class JavaTransient0Generator implements Generator { } case TypedStructDefinitionNode structDefinition -> { - var valueName = JavaIdentifier.of("value"); - var outputStreamName = JavaIdentifier.of("outputStream"); - var inputStreamName = JavaIdentifier.of("inputStream"); - var structJavaTypeRef = this.javaGenerator.generateTypeRef(structDefinition.type()); yield Stream.of( @@ -120,17 +122,25 @@ public class JavaTransient0Generator implements Generator { JavaMethodKind.STATIC, JavaTypeRef.VOID, List.of( - new JavaParam( - structJavaTypeRef, - valueName - ), + new JavaParam(structJavaTypeRef, VALUE_NAME), new JavaParam( JavaTypeRef.OUTPUT_STREAM, - outputStreamName + OUTPUT_STREAM_NAME ) ), - List.of(), - new JavaBlock(List.of()) + List.of(JavaTypeRef.IO_EXCEPTION), + new JavaBlock( + structDefinition.fields().stream() + .flatMap(field -> generateEncode( + new JavaMethodCall( + new JavaRef(VALUE_NAME), + this.javaGenerator.generateFieldName(field.name()), + List.of() + ), + field.type().value() + )) + .toList() + ) ), new JavaMethodDeclaration( DECODE_METHOD_NAME, @@ -140,13 +150,25 @@ public class JavaTransient0Generator implements Generator { List.of( new JavaParam( JavaTypeRef.INPUT_STREAM, - inputStreamName + INPUT_STREAM_NAME ) ), - List.of(), - new JavaBlock(List.of( - new JavaReturn(new JavaNullLiteral()) - )) + List.of(JavaTypeRef.IO_EXCEPTION), + new JavaBlock( + Stream.concat( + structDefinition.fields().stream() + .flatMap(field -> generateDecode( + this.javaGenerator.generateFieldName(field.name()), + field.type().value() + )), + Stream.of(new JavaReturn(new JavaNewExpression( + structJavaTypeRef, + structDefinition.fields().stream() + .<JavaExpression>map(field -> new JavaRef(this.javaGenerator.generateFieldName(field.name()))) + .toList() + ))) + ).toList() + ) ) ); } @@ -157,6 +179,127 @@ public class JavaTransient0Generator implements Generator { }; } + private Stream<JavaBlockStatement> generateEncode(JavaExpression value, Type type) { + return switch (type) { + case ConstructedNativeType constructedNativeType -> { + throw new UnsupportedOperationException("TODO"); + } + + case EnumType enumType -> { + throw new UnsupportedOperationException("TODO"); + } + + case SimpleNativeType simpleNativeType -> { + if (simpleNativeType.equals(NativeTypes.INT_32)) { + yield generateEncodeInt32(value); + } else { + throw new UnsupportedOperationException("TODO"); + } + } + + case StructType structType -> { + throw new UnsupportedOperationException("TODO"); + } + + case SumType sumType -> { + throw new UnsupportedOperationException("TODO"); + } + + case TypeLevelValueType typeLevelValueType -> { + throw new UnsupportedOperationException("TODO"); + } + + case TypeParam typeParam -> { + throw new UnsupportedOperationException("TODO"); + } + }; + } + + private Stream<JavaBlockStatement> generateDecode(JavaIdentifier target, Type type) { + return switch (type) { + case ConstructedNativeType constructedNativeType -> { + throw new UnsupportedOperationException("TODO"); + } + + case EnumType enumType -> { + throw new UnsupportedOperationException("TODO"); + } + + case SimpleNativeType simpleNativeType -> { + if (simpleNativeType.equals(NativeTypes.INT_32)) { + yield generateDecodeInt32(target); + } else { + throw new UnsupportedOperationException("TODO"); + } + } + + case StructType structType -> { + throw new UnsupportedOperationException("TODO"); + } + + case SumType sumType -> { + throw new UnsupportedOperationException("TODO"); + } + + case TypeLevelValueType typeLevelValueType -> { + throw new UnsupportedOperationException("TODO"); + } + + case TypeParam typeParam -> { + throw new UnsupportedOperationException("TODO"); + } + }; + } + + private static Stream<JavaBlockStatement> generateEncodeInt32(JavaExpression value) { + return Stream.of(0, 8, 16, 24) + .map(shiftBy -> new JavaExpressionStatement( + new JavaMethodCall( + new JavaRef(OUTPUT_STREAM_NAME), + JavaIdentifier.of("write"), + List.of(new JavaBinaryOperation( + JavaBinaryOperator.BITWISE_AND, + new JavaBinaryOperation( + JavaBinaryOperator.UNSIGNED_RIGHT_SHIFT, + value, + new JavaIntegerLiteral(shiftBy) + ), + new JavaIntegerLiteral(0xff) + )) + ) + )); + } + + private static Stream<JavaBlockStatement> generateDecodeInt32( + JavaIdentifier target + ) { + var statements = new ArrayList<JavaBlockStatement>(); + + statements.add(new JavaLocalVariableDeclaration(target, new JavaIntegerLiteral(0))); + + for (var shiftBy : List.of(0, 8, 16, 24)) { + statements.add(new JavaExpressionStatement(new JavaBinaryOperation( + JavaBinaryOperator.ASSIGN, + new JavaRef(target), + new JavaBinaryOperation( + JavaBinaryOperator.BITWISE_OR, + new JavaRef(target), + new JavaBinaryOperation( + JavaBinaryOperator.LEFT_SHIFT, + new JavaMethodCall( + new JavaRef(INPUT_STREAM_NAME), + JavaIdentifier.of("read"), + List.of() + ), + new JavaIntegerLiteral(shiftBy) + ) + ) + ))); + } + + return statements.stream(); + } + private static class Context { private final TypesInfo typesInfo; 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 b2478ed..1d1274e 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 @@ -58,6 +58,8 @@ public record JavaTypeRef(JavaPackageName packageName, List<JavaIdentifier> type public static final JavaTypeRef INPUT_STREAM = topLevel(JavaPackages.JAVA_IO, JavaIdentifier.of("InputStream")); + public static final JavaTypeRef IO_EXCEPTION = topLevel(JavaPackages.JAVA_IO, JavaIdentifier.of("IOException")); + public static final JavaTypeRef LIST = topLevel(JavaPackages.JAVA_UTIL, JavaIdentifier.of("List")); public static JavaTypeRef list(JavaTypeRef elementType) { |
