diff options
| author | Michael Williamson <mike@zwobble.org> | 2015-10-05 11:04:17 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2015-10-05 11:04:17 +0100 |
| commit | 96b1e6f776ae8ce8910c44a3bbec0b489fd10c88 (patch) | |
| tree | 86658222ef741b1620c1635d3fd98d99452a387b /src/main | |
| parent | cdf51cdb9b3f8cef5896315496f348d9f7220903 (diff) | |
Add the beginnings of a Python backend
Diffstat (limited to 'src/main')
18 files changed, 554 insertions, 0 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java index 72e57d2..3d785d2 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -35,4 +35,9 @@ public class ClassNode { String name; @Singular List<MethodNode> methods; + + public String getLocalName() { + // TODO Stronger notion of class names? Should look up the terminology + return name.substring(name.lastIndexOf(".") + 1); + } } diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java new file mode 100644 index 0000000..816a80d --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java @@ -0,0 +1,121 @@ +package org.zwobble.couscous.backends.python; + +import java.util.stream.Collectors; + +import org.zwobble.couscous.ast.Assignment; +import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ExpressionNode; +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.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.backends.python.ast.PythonExpressionNode; +import org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode; +import org.zwobble.couscous.backends.python.ast.PythonModuleNode; +import org.zwobble.couscous.backends.python.ast.PythonStatementNode; +import org.zwobble.couscous.values.IntegerValue; +import org.zwobble.couscous.values.StringValue; + +import static java.util.Arrays.asList; +import static org.zwobble.couscous.backends.python.ast.PythonClassNode.pythonClass; +import static org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode.pythonFunctionDefinition; +import static org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode.pythonIntegerLiteral; +import static org.zwobble.couscous.backends.python.ast.PythonModuleNode.pythonModule; +import static org.zwobble.couscous.backends.python.ast.PythonReturnNode.pythonReturn; +import static org.zwobble.couscous.backends.python.ast.PythonStringLiteralNode.pythonStringLiteral; + +import lombok.val; + +public class PythonCodeGenerator { + public static PythonModuleNode generateCode(ClassNode classNode) { + val pythonBody = classNode.getMethods() + .stream() + .map(PythonCodeGenerator::generateFunction) + .collect(Collectors.toList()); + + val pythonClass = pythonClass(classNode.getLocalName(), pythonBody); + return pythonModule(asList(pythonClass)); + } + + private static PythonFunctionDefinitionNode generateFunction(MethodNode method) { + val pythonBody = method.getBody() + .stream() + .map(PythonCodeGenerator::generateStatement) + .collect(Collectors.toList()); + return pythonFunctionDefinition(method.getName(), pythonBody); + } + + private static PythonStatementNode generateStatement(StatementNode statement) { + return statement.accept(new StatementGenerator()); + } + + private static class StatementGenerator implements StatementNodeVisitor<PythonStatementNode> { + @Override + public PythonStatementNode visit(ReturnNode returnNode) { + return pythonReturn(generateExpression(returnNode.getValue())); + } + + @Override + public PythonStatementNode visit(ExpressionStatementNode expressionStatement) { + throw new UnsupportedOperationException(); + } + + @Override + public PythonStatementNode visit(LocalVariableDeclarationNode localVariableDeclaration) { + throw new UnsupportedOperationException(); + } + } + + private static PythonExpressionNode generateExpression(ExpressionNode expression) { + return expression.accept(new ExpressionGenerator()); + } + + private static class ExpressionGenerator implements ExpressionNodeVisitor<PythonExpressionNode> { + @Override + public PythonExpressionNode visit(LiteralNode literal) { + // TODO: use visitor + if (literal.getValue() instanceof IntegerValue) { + return pythonIntegerLiteral(((IntegerValue)literal.getValue()).getValue()); + } else if (literal.getValue() instanceof StringValue) { + return pythonStringLiteral(((StringValue)literal.getValue()).getValue()); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public PythonExpressionNode visit( + VariableReferenceNode variableReference) { + throw new UnsupportedOperationException(); + } + + @Override + public PythonExpressionNode visit(Assignment assignment) { + throw new UnsupportedOperationException(); + } + + @Override + public PythonExpressionNode visit(TernaryConditionalNode ternaryConditional) { + throw new UnsupportedOperationException(); + } + + @Override + public PythonExpressionNode visit(MethodCallNode methodCall) { + throw new UnsupportedOperationException(); + } + + @Override + public PythonExpressionNode visit(StaticMethodCallNode staticMethodCall) { + throw new UnsupportedOperationException(); + } + + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java new file mode 100644 index 0000000..b38bc73 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java @@ -0,0 +1,34 @@ +package org.zwobble.couscous.backends.python; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.zwobble.couscous.ast.ClassNode; + +import static java.util.Arrays.asList; +import static org.zwobble.couscous.backends.python.PythonCodeGenerator.generateCode; +import static org.zwobble.couscous.backends.python.PythonSerializer.serialize; + +import lombok.SneakyThrows; +import lombok.val; + +public class PythonCompiler { + public void compile(List<ClassNode> classes, Path path) { + for (val classNode : classes) { + compileClass(classNode, pathForClass(classNode, path)); + } + } + + private Path pathForClass(ClassNode classNode, Path path) { + return path.resolve(classNode.getName().replace(".", File.separator) + ".py"); + } + + @SneakyThrows + private void compileClass(ClassNode classNode, Path path) { + Files.createDirectories(path.getParent()); + Files.write(path, asList(serialize(generateCode(classNode)))); + + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java b/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java new file mode 100644 index 0000000..e5a13e5 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java @@ -0,0 +1,107 @@ +package org.zwobble.couscous.backends.python; + +import org.zwobble.couscous.backends.python.ast.PythonBlock; +import org.zwobble.couscous.backends.python.ast.PythonClassNode; +import org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode; +import org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode; +import org.zwobble.couscous.backends.python.ast.PythonModuleNode; +import org.zwobble.couscous.backends.python.ast.PythonNode; +import org.zwobble.couscous.backends.python.ast.PythonPassNode; +import org.zwobble.couscous.backends.python.ast.PythonReturnNode; +import org.zwobble.couscous.backends.python.ast.PythonStringLiteralNode; +import org.zwobble.couscous.backends.python.ast.PythonWriter; +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +import lombok.val; + +public class PythonSerializer implements PythonNodeVisitor<Void> { + public static String serialize(PythonNode node) { + val writer = new PythonWriter(); + val serializer = new PythonSerializer(writer); + serializer.write(node); + return writer.asString(); + } + + private PythonWriter writer; + + private PythonSerializer(PythonWriter writer) { + this.writer = writer; + } + + public void write(PythonNode node) { + node.accept(this); + } + + @Override + public Void visit(PythonIntegerLiteralNode integerLiteral) { + writer.writeInteger(integerLiteral.getValue()); + return null; + } + + @Override + public Void visit(PythonStringLiteralNode stringLiteral) { + writer.writeStringLiteral(stringLiteral.getValue()); + return null; + } + + @Override + public Void visit(PythonReturnNode pythonReturn) { + writer.startStatement(); + writer.writeKeyword("return"); + writer.writeSpace(); + write(pythonReturn.getValue()); + writer.endStatement(); + return null; + } + + @Override + public Void visit(PythonPassNode pass) { + writer.startStatement(); + writer.writeKeyword("pass"); + writer.endStatement(); + return null; + } + + @Override + public Void visit(PythonClassNode pythonClass) { + writer.startStatement(); + writer.writeKeyword("class"); + writer.writeSpace(); + writer.writeIdentifier(pythonClass.getName()); + writer.writeSymbol("("); + writer.writeIdentifier("object"); + writer.writeSymbol(")"); + writeBlock(pythonClass.getBody()); + writer.endStatement(); + return null; + } + + @Override + public Void visit(PythonFunctionDefinitionNode functionDefinition) { + writer.startStatement(); + writer.writeKeyword("def"); + writer.writeSpace(); + writer.writeIdentifier(functionDefinition.getName()); + writer.writeSymbol("("); + writer.writeSymbol(")"); + writeBlock(functionDefinition.getBody()); + writer.endStatement(); + return null; + } + + @Override + public Void visit(PythonModuleNode module) { + for (val statement : module.getStatements()) { + write(statement); + } + return null; + } + + private void writeBlock(PythonBlock body) { + writer.startBlock(); + for (val statement : body) { + write(statement); + } + writer.endBlock(); + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonBlock.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonBlock.java new file mode 100644 index 0000000..8bc7132 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonBlock.java @@ -0,0 +1,25 @@ +package org.zwobble.couscous.backends.python.ast; + +import java.util.Iterator; +import java.util.List; + +import com.google.common.collect.ImmutableList; + +import static java.util.Arrays.asList; + +public class PythonBlock implements Iterable<PythonStatementNode> { + private final List<PythonStatementNode> statements; + + public PythonBlock(List<? extends PythonStatementNode> statements) { + if (statements.isEmpty()) { + this.statements = asList(PythonPassNode.PASS); + } else { + this.statements = ImmutableList.copyOf(statements); + } + } + + @Override + public Iterator<PythonStatementNode> iterator() { + return statements.iterator(); + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonClassNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonClassNode.java new file mode 100644 index 0000000..958a21a --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonClassNode.java @@ -0,0 +1,48 @@ +package org.zwobble.couscous.backends.python.ast; + +import java.util.List; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +import com.google.common.collect.ImmutableList; + +import lombok.Value; + +@Value(staticConstructor="pythonClass") +public class PythonClassNode implements PythonStatementNode { + public static Builder builder(String name) { + return new Builder(name); + } + + public static class Builder { + private final String name; + private final ImmutableList.Builder<PythonStatementNode> statements; + + public Builder(String name) { + this.name = name; + this.statements = ImmutableList.builder(); + } + + public PythonClassNode build() { + return new PythonClassNode(name, new PythonBlock(statements.build())); + } + + public Builder statement(PythonStatementNode statement) { + statements.add(statement); + return this; + } + } + + public static PythonClassNode pythonClass(String name, List<? extends PythonStatementNode> body) { + return new PythonClassNode(name, new PythonBlock(body)); + } + + String name; + PythonBlock body; + + @Override + public <T> T accept(PythonNodeVisitor<T> visitor) { + return visitor.visit(this); + } + +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonExpressionNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonExpressionNode.java new file mode 100644 index 0000000..9c8a259 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonExpressionNode.java @@ -0,0 +1,5 @@ +package org.zwobble.couscous.backends.python.ast; + +public interface PythonExpressionNode extends PythonNode { + +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonFunctionDefinitionNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonFunctionDefinitionNode.java new file mode 100644 index 0000000..6d66df8 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonFunctionDefinitionNode.java @@ -0,0 +1,22 @@ +package org.zwobble.couscous.backends.python.ast; + +import java.util.List; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +import lombok.Value; + +@Value(staticConstructor="pythonFunctionDefinition") +public class PythonFunctionDefinitionNode implements PythonStatementNode { + public static PythonFunctionDefinitionNode pythonFunctionDefinition(String name, List<PythonStatementNode> body) { + return pythonFunctionDefinition(name, new PythonBlock(body)); + } + + String name; + PythonBlock body; + + @Override + public <T> T accept(PythonNodeVisitor<T> visitor) { + return visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonIntegerLiteralNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonIntegerLiteralNode.java new file mode 100644 index 0000000..d8a9358 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonIntegerLiteralNode.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="pythonIntegerLiteral") +public class PythonIntegerLiteralNode implements PythonExpressionNode { + int value; + + @Override + public <T> T accept(PythonNodeVisitor<T> visitor) { + return visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonModuleNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonModuleNode.java new file mode 100644 index 0000000..92d63a8 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonModuleNode.java @@ -0,0 +1,18 @@ +package org.zwobble.couscous.backends.python.ast; + +import java.util.List; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +import lombok.Value; + +@Value(staticConstructor="pythonModule") +public class PythonModuleNode implements PythonNode { + List<PythonStatementNode> statements; + + @Override + public <T> T accept(PythonNodeVisitor<T> visitor) { + return visitor.visit(this); + } + +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonNode.java new file mode 100644 index 0000000..cdda65d --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonNode.java @@ -0,0 +1,7 @@ +package org.zwobble.couscous.backends.python.ast; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +public interface PythonNode { + <T> T accept(PythonNodeVisitor<T> visitor); +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonPassNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonPassNode.java new file mode 100644 index 0000000..36d6695 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonPassNode.java @@ -0,0 +1,16 @@ +package org.zwobble.couscous.backends.python.ast; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +public class PythonPassNode implements PythonStatementNode { + public static final PythonPassNode PASS = new PythonPassNode(); + + private PythonPassNode() { + // TODO Auto-generated constructor stub + } + + @Override + public <T> T accept(PythonNodeVisitor<T> visitor) { + return visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonReturnNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonReturnNode.java new file mode 100644 index 0000000..b204b4d --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonReturnNode.java @@ -0,0 +1,16 @@ +package org.zwobble.couscous.backends.python.ast; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +import lombok.Value; + +@Value(staticConstructor="pythonReturn") +public class PythonReturnNode implements PythonStatementNode { + PythonExpressionNode value; + + @Override + public <T> T accept(PythonNodeVisitor<T> visitor) { + return visitor.visit(this); + } + +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonStatementNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonStatementNode.java new file mode 100644 index 0000000..c27a7dd --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonStatementNode.java @@ -0,0 +1,5 @@ +package org.zwobble.couscous.backends.python.ast; + +public interface PythonStatementNode extends PythonNode { + +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonStringLiteralNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonStringLiteralNode.java new file mode 100644 index 0000000..82d91db --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonStringLiteralNode.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="pythonStringLiteral") +public class PythonStringLiteralNode implements PythonExpressionNode { + String value; + + @Override + public <T> T accept(PythonNodeVisitor<T> visitor) { + return visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonWriter.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonWriter.java new file mode 100644 index 0000000..4ab04c6 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonWriter.java @@ -0,0 +1,60 @@ +package org.zwobble.couscous.backends.python.ast; + +import com.google.common.base.Strings; + +public class PythonWriter { + private static final int SPACES_PER_INDENT = 4; + private final StringBuilder builder = new StringBuilder(); + private int depth = 0; + + public String asString() { + return builder.toString(); + } + + public void writeInteger(int value) { + builder.append(value); + } + + public void writeStringLiteral(String value) { + builder.append("\""); + builder.append(value); + builder.append("\""); + } + + public void writeKeyword(String keyword) { + builder.append(keyword); + } + + public void writeSpace() { + builder.append(" "); + } + + public void writeIdentifier(String name) { + builder.append(name); + } + + public void writeSymbol(String symbol) { + builder.append(symbol); + } + + public void startBlock() { + depth++; + writeSymbol(":"); + builder.append("\n"); + } + + public void endBlock() { + depth--; + } + + public void startStatement() { + builder.append(Strings.repeat(" ", depth * SPACES_PER_INDENT)); + } + + public void endStatement() { + if (builder.charAt(builder.length() - 1) != '\n') { + builder.append("\n"); + } + } + +} 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 new file mode 100644 index 0000000..0210df6 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java @@ -0,0 +1,22 @@ +package org.zwobble.couscous.backends.python.ast.visitors; + +import org.zwobble.couscous.backends.python.ast.PythonClassNode; +import org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode; +import org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode; +import org.zwobble.couscous.backends.python.ast.PythonModuleNode; +import org.zwobble.couscous.backends.python.ast.PythonPassNode; +import org.zwobble.couscous.backends.python.ast.PythonReturnNode; +import org.zwobble.couscous.backends.python.ast.PythonStringLiteralNode; + +public interface PythonNodeVisitor<T> { + T visit(PythonIntegerLiteralNode integerLiteral); + T visit(PythonStringLiteralNode stringLiteral); + + T visit(PythonReturnNode pythonReturn); + T visit(PythonPassNode pass); + T visit(PythonClassNode pythonClass); + T visit(PythonFunctionDefinitionNode functionDefinition); + + T visit(PythonModuleNode module); + +} diff --git a/src/main/java/org/zwobble/couscous/values/InterpreterValues.java b/src/main/java/org/zwobble/couscous/values/InterpreterValues.java new file mode 100644 index 0000000..6b220d0 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/values/InterpreterValues.java @@ -0,0 +1,13 @@ +package org.zwobble.couscous.values; + +public class InterpreterValues { + public static final UnitValue UNIT = UnitValue.UNIT; + + public static InterpreterValue value(int value) { + return new IntegerValue(value); + } + + public static InterpreterValue value(String value) { + return new StringValue(value); + } +} |
