diff options
Diffstat (limited to 'src/main/java/org/zwobble/couscous/backends/python')
5 files changed, 108 insertions, 17 deletions
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); |
