aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java')
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java90
1 files changed, 90 insertions, 0 deletions
diff --git a/src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java b/src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java
new file mode 100644
index 0000000..3ffaad5
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/transforms/DesugarSwitchToIfElse.java
@@ -0,0 +1,90 @@
+package org.zwobble.couscous.transforms;
+
+import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
+import org.zwobble.couscous.ast.NodeTypes;
+import org.zwobble.couscous.ast.StatementNode;
+import org.zwobble.couscous.ast.sugar.SwitchCaseNode;
+import org.zwobble.couscous.ast.sugar.SwitchNode;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.frontends.java.Scope;
+import org.zwobble.couscous.types.Types;
+import org.zwobble.couscous.util.ExtraLists;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.google.common.collect.Iterables.tryFind;
+import static org.zwobble.couscous.ast.IfStatementNode.ifStatement;
+import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
+import static org.zwobble.couscous.ast.TypeCoercionNode.coerce;
+import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
+import static org.zwobble.couscous.ast.sugar.SwitchCaseNode.switchCase;
+import static org.zwobble.couscous.util.ExtraIterables.lazyFlatMap;
+import static org.zwobble.couscous.util.ExtraLists.*;
+import static org.zwobble.couscous.util.Fold.foldRight;
+import static org.zwobble.couscous.util.Tails.tails;
+import static org.zwobble.couscous.util.UpToAndIncludingIterable.upToAndIncluding;
+
+public class DesugarSwitchToIfElse {
+ // TODO: ensure globally unique IDs, and locally unique variable names
+
+ public static NodeTransformer transformer(Scope scope) {
+ return NodeTransformer.builder()
+ .transformStatement(statement -> {
+ if (statement.type() == NodeTypes.SWITCH) {
+ return Optional.of(desugar(scope, ((SwitchNode) statement)));
+ } else {
+ return Optional.empty();
+ }
+ })
+ .build();
+ }
+
+ public static List<StatementNode> desugar(Scope scope, SwitchNode switchNode) {
+ LocalVariableDeclarationNode switchValueAssignment = scope.temporaryVariable(switchNode.getValue());
+
+ List<StatementNode> handleDefault = tryFind(switchNode.getCases(), SwitchCaseNode::isDefault)
+ .transform(SwitchCaseNode::getStatements)
+ .or(list());
+
+ List<SwitchCaseNode> cases = eagerMap(
+ tails(switchNode.getCases()),
+ remainingCases -> readSwitchCase(remainingCases.get(0), remainingCases)
+ );
+
+ return cons(
+ switchValueAssignment,
+ foldRight(cases, handleDefault, (handle, currentCase) ->
+ currentCase.getValue()
+ .map(value -> list(ifStatement(
+ methodCall(
+ reference(switchValueAssignment),
+ "equals",
+ list(coerce(value, Types.OBJECT)),
+ Types.BOOLEAN
+ ),
+ currentCase.getStatements(),
+ handle
+ )))
+ .orElse(handle)
+ )
+ );
+ }
+
+ private static SwitchCaseNode readSwitchCase(SwitchCaseNode caseStatement, List<SwitchCaseNode> cases)
+ {
+ Iterable<StatementNode> statementsForCase = upToAndIncluding(
+ lazyFlatMap(cases, SwitchCaseNode::getStatements),
+ DesugarSwitchToIfElse::isEndOfCase
+ );
+ return switchCase(
+ caseStatement.getValue(),
+ ExtraLists.copyOf(statementsForCase)
+ );
+ }
+
+ private static boolean isEndOfCase(StatementNode statement) {
+ // TODO: add attribute onto statement node to detect exit
+ return statement.type() == NodeTypes.RETURN;
+ }
+}