From bc06840e00425db342bc5a021c0900d9e63b71f0 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 15 Jul 2016 14:23:47 +0100 Subject: Split switch handling off to desugar step --- .../zwobble/couscous/tests/BackendMethodTests.java | 4 +- .../org/zwobble/couscous/tests/CompilerTests.java | 24 ++++ .../tests/frontends/java/JavaReaderTests.java | 31 ++--- .../tests/frontends/java/NodeMatchers.java | 37 +++++- .../transforms/DesugarSwitchToIfElseTests.java | 129 +++++++++++++++++++++ 5 files changed, 201 insertions(+), 24 deletions(-) create mode 100644 src/test/java/org/zwobble/couscous/tests/transforms/DesugarSwitchToIfElseTests.java (limited to 'src/test/java/org/zwobble') diff --git a/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java b/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java index dcb7d9e..990f376 100644 --- a/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java +++ b/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java @@ -146,7 +146,7 @@ public abstract class BackendMethodTests { .statement(returns(reference(x))); assertEquals(value(1), runMethod(method)); } - + protected PrimitiveValue runMethod(MethodNode.Builder methodBuilder, PrimitiveValue... arguments) { MethodNode method = methodBuilder.build(); String className = "com.example.Program"; @@ -156,4 +156,4 @@ public abstract class BackendMethodTests { } protected abstract MethodRunner buildMethodRunner(); -} \ No newline at end of file +} diff --git a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java index be3ca29..94c6e6a 100644 --- a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java +++ b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java @@ -288,6 +288,30 @@ public abstract class CompilerTests { Types.INT)); } + @Test + public void switchStatements() throws Exception { + switchStatementTest("simpleReturn", "one", 1); + switchStatementTest("simpleReturn", "zero", 0); + + switchStatementTest("simpleFallthroughReturn", "one", 1); + switchStatementTest("simpleFallthroughReturn", "two", 1); + switchStatementTest("simpleFallthroughReturn", "zero", 0); + + switchStatementTest("noDefaultReturn", "one", 1); + switchStatementTest("noDefaultReturn", "zero", 0); + } + + private void switchStatementTest(String methodName, String input, int expected) throws Exception { + assertEquals( + value(expected), + execTestProgram( + "switch-statements", + ScalarType.of("com.example.SwitchStatements"), + methodName, + list(value(input)), + Types.INT)); + } + @Test public void stringAdd() throws Exception { assertEquals(value("42"), evalExpression(Types.STRING, "\"4\" + \"2\"")); diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java index d455301..acda60c 100644 --- a/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java +++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java @@ -179,25 +179,18 @@ public class JavaReaderTests { } @Test - public void switchStatementWithoutFallThroughIsReadAsIfElseStatement() { - assertEquals( - readStatements( - "int", - "String _couscous_tmp_0 = \"one\";" + - "if (_couscous_tmp_0.equals(\"one\")) { return 0; }" + - "else if (_couscous_tmp_0.equals(\"two\")) { return 0; }" + - "else { return 1; }"), - readStatements("int", "switch (\"one\") { case \"one\": case \"two\": return 0; default: return 1; }")); - } - - @Test - public void switchStatementWithoutDefaultIsReadAsIfStatementWithoutElseStatement() { - assertEquals( - readStatements( - "int", - "String _couscous_tmp_0 = \"one\";" + - "if (_couscous_tmp_0.equals(\"one\")) { return 0; } return 1;"), - readStatements("int", "switch (\"one\") { case \"one\": return 0; } return 1;")); + public void canReadSwitchStatements() { + assertThat( + readStatement("int", "switch (\"one\") { case \"one\": case \"two\": return 0; default: return 1; }"), + isSwitch( + hasSwitchValue(equalTo(literal("one"))), + hasSwitchCases( + isCase(literal("one"), list()), + isCase(literal("two"), list(returns(literal(0)))), + isDefaultCase(list(returns(literal(1)))) + ) + ) + ); } @Test diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java index e9c77f0..5815e68 100644 --- a/src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java +++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java @@ -3,13 +3,15 @@ package org.zwobble.couscous.tests.frontends.java; import org.hamcrest.Matcher; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.identifiers.Identifier; +import org.zwobble.couscous.ast.sugar.SwitchCaseNode; +import org.zwobble.couscous.ast.sugar.SwitchNode; import org.zwobble.couscous.types.Type; import java.util.List; +import java.util.Optional; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.equalTo; +import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.*; import static org.zwobble.couscous.tests.util.ExtraMatchers.hasFeature; import static org.zwobble.couscous.tests.util.ExtraMatchers.isInstance; @@ -86,4 +88,33 @@ public class NodeMatchers { public static Matcher hasFinally(StatementNode... statements) { return hasFeature("finally clause", TryNode::getFinallyBody, contains(statements)); } + + @SafeVarargs + public static Matcher isSwitch(Matcher... matchers) { + return isInstance(SwitchNode.class, allOf(matchers)); + } + + public static Matcher hasSwitchValue(Matcher matcher) { + return hasFeature("switch value", SwitchNode::getValue, matcher); + } + + @SafeVarargs + public static Matcher hasSwitchCases(Matcher... matchers) { + return hasFeature("switch cases", SwitchNode::getCases, contains(asList(matchers))); + } + + public static Matcher isCase(ExpressionNode value, List statements) { + return isCase(Optional.of(value), statements); + } + + public static Matcher isDefaultCase(List statements) { + return isCase(Optional.empty(), statements); + } + + private static Matcher isCase(Optional value, List statements) { + return allOf( + hasFeature("value", SwitchCaseNode::getValue, equalTo(value)), + hasFeature("statements", SwitchCaseNode::getStatements, equalTo(statements)) + ); + } } diff --git a/src/test/java/org/zwobble/couscous/tests/transforms/DesugarSwitchToIfElseTests.java b/src/test/java/org/zwobble/couscous/tests/transforms/DesugarSwitchToIfElseTests.java new file mode 100644 index 0000000..9e458e7 --- /dev/null +++ b/src/test/java/org/zwobble/couscous/tests/transforms/DesugarSwitchToIfElseTests.java @@ -0,0 +1,129 @@ +package org.zwobble.couscous.tests.transforms; + +import org.junit.Test; +import org.zwobble.couscous.ast.Operations; +import org.zwobble.couscous.ast.VariableDeclaration; +import org.zwobble.couscous.ast.identifiers.Identifiers; +import org.zwobble.couscous.ast.sugar.SwitchNode; +import org.zwobble.couscous.frontends.java.Scope; +import org.zwobble.couscous.transforms.DesugarSwitchToIfElse; +import org.zwobble.couscous.types.Types; + +import static org.junit.Assert.assertEquals; +import static org.zwobble.couscous.ast.ExpressionStatementNode.expressionStatement; +import static org.zwobble.couscous.ast.IfStatementNode.ifStatement; +import static org.zwobble.couscous.ast.LiteralNode.literal; +import static org.zwobble.couscous.ast.LocalVariableDeclarationNode.localVariableDeclaration; +import static org.zwobble.couscous.ast.ReturnNode.returns; +import static org.zwobble.couscous.ast.VariableDeclaration.var; +import static org.zwobble.couscous.ast.VariableReferenceNode.reference; +import static org.zwobble.couscous.ast.sugar.SwitchCaseNode.switchCase; +import static org.zwobble.couscous.util.ExtraLists.list; + +public class DesugarSwitchToIfElseTests { + @Test + public void switchStatementWithoutFallThroughIsReadAsIfElseStatement() { + VariableDeclaration tmp = var( + Identifiers.variable(Identifiers.TOP, "_couscous_tmp_0"), + "_couscous_tmp_0", + Types.STRING + ); + assertEquals( + list( + localVariableDeclaration(tmp, literal("one")), + ifStatement( + Operations.methodEquals(reference(tmp), literal("one")), + list(returns(literal(1))), + list( + ifStatement( + Operations.methodEquals(reference(tmp), literal("two")), + list(returns(literal(2))), + list( + returns(literal(0)) + ) + ) + ) + ) + ), + DesugarSwitchToIfElse.desugar( + Scope.create(), + new SwitchNode( + literal("one"), + list( + switchCase(literal("one"), list(returns(literal(1)))), + switchCase(literal("two"), list(returns(literal(2)))), + switchCase(list(returns(literal(0)))) + ) + ) + ) + ); + } + + @Test + public void switchStatementWithoutDefaultIsReadAsIfStatementWithoutElseStatement() { + VariableDeclaration tmp = var( + Identifiers.variable(Identifiers.TOP, "_couscous_tmp_0"), + "_couscous_tmp_0", + Types.STRING + ); + assertEquals( + list( + localVariableDeclaration(tmp, literal("one")), + ifStatement( + Operations.methodEquals(reference(tmp), literal("two")), + list(returns(literal(0))), + list() + ) + ), + DesugarSwitchToIfElse.desugar( + Scope.create(), + new SwitchNode( + literal("one"), + list( + switchCase(literal("two"), list(returns(literal(0)))) + ) + ) + ) + ); + } + + @Test + public void casesCanFallthrough() { + VariableDeclaration tmp = var( + Identifiers.variable(Identifiers.TOP, "_couscous_tmp_0"), + "_couscous_tmp_0", + Types.STRING + ); + assertEquals( + list( + localVariableDeclaration(tmp, literal("one")), + ifStatement( + Operations.methodEquals(reference(tmp), literal("one")), + list( + expressionStatement(literal(1)), + returns(literal(2)) + ), + list( + ifStatement( + Operations.methodEquals(reference(tmp), literal("two")), + list( + returns(literal(2)) + ), + list() + ) + ) + ) + ), + DesugarSwitchToIfElse.desugar( + Scope.create(), + new SwitchNode( + literal("one"), + list( + switchCase(literal("one"), list(expressionStatement(literal(1)))), + switchCase(literal("two"), list(returns(literal(2)))) + ) + ) + ) + ); + } +} -- cgit v1.2.3