From 4189eb24789bd5d1d9ef4ecea567f4119cbb325c Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 31 Jul 2016 19:13:45 +0100 Subject: Implement for loops using desugar --- .../java/org/zwobble/couscous/ast/ForNode.java | 101 +++++++++++++++++++++ .../java/org/zwobble/couscous/ast/NodeTypes.java | 2 + .../couscous/ast/visitors/NodeTransformer.java | 2 +- .../backends/csharp/CsharpCodeGenerator.java | 7 +- .../backends/python/PythonCodeGenerator.java | 8 +- .../frontends/java/JavaStatementReader.java | 17 ++-- .../zwobble/couscous/interpreter/JavaProject.java | 6 +- .../couscous/transforms/DesugarForToWhile.java | 44 +++++++++ 8 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/ast/ForNode.java create mode 100644 src/main/java/org/zwobble/couscous/transforms/DesugarForToWhile.java (limited to 'src/main') diff --git a/src/main/java/org/zwobble/couscous/ast/ForNode.java b/src/main/java/org/zwobble/couscous/ast/ForNode.java new file mode 100644 index 0000000..4642b85 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/ForNode.java @@ -0,0 +1,101 @@ +package org.zwobble.couscous.ast; + +import com.google.common.collect.Iterables; +import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.util.ExtraIterables; +import org.zwobble.couscous.util.ExtraLists; + +import java.util.List; + +public class ForNode implements StatementNode { + private final List initializers; + private final ExpressionNode condition; + private final List updaters; + private final List statements; + + public ForNode( + List initializers, + ExpressionNode condition, + List updaters, + List statements + ) { + this.initializers = initializers; + this.condition = condition; + this.updaters = updaters; + this.statements = statements; + } + + public List getInitializers() { + return initializers; + } + + public ExpressionNode getCondition() { + return condition; + } + + public List getUpdaters() { + return updaters; + } + + public List getStatements() { + return statements; + } + + @Override + public int type() { + return NodeTypes.FOR; + } + + @Override + public Iterable childNodes() { + return Iterables.concat( + initializers, + ExtraIterables.of(condition), + updaters, + statements + ); + } + + @Override + public StatementNode transformSubtree(NodeTransformer transformer) { + return new ForNode( + ExtraLists.copyOf(ExtraIterables.cast(LocalVariableDeclarationNode.class, transformer.transformStatements(initializers))), + transformer.transformExpression(condition), + transformer.transformExpressions(updaters), + transformer.transformStatements(statements) + ); + } + + @Override + public String toString() { + return "ForNode(" + + "initializers=" + initializers + + ", condition=" + condition + + ", updaters=" + updaters + + ", statements=" + statements + + ')'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ForNode forNode = (ForNode) o; + + if (!initializers.equals(forNode.initializers)) return false; + if (!condition.equals(forNode.condition)) return false; + if (!updaters.equals(forNode.updaters)) return false; + return statements.equals(forNode.statements); + + } + + @Override + public int hashCode() { + int result = initializers.hashCode(); + result = 31 * result + condition.hashCode(); + result = 31 * result + updaters.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 82090aa..7edaaab 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 = 10036; public static final int FOR_EACH = 10034; public static final int STATEMENT_BLOCK = 10035; @@ -85,6 +86,7 @@ public class NodeTypes { .put(TypeCoercionNode.class, TYPE_COERCION) .put(VariableReferenceNode.class, VARIABLE_REFERENCE) .put(WhileNode.class, WHILE) + .put(ForNode.class, FOR) .put(ForEachNode.class, FOR_EACH) .put(StatementBlockNode.class, STATEMENT_BLOCK) .put(PrimitiveInstanceMethodCall.class, CsharpNodeTypes.PRIMITIVE_INSTANCE_METHOD_CALL) diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java index a1bbda6..d44bb39 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java @@ -216,7 +216,7 @@ public class NodeTransformer { return transformList(methods, this::transformMethod); } - public List transformStatements(List body) { + public List transformStatements(List body) { return eagerFlatMap(body, this::transformStatement); } 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 f542625..28d2a3e 100644 --- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java @@ -6,6 +6,7 @@ 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.transforms.DesugarForToWhile; import org.zwobble.couscous.types.*; import java.util.Map; @@ -45,8 +46,10 @@ public class CsharpCodeGenerator { private Node generateCode(Node node) { return nodeTransformer.transform( CsharpPrimitiveMethods.TRANSFORMER.transform( - DesugarForEachToWhile.transformer().transform( - node + DesugarForToWhile.transformer().transform( + DesugarForEachToWhile.transformer().transform( + node + ) ) ) ); 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 477a26f..29e7274 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java @@ -11,6 +11,7 @@ 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.DesugarForToWhile; import org.zwobble.couscous.transforms.DesugarSwitchToIfElse; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; @@ -61,8 +62,13 @@ import static org.zwobble.couscous.util.ExtraLists.*; public class PythonCodeGenerator { public static PythonModuleNode generateCode(TypeNode sugaredTypeNode) { NodeTransformer switchTransformer = DesugarSwitchToIfElse.transformer(); + NodeTransformer forTransformer = DesugarForToWhile.transformer(); NodeTransformer forEachTransformer = DesugarForEachToWhile.transformer(); - TypeNode typeNode = forEachTransformer.transformTypeDeclaration(switchTransformer.transformTypeDeclaration(sugaredTypeNode)); + TypeNode typeNode = forTransformer.transformTypeDeclaration( + forEachTransformer.transformTypeDeclaration( + switchTransformer.transformTypeDeclaration(sugaredTypeNode) + ) + ); Iterator 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 72f94d5..6e9444c 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaStatementReader.java @@ -20,6 +20,7 @@ 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.util.ExtraIterables.only; import static org.zwobble.couscous.util.ExtraIterables.takeUntil; import static org.zwobble.couscous.util.ExtraLists.*; import static org.zwobble.couscous.util.Tails.tail; @@ -180,19 +181,15 @@ class JavaStatementReader { private List readForStatement(ForStatement statement) { @SuppressWarnings("unchecked") List javaInitialisers = statement.initializers(); - if (javaInitialisers.size() != 1) { - throw new UnsupportedOperationException(); - } @SuppressWarnings("unchecked") List updaters = statement.updaters(); - return append( - readVariableDeclarationExpression(javaInitialisers.get(0)), - whileLoop( - readExpression(Types.BOOLEAN, statement.getExpression()), - concat( - readBody(statement.getBody()), - eagerMap(updaters, updater -> expressionStatement(readExpressionWithoutBoxing(updater)))))); + return list(new ForNode( + readVariableDeclarationExpression(only(javaInitialisers)), + readExpression(Types.BOOLEAN, statement.getExpression()), + eagerMap(updaters, this::readExpressionWithoutBoxing), + readBody(statement.getBody()) + )); } private List readEnhancedForStatement(EnhancedForStatement statement) { diff --git a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java index 7b5a0bc..3e4b182 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java +++ b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java @@ -8,6 +8,7 @@ import org.zwobble.couscous.interpreter.types.IntrinsicInterpreterType; import org.zwobble.couscous.interpreter.types.UserDefinedInterpreterType; import org.zwobble.couscous.interpreter.values.BoxedIntegerInterpreterValue; import org.zwobble.couscous.interpreter.values.InternalCouscousInterpreterValue; +import org.zwobble.couscous.transforms.DesugarForToWhile; import org.zwobble.couscous.transforms.DesugarSwitchToIfElse; import java.util.List; @@ -24,10 +25,11 @@ public class JavaProject { } public static Project of(List classNodes) { - NodeTransformer transformer = DesugarSwitchToIfElse.transformer(); + NodeTransformer switchTransformer = DesugarSwitchToIfElse.transformer(); + NodeTransformer forTransformer = DesugarForToWhile.transformer(); Iterable concreteTypes = Iterables.transform( classNodes, - classNode -> new UserDefinedInterpreterType(transformer.transformTypeDeclaration(classNode)) + classNode -> new UserDefinedInterpreterType(forTransformer.transformTypeDeclaration(switchTransformer.transformTypeDeclaration(classNode))) ); return builder() .addClasses(concreteTypes) diff --git a/src/main/java/org/zwobble/couscous/transforms/DesugarForToWhile.java b/src/main/java/org/zwobble/couscous/transforms/DesugarForToWhile.java new file mode 100644 index 0000000..5989838 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/transforms/DesugarForToWhile.java @@ -0,0 +1,44 @@ +package org.zwobble.couscous.transforms; + +import org.zwobble.couscous.ast.ForNode; +import org.zwobble.couscous.ast.NodeTypes; +import org.zwobble.couscous.ast.StatementNode; +import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.frontends.java.Scope; + +import java.util.List; +import java.util.Optional; + +import static org.zwobble.couscous.ast.ExpressionStatementNode.expressionStatement; +import static org.zwobble.couscous.ast.WhileNode.whileLoop; +import static org.zwobble.couscous.util.ExtraIterables.lazyMap; +import static org.zwobble.couscous.util.ExtraLists.append; +import static org.zwobble.couscous.util.ExtraLists.concat; + +public class DesugarForToWhile { + public static NodeTransformer transformer() { + Scope scope = Scope.create().temporaryPrefix("_couscous_desugar_for_to_while"); + return NodeTransformer.builder() + .transformStatement(statement -> { + if (statement.type() == NodeTypes.FOR) { + return Optional.of(desugar(scope, ((ForNode) statement))); + } else { + return Optional.empty(); + } + }) + .build(); + } + + public static List desugar(Scope scope, ForNode forNode) { + return append( + forNode.getInitializers(), + whileLoop( + forNode.getCondition(), + concat( + forNode.getStatements(), + lazyMap(forNode.getUpdaters(), updater -> expressionStatement(updater)) + ) + ) + ); + } +} -- cgit v1.2.3