diff options
| author | Michael Williamson <mike@zwobble.org> | 2015-10-05 22:26:29 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2015-10-05 22:26:29 +0100 |
| commit | 166a205aeb4c595b40b8ffe4c7d745b13031e3b3 (patch) | |
| tree | aa493482f4ca312c1f23913ebc2f57b71d8aa590 /src/main | |
| parent | 44e4b877baea47c0465e1029bdbedc848381999f (diff) | |
Add support for static method calls to Python backend
Diffstat (limited to 'src/main')
27 files changed, 432 insertions, 50 deletions
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> T accept(ExpressionNodeVisitor<T> visitor) { + public <T> T accept(ExpressionNodeMapper<T> 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> T accept(ExpressionNodeVisitor<T> visitor); + <T> T accept(ExpressionNodeMapper<T> visitor); + + @Override + default void accept(NodeVisitor visitor) { + accept(new ExpressionNodeMapper<Void>() { + @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> T accept(StatementNodeVisitor<T> visitor) { + public <T> T accept(StatementNodeMapper<T> 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> T accept(ExpressionNodeVisitor<T> visitor) { + public <T> T accept(ExpressionNodeMapper<T> 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> T accept(StatementNodeVisitor<T> visitor) { + public <T> T accept(StatementNodeMapper<T> 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> T accept(ExpressionNodeVisitor<T> visitor) { + public <T> T accept(ExpressionNodeMapper<T> 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<FormalArgumentNode> arguments; @Singular("statement") List<StatementNode> 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> T accept(StatementNodeVisitor<T> visitor) { + public <T> T accept(StatementNodeMapper<T> 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> T accept(StatementNodeVisitor<T> visitor); +public interface StatementNode extends Node { + <T> T accept(StatementNodeMapper<T> visitor); + + @Override + default void accept(NodeVisitor visitor) { + accept(new StatementNodeMapper<Void>() { + @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<ExpressionNode> arguments; @Override - public <T> T accept(ExpressionNodeVisitor<T> visitor) { + public <T> T accept(ExpressionNodeMapper<T> 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> T accept(ExpressionNodeVisitor<T> visitor) { + public <T> T accept(ExpressionNodeMapper<T> 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> T accept(ExpressionNodeVisitor<T> visitor) { + public <T> T accept(ExpressionNodeMapper<T> visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java index c47b942..2be2894 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java @@ -7,7 +7,7 @@ import org.zwobble.couscous.ast.StaticMethodCallNode; import org.zwobble.couscous.ast.TernaryConditionalNode; import org.zwobble.couscous.ast.VariableReferenceNode; -public interface ExpressionNodeVisitor<T> { +public interface ExpressionNodeMapper<T> { T visit(LiteralNode literal); T visit(VariableReferenceNode variableReference); T visit(AssignmentNode assignment); 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/StatementNodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java index 620f6f8..4bae481 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java @@ -4,7 +4,7 @@ import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; import org.zwobble.couscous.ast.ReturnNode; -public interface StatementNodeVisitor<T> { +public interface StatementNodeMapper<T> { 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<String> findImports(ClassNode classNode) { + val imports = ImmutableSet.<String>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<PythonStatementNode> { + private static class StatementGenerator implements StatementNodeMapper<PythonStatementNode> { @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<PythonExpressionNode> { + private static class ExpressionGenerator implements ExpressionNodeMapper<PythonExpressionNode> { @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<PythonExpressionNode> 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<ClassNode> classes, Path path) { + private final Path root; + + public PythonCompiler(Path root) { + this.root = root; + } + + public void compile(List<ClassNode> 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<InterpreterValue> { +public class Evaluator implements ExpressionNodeMapper<InterpreterValue> { 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<Optional<InterpreterValue>> { +public class Executor implements StatementNodeMapper<Optional<InterpreterValue>> { 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<Optional<InterpreterValue> private static Stream<VariableNode> findDeclarations(List<StatementNode> body) { return body.stream() - .flatMap(statement -> statement.accept(new StatementNodeVisitor<Stream<VariableNode>>() { + .flatMap(statement -> statement.accept(new StatementNodeMapper<Stream<VariableNode>>() { @Override public Stream<VariableNode> 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 <T, R> R foldLeft( + List<T> list, + Function<T, R> initialValue, + BiFunction<R, T, R> 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; + } +} |
