From cffab4d76d6358dde9b708691d96852d39f9e977 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 3 Jul 2026 15:48:13 +0100 Subject: Add subsections for Rust items --- hobgoblin/src/output/lang/rust/ast.hob | 62 +++--- .../compiler/output/lang/rust/RustWriter.java | 124 ++++++------ .../compiler/output/lang/rust/RustWriterTests.java | 208 +++++++++++---------- 3 files changed, 211 insertions(+), 183 deletions(-) diff --git a/hobgoblin/src/output/lang/rust/ast.hob b/hobgoblin/src/output/lang/rust/ast.hob index d230543..e6d1f11 100644 --- a/hobgoblin/src/output/lang/rust/ast.hob +++ b/hobgoblin/src/output/lang/rust/ast.hob @@ -15,6 +15,39 @@ sum RustItem { variant RustStructStruct; } +// === Functions === + +struct RustFunction { + field name: RustIdentifier; + field params: List[RustFunctionParam]; + field returnType: Option[RustType]; + field body: Option[RustBlockExpression]; +} + +struct RustFunctionParam { + field name: RustIdentifier; + field type: RustType; +} + +// === Structs === + +struct RustStructStruct { + field name: RustIdentifier; + field fields: List[RustStructField]; + field docComment: DocComment; +} + +struct RustStructField { + field name: RustIdentifier; + field type: RustType; +} + +struct RustTupleField { + field type: RustType; +} + +// === Enumerations === + struct RustEnum { field name: RustIdentifier; field variants: List[RustEnumVariant]; @@ -38,42 +71,19 @@ struct RustEnumVariantTuple { field fields: List[RustTupleField]; } -struct RustFunction { - field name: RustIdentifier; - field params: List[RustFunctionParam]; - field returnType: Option[RustType]; - field body: Option[RustBlockExpression]; -} - -struct RustFunctionParam { - field name: RustIdentifier; - field type: RustType; -} +// === Implementations === struct RustInherentImpl { field type: RustType; field items: List[RustAssociatedItem]; } +// === Associated items === + sum RustAssociatedItem { variant RustFunction; } -struct RustStructStruct { - field name: RustIdentifier; - field fields: List[RustStructField]; - field docComment: DocComment; -} - -struct RustStructField { - field name: RustIdentifier; - field type: RustType; -} - -struct RustTupleField { - field type: RustType; -} - // == Statements == sum RustStatement { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java index b732390..6cd5860 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java @@ -73,6 +73,70 @@ public class RustWriter implements AutoCloseable { } } + // === Functions === + + private void writeFunction(RustFunction function) throws IOException { + this.writer.write("pub fn "); + this.writeIdentifier(function.name()); + this.writer.write("("); + + writeWithSeparator( + function.params(), + param -> { + this.writeIdentifier(param.name()); + this.writer.write(": "); + this.writeType(param.type()); + }, + () -> { + this.writer.write(", "); + } + ); + + this.writer.write(")"); + + if (function.returnType().isPresent()) { + this.writer.write(" "); + this.writer.write("-> "); + this.writeType(function.returnType().get()); + } + + if (function.body().isPresent()) { + this.writer.write(" "); + this.writeBlockExpression(function.body().get()); + } else { + this.writer.write(";"); + } + } + + // === Structs === + + private void writeStructStruct(RustStructStruct structStruct) throws IOException { + this.writeDocComment(structStruct.docComment()); + this.writer.write("pub struct "); + this.writeIdentifier(structStruct.name()); + this.writer.write(" {"); + + if (!structStruct.fields().isEmpty()) { + this.writer.indent(); + + for (var field : structStruct.fields()) { + this.writer.newLine(); + this.writer.write("pub "); + this.writeIdentifier(field.name()); + this.writer.write(": "); + this.writeType(field.type()); + this.writer.write(","); + } + + this.writer.dedent(); + } + + this.writer.newLine(); + this.writer.write("}"); + } + + // === Enumerations === + private void writeEnum(RustEnum enum_) throws IOException { this.writeDocComment(enum_.docComment()); this.writer.write("pub enum "); @@ -112,38 +176,7 @@ public class RustWriter implements AutoCloseable { this.writer.write("}"); } - private void writeFunction(RustFunction function) throws IOException { - this.writer.write("pub fn "); - this.writeIdentifier(function.name()); - this.writer.write("("); - - writeWithSeparator( - function.params(), - param -> { - this.writeIdentifier(param.name()); - this.writer.write(": "); - this.writeType(param.type()); - }, - () -> { - this.writer.write(", "); - } - ); - - this.writer.write(")"); - - if (function.returnType().isPresent()) { - this.writer.write(" "); - this.writer.write("-> "); - this.writeType(function.returnType().get()); - } - - if (function.body().isPresent()) { - this.writer.write(" "); - this.writeBlockExpression(function.body().get()); - } else { - this.writer.write(";"); - } - } + // === Implementations === private void writeInherentImpl(RustInherentImpl inherentImpl) throws IOException { this.writer.write("impl "); @@ -173,6 +206,8 @@ public class RustWriter implements AutoCloseable { this.writer.write("}"); } + // === Associated items === + void writeAssociatedItem(RustAssociatedItem item) throws IOException { switch (item) { case RustFunction function -> { @@ -181,31 +216,6 @@ public class RustWriter implements AutoCloseable { } } - private void writeStructStruct(RustStructStruct structStruct) throws IOException { - this.writeDocComment(structStruct.docComment()); - this.writer.write("pub struct "); - this.writeIdentifier(structStruct.name()); - this.writer.write(" {"); - - if (!structStruct.fields().isEmpty()) { - this.writer.indent(); - - for (var field : structStruct.fields()) { - this.writer.newLine(); - this.writer.write("pub "); - this.writeIdentifier(field.name()); - this.writer.write(": "); - this.writeType(field.type()); - this.writer.write(","); - } - - this.writer.dedent(); - } - - this.writer.newLine(); - this.writer.write("}"); - } - // == Statements == void writeStatement(RustStatement statement) throws IOException { diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java index fe82fb9..3ed6e3e 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriterTests.java @@ -38,6 +38,113 @@ public class RustWriterTests { // == Items == + // === Functions === + + @Test + public void emptyFunction() throws IOException { + var rust = RustFunction.arbitrary() + .withName(new RustIdentifier("width")) + .build(); + + var string = write(writer -> writer.writeAssociatedItem(rust)); + + assertThat(string, equalTo(""" + pub fn width();""")); + } + + @Test + public void functionWithParams() throws IOException { + var rust = RustFunction.arbitrary() + .withName(new RustIdentifier("area")) + .addParam(new RustFunctionParam(new RustIdentifier("width"), RustTypePath.of("i32"))) + .addParam(new RustFunctionParam(new RustIdentifier("height"), RustTypePath.of("u64"))) + .build(); + + var string = write(writer -> writer.writeAssociatedItem(rust)); + + assertThat(string, equalTo(""" + pub fn area(width: i32, height: u64);""")); + } + + @Test + public void functionWithReturnType() throws IOException { + var rust = RustFunction.arbitrary() + .withName(new RustIdentifier("area")) + .withReturnType(Optional.of(RustTypePath.of("i32"))) + .build(); + + var string = write(writer -> writer.writeAssociatedItem(rust)); + + assertThat(string, equalTo(""" + pub fn area() -> i32;""")); + } + + @Test + public void functionWithBody() throws IOException { + var rust = RustFunction.arbitrary() + .withName(new RustIdentifier("predicate")) + .withBody(RustBlockExpression.arbitrary() + .withFinalOperand(RustBoolLiteral.arbitrary().withValue(false).build()) + .build()) + .build(); + + var string = write(writer -> writer.writeAssociatedItem(rust)); + + assertThat(string, equalTo(""" + pub fn predicate() { + false + }""")); + } + + // === Structs === + + @Test + public void emptyStruct() throws IOException { + var rust = RustStructStruct.arbitrary().withName(new RustIdentifier("Rectangle")).build(); + + var string = write(writer -> writer.writeItem(rust)); + + assertThat(string, equalTo(""" + pub struct Rectangle { + }""")); + } + + @Test + public void structWithFields() throws IOException { + var rust = RustStructStruct.arbitrary() + .withName(new RustIdentifier("Rectangle")) + .withFields(List.of( + new RustStructField(new RustIdentifier("width"), RustTypePath.of("i32")), + new RustStructField(new RustIdentifier("height"), RustTypePath.of("i32")) + )) + .build(); + + var string = write(writer -> writer.writeItem(rust)); + + assertThat(string, equalTo(""" + pub struct Rectangle { + pub width: i32, + pub height: i32, + }""")); + } + + @Test + public void structWithDocComment() throws IOException { + var rust = RustStructStruct.arbitrary() + .withName(new RustIdentifier("Rectangle")) + .withDocComment(new DocComment("A 2D rectangle.")) + .build(); + + var string = write(writer -> writer.writeItem(rust)); + + assertThat(string, equalTo(""" + /// A 2D rectangle. + pub struct Rectangle { + }""")); + } + + // === Enumerations === + @Test public void emptyEnum() throws IOException { var rust = RustEnum.arbitrary().withName(new RustIdentifier("Shape")).build(); @@ -127,61 +234,7 @@ public class RustWriterTests { }""")); } - @Test - public void emptyFunction() throws IOException { - var rust = RustFunction.arbitrary() - .withName(new RustIdentifier("width")) - .build(); - - var string = write(writer -> writer.writeAssociatedItem(rust)); - - assertThat(string, equalTo(""" - pub fn width();""")); - } - - @Test - public void functionWithParams() throws IOException { - var rust = RustFunction.arbitrary() - .withName(new RustIdentifier("area")) - .addParam(new RustFunctionParam(new RustIdentifier("width"), RustTypePath.of("i32"))) - .addParam(new RustFunctionParam(new RustIdentifier("height"), RustTypePath.of("u64"))) - .build(); - - var string = write(writer -> writer.writeAssociatedItem(rust)); - - assertThat(string, equalTo(""" - pub fn area(width: i32, height: u64);""")); - } - - @Test - public void functionWithReturnType() throws IOException { - var rust = RustFunction.arbitrary() - .withName(new RustIdentifier("area")) - .withReturnType(Optional.of(RustTypePath.of("i32"))) - .build(); - - var string = write(writer -> writer.writeAssociatedItem(rust)); - - assertThat(string, equalTo(""" - pub fn area() -> i32;""")); - } - - @Test - public void functionWithBody() throws IOException { - var rust = RustFunction.arbitrary() - .withName(new RustIdentifier("predicate")) - .withBody(RustBlockExpression.arbitrary() - .withFinalOperand(RustBoolLiteral.arbitrary().withValue(false).build()) - .build()) - .build(); - - var string = write(writer -> writer.writeAssociatedItem(rust)); - - assertThat(string, equalTo(""" - pub fn predicate() { - false - }""")); - } + // === Implementations === @Test public void emptyInherentImpl() throws IOException { @@ -212,51 +265,6 @@ public class RustWriterTests { }""")); } - @Test - public void emptyStruct() throws IOException { - var rust = RustStructStruct.arbitrary().withName(new RustIdentifier("Rectangle")).build(); - - var string = write(writer -> writer.writeItem(rust)); - - assertThat(string, equalTo(""" - pub struct Rectangle { - }""")); - } - - @Test - public void structWithFields() throws IOException { - var rust = RustStructStruct.arbitrary() - .withName(new RustIdentifier("Rectangle")) - .withFields(List.of( - new RustStructField(new RustIdentifier("width"), RustTypePath.of("i32")), - new RustStructField(new RustIdentifier("height"), RustTypePath.of("i32")) - )) - .build(); - - var string = write(writer -> writer.writeItem(rust)); - - assertThat(string, equalTo(""" - pub struct Rectangle { - pub width: i32, - pub height: i32, - }""")); - } - - @Test - public void structWithDocComment() throws IOException { - var rust = RustStructStruct.arbitrary() - .withName(new RustIdentifier("Rectangle")) - .withDocComment(new DocComment("A 2D rectangle.")) - .build(); - - var string = write(writer -> writer.writeItem(rust)); - - assertThat(string, equalTo(""" - /// A 2D rectangle. - pub struct Rectangle { - }""")); - } - // == Statements == @Test -- cgit v1.2.3