aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ForEachNode.java79
-rw-r--r--src/main/java/org/zwobble/couscous/ast/NodeTypes.java2
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java9
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java4
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java16
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/DesugarForEachToWhile.java49
-rw-r--r--src/main/java/org/zwobble/couscous/util/ExtraIterables.java10
-rw-r--r--src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java27
-rw-r--r--src/test/java/org/zwobble/couscous/tests/frontends/java/JavaStatementSourceReader.java46
9 files changed, 227 insertions, 15 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/ForEachNode.java b/src/main/java/org/zwobble/couscous/ast/ForEachNode.java
new file mode 100644
index 0000000..e30f130
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/ForEachNode.java
@@ -0,0 +1,79 @@
+package org.zwobble.couscous.ast;
+
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.util.ExtraIterables;
+
+import java.util.List;
+
+public class ForEachNode implements StatementNode {
+ private final VariableDeclaration target;
+ private final ExpressionNode iterable;
+ private final List<StatementNode> statements;
+
+ public ForEachNode(VariableDeclaration target, ExpressionNode iterable, List<StatementNode> statements) {
+ this.target = target;
+ this.iterable = iterable;
+ this.statements = statements;
+ }
+
+ public VariableDeclaration getTarget() {
+ return target;
+ }
+
+ public ExpressionNode getIterable() {
+ return iterable;
+ }
+
+ public List<StatementNode> getStatements() {
+ return statements;
+ }
+
+ @Override
+ public int type() {
+ return NodeTypes.FOR_EACH;
+ }
+
+ @Override
+ public Iterable<? extends Node> childNodes() {
+ return ExtraIterables.of(iterable);
+ }
+
+ @Override
+ public StatementNode transformSubtree(NodeTransformer transformer) {
+ return new ForEachNode(
+ transformer.transform(target),
+ transformer.transformExpression(iterable),
+ transformer.transformStatements(statements)
+ );
+ }
+
+ @Override
+ public String toString() {
+ return "ForEachNode(" +
+ "target=" + target +
+ ", iterable=" + iterable +
+ ", statements=" + statements +
+ ')';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ForEachNode that = (ForEachNode) o;
+
+ if (!target.equals(that.target)) return false;
+ if (!iterable.equals(that.iterable)) return false;
+ return statements.equals(that.statements);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = target.hashCode();
+ result = 31 * result + iterable.hashCode();
+ result = 31 * result + statements.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
index 9a9a244..f7f8bf6 100644
--- a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
+++ b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
@@ -44,6 +44,7 @@ public class NodeTypes {
public static final int TYPE_COERCION = 10028;
public static final int VARIABLE_REFERENCE = 10029;
public static final int WHILE = 10030;
+ public static final int FOR_EACH = 10034;
private NodeTypes() {
}
@@ -83,6 +84,7 @@ public class NodeTypes {
.put(TypeCoercionNode.class, TYPE_COERCION)
.put(VariableReferenceNode.class, VARIABLE_REFERENCE)
.put(WhileNode.class, WHILE)
+ .put(ForEachNode.class, FOR_EACH)
.put(PrimitiveInstanceMethodCall.class, CsharpNodeTypes.PRIMITIVE_INSTANCE_METHOD_CALL)
.put(PrimitiveStaticMethodCall.class, CsharpNodeTypes.PRIMITIVE_STATIC_METHOD_CALL)
.build();
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
index 75a23ec..f542625 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
@@ -5,6 +5,7 @@ import org.zwobble.couscous.ast.Node;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.backends.csharp.primitives.CsharpPrimitiveMethods;
import org.zwobble.couscous.backends.naming.Naming;
+import org.zwobble.couscous.transforms.DesugarForEachToWhile;
import org.zwobble.couscous.types.*;
import java.util.Map;
@@ -42,7 +43,13 @@ public class CsharpCodeGenerator {
}
private Node generateCode(Node node) {
- return nodeTransformer.transform(CsharpPrimitiveMethods.TRANSFORMER.transform(node));
+ return nodeTransformer.transform(
+ CsharpPrimitiveMethods.TRANSFORMER.transform(
+ DesugarForEachToWhile.transformer().transform(
+ node
+ )
+ )
+ );
}
private Type transformType(Type type) {
diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
index 84ffdbd..f0f2e00 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
@@ -10,6 +10,7 @@ import org.zwobble.couscous.ast.visitors.DynamicNodeMapper;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.backends.naming.Names;
import org.zwobble.couscous.backends.python.ast.*;
+import org.zwobble.couscous.transforms.DesugarForEachToWhile;
import org.zwobble.couscous.transforms.DesugarSwitchToIfElse;
import org.zwobble.couscous.types.ScalarType;
import org.zwobble.couscous.types.Type;
@@ -60,7 +61,8 @@ import static org.zwobble.couscous.util.ExtraLists.*;
public class PythonCodeGenerator {
public static PythonModuleNode generateCode(TypeNode sugaredTypeNode) {
NodeTransformer switchTransformer = DesugarSwitchToIfElse.transformer();
- TypeNode typeNode = switchTransformer.transformTypeDeclaration(sugaredTypeNode);
+ NodeTransformer forEachTransformer = DesugarForEachToWhile.transformer();
+ TypeNode typeNode = forEachTransformer.transformTypeDeclaration(switchTransformer.transformTypeDeclaration(sugaredTypeNode));
Iterator<PythonImportNode> imports = generateImports(typeNode).iterator();
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java
index 5692f8a..df75ec4 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java
@@ -21,7 +21,6 @@ import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
import static org.zwobble.couscous.ast.WhileNode.whileLoop;
import static org.zwobble.couscous.ast.sugar.SwitchCaseNode.switchCase;
import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf;
-import static org.zwobble.couscous.types.BoundTypeParameter.boundTypeParameter;
import static org.zwobble.couscous.util.ExtraIterables.takeUntil;
import static org.zwobble.couscous.util.ExtraLists.*;
import static org.zwobble.couscous.util.Tails.tail;
@@ -203,17 +202,14 @@ class JavaStatementReader {
Type elementType = typeOf(parameter.getType());
ExpressionNode iterableValue = readExpression(JavaTypes.iterable(elementType), statement.getExpression());
- ExpressionNode iteratorValue = methodCall(iterableValue, "iterator", list(), JavaTypes.iterator(elementType));
- LocalVariableDeclarationNode iterator = scope.temporaryVariable(iteratorValue);
-
- ExpressionNode hasNext = methodCall(reference(iterator), "hasNext", list(), Types.BOOLEAN);
- ExpressionNode next = methodCall(reference(iterator), "next", list(), boundTypeParameter(JavaTypes.ITERATOR_TYPE_PARAMETER, elementType));
IVariableBinding parameterBinding = parameter.resolveBinding();
- WhileNode loop = whileLoop(hasNext, cons(
- scope.localVariable(parameterBinding.getKey(), parameterBinding.getName(), elementType, next),
- readStatement(statement.getBody())));
- return list(iterator, loop);
+ ForEachNode node = new ForEachNode(
+ scope.generateVariable(parameterBinding.getKey(), parameterBinding.getName(), elementType),
+ iterableValue,
+ readStatement(statement.getBody())
+ );
+ return list(node);
}
private List<StatementNode> readVariableDeclarationStatement(VariableDeclarationStatement statement) {
diff --git a/src/main/java/org/zwobble/couscous/transforms/DesugarForEachToWhile.java b/src/main/java/org/zwobble/couscous/transforms/DesugarForEachToWhile.java
new file mode 100644
index 0000000..ab6f1e5
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/transforms/DesugarForEachToWhile.java
@@ -0,0 +1,49 @@
+package org.zwobble.couscous.transforms;
+
+import org.zwobble.couscous.ast.*;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.frontends.java.JavaTypes;
+import org.zwobble.couscous.frontends.java.Scope;
+import org.zwobble.couscous.types.Type;
+import org.zwobble.couscous.types.Types;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.zwobble.couscous.ast.LocalVariableDeclarationNode.localVariableDeclaration;
+import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
+import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
+import static org.zwobble.couscous.ast.WhileNode.whileLoop;
+import static org.zwobble.couscous.types.BoundTypeParameter.boundTypeParameter;
+import static org.zwobble.couscous.util.ExtraLists.cons;
+import static org.zwobble.couscous.util.ExtraLists.list;
+
+public class DesugarForEachToWhile {
+ public static NodeTransformer transformer() {
+ Scope scope = Scope.create().temporaryPrefix("_couscous_desugar_foreach_to_while");
+ return NodeTransformer.builder()
+ .transformStatement(statement -> {
+ if (statement.type() == NodeTypes.FOR_EACH) {
+ return Optional.of(desugar(scope, ((ForEachNode) statement)));
+ } else {
+ return Optional.empty();
+ }
+ })
+ .build();
+ }
+
+ public static List<StatementNode> desugar(Scope scope, ForEachNode forEach) {
+ Type elementType = forEach.getTarget().getType();
+ ExpressionNode iteratorValue = methodCall(forEach.getIterable(), "iterator", list(), JavaTypes.iterator(elementType));
+ LocalVariableDeclarationNode iterator = scope.temporaryVariable(iteratorValue);
+
+ ExpressionNode hasNext = methodCall(reference(iterator), "hasNext", list(), Types.BOOLEAN);
+ ExpressionNode next = methodCall(reference(iterator), "next", list(), boundTypeParameter(JavaTypes.ITERATOR_TYPE_PARAMETER, elementType));
+ WhileNode loop = whileLoop(hasNext, cons(
+ localVariableDeclaration(forEach.getTarget(), next),
+ forEach.getStatements()
+ ));
+
+ return list(iterator, loop);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/util/ExtraIterables.java b/src/main/java/org/zwobble/couscous/util/ExtraIterables.java
index a40b409..fcc13d2 100644
--- a/src/main/java/org/zwobble/couscous/util/ExtraIterables.java
+++ b/src/main/java/org/zwobble/couscous/util/ExtraIterables.java
@@ -201,4 +201,14 @@ public class ExtraIterables {
}
};
}
+
+ public static <T> T only(Iterable<T> iterable) {
+ Iterator<T> iterator = iterable.iterator();
+ T value = iterator.next();
+ if (iterator.hasNext()) {
+ throw new RuntimeException("iterable had more than one element");
+ } else {
+ return value;
+ }
+ }
}
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 c6055e7..5718dca 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
@@ -3,14 +3,13 @@ package org.zwobble.couscous.tests.frontends.java;
import org.junit.Test;
import org.zwobble.couscous.ast.*;
import org.zwobble.couscous.ast.identifiers.Identifier;
+import org.zwobble.couscous.frontends.java.JavaTypes;
import org.zwobble.couscous.types.ScalarType;
import org.zwobble.couscous.types.Types;
import java.util.List;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.zwobble.couscous.ast.AnnotationNode.annotation;
@@ -32,6 +31,7 @@ import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference;
import static org.zwobble.couscous.ast.ThrowNode.throwNode;
import static org.zwobble.couscous.ast.TryNode.tryStatement;
import static org.zwobble.couscous.ast.TypeCoercionNode.typeCoercion;
+import static org.zwobble.couscous.ast.VariableDeclaration.var;
import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
import static org.zwobble.couscous.ast.WhileNode.whileLoop;
import static org.zwobble.couscous.tests.frontends.java.JavaReading.*;
@@ -178,6 +178,27 @@ public class JavaReaderTests {
}
@Test
+ public void canReadForEachLoops() {
+ List<StatementNode> statements = new JavaStatementSourceReader()
+ .returns("String")
+ .addVariable("iterable", "java.lang.Iterable<String>")
+ .readStatement("for (String x : iterable) { return x; } return \"\";");
+
+ VariableDeclaration expectedTarget = var(Identifier.TOP.type("com.example.Example").method("main").variable("x"), "x", Types.STRING);
+ assertEquals(
+ list(
+ new ForEachNode(
+ expectedTarget,
+ reference(var(Identifier.TOP.type("com.example.Example").method("main").variable("iterable"), "iterable", JavaTypes.iterable(Types.STRING))),
+ list(returns(reference(expectedTarget)))
+ ),
+ returns(literal(""))
+ ),
+ statements
+ );
+ }
+
+ @Test
public void canReadSwitchStatements() {
assertThat(
readStatement("int", "switch (\"one\") { case \"one\": case \"two\": return 0; default: return 1; }"),
diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaStatementSourceReader.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaStatementSourceReader.java
new file mode 100644
index 0000000..69be77d
--- /dev/null
+++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaStatementSourceReader.java
@@ -0,0 +1,46 @@
+package org.zwobble.couscous.tests.frontends.java;
+
+import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.StatementNode;
+
+import java.util.List;
+
+import static org.zwobble.couscous.tests.frontends.java.JavaReading.readClass;
+import static org.zwobble.couscous.util.ExtraIterables.only;
+import static org.zwobble.couscous.util.ExtraLists.append;
+import static org.zwobble.couscous.util.ExtraLists.list;
+
+class JavaStatementSourceReader {
+ private final List<String> variables;
+ private final String returnType;
+
+ JavaStatementSourceReader() {
+ this(list(), "void");
+ }
+
+ private JavaStatementSourceReader(List<String> variables, String returnType) {
+ this.variables = variables;
+ this.returnType = returnType;
+ }
+
+ JavaStatementSourceReader returns(String returnType) {
+ return new JavaStatementSourceReader(variables, returnType);
+ }
+
+ JavaStatementSourceReader addVariable(String name, String type) {
+ return new JavaStatementSourceReader(append(variables, type + " " + name), returnType);
+ }
+
+ public List<StatementNode> readStatement(String statement) {
+ String javaClass = generateMethodSource(variables, returnType, statement);
+ ClassNode classNode = readClass(javaClass);
+ return only(classNode.getMethods()).getBody().get();
+
+ }
+
+ private String generateMethodSource(List<String> variables, String returnType, String statement) {
+ return "public static " + returnType + " main(" + String.join(", ", variables) + ") throws Exception {" +
+ statement +
+ "}";
+ }
+}