aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNode.java5
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java121
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java34
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java107
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonBlock.java25
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonClassNode.java48
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonExpressionNode.java5
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonFunctionDefinitionNode.java22
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonIntegerLiteralNode.java15
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonModuleNode.java18
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonNode.java7
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonPassNode.java16
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonReturnNode.java16
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonStatementNode.java5
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonStringLiteralNode.java15
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonWriter.java60
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java22
-rw-r--r--src/main/java/org/zwobble/couscous/values/InterpreterValues.java13
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);
+ }
+}