diff options
11 files changed, 484 insertions, 103 deletions
diff --git a/examples/10-transient-0/hobgoblin.json5 b/examples/10-transient-0/hobgoblin.json5 index 37617ed..2160c60 100644 --- a/examples/10-transient-0/hobgoblin.json5 +++ b/examples/10-transient-0/hobgoblin.json5 @@ -16,11 +16,11 @@ }, { generator: "rust-types", - path: "output/rust/src", + path: "output/rust/src/gen", }, { generator: "rust-transient-0", - path: "output/rust/src", + path: "output/rust/src/gen", } ], } diff --git a/examples/10-transient-0/output/rust/src/data.rs b/examples/10-transient-0/output/rust/src/data.rs index 814e4ca..7a0c4fa 100644 --- a/examples/10-transient-0/output/rust/src/data.rs +++ b/examples/10-transient-0/output/rust/src/data.rs @@ -1,84 +1,5 @@ -// Generated by hobgoblin. +include!("./gen/data.rs"); -#[derive(Debug, PartialEq)] -pub struct StructWithBool { - pub a: ::core::primitive::bool, - pub b: ::core::primitive::bool, -} - -#[derive(Debug, PartialEq)] -pub struct StructWithInt32 { - pub a: ::core::primitive::i32, - pub b: ::core::primitive::i32, -} - -#[derive(Debug, PartialEq)] -pub struct StructWithInt64 { - pub a: ::core::primitive::i64, - pub b: ::core::primitive::i64, -} - -#[derive(Debug, PartialEq)] -pub struct StructWithString { - pub a: ::std::string::String, - pub b: ::std::string::String, -} - -#[derive(Debug, PartialEq)] -pub struct OuterStruct { - pub inner: crate::data::InnerStruct, - pub c: ::core::primitive::i32, -} - -#[derive(Debug, PartialEq)] -pub struct InnerStruct { - pub a: ::core::primitive::i32, - pub b: ::core::primitive::i32, -} - -#[derive(Debug, PartialEq)] -pub struct StructWithList { - pub a: ::std::vec::Vec::<::core::primitive::i64>, - pub b: ::std::vec::Vec::<crate::data::InnerStruct>, -} - -#[derive(Debug, PartialEq)] -pub struct StructWithOption { - pub a: ::std::option::Option::<::core::primitive::i64>, - pub b: ::std::option::Option::<crate::data::InnerStruct>, -} - -#[derive(Debug, PartialEq)] -pub enum EnumWithVariants { - Zero, - One, - Two, -} - -#[derive(Debug, PartialEq)] -pub enum SumWithVariants { - VariantOne(crate::data::VariantOne), - VariantTwo(crate::data::VariantTwo), -} - -impl ::std::convert::From::<crate::data::VariantOne> for crate::data::SumWithVariants { - fn from(value: crate::data::VariantOne) -> Self { - Self::VariantOne(value) - } -} - -impl ::std::convert::From::<crate::data::VariantTwo> for crate::data::SumWithVariants { - fn from(value: crate::data::VariantTwo) -> Self { - Self::VariantTwo(value) - } -} - -#[derive(Debug, PartialEq)] -pub struct VariantOne { - pub a: ::core::primitive::i32, -} - -#[derive(Debug, PartialEq)] -pub struct VariantTwo { - pub a: ::core::primitive::i64, +pub mod transient_0 { + include!("./gen/data/transient_0.rs"); } diff --git a/examples/10-transient-0/output/rust/src/data/transient_0.rs b/examples/10-transient-0/output/rust/src/data/transient_0.rs deleted file mode 100644 index 1e240de..0000000 --- a/examples/10-transient-0/output/rust/src/data/transient_0.rs +++ /dev/null @@ -1,3 +0,0 @@ -// Generated by hobgoblin. - - diff --git a/examples/10-transient-0/output/rust/src/gen/data.rs b/examples/10-transient-0/output/rust/src/gen/data.rs new file mode 100644 index 0000000..814e4ca --- /dev/null +++ b/examples/10-transient-0/output/rust/src/gen/data.rs @@ -0,0 +1,84 @@ +// Generated by hobgoblin. + +#[derive(Debug, PartialEq)] +pub struct StructWithBool { + pub a: ::core::primitive::bool, + pub b: ::core::primitive::bool, +} + +#[derive(Debug, PartialEq)] +pub struct StructWithInt32 { + pub a: ::core::primitive::i32, + pub b: ::core::primitive::i32, +} + +#[derive(Debug, PartialEq)] +pub struct StructWithInt64 { + pub a: ::core::primitive::i64, + pub b: ::core::primitive::i64, +} + +#[derive(Debug, PartialEq)] +pub struct StructWithString { + pub a: ::std::string::String, + pub b: ::std::string::String, +} + +#[derive(Debug, PartialEq)] +pub struct OuterStruct { + pub inner: crate::data::InnerStruct, + pub c: ::core::primitive::i32, +} + +#[derive(Debug, PartialEq)] +pub struct InnerStruct { + pub a: ::core::primitive::i32, + pub b: ::core::primitive::i32, +} + +#[derive(Debug, PartialEq)] +pub struct StructWithList { + pub a: ::std::vec::Vec::<::core::primitive::i64>, + pub b: ::std::vec::Vec::<crate::data::InnerStruct>, +} + +#[derive(Debug, PartialEq)] +pub struct StructWithOption { + pub a: ::std::option::Option::<::core::primitive::i64>, + pub b: ::std::option::Option::<crate::data::InnerStruct>, +} + +#[derive(Debug, PartialEq)] +pub enum EnumWithVariants { + Zero, + One, + Two, +} + +#[derive(Debug, PartialEq)] +pub enum SumWithVariants { + VariantOne(crate::data::VariantOne), + VariantTwo(crate::data::VariantTwo), +} + +impl ::std::convert::From::<crate::data::VariantOne> for crate::data::SumWithVariants { + fn from(value: crate::data::VariantOne) -> Self { + Self::VariantOne(value) + } +} + +impl ::std::convert::From::<crate::data::VariantTwo> for crate::data::SumWithVariants { + fn from(value: crate::data::VariantTwo) -> Self { + Self::VariantTwo(value) + } +} + +#[derive(Debug, PartialEq)] +pub struct VariantOne { + pub a: ::core::primitive::i32, +} + +#[derive(Debug, PartialEq)] +pub struct VariantTwo { + pub a: ::core::primitive::i64, +} diff --git a/examples/10-transient-0/output/rust/src/gen/data/transient_0.rs b/examples/10-transient-0/output/rust/src/gen/data/transient_0.rs new file mode 100644 index 0000000..e899c4c --- /dev/null +++ b/examples/10-transient-0/output/rust/src/gen/data/transient_0.rs @@ -0,0 +1,149 @@ +// Generated by hobgoblin. + +pub fn encode_struct_with_bool(value: &crate::data::StructWithBool, writer: &mut impl std::io::Write) { + todo!(); + todo!(); +} + +pub fn decode_struct_with_bool(reader: &mut impl std::io::Read) -> crate::data::StructWithBool { + let a = todo!(); + let b = todo!(); + crate::data::StructWithBool { a: a, b: b } +} + +pub fn encode_struct_with_int_32(value: &crate::data::StructWithInt32, writer: &mut impl std::io::Write) { + writer.write_all(&value.a.to_le_bytes()); + writer.write_all(&value.b.to_le_bytes()); +} + +pub fn decode_struct_with_int_32(reader: &mut impl std::io::Read) -> crate::data::StructWithInt32 { + let a = { + let bytes = { + let mut bytes = [0; 4]; + reader.read_exact(&mut bytes); + bytes + }; + i32::from_le_bytes(bytes) + }; + let b = { + let bytes = { + let mut bytes = [0; 4]; + reader.read_exact(&mut bytes); + bytes + }; + i32::from_le_bytes(bytes) + }; + crate::data::StructWithInt32 { a: a, b: b } +} + +pub fn encode_struct_with_int_64(value: &crate::data::StructWithInt64, writer: &mut impl std::io::Write) { + todo!(); + todo!(); +} + +pub fn decode_struct_with_int_64(reader: &mut impl std::io::Read) -> crate::data::StructWithInt64 { + let a = todo!(); + let b = todo!(); + crate::data::StructWithInt64 { a: a, b: b } +} + +pub fn encode_struct_with_string(value: &crate::data::StructWithString, writer: &mut impl std::io::Write) { + todo!(); + todo!(); +} + +pub fn decode_struct_with_string(reader: &mut impl std::io::Read) -> crate::data::StructWithString { + let a = todo!(); + let b = todo!(); + crate::data::StructWithString { a: a, b: b } +} + +pub fn encode_outer_struct(value: &crate::data::OuterStruct, writer: &mut impl std::io::Write) { + todo!(); + writer.write_all(&value.c.to_le_bytes()); +} + +pub fn decode_outer_struct(reader: &mut impl std::io::Read) -> crate::data::OuterStruct { + let inner = todo!(); + let c = { + let bytes = { + let mut bytes = [0; 4]; + reader.read_exact(&mut bytes); + bytes + }; + i32::from_le_bytes(bytes) + }; + crate::data::OuterStruct { inner: inner, c: c } +} + +pub fn encode_inner_struct(value: &crate::data::InnerStruct, writer: &mut impl std::io::Write) { + writer.write_all(&value.a.to_le_bytes()); + writer.write_all(&value.b.to_le_bytes()); +} + +pub fn decode_inner_struct(reader: &mut impl std::io::Read) -> crate::data::InnerStruct { + let a = { + let bytes = { + let mut bytes = [0; 4]; + reader.read_exact(&mut bytes); + bytes + }; + i32::from_le_bytes(bytes) + }; + let b = { + let bytes = { + let mut bytes = [0; 4]; + reader.read_exact(&mut bytes); + bytes + }; + i32::from_le_bytes(bytes) + }; + crate::data::InnerStruct { a: a, b: b } +} + +pub fn encode_struct_with_list(value: &crate::data::StructWithList, writer: &mut impl std::io::Write) { + todo!(); + todo!(); +} + +pub fn decode_struct_with_list(reader: &mut impl std::io::Read) -> crate::data::StructWithList { + let a = todo!(); + let b = todo!(); + crate::data::StructWithList { a: a, b: b } +} + +pub fn encode_struct_with_option(value: &crate::data::StructWithOption, writer: &mut impl std::io::Write) { + todo!(); + todo!(); +} + +pub fn decode_struct_with_option(reader: &mut impl std::io::Read) -> crate::data::StructWithOption { + let a = todo!(); + let b = todo!(); + crate::data::StructWithOption { a: a, b: b } +} + +pub fn encode_variant_one(value: &crate::data::VariantOne, writer: &mut impl std::io::Write) { + writer.write_all(&value.a.to_le_bytes()); +} + +pub fn decode_variant_one(reader: &mut impl std::io::Read) -> crate::data::VariantOne { + let a = { + let bytes = { + let mut bytes = [0; 4]; + reader.read_exact(&mut bytes); + bytes + }; + i32::from_le_bytes(bytes) + }; + crate::data::VariantOne { a: a } +} + +pub fn encode_variant_two(value: &crate::data::VariantTwo, writer: &mut impl std::io::Write) { + todo!(); +} + +pub fn decode_variant_two(reader: &mut impl std::io::Read) -> crate::data::VariantTwo { + let a = todo!(); + crate::data::VariantTwo { a: a } +} diff --git a/examples/10-transient-0/output/rust/src/lib.rs b/examples/10-transient-0/output/rust/src/lib.rs new file mode 100644 index 0000000..865555e --- /dev/null +++ b/examples/10-transient-0/output/rust/src/lib.rs @@ -0,0 +1,33 @@ +pub mod data; + +#[cfg(test)] +mod test { + use std::io::Cursor; + use super::data::StructWithInt32; + use super::data::StructWithInt64; + + #[test] + fn struct_with_int_32() { + let value = StructWithInt32 { a: 10, b: 25 }; + + assert_round_trip_encoding( + value, + super::data::transient_0::encode_struct_with_int_32, + super::data::transient_0::decode_struct_with_int_32, + ); + } + + fn assert_round_trip_encoding<T: std::cmp::PartialEq + std::fmt::Debug>( + value: T, + encode: impl Fn(&T, &mut Cursor<Vec<u8>>), + decode: impl Fn(&mut Cursor<Vec<u8>>) -> T, + ) { + let mut bytes = Cursor::new(Vec::new()); + encode(&value, &mut bytes); + + bytes.set_position(0); + let decoded_value = decode(&mut bytes); + + assert_eq!(value, decoded_value); + } +} diff --git a/examples/10-transient-0/output/rust/src/main.rs b/examples/10-transient-0/output/rust/src/main.rs deleted file mode 100644 index 32b5edf..0000000 --- a/examples/10-transient-0/output/rust/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod data; - -#[cfg(test)] -mod test { -} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java index 1b05b47..db572fb 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java @@ -32,15 +32,15 @@ public class RustGenerator { } } - public RustType generateRustTypeExpression(Type type) { + public RustPath generateRustTypeExpression(Type type) { return switch (type) { case ConstructedNativeType constructedNativeType -> { // TODO: avoid cast - var rustType = (RustPath) generateRustTypeExpression( + var rustType = generateRustTypeExpression( constructedNativeType.constructor().genericType() ); var rustArgs = constructedNativeType.args().stream() - .map(arg -> generateRustTypeExpression(arg)) + .<RustType>map(arg -> generateRustTypeExpression(arg)) .toList(); yield rustType.withArgs(rustArgs); } @@ -82,7 +82,7 @@ public class RustGenerator { }; } - private RustType generateTypePath(NamespaceName namespaceName, String typeName) { + private RustPath generateTypePath(NamespaceName namespaceName, String typeName) { var segments = new ArrayList<RustPathSegment>(); segments.add(RustPathSegment.crate()); for (var moduleName : this.namespaceNameToRustCrateModulePath(namespaceName)) { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustNativeTypeConfig.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustNativeTypeConfig.java index 27a6dc8..584527f 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustNativeTypeConfig.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustNativeTypeConfig.java @@ -1,8 +1,8 @@ package org.zwobble.hobgoblin.compiler.output.generators.rust; -import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustType; +import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustPath; public record RustNativeTypeConfig( - RustType type + RustPath type ) { } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java index 6f53ba2..d67ccd6 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java @@ -1,13 +1,14 @@ package org.zwobble.hobgoblin.compiler.output.generators.rusttransient0; -import org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceNode; +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.rust.RustGenerator; import org.zwobble.hobgoblin.compiler.output.generators.rust.RustGeneratorConfig; -import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustIdentifier; -import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.RustModule; +import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.*; import org.zwobble.hobgoblin.compiler.typechecker.TypesInfo; +import org.zwobble.hobgoblin.compiler.types.Type; import org.zwobble.json5.reader.Json5ObjectReader; import java.io.IOException; @@ -15,10 +16,15 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; public class RustTransient0Generator implements Generator { public static final String NAME = "rust-transient-0"; + private static final RustIdentifier VALUE_NAME = RustIdentifier.of("value"); + private static final RustIdentifier READER_NAME = RustIdentifier.of("reader"); + private static final RustIdentifier WRITER_NAME = RustIdentifier.of("writer"); + public static OutputConfig parseGeneratorConfig( Path projectRoot, Json5ObjectReader output, @@ -59,7 +65,203 @@ public class RustTransient0Generator implements Generator { return new RustModule( moduleName, + namespace.body().stream() + .flatMap(statement -> this.generateNamespaceStatement(statement)) + .toList() + ); + } + + private Stream<RustItem> generateNamespaceStatement( + TypedNamespaceStatementNode statement + ) { + return switch (statement) { + case TypedEnumDefinitionNode enumDefinition -> { + yield Stream.of(); + } + + case TypedNativeTypeDefinitionNode nativeTypeDefinition -> { + yield Stream.of(); + } + + case TypedStructDefinitionNode structDefinition -> { + // TODO: remove cast + var rustType = this.rustGenerator.generateRustTypeExpression(structDefinition.type()); + + yield Stream.of( + generateEncodeFunction( + structDefinition.type(), + new RustBlockExpression( + structDefinition.fields().stream() + .flatMap(field -> generateEncode( + new RustFieldExpression( + RustPath.of(VALUE_NAME), + this.rustGenerator.generateFieldName(field.name()) + ), + field.type().value() + ).stream()) + .toList(), + Optional.empty() + ) + ), + generateDecodeFunction( + structDefinition.type(), + new RustBlockExpression( + structDefinition.fields().stream() + .<RustStatement>map(field -> new RustLetStatement( + this.rustGenerator.generateFieldName(field.name()), + false, + generateDecode(field.type().value()) + )) + .toList(), + Optional.of(new RustStructExpression( + rustType, + structDefinition.fields().stream() + .map(field -> new RustStructExprField( + this.rustGenerator.generateFieldName(field.name()), + RustPath.of(this.rustGenerator.generateFieldName(field.name())) + )) + .toList() + )) + ) + ) + ); + } + + case TypedSumDefinitionNode sumDefinition -> { + yield Stream.of(); + } + }; + } + + private RustItem generateEncodeFunction( + Type type, + RustBlockExpression body + ) { + var rustType = this.rustGenerator.generateRustTypeExpression(type); + + return new RustFunction( + Optional.of(RustVisibility.PUB), + encodeMethodName(type), + List.of( + new RustFunctionParam(VALUE_NAME, new RustSharedReferenceType(rustType)), + new RustFunctionParam(WRITER_NAME, new RustMutableReferenceType(new RustImplTraitType(RustPath.of("std", "io", "Write")))) + ), + Optional.empty(), + Optional.of(body) + ); + } + + private RustItem generateDecodeFunction( + Type type, + RustBlockExpression body + ) { + var rustType = this.rustGenerator.generateRustTypeExpression(type); + + return new RustFunction( + Optional.of(RustVisibility.PUB), + decodeMethodName(type), + List.of( + new RustFunctionParam(READER_NAME, new RustMutableReferenceType(new RustImplTraitType(RustPath.of("std", "io", "Read")))) + ), + Optional.of(rustType), + Optional.of(body) + ); + } + + private List<RustStatement> generateEncode(RustExpression value, Type type) { + if (type.equals(NativeTypes.INT_32)) { + return List.of( + new RustExpressionStatement( + generateWriterWrite(new RustPrefixExpression( + RustPrefixOperator.BORROW, + new RustCallExpression( + new RustFieldExpression( + value, + RustIdentifier.of("to_le_bytes") + ), + List.of() + ) + )) + ) + ); + } else { + return List.of(new RustExpressionStatement(generateTodo())); + } + } + + private RustExpression generateDecode(Type type) { + if (type.equals(NativeTypes.INT_32)) { + var bytes = RustIdentifier.of("bytes"); + return new RustBlockExpression( + List.of( + new RustLetStatement( + bytes, + false, + generateReaderReadExact(4) + ) + ), + Optional.of(new RustCallExpression( + RustPath.of("i32", "from_le_bytes"), + List.of(RustPath.of(bytes)) + )) + ); + } else { + return generateTodo(); + } + } + + private RustExpression generateWriterWrite(RustExpression valueToWrite) { + return new RustCallExpression( + new RustFieldExpression( + RustPath.of(WRITER_NAME), + RustIdentifier.of("write_all") + ), + List.of(valueToWrite) + ); + } + + private RustExpression generateReaderReadExact(int length) { + var bytes = RustIdentifier.of("bytes"); + + return new RustBlockExpression( + List.of( + new RustLetStatement( + bytes, + true, + new RustArrayRepeatExpression( + new RustIntegerLiteral(0), + new RustIntegerLiteral(length) + ) + ), + new RustExpressionStatement(new RustCallExpression( + new RustFieldExpression( + RustPath.of(READER_NAME), + RustIdentifier.of("read_exact") + ), + List.of( + new RustPrefixExpression( + RustPrefixOperator.BORROW_MUTABLE, + RustPath.of(bytes) + ) + ) + )) + ), + Optional.of(RustPath.of(bytes)) + ); + } + + private RustExpression generateTodo() { + return new RustCallExpression( + RustPath.of("todo!"), List.of() ); } + + private RustIdentifier encodeMethodName(Type type) { + return this.rustGenerator.generateFieldName("encode" + type.name()); + } + + private RustIdentifier decodeMethodName(Type type) { + return this.rustGenerator.generateFieldName("decode" + type.name()); + } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustPath.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustPath.java index 895e194..315271b 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustPath.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/ast/RustPath.java @@ -55,7 +55,7 @@ public record RustPath(java.util.List<org.zwobble.hobgoblin.compiler.output.lang return new RustPath(segments); } - public static RustType of(RustIdentifier... names) { + public static RustPath of(RustIdentifier... names) { var segments = Arrays.stream(names) .map(name -> new RustPathSegment( new RustPathIdentSegmentIdentifier(name), @@ -65,7 +65,7 @@ public record RustPath(java.util.List<org.zwobble.hobgoblin.compiler.output.lang return new RustPath(segments); } - public static RustType crate(String... names) { + public static RustPath crate(String... names) { return qualified(RustPathSegment.crate(), names); } |
