summaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/zwobble')
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaAssociativity.java6
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaPrecedence.java29
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java88
3 files changed, 112 insertions, 11 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaAssociativity.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaAssociativity.java
new file mode 100644
index 0000000..938c062
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaAssociativity.java
@@ -0,0 +1,6 @@
+package org.zwobble.hobgoblin.compiler.output.lang.java;
+
+public enum JavaAssociativity {
+ LEFT,
+ RIGHT
+}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaPrecedence.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaPrecedence.java
new file mode 100644
index 0000000..5318697
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaPrecedence.java
@@ -0,0 +1,29 @@
+package org.zwobble.hobgoblin.compiler.output.lang.java;
+
+public enum JavaPrecedence {
+ PRIMARY(14),
+ POSTFIX(13),
+ UNARY(12),
+ MULTIPLICATIVE(11),
+ ADDITIVE(10),
+ SHIFT(9),
+ RELATIONAL(8),
+ EQUALITY(7),
+ BITWISE_AND(6),
+ BITWISE_EXCLUSIVE_OR(5),
+ BITWISE_INCLUSIVE_OR(4),
+ LOGICAL_AND(3),
+ LOGICAL_OR(2),
+ TERNARY(1),
+ ASSIGNMENT(0);
+
+ private final int value;
+
+ JavaPrecedence(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return this.value;
+ }
+}
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 e8e0f40..95e3081 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
@@ -40,7 +40,14 @@ public class JavaWriter implements AutoCloseable {
}
private void writeBinaryOperation(JavaBinaryOperation binaryOperation) throws IOException {
- this.writeExpression(binaryOperation.left());
+ 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 -> "=";
@@ -48,7 +55,19 @@ public class JavaWriter implements AutoCloseable {
case UNSIGNED_RIGHT_SHIFT -> ">>>";
});
this.writer.write(" ");
- this.writeExpression(binaryOperation.right());
+ this.writeSubExpression(
+ binaryOperation.right(),
+ precedence,
+ !isLeftAssociative
+ );
+ }
+
+ private boolean isLeftAssociative(JavaBinaryOperator operator) {
+ return switch (operator) {
+ case ASSIGN -> false;
+ case BITWISE_AND -> true;
+ case UNSIGNED_RIGHT_SHIFT -> true;
+ };
}
private void writeBooleanLiteral(JavaBooleanLiteral booleanLiteral) throws IOException {
@@ -230,7 +249,7 @@ public class JavaWriter implements AutoCloseable {
this.writer.write("}");
}
- void writeExpression(JavaExpression expression) throws IOException {
+ void writeTopLevelExpression(JavaExpression expression) throws IOException {
switch (expression) {
case JavaBinaryOperation binaryOperation -> {
writeBinaryOperation(binaryOperation);
@@ -282,13 +301,60 @@ public class JavaWriter implements AutoCloseable {
}
}
+ void writeSubExpression(
+ JavaExpression expression,
+ JavaPrecedence parentPrecedence,
+ boolean matchesAssociativity
+ ) throws IOException {
+ var precedence = precedence(expression);
+ var requiresParens = parentPrecedence != JavaPrecedence.PRIMARY && (
+ parentPrecedence.value() > precedence.value() ||
+ (parentPrecedence.value() == precedence.value() && !matchesAssociativity)
+ );
+
+ if (requiresParens) {
+ this.writer.write("(");
+ }
+
+ this.writeTopLevelExpression(expression);
+
+ if (requiresParens) {
+ this.writer.write(")");
+ }
+ }
+
+ private JavaPrecedence precedence(JavaExpression expression) {
+ return switch (expression) {
+ case JavaBinaryOperation binaryOperation -> precedence(binaryOperation);
+ case JavaBooleanLiteral booleanLiteral -> JavaPrecedence.PRIMARY;
+ case JavaFieldAccess fieldAccess -> JavaPrecedence.PRIMARY;
+ case JavaIntegerLiteral integerLiteral -> JavaPrecedence.PRIMARY;
+ case JavaMethodCall methodCall -> JavaPrecedence.PRIMARY;
+ case JavaMethodRef methodRef -> JavaPrecedence.PRIMARY;
+ case JavaNewExpression newExpression -> JavaPrecedence.PRIMARY;
+ case JavaNullLiteral nullLiteral -> JavaPrecedence.PRIMARY;
+ case JavaRef ref -> JavaPrecedence.PRIMARY;
+ case JavaStaticFieldAccess staticFieldAccess -> JavaPrecedence.PRIMARY;
+ case JavaStaticMethodCall staticMethodCall -> JavaPrecedence.PRIMARY;
+ case JavaStringLiteral stringLiteral -> JavaPrecedence.PRIMARY;
+ };
+ }
+
+ private static JavaPrecedence precedence(JavaBinaryOperation binaryOperation) {
+ return switch (binaryOperation.operator()) {
+ case ASSIGN -> JavaPrecedence.ASSIGNMENT;
+ case BITWISE_AND -> JavaPrecedence.BITWISE_AND;
+ case UNSIGNED_RIGHT_SHIFT -> JavaPrecedence.SHIFT;
+ };
+ }
+
private void writeExpressionStatement(JavaExpressionStatement expressionStatement) throws IOException {
- writeExpression(expressionStatement.expression());
+ writeTopLevelExpression(expressionStatement.expression());
this.writer.write(";");
}
private void writeFieldAccess(JavaFieldAccess fieldAccess) throws IOException {
- this.writeExpression(fieldAccess.receiver());
+ this.writeSubExpression(fieldAccess.receiver(), JavaPrecedence.PRIMARY, true);
this.writer.write(".");
this.writeIdentifier(fieldAccess.fieldName());
}
@@ -370,18 +436,18 @@ public class JavaWriter implements AutoCloseable {
this.writer.write("var ");
this.writeIdentifier(localVariableDeclaration.name());
this.writer.write(" = ");
- this.writeExpression(localVariableDeclaration.initializer());
+ this.writeTopLevelExpression(localVariableDeclaration.initializer());
this.writer.write(";");
}
private void writeMethodCall(JavaMethodCall call) throws IOException {
- this.writeExpression(call.receiver());
+ this.writeSubExpression(call.receiver(), JavaPrecedence.PRIMARY, true);
this.writer.write(".");
this.writeIdentifier(call.methodName());
this.writer.write("(");
writeWithSeparator(
call.args(),
- this::writeExpression,
+ this::writeTopLevelExpression,
() -> this.writer.write(", ")
);
this.writer.write(")");
@@ -443,7 +509,7 @@ public class JavaWriter implements AutoCloseable {
this.writer.write("(");
writeWithSeparator(
newExpression.args(),
- this::writeExpression,
+ this::writeTopLevelExpression,
() -> this.writer.write(", ")
);
this.writer.write(")");
@@ -504,7 +570,7 @@ public class JavaWriter implements AutoCloseable {
private void writeReturnStatement(JavaReturn returnStatement) throws IOException {
this.writer.write("return ");
- writeExpression(returnStatement.value());
+ writeTopLevelExpression(returnStatement.value());
this.writer.write(";");
}
@@ -521,7 +587,7 @@ public class JavaWriter implements AutoCloseable {
this.writer.write("(");
writeWithSeparator(
call.args(),
- this::writeExpression,
+ this::writeTopLevelExpression,
() -> this.writer.write(", ")
);
this.writer.write(")");