diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-07-07 19:36:00 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-07-07 19:36:00 +0100 |
| commit | 9bc1704339c846de19f68543b05901ee18d1befc (patch) | |
| tree | 08d609a10e972bc2a9765973bf945dcc22c0e389 /src/main/java | |
| parent | 2536899969733a81a0001eb2da0d1d478c092e4b (diff) | |
Reorder Java writer body to match AST declarations
Diffstat (limited to 'src/main/java')
| -rw-r--r-- | src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java | 670 |
1 files changed, 346 insertions, 324 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java index 9eea1ca..38dde1f 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java @@ -39,48 +39,7 @@ public class JavaWriter implements AutoCloseable { this.writer.close(); } - private void writeBinaryOperation(JavaBinaryOperation binaryOperation) throws IOException { - var precedence = precedence(binaryOperation); - var isLeftAssociative = isLeftAssociative(binaryOperation.operator()); - - this.writeSubExpression( - binaryOperation.left(), - precedence, - isLeftAssociative - ); - this.writer.write(" "); - this.writer.write(switch (binaryOperation.operator()) { - case ASSIGN -> "="; - case BITWISE_AND -> "&"; - case BITWISE_OR -> "|"; - case EQUAL_TO -> "=="; - case LEFT_SHIFT -> "<<"; - case NOT_EQUAL_TO -> "!="; - case UNSIGNED_RIGHT_SHIFT -> ">>>"; - }); - this.writer.write(" "); - this.writeSubExpression( - binaryOperation.right(), - precedence, - !isLeftAssociative - ); - } - - private boolean isLeftAssociative(JavaBinaryOperator operator) { - return switch (operator) { - case ASSIGN -> false; - case BITWISE_AND -> true; - case BITWISE_OR -> true; - case EQUAL_TO -> true; - case LEFT_SHIFT -> true; - case NOT_EQUAL_TO -> true; - case UNSIGNED_RIGHT_SHIFT -> true; - }; - } - - private void writeBooleanLiteral(JavaBooleanLiteral booleanLiteral) throws IOException { - this.writer.write(booleanLiteral.value() ? "true" : "false"); - } + // == Compilation units == public void writeCompilationUnit(JavaCompilationUnit compilationUnit) throws IOException { this.writer.writeHeader(); @@ -97,6 +56,8 @@ public class JavaWriter implements AutoCloseable { this.writer.newLine(); } + // == Type declarations == + void writeTypeDeclaration(JavaTypeDeclaration typeDeclaration) throws IOException { switch (typeDeclaration) { case JavaClassDeclaration classDeclaration -> { @@ -117,71 +78,148 @@ public class JavaWriter implements AutoCloseable { } } - void writeBlock(JavaBlock block) throws IOException { - writeWithSeparator( - block.statements(), - this::writeBlockStatement, - () -> this.writer.newLine() - ); - } + private void writeClassDeclaration(JavaClassDeclaration classDeclaration) throws IOException { + writeDocComment(classDeclaration.docComment()); - void writeBlockStatement(JavaBlockStatement statement) throws IOException { - switch (statement) { - case JavaExpressionStatement expressionStatement -> { - writeExpressionStatement(expressionStatement); - } + this.writer.write("public class "); + this.writeIdentifier(classDeclaration.name()); + this.writer.write(" "); - case JavaLocalVariableDeclaration localVariableDeclaration -> { - writeLocalVariableDeclaration(localVariableDeclaration); - } + this.writeImplements(classDeclaration.implementsTypes()); - case JavaReturn returnStatement -> { - writeReturnStatement(returnStatement); - } + this.writer.write("{"); + this.writer.indent(); + + for (var bodyDeclaration: classDeclaration.body()) { + writer.newLine(); + writeClassBodyDeclaration(bodyDeclaration); + writer.newLine(); } - } - private void writeCast(JavaCast cast) throws IOException { - this.writer.write("("); - this.writeTypeRef(cast.type()); - this.writer.write(") "); + this.writer.newLine(); + this.writeCustomArea(classDeclaration.customArea()); - this.writeSubExpression(cast.operand(), JavaPrecedence.PRIMARY, true); + this.writer.dedent(); + this.writer.write("}"); } - private void writeDocComment(DocComment docComment) throws IOException { - for (var line : docComment.lines()) { - this.writer.write("/// "); - this.writer.write(line); - this.writer.write("\n"); + private void writeEnumDeclaration(JavaEnumDeclaration enumDeclaration) throws IOException { + this.writeDocComment(enumDeclaration.docComment()); + this.writer.write("public enum "); + this.writeIdentifier(enumDeclaration.name()); + this.writer.write(" {"); + this.writer.indent(); + this.writer.newLine(); + + if (!enumDeclaration.constants().isEmpty()) { + writeWithSeparator( + enumDeclaration.constants(), + constant -> this.writeIdentifier(constant.name()), + () -> { + this.writer.write(","); + this.writer.newLine(); + } + ); + this.writer.newLine(); + this.writer.newLine(); } + + this.writeCustomArea(enumDeclaration.customArea()); + this.writer.dedent(); + this.writer.write("}"); } - private void writeClassDeclaration(JavaClassDeclaration classDeclaration) throws IOException { - writeDocComment(classDeclaration.docComment()); + private void writeInterfaceDeclaration(JavaInterfaceDeclaration interfaceDeclaration) throws IOException { + writeDocComment(interfaceDeclaration.docComment()); - this.writer.write("public class "); - this.writeIdentifier(classDeclaration.name()); + this.writer.write("public "); + switch (interfaceDeclaration.openness()) { + case OPEN -> { + } + case SEALED -> { + this.writer.write("sealed "); + } + } + this.writer.write("interface "); + this.writeIdentifier(interfaceDeclaration.name()); this.writer.write(" "); - this.writeImplements(classDeclaration.implementsTypes()); + if (!interfaceDeclaration.permitsTypes().isEmpty()) { + writer.write("permits "); + writeWithSeparator( + interfaceDeclaration.permitsTypes(), + this::writeTypeRef, + () -> writer.write(", ") + ); + writer.write(" "); + } this.writer.write("{"); this.writer.indent(); - for (var bodyDeclaration: classDeclaration.body()) { + for (var bodyDeclaration: interfaceDeclaration.body()) { writer.newLine(); writeClassBodyDeclaration(bodyDeclaration); writer.newLine(); } this.writer.newLine(); - this.writeCustomArea(classDeclaration.customArea()); + this.writeCustomArea(interfaceDeclaration.customArea()); + this.writer.dedent(); + this.writer.write("}"); + } + + private void writeRecordDeclaration(JavaRecordDeclaration recordDeclaration) throws IOException { + writeDocComment(recordDeclaration.docComment()); + + this.writer.write("public record "); + this.writeIdentifier(recordDeclaration.name()); + this.writer.write("("); + + writeWithSeparator( + recordDeclaration.components(), + component -> { + writeTypeRef(component.type()); + writer.write(" "); + this.writeIdentifier(component.name()); + }, + () -> writer.write(", ") + ); + + writer.write(") "); + + this.writeImplements(recordDeclaration.implementsTypes()); + + this.writer.write("{"); + this.writer.indent(); + + for (var bodyDeclaration : recordDeclaration.body()) { + writer.newLine(); + writeClassBodyDeclaration(bodyDeclaration); + writer.newLine(); + } + + this.writer.newLine(); + this.writeCustomArea(recordDeclaration.customArea()); this.writer.dedent(); this.writer.write("}"); } + private void writeImplements(List<JavaTypeRef> implementsTypes) throws IOException { + if (!implementsTypes.isEmpty()) { + this.writer.write("implements "); + writeWithSeparator( + implementsTypes, + this::writeTypeRef, + () -> writer.write(", ") + ); + writer.write(" "); + } + } + + // == Type declaration bodies == + void writeClassBodyDeclaration(JavaClassBodyDeclaration bodyDeclaration) throws IOException { switch (bodyDeclaration) { case JavaConstructorDeclaration constructorDeclaration -> { @@ -231,40 +269,173 @@ public class JavaWriter implements AutoCloseable { this.writer.write("}"); } - private void writeCustomArea(JavaCustomArea customArea) throws IOException { - var customAreaName = new StringWriter(); - writeTypeRef(customArea.type(), customAreaName::write); - customAreaName.write(" "); - customAreaName.write(customArea.name()); - this.writer.writeCustomArea(customAreaName.toString()); + private void writeFieldDeclaration(JavaFieldDeclaration fieldDeclaration) throws IOException { + this.writeVisibility(fieldDeclaration.visibility()); + this.writer.write(" final "); + this.writeTypeRef(fieldDeclaration.type()); + this.writer.write(" "); + this.writeIdentifier(fieldDeclaration.name()); + this.writer.write(";"); } - private void writeEnumDeclaration(JavaEnumDeclaration enumDeclaration) throws IOException { - this.writeDocComment(enumDeclaration.docComment()); - this.writer.write("public enum "); - this.writeIdentifier(enumDeclaration.name()); - this.writer.write(" {"); - this.writer.indent(); - this.writer.newLine(); + void writeMethodDeclaration(JavaMethodDeclaration method) throws IOException { + writeVisibility(method.visibility()); + this.writer.write(" "); - if (!enumDeclaration.constants().isEmpty()) { + switch (method.kind()) { + case STATIC -> { + this.writer.write("static "); + } + case INSTANCE -> { + } + } + + writeTypeRef(method.returnType()); + this.writer.write(" "); + this.writeIdentifier(method.name()); + this.writer.write("("); + + writeWithSeparator( + method.params(), + param -> { + writeTypeRef(param.type()); + writer.write(" "); + this.writeIdentifier(param.name()); + }, + () -> writer.write(", ") + ); + + this.writer.write(")"); + + if (!method.throws_().isEmpty()) { + this.writer.write(" throws "); writeWithSeparator( - enumDeclaration.constants(), - constant -> this.writeIdentifier(constant.name()), - () -> { - this.writer.write(","); - this.writer.newLine(); - } + method.throws_(), + exceptionType -> writeTypeRef(exceptionType), + () -> this.writer.write(", ") ); + } + + if (method.body().isEmpty()) { + this.writer.write(";"); + } else { + this.writer.write(" {"); + this.writer.indent(); + if (!method.body().get().isEmpty()) { + this.writer.newLine(); + writeBlock(method.body().get()); + } + this.writer.dedent(); this.writer.newLine(); - this.writer.newLine(); + this.writer.write("}"); } + } - this.writeCustomArea(enumDeclaration.customArea()); - this.writer.dedent(); - this.writer.write("}"); + // == Methods == + + // == Type references == + + void writeTypeRef(JavaTypeRef type) throws IOException { + writeTypeRef(type, writer::write); + } + + private static void writeTypeRef(JavaTypeRef type, WriteString writer) throws IOException { + for (var part : type.packageName().parts()) { + writeIdentifier(part, writer); + writer.write("."); + } + + writeWithSeparator( + type.typeNames(), + typeName -> writeIdentifier(typeName, writer), + () -> writer.write(".") + ); + + if (!type.args().isEmpty()) { + writer.write("<"); + writeWithSeparator( + type.args(), + arg -> writeTypeArg(arg, writer), + () -> writer.write(", ") + ); + writer.write(">"); + } } + private static void writeTypeArg(JavaTypeArg arg, WriteString writer) throws IOException { + switch (arg.variance()) { + case INVARIANT -> { + // Do nothing. + } + case SUPER -> { + writer.write("? super "); + } + } + writeTypeRef(arg.type(), writer); + } + + // == Visibility == + + private void writeVisibility(JavaVisibility visibility) throws IOException { + this.writer.write(switch (visibility) { + case PUBLIC -> "public"; + case PRIVATE -> "private"; + }); + } + + // == Statements == + + void writeBlock(JavaBlock block) throws IOException { + writeWithSeparator( + block.statements(), + this::writeBlockStatement, + () -> this.writer.newLine() + ); + } + + void writeBlockStatement(JavaBlockStatement statement) throws IOException { + switch (statement) { + case JavaExpressionStatement expressionStatement -> { + writeExpressionStatement(expressionStatement); + } + + case JavaLocalVariableDeclaration localVariableDeclaration -> { + writeLocalVariableDeclaration(localVariableDeclaration); + } + + case JavaReturn returnStatement -> { + writeReturnStatement(returnStatement); + } + } + } + + private void writeExpressionStatement(JavaExpressionStatement expressionStatement) throws IOException { + writeTopLevelExpression(expressionStatement.expression()); + this.writer.write(";"); + } + + private void writeLocalVariableDeclaration(JavaLocalVariableDeclaration localVariableDeclaration) throws IOException { + if (localVariableDeclaration.type().isPresent()) { + this.writeTypeRef(localVariableDeclaration.type().get()); + } else { + this.writer.write("var"); + } + + this.writer.write(" "); + this.writeIdentifier(localVariableDeclaration.name()); + this.writer.write(" = "); + this.writeTopLevelExpression(localVariableDeclaration.initializer()); + this.writer.write(";"); + } + + private void writeReturnStatement(JavaReturn returnStatement) throws IOException { + this.writer.write("return "); + writeTopLevelExpression(returnStatement.value()); + this.writer.write(";"); + } + + // == Expressions == + void writeTopLevelExpression(JavaExpression expression) throws IOException { switch (expression) { case JavaBinaryOperation binaryOperation -> { @@ -378,102 +549,65 @@ public class JavaWriter implements AutoCloseable { }; } - private void writeExpressionStatement(JavaExpressionStatement expressionStatement) throws IOException { - writeTopLevelExpression(expressionStatement.expression()); - this.writer.write(";"); - } - - private void writeFieldAccess(JavaFieldAccess fieldAccess) throws IOException { - this.writeSubExpression(fieldAccess.receiver(), JavaPrecedence.PRIMARY, true); - this.writer.write("."); - this.writeIdentifier(fieldAccess.fieldName()); - } + private void writeBinaryOperation(JavaBinaryOperation binaryOperation) throws IOException { + var precedence = precedence(binaryOperation); + var isLeftAssociative = isLeftAssociative(binaryOperation.operator()); - private void writeFieldDeclaration(JavaFieldDeclaration fieldDeclaration) throws IOException { - this.writeVisibility(fieldDeclaration.visibility()); - this.writer.write(" final "); - this.writeTypeRef(fieldDeclaration.type()); + this.writeSubExpression( + binaryOperation.left(), + precedence, + isLeftAssociative + ); this.writer.write(" "); - this.writeIdentifier(fieldDeclaration.name()); - this.writer.write(";"); + this.writer.write(switch (binaryOperation.operator()) { + case ASSIGN -> "="; + case BITWISE_AND -> "&"; + case BITWISE_OR -> "|"; + case EQUAL_TO -> "=="; + case LEFT_SHIFT -> "<<"; + case NOT_EQUAL_TO -> "!="; + case UNSIGNED_RIGHT_SHIFT -> ">>>"; + }); + this.writer.write(" "); + this.writeSubExpression( + binaryOperation.right(), + precedence, + !isLeftAssociative + ); } - private void writeIdentifier(JavaIdentifier identifier) throws IOException { - this.writer.write(identifier.value()); + private boolean isLeftAssociative(JavaBinaryOperator operator) { + return switch (operator) { + case ASSIGN -> false; + case BITWISE_AND -> true; + case BITWISE_OR -> true; + case EQUAL_TO -> true; + case LEFT_SHIFT -> true; + case NOT_EQUAL_TO -> true; + case UNSIGNED_RIGHT_SHIFT -> true; + }; } - private static void writeIdentifier(JavaIdentifier identifier, WriteString writer) throws IOException { - writer.write(identifier.value()); + private void writeBooleanLiteral(JavaBooleanLiteral booleanLiteral) throws IOException { + this.writer.write(booleanLiteral.value() ? "true" : "false"); } - private void writeImplements(List<JavaTypeRef> implementsTypes) throws IOException { - if (!implementsTypes.isEmpty()) { - this.writer.write("implements "); - writeWithSeparator( - implementsTypes, - this::writeTypeRef, - () -> writer.write(", ") - ); - writer.write(" "); - } - } + private void writeCast(JavaCast cast) throws IOException { + this.writer.write("("); + this.writeTypeRef(cast.type()); + this.writer.write(") "); - private void writeIntegerLiteral(JavaIntegerLiteral integerLiteral) throws IOException { - this.writer.write(Long.toString(integerLiteral.value())); + this.writeSubExpression(cast.operand(), JavaPrecedence.PRIMARY, true); } - private void writeInterfaceDeclaration(JavaInterfaceDeclaration interfaceDeclaration) throws IOException { - writeDocComment(interfaceDeclaration.docComment()); - - this.writer.write("public "); - switch (interfaceDeclaration.openness()) { - case OPEN -> { - } - case SEALED -> { - this.writer.write("sealed "); - } - } - this.writer.write("interface "); - this.writeIdentifier(interfaceDeclaration.name()); - this.writer.write(" "); - - if (!interfaceDeclaration.permitsTypes().isEmpty()) { - writer.write("permits "); - writeWithSeparator( - interfaceDeclaration.permitsTypes(), - this::writeTypeRef, - () -> writer.write(", ") - ); - writer.write(" "); - } - - this.writer.write("{"); - this.writer.indent(); - - for (var bodyDeclaration: interfaceDeclaration.body()) { - writer.newLine(); - writeClassBodyDeclaration(bodyDeclaration); - writer.newLine(); - } - - this.writer.newLine(); - this.writeCustomArea(interfaceDeclaration.customArea()); - this.writer.dedent(); - this.writer.write("}"); + private void writeFieldAccess(JavaFieldAccess fieldAccess) throws IOException { + this.writeSubExpression(fieldAccess.receiver(), JavaPrecedence.PRIMARY, true); + this.writer.write("."); + this.writeIdentifier(fieldAccess.fieldName()); } - private void writeLocalVariableDeclaration(JavaLocalVariableDeclaration localVariableDeclaration) throws IOException { - if (localVariableDeclaration.type().isPresent()) { - this.writeTypeRef(localVariableDeclaration.type().get()); - } else { - this.writer.write("var"); - } - - this.writer.write(" "); - this.writeIdentifier(localVariableDeclaration.name()); - this.writer.write(" = "); - this.writeTopLevelExpression(localVariableDeclaration.initializer()); - this.writer.write(";"); + private void writeIntegerLiteral(JavaIntegerLiteral integerLiteral) throws IOException { + this.writer.write(Long.toString(integerLiteral.value())); } private void writeMethodCall(JavaMethodCall call) throws IOException { @@ -489,59 +623,6 @@ public class JavaWriter implements AutoCloseable { this.writer.write(")"); } - void writeMethodDeclaration(JavaMethodDeclaration method) throws IOException { - writeVisibility(method.visibility()); - this.writer.write(" "); - - switch (method.kind()) { - case STATIC -> { - this.writer.write("static "); - } - case INSTANCE -> { - } - } - - writeTypeRef(method.returnType()); - this.writer.write(" "); - this.writeIdentifier(method.name()); - this.writer.write("("); - - writeWithSeparator( - method.params(), - param -> { - writeTypeRef(param.type()); - writer.write(" "); - this.writeIdentifier(param.name()); - }, - () -> writer.write(", ") - ); - - this.writer.write(")"); - - if (!method.throws_().isEmpty()) { - this.writer.write(" throws "); - writeWithSeparator( - method.throws_(), - exceptionType -> writeTypeRef(exceptionType), - () -> this.writer.write(", ") - ); - } - - if (method.body().isEmpty()) { - this.writer.write(";"); - } else { - this.writer.write(" {"); - this.writer.indent(); - if (!method.body().get().isEmpty()) { - this.writer.newLine(); - writeBlock(method.body().get()); - } - this.writer.dedent(); - this.writer.newLine(); - this.writer.write("}"); - } - } - private void writeMethodRef(JavaMethodRef methodRef) throws IOException { this.writeTypeRef(methodRef.type()); this.writer.write("::"); @@ -564,61 +645,10 @@ public class JavaWriter implements AutoCloseable { this.writer.write("null"); } - private void writePackageName(JavaPackageName packageName) throws IOException { - writeWithSeparator( - packageName.parts(), - this::writeIdentifier, - () -> this.writer.write(".") - ); - } - - private void writeRecordDeclaration(JavaRecordDeclaration recordDeclaration) throws IOException { - writeDocComment(recordDeclaration.docComment()); - - this.writer.write("public record "); - this.writeIdentifier(recordDeclaration.name()); - this.writer.write("("); - - writeWithSeparator( - recordDeclaration.components(), - component -> { - writeTypeRef(component.type()); - writer.write(" "); - this.writeIdentifier(component.name()); - }, - () -> writer.write(", ") - ); - - writer.write(") "); - - this.writeImplements(recordDeclaration.implementsTypes()); - - this.writer.write("{"); - this.writer.indent(); - - for (var bodyDeclaration : recordDeclaration.body()) { - writer.newLine(); - writeClassBodyDeclaration(bodyDeclaration); - writer.newLine(); - } - - this.writer.newLine(); - this.writeCustomArea(recordDeclaration.customArea()); - - this.writer.dedent(); - this.writer.write("}"); - } - private void writeRef(JavaRef ref) throws IOException { this.writeIdentifier(ref.name()); } - private void writeReturnStatement(JavaReturn returnStatement) throws IOException { - this.writer.write("return "); - writeTopLevelExpression(returnStatement.value()); - this.writer.write(";"); - } - private void writeStaticFieldAccess(JavaStaticFieldAccess staticFieldAccess) throws IOException { this.writeTypeRef(staticFieldAccess.type()); this.writer.write("."); @@ -653,50 +683,42 @@ public class JavaWriter implements AutoCloseable { this.writeSubExpression(ternaryConditional.ifFalse(), JavaPrecedence.TERNARY, true); } - private static void writeTypeArg(JavaTypeArg arg, WriteString writer) throws IOException { - switch (arg.variance()) { - case INVARIANT -> { - // Do nothing. - } - case SUPER -> { - writer.write("? super "); - } - } - writeTypeRef(arg.type(), writer); - } + // == Identifiers == - void writeTypeRef(JavaTypeRef type) throws IOException { - writeTypeRef(type, writer::write); + private void writeIdentifier(JavaIdentifier identifier) throws IOException { + this.writer.write(identifier.value()); } - private static void writeTypeRef(JavaTypeRef type, WriteString writer) throws IOException { - for (var part : type.packageName().parts()) { - writeIdentifier(part, writer); - writer.write("."); - } + private static void writeIdentifier(JavaIdentifier identifier, WriteString writer) throws IOException { + writer.write(identifier.value()); + } + private void writePackageName(JavaPackageName packageName) throws IOException { writeWithSeparator( - type.typeNames(), - typeName -> writeIdentifier(typeName, writer), - () -> writer.write(".") + packageName.parts(), + this::writeIdentifier, + () -> this.writer.write(".") ); + } - if (!type.args().isEmpty()) { - writer.write("<"); - writeWithSeparator( - type.args(), - arg -> writeTypeArg(arg, writer), - () -> writer.write(", ") - ); - writer.write(">"); - } + // == Custom areas == + + private void writeCustomArea(JavaCustomArea customArea) throws IOException { + var customAreaName = new StringWriter(); + writeTypeRef(customArea.type(), customAreaName::write); + customAreaName.write(" "); + customAreaName.write(customArea.name()); + this.writer.writeCustomArea(customAreaName.toString()); } - private void writeVisibility(JavaVisibility visibility) throws IOException { - this.writer.write(switch (visibility) { - case PUBLIC -> "public"; - case PRIVATE -> "private"; - }); + // == Doc comments == + + private void writeDocComment(DocComment docComment) throws IOException { + for (var line : docComment.lines()) { + this.writer.write("/// "); + this.writer.write(line); + this.writer.write("\n"); + } } interface WriteString { |
