From 166a205aeb4c595b40b8ffe4c7d745b13031e3b3 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 5 Oct 2015 22:26:29 +0100 Subject: Add support for static method calls to Python backend --- .../org/zwobble/couscous/ast/AssignmentNode.java | 4 +- .../java/org/zwobble/couscous/ast/ClassNode.java | 9 +- .../org/zwobble/couscous/ast/ExpressionNode.java | 48 ++++++++++- .../couscous/ast/ExpressionStatementNode.java | 4 +- .../java/org/zwobble/couscous/ast/LiteralNode.java | 4 +- .../couscous/ast/LocalVariableDeclarationNode.java | 4 +- .../org/zwobble/couscous/ast/MethodCallNode.java | 4 +- .../java/org/zwobble/couscous/ast/MethodNode.java | 9 +- src/main/java/org/zwobble/couscous/ast/Node.java | 7 ++ .../java/org/zwobble/couscous/ast/ReturnNode.java | 4 +- .../org/zwobble/couscous/ast/StatementNode.java | 31 ++++++- .../zwobble/couscous/ast/StaticMethodCallNode.java | 4 +- .../couscous/ast/TernaryConditionalNode.java | 4 +- .../couscous/ast/VariableReferenceNode.java | 4 +- .../ast/visitors/ExpressionNodeMapper.java | 17 ++++ .../ast/visitors/ExpressionNodeVisitor.java | 17 ---- .../zwobble/couscous/ast/visitors/NodeVisitor.java | 29 +++++++ .../ast/visitors/NodeVisitorWithEmptyDefaults.java | 59 +++++++++++++ .../couscous/ast/visitors/NodeVisitors.java | 97 ++++++++++++++++++++++ .../couscous/ast/visitors/StatementNodeMapper.java | 11 +++ .../ast/visitors/StatementNodeVisitor.java | 11 --- .../backends/python/PythonCodeGenerator.java | 69 ++++++++++++--- .../couscous/backends/python/PythonCompiler.java | 29 +++++-- .../couscous/backends/python/PythonSerializer.java | 10 +++ .../backends/python/ast/PythonImportNode.java | 15 ++++ .../python/ast/visitors/PythonNodeVisitor.java | 2 + .../zwobble/couscous/interpreter/Evaluator.java | 4 +- .../org/zwobble/couscous/interpreter/Executor.java | 6 +- .../java/org/zwobble/couscous/util/ExtraLists.java | 18 ++++ 29 files changed, 458 insertions(+), 76 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/ast/Node.java create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java delete mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java delete mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java create mode 100644 src/main/java/org/zwobble/couscous/backends/python/ast/PythonImportNode.java create mode 100644 src/main/java/org/zwobble/couscous/util/ExtraLists.java (limited to 'src/main/java') diff --git a/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java b/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java index 8989ef3..349b492 100644 --- a/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java +++ b/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java @@ -1,6 +1,6 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.values.TypeReference; import static org.zwobble.couscous.ast.VariableReferenceNode.reference; @@ -17,7 +17,7 @@ public class AssignmentNode implements ExpressionNode { ExpressionNode value; @Override - public T accept(ExpressionNodeVisitor visitor) { + public T accept(ExpressionNodeMapper visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java index 3d785d2..77480b5 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -2,13 +2,15 @@ package org.zwobble.couscous.ast; import java.util.List; +import org.zwobble.couscous.ast.visitors.NodeVisitor; + import com.google.common.collect.ImmutableList; import lombok.Singular; import lombok.Value; @Value -public class ClassNode { +public class ClassNode implements Node { public static ClassNode.Builder builder(String name) { return new Builder(name); } @@ -40,4 +42,9 @@ public class ClassNode { // TODO Stronger notion of class names? Should look up the terminology return name.substring(name.lastIndexOf(".") + 1); } + + @Override + public void accept(NodeVisitor visitor) { + visitor.visit(this); + } } diff --git a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java index bc27a99..297133d 100644 --- a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java @@ -1,9 +1,51 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; +import org.zwobble.couscous.ast.visitors.NodeVisitor; import org.zwobble.couscous.values.TypeReference; -public interface ExpressionNode { +public interface ExpressionNode extends Node { TypeReference getType(); - T accept(ExpressionNodeVisitor visitor); + T accept(ExpressionNodeMapper visitor); + + @Override + default void accept(NodeVisitor visitor) { + accept(new ExpressionNodeMapper() { + @Override + public Void visit(LiteralNode literal) { + visitor.visit(literal); + return null; + } + + @Override + public Void visit(VariableReferenceNode variableReference) { + visitor.visit(variableReference); + return null; + } + + @Override + public Void visit(AssignmentNode assignment) { + visitor.visit(assignment); + return null; + } + + @Override + public Void visit(TernaryConditionalNode ternaryConditional) { + visitor.visit(ternaryConditional); + return null; + } + + @Override + public Void visit(MethodCallNode methodCall) { + visitor.visit(methodCall); + return null; + } + + @Override + public Void visit(StaticMethodCallNode staticMethodCall) { + visitor.visit(staticMethodCall); + return null; + } + }); + } } diff --git a/src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java b/src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java index ec04663..5bbe2b5 100644 --- a/src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java @@ -1,6 +1,6 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; +import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import lombok.Value; @@ -9,7 +9,7 @@ public class ExpressionStatementNode implements StatementNode { ExpressionNode expression; @Override - public T accept(StatementNodeVisitor visitor) { + public T accept(StatementNodeMapper visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java index 2d77cd4..a608104 100644 --- a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java +++ b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java @@ -1,6 +1,6 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.values.BooleanValue; import org.zwobble.couscous.values.IntegerValue; import org.zwobble.couscous.values.PrimitiveValue; @@ -33,7 +33,7 @@ public class LiteralNode implements ExpressionNode { TypeReference type; @Override - public T accept(ExpressionNodeVisitor visitor) { + public T accept(ExpressionNodeMapper visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java b/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java index cf6c5f0..2b5c63a 100644 --- a/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java +++ b/src/main/java/org/zwobble/couscous/ast/LocalVariableDeclarationNode.java @@ -1,6 +1,6 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; +import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import org.zwobble.couscous.values.TypeReference; import static org.zwobble.couscous.ast.VariableDeclaration.var; @@ -21,7 +21,7 @@ public class LocalVariableDeclarationNode implements VariableNode, StatementNode ExpressionNode initialValue; @Override - public T accept(StatementNodeVisitor visitor) { + public T accept(StatementNodeMapper visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java b/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java index f240971..e1358c2 100644 --- a/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java +++ b/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java @@ -2,7 +2,7 @@ package org.zwobble.couscous.ast; import java.util.List; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.values.TypeReference; import lombok.Value; @@ -23,7 +23,7 @@ public class MethodCallNode implements ExpressionNode { } @Override - public T accept(ExpressionNodeVisitor visitor) { + public T accept(ExpressionNodeMapper visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/zwobble/couscous/ast/MethodNode.java b/src/main/java/org/zwobble/couscous/ast/MethodNode.java index 91acbd5..e924299 100644 --- a/src/main/java/org/zwobble/couscous/ast/MethodNode.java +++ b/src/main/java/org/zwobble/couscous/ast/MethodNode.java @@ -2,13 +2,15 @@ package org.zwobble.couscous.ast; import java.util.List; +import org.zwobble.couscous.ast.visitors.NodeVisitor; + import lombok.Builder; import lombok.Singular; import lombok.Value; @Builder @Value -public class MethodNode { +public class MethodNode implements Node { public static MethodNodeBuilder staticMethod(String name) { return builder().isStatic(true).name(name); } @@ -19,4 +21,9 @@ public class MethodNode { List arguments; @Singular("statement") List body; + + @Override + public void accept(NodeVisitor visitor) { + visitor.visit(this); + } } diff --git a/src/main/java/org/zwobble/couscous/ast/Node.java b/src/main/java/org/zwobble/couscous/ast/Node.java new file mode 100644 index 0000000..acde931 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/Node.java @@ -0,0 +1,7 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.NodeVisitor; + +public interface Node { + void accept(NodeVisitor visitor); +} diff --git a/src/main/java/org/zwobble/couscous/ast/ReturnNode.java b/src/main/java/org/zwobble/couscous/ast/ReturnNode.java index 529dbb5..7f4a57e 100644 --- a/src/main/java/org/zwobble/couscous/ast/ReturnNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ReturnNode.java @@ -1,6 +1,6 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; +import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import lombok.Value; @@ -9,7 +9,7 @@ public class ReturnNode implements StatementNode { ExpressionNode value; @Override - public T accept(StatementNodeVisitor visitor) { + public T accept(StatementNodeMapper visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/zwobble/couscous/ast/StatementNode.java b/src/main/java/org/zwobble/couscous/ast/StatementNode.java index 773420e..7a1b25b 100644 --- a/src/main/java/org/zwobble/couscous/ast/StatementNode.java +++ b/src/main/java/org/zwobble/couscous/ast/StatementNode.java @@ -1,7 +1,32 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; +import org.zwobble.couscous.ast.visitors.NodeVisitor; +import org.zwobble.couscous.ast.visitors.StatementNodeMapper; -public interface StatementNode { - T accept(StatementNodeVisitor visitor); +public interface StatementNode extends Node { + T accept(StatementNodeMapper visitor); + + @Override + default void accept(NodeVisitor visitor) { + accept(new StatementNodeMapper() { + @Override + public Void visit(ReturnNode returnNode) { + visitor.visit(returnNode); + return null; + } + + @Override + public Void visit(ExpressionStatementNode expressionStatement) { + visitor.visit(expressionStatement); + return null; + } + + @Override + public Void visit( + LocalVariableDeclarationNode localVariableDeclaration) { + visitor.visit(localVariableDeclaration); + return null; + } + }); + } } diff --git a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java index 0228514..96e2e4c 100644 --- a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java +++ b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java @@ -2,7 +2,7 @@ package org.zwobble.couscous.ast; import java.util.List; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.values.TypeReference; import static java.util.Arrays.asList; @@ -24,7 +24,7 @@ public class StaticMethodCallNode implements ExpressionNode { List arguments; @Override - public T accept(ExpressionNodeVisitor visitor) { + public T accept(ExpressionNodeMapper visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/zwobble/couscous/ast/TernaryConditionalNode.java b/src/main/java/org/zwobble/couscous/ast/TernaryConditionalNode.java index 8b30024..99b5f60 100644 --- a/src/main/java/org/zwobble/couscous/ast/TernaryConditionalNode.java +++ b/src/main/java/org/zwobble/couscous/ast/TernaryConditionalNode.java @@ -1,6 +1,6 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.values.TypeReference; import lombok.Value; @@ -12,7 +12,7 @@ public class TernaryConditionalNode implements ExpressionNode { ExpressionNode ifFalse; @Override - public T accept(ExpressionNodeVisitor visitor) { + public T accept(ExpressionNodeMapper visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java index 5499d50..fe47a84 100644 --- a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java @@ -1,6 +1,6 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.values.TypeReference; import lombok.Value; @@ -14,7 +14,7 @@ public class VariableReferenceNode implements ExpressionNode { VariableDeclaration referent; @Override - public T accept(ExpressionNodeVisitor visitor) { + public T accept(ExpressionNodeMapper visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java new file mode 100644 index 0000000..2be2894 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java @@ -0,0 +1,17 @@ +package org.zwobble.couscous.ast.visitors; + +import org.zwobble.couscous.ast.AssignmentNode; +import org.zwobble.couscous.ast.LiteralNode; +import org.zwobble.couscous.ast.MethodCallNode; +import org.zwobble.couscous.ast.StaticMethodCallNode; +import org.zwobble.couscous.ast.TernaryConditionalNode; +import org.zwobble.couscous.ast.VariableReferenceNode; + +public interface ExpressionNodeMapper { + T visit(LiteralNode literal); + T visit(VariableReferenceNode variableReference); + T visit(AssignmentNode assignment); + T visit(TernaryConditionalNode ternaryConditional); + T visit(MethodCallNode methodCall); + T visit(StaticMethodCallNode staticMethodCall); +} diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java deleted file mode 100644 index c47b942..0000000 --- a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.zwobble.couscous.ast.visitors; - -import org.zwobble.couscous.ast.AssignmentNode; -import org.zwobble.couscous.ast.LiteralNode; -import org.zwobble.couscous.ast.MethodCallNode; -import org.zwobble.couscous.ast.StaticMethodCallNode; -import org.zwobble.couscous.ast.TernaryConditionalNode; -import org.zwobble.couscous.ast.VariableReferenceNode; - -public interface ExpressionNodeVisitor { - T visit(LiteralNode literal); - T visit(VariableReferenceNode variableReference); - T visit(AssignmentNode assignment); - T visit(TernaryConditionalNode ternaryConditional); - T visit(MethodCallNode methodCall); - T visit(StaticMethodCallNode staticMethodCall); -} diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java new file mode 100644 index 0000000..014b48e --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java @@ -0,0 +1,29 @@ +package org.zwobble.couscous.ast.visitors; + +import org.zwobble.couscous.ast.AssignmentNode; +import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.LiteralNode; +import org.zwobble.couscous.ast.LocalVariableDeclarationNode; +import org.zwobble.couscous.ast.MethodCallNode; +import org.zwobble.couscous.ast.MethodNode; +import org.zwobble.couscous.ast.ReturnNode; +import org.zwobble.couscous.ast.StaticMethodCallNode; +import org.zwobble.couscous.ast.TernaryConditionalNode; +import org.zwobble.couscous.ast.VariableReferenceNode; + +public interface NodeVisitor { + void visit(LiteralNode literal); + void visit(VariableReferenceNode variableReference); + void visit(AssignmentNode assignment); + void visit(TernaryConditionalNode ternaryConditional); + void visit(MethodCallNode methodCall); + void visit(StaticMethodCallNode staticMethodCall); + + void visit(ReturnNode returnNode); + void visit(ExpressionStatementNode expressionStatement); + void visit(LocalVariableDeclarationNode localVariableDeclaration); + + void visit(ClassNode classNode); + void visit(MethodNode methodNode); +} diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java new file mode 100644 index 0000000..3a58614 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java @@ -0,0 +1,59 @@ +package org.zwobble.couscous.ast.visitors; + +import org.zwobble.couscous.ast.AssignmentNode; +import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.LiteralNode; +import org.zwobble.couscous.ast.LocalVariableDeclarationNode; +import org.zwobble.couscous.ast.MethodCallNode; +import org.zwobble.couscous.ast.MethodNode; +import org.zwobble.couscous.ast.ReturnNode; +import org.zwobble.couscous.ast.StaticMethodCallNode; +import org.zwobble.couscous.ast.TernaryConditionalNode; +import org.zwobble.couscous.ast.VariableReferenceNode; + +public interface NodeVisitorWithEmptyDefaults extends NodeVisitor { + @Override + default void visit(LiteralNode literal) { + } + + @Override + default void visit(VariableReferenceNode variableReference) { + } + + @Override + default void visit(AssignmentNode assignment) { + } + + @Override + default void visit(TernaryConditionalNode ternaryConditional) { + } + + @Override + default void visit(MethodCallNode methodCall) { + } + + @Override + default void visit(StaticMethodCallNode staticMethodCall) { + } + + @Override + default void visit(ReturnNode returnNode) { + } + + @Override + default void visit(ExpressionStatementNode expressionStatement) { + } + + @Override + default void visit(LocalVariableDeclarationNode localVariableDeclaration) { + } + + @Override + default void visit(ClassNode classNode) { + } + + @Override + default void visit(MethodNode methodNode) { + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java new file mode 100644 index 0000000..88c6e10 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java @@ -0,0 +1,97 @@ +package org.zwobble.couscous.ast.visitors; + +import org.zwobble.couscous.ast.AssignmentNode; +import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.LiteralNode; +import org.zwobble.couscous.ast.LocalVariableDeclarationNode; +import org.zwobble.couscous.ast.MethodCallNode; +import org.zwobble.couscous.ast.MethodNode; +import org.zwobble.couscous.ast.Node; +import org.zwobble.couscous.ast.ReturnNode; +import org.zwobble.couscous.ast.StaticMethodCallNode; +import org.zwobble.couscous.ast.TernaryConditionalNode; +import org.zwobble.couscous.ast.VariableReferenceNode; + +import lombok.val; + +public class NodeVisitors { + public static void visitAll(Node node, NodeVisitor visitor) { + node.accept(new NodeVisitor() { + @Override + public void visit(LiteralNode literal) { + visitor.visit(literal); + } + + @Override + public void visit(VariableReferenceNode variableReference) { + visitor.visit(variableReference); + } + + @Override + public void visit(AssignmentNode assignment) { + visitor.visit(assignment); + assignment.getValue().accept(this); + visit(assignment.getTarget()); + } + + @Override + public void visit(TernaryConditionalNode ternaryConditional) { + visitor.visit(ternaryConditional); + ternaryConditional.getCondition().accept(this); + ternaryConditional.getIfTrue().accept(this); + ternaryConditional.getIfFalse().accept(this); + } + + @Override + public void visit(MethodCallNode methodCall) { + visitor.visit(methodCall); + methodCall.getReceiver().accept(this); + for (val argument : methodCall.getArguments()) { + argument.accept(this); + } + } + + @Override + public void visit(StaticMethodCallNode staticMethodCall) { + visitor.visit(staticMethodCall); + for (val argument : staticMethodCall.getArguments()) { + argument.accept(this); + } + } + + @Override + public void visit(ReturnNode returnNode) { + visitor.visit(returnNode); + returnNode.getValue().accept(this); + } + + @Override + public void visit(ExpressionStatementNode expressionStatement) { + visitor.visit(expressionStatement); + expressionStatement.getExpression().accept(this); + } + + @Override + public void visit(LocalVariableDeclarationNode localVariableDeclaration) { + visitor.visit(localVariableDeclaration); + localVariableDeclaration.getInitialValue().accept(this); + } + + @Override + public void visit(ClassNode classNode) { + visitor.visit(classNode); + for (val method : classNode.getMethods()) { + method.accept(this); + } + } + + @Override + public void visit(MethodNode methodNode) { + for (val statement : methodNode.getBody()) { + statement.accept(this); + } + } + }); + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java new file mode 100644 index 0000000..4bae481 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java @@ -0,0 +1,11 @@ +package org.zwobble.couscous.ast.visitors; + +import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.LocalVariableDeclarationNode; +import org.zwobble.couscous.ast.ReturnNode; + +public interface StatementNodeMapper { + T visit(ReturnNode returnNode); + T visit(ExpressionStatementNode expressionStatement); + T visit(LocalVariableDeclarationNode localVariableDeclaration); +} diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java deleted file mode 100644 index 620f6f8..0000000 --- a/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.zwobble.couscous.ast.visitors; - -import org.zwobble.couscous.ast.ExpressionStatementNode; -import org.zwobble.couscous.ast.LocalVariableDeclarationNode; -import org.zwobble.couscous.ast.ReturnNode; - -public interface StatementNodeVisitor { - T visit(ReturnNode returnNode); - T visit(ExpressionStatementNode expressionStatement); - T visit(LocalVariableDeclarationNode localVariableDeclaration); -} 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 cf48ba0..551abcb 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java @@ -1,6 +1,8 @@ package org.zwobble.couscous.backends.python; import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.zwobble.couscous.ast.AssignmentNode; @@ -16,11 +18,14 @@ import org.zwobble.couscous.ast.StatementNode; import org.zwobble.couscous.ast.StaticMethodCallNode; import org.zwobble.couscous.ast.TernaryConditionalNode; import org.zwobble.couscous.ast.VariableReferenceNode; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; -import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; +import org.zwobble.couscous.ast.visitors.NodeVisitorWithEmptyDefaults; +import org.zwobble.couscous.ast.visitors.NodeVisitors; +import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import org.zwobble.couscous.backends.python.ast.PythonBlock; import org.zwobble.couscous.backends.python.ast.PythonExpressionNode; import org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode; +import org.zwobble.couscous.backends.python.ast.PythonImportNode; import org.zwobble.couscous.backends.python.ast.PythonModuleNode; import org.zwobble.couscous.backends.python.ast.PythonStatementNode; import org.zwobble.couscous.backends.python.ast.PythonVariableReferenceNode; @@ -31,6 +36,11 @@ import org.zwobble.couscous.values.PrimitiveValueVisitor; import org.zwobble.couscous.values.StringValue; import org.zwobble.couscous.values.UnitValue; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterators; + +import static com.google.common.collect.Iterators.singletonIterator; import static java.util.Arrays.asList; import static org.zwobble.couscous.backends.python.ast.PythonAssignmentNode.pythonAssignment; import static org.zwobble.couscous.backends.python.ast.PythonAttributeAccessNode.pythonAttributeAccess; @@ -44,18 +54,37 @@ import static org.zwobble.couscous.backends.python.ast.PythonModuleNode.pythonMo import static org.zwobble.couscous.backends.python.ast.PythonReturnNode.pythonReturn; import static org.zwobble.couscous.backends.python.ast.PythonStringLiteralNode.pythonStringLiteral; import static org.zwobble.couscous.backends.python.ast.PythonVariableReferenceNode.pythonVariableReference; +import static org.zwobble.couscous.util.ExtraLists.foldLeft; import lombok.val; public class PythonCodeGenerator { public static PythonModuleNode generateCode(ClassNode classNode) { + val imports = findImports(classNode).stream() + .map(PythonImportNode::pythonImport) + .iterator(); + val pythonBody = classNode.getMethods() .stream() .map(PythonCodeGenerator::generateFunction) .collect(Collectors.toList()); val pythonClass = pythonClass(classNode.getLocalName(), pythonBody); - return pythonModule(asList(pythonClass)); + + return pythonModule(ImmutableList.copyOf(Iterators.concat( + imports, + singletonIterator(pythonClass)))); + } + + private static Set findImports(ClassNode classNode) { + val imports = ImmutableSet.builder(); + NodeVisitors.visitAll(classNode, new NodeVisitorWithEmptyDefaults() { + @Override + public void visit(StaticMethodCallNode staticMethodCall) { + imports.add(staticMethodCall.getClassName()); + } + }); + return imports.build(); } public static PythonExpressionNode generateCode(PrimitiveValue value) { @@ -99,7 +128,7 @@ public class PythonCodeGenerator { return statement.accept(new StatementGenerator()); } - private static class StatementGenerator implements StatementNodeVisitor { + private static class StatementGenerator implements StatementNodeMapper { @Override public PythonStatementNode visit(ReturnNode returnNode) { return pythonReturn(generateExpression(returnNode.getValue())); @@ -131,7 +160,7 @@ public class PythonCodeGenerator { private static final ExpressionGenerator EXPRESSION_GENERATOR = new ExpressionGenerator(); - private static class ExpressionGenerator implements ExpressionNodeVisitor { + private static class ExpressionGenerator implements ExpressionNodeMapper { @Override public PythonExpressionNode visit(LiteralNode literal) { return generateCode(literal.getValue()); @@ -171,16 +200,36 @@ public class PythonCodeGenerator { } } + @Override + public PythonExpressionNode visit(StaticMethodCallNode staticMethodCall) { + val className = staticMethodCall.getClassName(); + val moduleParts = asList(className.split(Pattern.quote("."))); + + val module = foldLeft( + moduleParts, + part -> (PythonExpressionNode)pythonVariableReference(part), + (parent, part) -> pythonAttributeAccess(parent, part)); + + val classReference = pythonAttributeAccess( + module, + className.substring(className.lastIndexOf(".") + 1)); + + val methodReference = pythonAttributeAccess( + classReference, + staticMethodCall.getMethodName()); + + val arguments = staticMethodCall.getArguments().stream() + .map(PythonCodeGenerator::generateExpression) + .collect(Collectors.toList());; + + return pythonCall(methodReference, arguments); + } + private List generateArguments(MethodCallNode methodCall) { return methodCall.getArguments().stream() .map(PythonCodeGenerator::generateExpression) .collect(Collectors.toList()); } - - @Override - public PythonExpressionNode visit(StaticMethodCallNode staticMethodCall) { - throw new UnsupportedOperationException(); - } } private static boolean isPrimitive(ExpressionNode value) { diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java index b38bc73..a54f037 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java @@ -15,20 +15,35 @@ import lombok.SneakyThrows; import lombok.val; public class PythonCompiler { - public void compile(List classes, Path path) { + private final Path root; + + public PythonCompiler(Path root) { + this.root = root; + } + + public void compile(List classes) { for (val classNode : classes) { - compileClass(classNode, pathForClass(classNode, path)); + compileClass(classNode); } + writeClass("java.lang.Integer", + "class Integer(object):\n" + + " def parseInt(value):\n" + + " return int(value)" + ); + } + + private Path pathForClass(String className) { + return root.resolve(className.replace(".", File.separator) + ".py"); } - private Path pathForClass(ClassNode classNode, Path path) { - return path.resolve(classNode.getName().replace(".", File.separator) + ".py"); + private void compileClass(ClassNode classNode) { + writeClass(classNode.getName(), serialize(generateCode(classNode))); } @SneakyThrows - private void compileClass(ClassNode classNode, Path path) { + private void writeClass(String name, String contents) { + val path = pathForClass(name); Files.createDirectories(path.getParent()); - Files.write(path, asList(serialize(generateCode(classNode)))); - + Files.write(path, asList(contents)); } } diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java b/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java index 0bd71f7..9c82b5d 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java @@ -13,6 +13,7 @@ import org.zwobble.couscous.backends.python.ast.PythonConditionalExpressionNode; import org.zwobble.couscous.backends.python.ast.PythonExpressionNode; import org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode; import org.zwobble.couscous.backends.python.ast.PythonGetSliceNode; +import org.zwobble.couscous.backends.python.ast.PythonImportNode; import org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode; import org.zwobble.couscous.backends.python.ast.PythonModuleNode; import org.zwobble.couscous.backends.python.ast.PythonNode; @@ -160,6 +161,15 @@ public class PythonSerializer implements PythonNodeVisitor { }); } + @Override + public void visit(PythonImportNode importNode) { + writer.writeStatement(() -> { + writer.writeKeyword("import"); + writer.writeSpace(); + writer.writeIdentifier(importNode.getName()); + }); + } + private void writeParenthesised(PythonExpressionNode expression) { writer.writeSymbol("("); write(expression); diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonImportNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonImportNode.java new file mode 100644 index 0000000..2d38004 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonImportNode.java @@ -0,0 +1,15 @@ +package org.zwobble.couscous.backends.python.ast; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +import lombok.Value; + +@Value(staticConstructor="pythonImport") +public class PythonImportNode implements PythonStatementNode { + String name; + + @Override + public void accept(PythonNodeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java b/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java index 29d6108..7632342 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java @@ -8,6 +8,7 @@ import org.zwobble.couscous.backends.python.ast.PythonClassNode; import org.zwobble.couscous.backends.python.ast.PythonConditionalExpressionNode; import org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode; import org.zwobble.couscous.backends.python.ast.PythonGetSliceNode; +import org.zwobble.couscous.backends.python.ast.PythonImportNode; import org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode; import org.zwobble.couscous.backends.python.ast.PythonModuleNode; import org.zwobble.couscous.backends.python.ast.PythonPassNode; @@ -30,6 +31,7 @@ public interface PythonNodeVisitor { void visit(PythonClassNode pythonClass); void visit(PythonFunctionDefinitionNode functionDefinition); void visit(PythonAssignmentNode assignment); + void visit(PythonImportNode importNode); void visit(PythonModuleNode module); diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java index c2eeb59..9f183ec 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java @@ -10,14 +10,14 @@ import org.zwobble.couscous.ast.MethodCallNode; import org.zwobble.couscous.ast.StaticMethodCallNode; import org.zwobble.couscous.ast.TernaryConditionalNode; import org.zwobble.couscous.ast.VariableReferenceNode; -import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.interpreter.values.BooleanInterpreterValue; import org.zwobble.couscous.interpreter.values.InterpreterValue; import org.zwobble.couscous.interpreter.values.InterpreterValues; import lombok.val; -public class Evaluator implements ExpressionNodeVisitor { +public class Evaluator implements ExpressionNodeMapper { public static InterpreterValue eval(Environment environment, ExpressionNode expression) { return new Evaluator(environment).eval(expression); } diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java index 4ed4a04..93adf16 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Executor.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java @@ -10,7 +10,7 @@ import org.zwobble.couscous.ast.MethodNode; import org.zwobble.couscous.ast.ReturnNode; import org.zwobble.couscous.ast.StatementNode; import org.zwobble.couscous.ast.VariableNode; -import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; +import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import org.zwobble.couscous.interpreter.values.InterpreterValue; import org.zwobble.couscous.interpreter.values.UnitInterpreterValue; @@ -18,7 +18,7 @@ import static org.zwobble.couscous.interpreter.Evaluator.eval; import lombok.val; -public class Executor implements StatementNodeVisitor> { +public class Executor implements StatementNodeMapper> { public static InterpreterValue callMethod(Environment environment, MethodNode method, Arguments arguments) { val innerEnvironment = buildEnvironment(environment, method, arguments); @@ -49,7 +49,7 @@ public class Executor implements StatementNodeVisitor private static Stream findDeclarations(List body) { return body.stream() - .flatMap(statement -> statement.accept(new StatementNodeVisitor>() { + .flatMap(statement -> statement.accept(new StatementNodeMapper>() { @Override public Stream visit(ReturnNode returnNode) { return Stream.empty(); diff --git a/src/main/java/org/zwobble/couscous/util/ExtraLists.java b/src/main/java/org/zwobble/couscous/util/ExtraLists.java new file mode 100644 index 0000000..b3c017f --- /dev/null +++ b/src/main/java/org/zwobble/couscous/util/ExtraLists.java @@ -0,0 +1,18 @@ +package org.zwobble.couscous.util; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class ExtraLists { + public static R foldLeft( + List list, + Function initialValue, + BiFunction function) { + R accumulator = initialValue.apply(list.get(0)); + for (int index = 1; index < list.size(); index++) { + accumulator = function.apply(accumulator, list.get(index)); + } + return accumulator; + } +} -- cgit v1.2.3