From f7a7bfd4764f2787e022fc0a9dd8eb57c151e90d Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 7 Nov 2015 15:54:07 +0000 Subject: Add if-statement node --- .../org/zwobble/couscous/ast/IfStatementNode.java | 90 ++++++++++++++++++++++ .../org/zwobble/couscous/ast/StatementNode.java | 6 ++ .../zwobble/couscous/ast/visitors/NodeVisitor.java | 2 + .../ast/visitors/NodeVisitorWithEmptyDefaults.java | 5 ++ .../couscous/ast/visitors/NodeVisitors.java | 42 +++++----- .../couscous/ast/visitors/StatementNodeMapper.java | 2 + .../backends/python/PythonCodeGenerator.java | 22 +++++- .../couscous/backends/python/PythonSerializer.java | 15 ++++ .../backends/python/ast/PythonIfStatementNode.java | 53 +++++++++++++ .../python/ast/visitors/PythonNodeVisitor.java | 2 + .../org/zwobble/couscous/interpreter/Executor.java | 31 +++++++- 11 files changed, 246 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/ast/IfStatementNode.java create mode 100644 src/main/java/org/zwobble/couscous/backends/python/ast/PythonIfStatementNode.java (limited to 'src/main/java/org') diff --git a/src/main/java/org/zwobble/couscous/ast/IfStatementNode.java b/src/main/java/org/zwobble/couscous/ast/IfStatementNode.java new file mode 100644 index 0000000..ae3e29b --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/IfStatementNode.java @@ -0,0 +1,90 @@ +package org.zwobble.couscous.ast; + +import java.util.List; + +import org.zwobble.couscous.ast.visitors.StatementNodeMapper; + +public class IfStatementNode implements StatementNode { + public static IfStatementNode ifStatement( + ExpressionNode condition, + List trueBranch, + List falseBranch) { + return new IfStatementNode(condition, trueBranch, falseBranch); + } + + private final ExpressionNode condition; + private final List trueBranch; + private final List falseBranch; + + private IfStatementNode( + ExpressionNode condition, + List trueBranch, + List falseBranch) { + this.condition = condition; + this.trueBranch = trueBranch; + this.falseBranch = falseBranch; + } + + public ExpressionNode getCondition() { + return condition; + } + + public List getTrueBranch() { + return trueBranch; + } + + public List getFalseBranch() { + return falseBranch; + } + + @Override + public T accept(StatementNodeMapper visitor) { + return visitor.visit(this); + } + + @Override + public String toString() { + return "IfStatementNode(condition=" + condition + ", trueBranch=" + + trueBranch + ", falseBranch=" + falseBranch + ")"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((condition == null) ? 0 : condition.hashCode()); + result = prime * result + + ((falseBranch == null) ? 0 : falseBranch.hashCode()); + result = prime * result + + ((trueBranch == null) ? 0 : trueBranch.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IfStatementNode other = (IfStatementNode) obj; + if (condition == null) { + if (other.condition != null) + return false; + } else if (!condition.equals(other.condition)) + return false; + if (falseBranch == null) { + if (other.falseBranch != null) + return false; + } else if (!falseBranch.equals(other.falseBranch)) + return false; + if (trueBranch == null) { + if (other.trueBranch != null) + return false; + } else if (!trueBranch.equals(other.trueBranch)) + return false; + return true; + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/StatementNode.java b/src/main/java/org/zwobble/couscous/ast/StatementNode.java index 7a1b25b..1186619 100644 --- a/src/main/java/org/zwobble/couscous/ast/StatementNode.java +++ b/src/main/java/org/zwobble/couscous/ast/StatementNode.java @@ -27,6 +27,12 @@ public interface StatementNode extends Node { visitor.visit(localVariableDeclaration); return null; } + + @Override + public Void visit(IfStatementNode ifStatement) { + visitor.visit(ifStatement); + return null; + } }); } } diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java index 8a88052..ce6bb52 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java @@ -6,6 +6,7 @@ import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ConstructorNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.FieldAccessNode; +import org.zwobble.couscous.ast.IfStatementNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; import org.zwobble.couscous.ast.MethodCallNode; @@ -30,6 +31,7 @@ public interface NodeVisitor { void visit(ReturnNode returnNode); void visit(ExpressionStatementNode expressionStatement); void visit(LocalVariableDeclarationNode localVariableDeclaration); + void visit(IfStatementNode ifStatement); 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 index 8408b7a..fe57a28 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java @@ -6,6 +6,7 @@ import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ConstructorNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.FieldAccessNode; +import org.zwobble.couscous.ast.IfStatementNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; import org.zwobble.couscous.ast.MethodCallNode; @@ -65,6 +66,10 @@ public interface NodeVisitorWithEmptyDefaults extends NodeVisitor { default void visit(LocalVariableDeclarationNode localVariableDeclaration) { } + @Override + default void visit(IfStatementNode ifStatement) { + } + @Override default void visit(ClassNode classNode) { } diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java index 9424e39..1bcc226 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java @@ -1,19 +1,20 @@ package org.zwobble.couscous.ast.visitors; +import java.util.List; + import org.zwobble.couscous.ast.AssignmentNode; import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ConstructorNode; -import org.zwobble.couscous.ast.ExpressionNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.FieldAccessNode; +import org.zwobble.couscous.ast.IfStatementNode; 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.StatementNode; import org.zwobble.couscous.ast.StaticMethodCallNode; import org.zwobble.couscous.ast.TernaryConditionalNode; import org.zwobble.couscous.ast.ThisReferenceNode; @@ -56,25 +57,19 @@ public class NodeVisitors { public void visit(MethodCallNode methodCall) { visitor.visit(methodCall); methodCall.getReceiver().accept(this); - for (ExpressionNode argument : methodCall.getArguments()) { - argument.accept(this); - } + visitAll(methodCall.getArguments()); } @Override public void visit(StaticMethodCallNode staticMethodCall) { visitor.visit(staticMethodCall); - for (ExpressionNode argument : staticMethodCall.getArguments()) { - argument.accept(this); - } + visitAll(staticMethodCall.getArguments()); } @Override public void visit(ConstructorCallNode call) { visitor.visit(call); - for (ExpressionNode argument : call.getArguments()) { - argument.accept(this); - } + visitAll(call.getArguments()); } @Override @@ -99,27 +94,34 @@ public class NodeVisitors { visitor.visit(localVariableDeclaration); localVariableDeclaration.getInitialValue().accept(this); } - + + @Override + public void visit(IfStatementNode ifStatement) { + ifStatement.getCondition().accept(visitor); + visitAll(ifStatement.getTrueBranch()); + visitAll(ifStatement.getFalseBranch()); + } + @Override public void visit(ClassNode classNode) { visitor.visit(classNode); classNode.getConstructor().accept(this); - for (MethodNode method : classNode.getMethods()) { - method.accept(this); - } + visitAll(classNode.getMethods()); } @Override public void visit(MethodNode methodNode) { - for (StatementNode statement : methodNode.getBody()) { - statement.accept(this); - } + visitAll(methodNode.getBody()); } @Override public void visit(ConstructorNode constructorNode) { - for (StatementNode statement : constructorNode.getBody()) { - statement.accept(this); + visitAll(constructorNode.getBody()); + } + + private void visitAll(List nodes) { + for (Node node : nodes) { + node.accept(this); } } }); diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java index 4bae481..fe639bd 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeMapper.java @@ -1,6 +1,7 @@ package org.zwobble.couscous.ast.visitors; import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.IfStatementNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; import org.zwobble.couscous.ast.ReturnNode; @@ -8,4 +9,5 @@ public interface StatementNodeMapper { T visit(ReturnNode returnNode); T visit(ExpressionStatementNode expressionStatement); T visit(LocalVariableDeclarationNode localVariableDeclaration); + T visit(IfStatementNode ifStatement); } 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 fb1851c..75b9350 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; + import org.zwobble.couscous.ast.AssignmentNode; import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.ast.ConstructorCallNode; @@ -13,6 +14,7 @@ import org.zwobble.couscous.ast.ConstructorNode; import org.zwobble.couscous.ast.ExpressionNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.FieldAccessNode; +import org.zwobble.couscous.ast.IfStatementNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; import org.zwobble.couscous.ast.MethodCallNode; @@ -45,11 +47,13 @@ import org.zwobble.couscous.values.PrimitiveValue; import org.zwobble.couscous.values.PrimitiveValueVisitor; import org.zwobble.couscous.values.StringValue; import org.zwobble.couscous.values.UnitValue; + import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; 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; @@ -59,6 +63,7 @@ import static org.zwobble.couscous.backends.python.ast.PythonCallNode.pythonCall import static org.zwobble.couscous.backends.python.ast.PythonClassNode.pythonClass; import static org.zwobble.couscous.backends.python.ast.PythonConditionalExpressionNode.pythonConditionalExpression; import static org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode.pythonFunctionDefinition; +import static org.zwobble.couscous.backends.python.ast.PythonIfStatementNode.pythonIfStatement; import static org.zwobble.couscous.backends.python.ast.PythonImportAliasNode.pythonImportAlias; import static org.zwobble.couscous.backends.python.ast.PythonImportNode.pythonImport; import static org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode.pythonIntegerLiteral; @@ -117,7 +122,6 @@ public class PythonCodeGenerator { private static Set findReferencedClasses(ClassNode classNode) { ImmutableSet.Builder imports = ImmutableSet.builder(); NodeVisitors.visitAll(classNode, new NodeVisitorWithEmptyDefaults(){ - @Override public void visit(StaticMethodCallNode staticMethodCall) { imports.add(staticMethodCall.getClassName()); @@ -168,10 +172,16 @@ public class PythonCodeGenerator { Iterable argumentNames = Iterables.concat( method.isStatic() ? Collections.emptyList() : asList("self"), explicitArgumentNames); - List pythonBody = method.getBody().stream().map(PythonCodeGenerator::generateStatement).collect(Collectors.toList()); + List pythonBody = generateStatements(method.getBody()); return pythonFunctionDefinition(method.getName(), ImmutableList.copyOf(argumentNames), new PythonBlock(pythonBody)); } + private static List generateStatements(List statements) { + return statements.stream() + .map(PythonCodeGenerator::generateStatement) + .collect(Collectors.toList()); + } + private static PythonStatementNode generateStatement(StatementNode statement) { return statement.accept(new StatementGenerator()); } @@ -196,6 +206,14 @@ public class PythonCodeGenerator { public PythonStatementNode visit(LocalVariableDeclarationNode declaration) { return pythonAssignment(pythonVariableReference(declaration.getDeclaration().getName()), generateExpression(declaration.getInitialValue())); } + + @Override + public PythonStatementNode visit(IfStatementNode ifStatement) { + return pythonIfStatement( + generateExpression(ifStatement.getCondition()), + generateStatements(ifStatement.getTrueBranch()), + generateStatements(ifStatement.getFalseBranch())); + } } private static PythonExpressionNode generateExpression(ExpressionNode expression) { 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 2fb341f..4eea760 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.PythonIfStatementNode; import org.zwobble.couscous.backends.python.ast.PythonImportNode; import org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode; import org.zwobble.couscous.backends.python.ast.PythonModuleNode; @@ -192,6 +193,20 @@ public class PythonSerializer implements PythonNodeVisitor { }); }); } + + @Override + public void visit(PythonIfStatementNode ifStatement) { + writer.writeStatement(() -> { + writer.writeKeyword("if"); + writer.writeSpace(); + write(ifStatement.getCondition()); + writeBlock(ifStatement.getTrueBranch()); + }); + writer.writeStatement(() -> { + writer.writeKeyword("else"); + writeBlock(ifStatement.getFalseBranch()); + }); + } private void writeParenthesised(PythonExpressionNode expression) { writer.writeSymbol("("); diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonIfStatementNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonIfStatementNode.java new file mode 100644 index 0000000..313f722 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonIfStatementNode.java @@ -0,0 +1,53 @@ +package org.zwobble.couscous.backends.python.ast; + +import java.util.List; + +import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor; + +public class PythonIfStatementNode implements PythonStatementNode { + public static PythonIfStatementNode pythonIfStatement( + PythonExpressionNode condition, + List trueBranch, + List falseBranch) { + return new PythonIfStatementNode( + condition, + new PythonBlock(trueBranch), + new PythonBlock(falseBranch)); + } + + private final PythonExpressionNode condition; + private final PythonBlock trueBranch; + private final PythonBlock falseBranch; + + private PythonIfStatementNode( + PythonExpressionNode condition, + PythonBlock trueBranch, + PythonBlock falseBranch) { + this.condition = condition; + this.trueBranch = trueBranch; + this.falseBranch = falseBranch; + } + + public PythonExpressionNode getCondition() { + return condition; + } + + public PythonBlock getTrueBranch() { + return trueBranch; + } + + public PythonBlock getFalseBranch() { + return falseBranch; + } + + @Override + public String toString() { + return "PythonIfStatementNode(condition=" + condition + ", trueBranch=" + + trueBranch + ", falseBranch=" + falseBranch + ")"; + } + + @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 a1ed7d5..fb8fa71 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 @@ -9,6 +9,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.PythonIfStatementNode; import org.zwobble.couscous.backends.python.ast.PythonImportNode; import org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode; import org.zwobble.couscous.backends.python.ast.PythonModuleNode; @@ -36,6 +37,7 @@ public interface PythonNodeVisitor { void visit(PythonFunctionDefinitionNode functionDefinition); void visit(PythonAssignmentNode assignment); void visit(PythonImportNode importNode); + void visit(PythonIfStatementNode ifStatement); void visit(PythonModuleNode module); diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java index e47106d..6f33b4d 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Executor.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java @@ -3,19 +3,22 @@ package org.zwobble.couscous.interpreter; import java.util.List; import java.util.Optional; import java.util.stream.Stream; + import org.zwobble.couscous.ast.CallableNode; import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.IfStatementNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; import org.zwobble.couscous.ast.ReturnNode; import org.zwobble.couscous.ast.StatementNode; import org.zwobble.couscous.ast.VariableNode; import org.zwobble.couscous.ast.visitors.StatementNodeMapper; +import org.zwobble.couscous.interpreter.values.BooleanInterpreterValue; import org.zwobble.couscous.interpreter.values.InterpreterValue; import org.zwobble.couscous.interpreter.values.UnitInterpreterValue; + import static org.zwobble.couscous.interpreter.Evaluator.eval; public class Executor implements StatementNodeMapper> { - public static InterpreterValue callMethod(Environment environment, CallableNode method, Optional thisValue, PositionalArguments arguments) { final org.zwobble.couscous.interpreter.Environment innerEnvironment = buildEnvironment(environment, method, thisValue, arguments); for (final org.zwobble.couscous.ast.StatementNode statement : method.getBody()) { @@ -38,7 +41,6 @@ public class Executor implements StatementNodeMapper> private static Stream findDeclarations(List body) { return body.stream().flatMap(statement -> statement.accept(new StatementNodeMapper>(){ - @Override public Stream visit(ReturnNode returnNode) { return Stream.empty(); @@ -53,6 +55,12 @@ public class Executor implements StatementNodeMapper> public Stream visit(LocalVariableDeclarationNode localVariableDeclaration) { return Stream.of(localVariableDeclaration); } + + @Override + public Stream visit(IfStatementNode ifStatement) { + // TODO: Handle declarations in branches + return Stream.empty(); + } })); } @@ -82,4 +90,23 @@ public class Executor implements StatementNodeMapper> environment.put(localVariableDeclaration, value); return Optional.empty(); } + + @Override + public Optional visit(IfStatementNode ifStatement) { + BooleanInterpreterValue condition = (BooleanInterpreterValue) eval(environment, ifStatement.getCondition()); + if (condition.getValue()) { + return exec(ifStatement.getTrueBranch()); + } + return Optional.empty(); + } + + private Optional exec(List statements) { + for (StatementNode statement : statements) { + Optional result = exec(environment, statement); + if (result.isPresent()) { + return result; + } + } + return Optional.empty(); + } } \ No newline at end of file -- cgit v1.2.3