From ccac9247fe4663874be8026ab9cc968ecec7157b Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Tue, 29 Sep 2015 20:14:34 +0100 Subject: Initial commit --- src/main/java/org/zwobble/couscous/Project.java | 7 ++ .../java/org/zwobble/couscous/ast/Assignment.java | 16 +++ .../java/org/zwobble/couscous/ast/CallNode.java | 5 + .../java/org/zwobble/couscous/ast/ClassNode.java | 14 +++ .../org/zwobble/couscous/ast/ExpressionNode.java | 7 ++ .../couscous/ast/ExpressionStatementNode.java | 15 +++ .../zwobble/couscous/ast/FormalArgumentNode.java | 9 ++ .../java/org/zwobble/couscous/ast/LiteralNode.java | 26 +++++ .../java/org/zwobble/couscous/ast/MethodNode.java | 22 +++++ .../java/org/zwobble/couscous/ast/ReturnNode.java | 15 +++ .../org/zwobble/couscous/ast/StatementNode.java | 7 ++ .../org/zwobble/couscous/ast/VariableNode.java | 5 + .../couscous/ast/VariableReferenceNode.java | 19 ++++ .../ast/visitors/ExpressionNodeVisitor.java | 11 +++ .../ast/visitors/StatementNodeVisitor.java | 9 ++ .../zwobble/couscous/interpreter/Interpreter.java | 110 +++++++++++++++++++++ .../couscous/interpreter/InterpreterException.java | 6 ++ .../interpreter/WrongNumberOfArguments.java | 13 +++ .../org/zwobble/couscous/values/IntegerValue.java | 8 ++ .../zwobble/couscous/values/InterpreterValue.java | 5 + .../org/zwobble/couscous/values/StringValue.java | 8 ++ .../org/zwobble/couscous/values/UnitValue.java | 11 +++ 22 files changed, 348 insertions(+) create mode 100644 src/main/java/org/zwobble/couscous/Project.java create mode 100644 src/main/java/org/zwobble/couscous/ast/Assignment.java create mode 100644 src/main/java/org/zwobble/couscous/ast/CallNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/ClassNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/ExpressionNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/LiteralNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/MethodNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/ReturnNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/StatementNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/VariableNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java create mode 100644 src/main/java/org/zwobble/couscous/interpreter/Interpreter.java create mode 100644 src/main/java/org/zwobble/couscous/interpreter/InterpreterException.java create mode 100644 src/main/java/org/zwobble/couscous/interpreter/WrongNumberOfArguments.java create mode 100644 src/main/java/org/zwobble/couscous/values/IntegerValue.java create mode 100644 src/main/java/org/zwobble/couscous/values/InterpreterValue.java create mode 100644 src/main/java/org/zwobble/couscous/values/StringValue.java create mode 100644 src/main/java/org/zwobble/couscous/values/UnitValue.java (limited to 'src/main/java/org/zwobble') diff --git a/src/main/java/org/zwobble/couscous/Project.java b/src/main/java/org/zwobble/couscous/Project.java new file mode 100644 index 0000000..6afac6e --- /dev/null +++ b/src/main/java/org/zwobble/couscous/Project.java @@ -0,0 +1,7 @@ +package org.zwobble.couscous; + +import org.zwobble.couscous.ast.ClassNode; + +public interface Project { + ClassNode findClass(String name); +} diff --git a/src/main/java/org/zwobble/couscous/ast/Assignment.java b/src/main/java/org/zwobble/couscous/ast/Assignment.java new file mode 100644 index 0000000..aa7f89b --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/Assignment.java @@ -0,0 +1,16 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; + +import lombok.Value; + +@Value +public class Assignment implements ExpressionNode { + VariableReferenceNode target; + ExpressionNode value; + + @Override + public T accept(ExpressionNodeVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/CallNode.java b/src/main/java/org/zwobble/couscous/ast/CallNode.java new file mode 100644 index 0000000..d71c891 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/CallNode.java @@ -0,0 +1,5 @@ +package org.zwobble.couscous.ast; + +public class CallNode { + +} diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java new file mode 100644 index 0000000..ffa5165 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -0,0 +1,14 @@ +package org.zwobble.couscous.ast; + +import java.util.List; + +import lombok.Builder; +import lombok.Singular; +import lombok.Value; + +@Builder +@Value +public class ClassNode { + @Singular + List methods; +} diff --git a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java new file mode 100644 index 0000000..097377a --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java @@ -0,0 +1,7 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; + +public interface ExpressionNode { + T accept(ExpressionNodeVisitor visitor); +} diff --git a/src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java b/src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java new file mode 100644 index 0000000..ec04663 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java @@ -0,0 +1,15 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; + +import lombok.Value; + +@Value +public class ExpressionStatementNode implements StatementNode { + ExpressionNode expression; + + @Override + public T accept(StatementNodeVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java b/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java new file mode 100644 index 0000000..da3a838 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java @@ -0,0 +1,9 @@ +package org.zwobble.couscous.ast; + +import lombok.Value; + +@Value +public class FormalArgumentNode implements VariableNode { + int id; + String name; +} diff --git a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java new file mode 100644 index 0000000..38688d8 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java @@ -0,0 +1,26 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.values.IntegerValue; +import org.zwobble.couscous.values.InterpreterValue; +import org.zwobble.couscous.values.StringValue; + +import lombok.Value; + +@Value +public class LiteralNode implements ExpressionNode { + public static LiteralNode of(String value) { + return new LiteralNode(new StringValue(value)); + } + + public static LiteralNode of(int value) { + return new LiteralNode(new IntegerValue(value)); + } + + InterpreterValue value; + + @Override + public T accept(ExpressionNodeVisitor 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 new file mode 100644 index 0000000..91acbd5 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/MethodNode.java @@ -0,0 +1,22 @@ +package org.zwobble.couscous.ast; + +import java.util.List; + +import lombok.Builder; +import lombok.Singular; +import lombok.Value; + +@Builder +@Value +public class MethodNode { + public static MethodNodeBuilder staticMethod(String name) { + return builder().isStatic(true).name(name); + } + + boolean isStatic; + String name; + @Singular + List arguments; + @Singular("statement") + List body; +} diff --git a/src/main/java/org/zwobble/couscous/ast/ReturnNode.java b/src/main/java/org/zwobble/couscous/ast/ReturnNode.java new file mode 100644 index 0000000..529dbb5 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/ReturnNode.java @@ -0,0 +1,15 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; + +import lombok.Value; + +@Value +public class ReturnNode implements StatementNode { + ExpressionNode value; + + @Override + public T accept(StatementNodeVisitor 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 new file mode 100644 index 0000000..773420e --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/StatementNode.java @@ -0,0 +1,7 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; + +public interface StatementNode { + T accept(StatementNodeVisitor visitor); +} diff --git a/src/main/java/org/zwobble/couscous/ast/VariableNode.java b/src/main/java/org/zwobble/couscous/ast/VariableNode.java new file mode 100644 index 0000000..4c689c5 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/VariableNode.java @@ -0,0 +1,5 @@ +package org.zwobble.couscous.ast; + +public interface VariableNode { + int getId(); +} diff --git a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java new file mode 100644 index 0000000..536a7c3 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java @@ -0,0 +1,19 @@ +package org.zwobble.couscous.ast; + +import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; + +import lombok.Value; + +@Value +public class VariableReferenceNode implements ExpressionNode { + public static VariableReferenceNode reference(VariableNode variable) { + return new VariableReferenceNode(variable.getId()); + } + + int referentId; + + @Override + public T accept(ExpressionNodeVisitor 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/ExpressionNodeVisitor.java new file mode 100644 index 0000000..0167abc --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java @@ -0,0 +1,11 @@ +package org.zwobble.couscous.ast.visitors; + +import org.zwobble.couscous.ast.Assignment; +import org.zwobble.couscous.ast.LiteralNode; +import org.zwobble.couscous.ast.VariableReferenceNode; + +public interface ExpressionNodeVisitor { + T visit(LiteralNode literal); + T visit(VariableReferenceNode variableReference); + T visit(Assignment assignment); +} diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java new file mode 100644 index 0000000..6cec404 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java @@ -0,0 +1,9 @@ +package org.zwobble.couscous.ast.visitors; + +import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.ReturnNode; + +public interface StatementNodeVisitor { + T visit(ReturnNode returnNode); + T visit(ExpressionStatementNode expressionStatement); +} diff --git a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java new file mode 100644 index 0000000..49c651b --- /dev/null +++ b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java @@ -0,0 +1,110 @@ +package org.zwobble.couscous.interpreter; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.IntStream; + +import org.zwobble.couscous.Project; +import org.zwobble.couscous.ast.Assignment; +import org.zwobble.couscous.ast.ExpressionNode; +import org.zwobble.couscous.ast.ExpressionStatementNode; +import org.zwobble.couscous.ast.LiteralNode; +import org.zwobble.couscous.ast.MethodNode; +import org.zwobble.couscous.ast.ReturnNode; +import org.zwobble.couscous.ast.VariableReferenceNode; +import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor; +import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; +import org.zwobble.couscous.values.InterpreterValue; + +import static com.google.common.collect.Iterables.find; +import static java.util.stream.Collectors.toMap; +import static org.zwobble.couscous.values.UnitValue.UNIT; + +import lombok.val; + +public class Interpreter { + private Project project; + + public Interpreter(Project project) { + this.project = project; + } + + public InterpreterValue run(String className, String methodName, List arguments) { + val clazz = project.findClass(className); + val method = find(clazz.getMethods(), + method -> method.getName().equals(methodName)); + val stackFrame = buildStackFrame(method, arguments); + + for (val statement : method.getBody()) { + val result = statement.accept(new Executor(new Evaluator(stackFrame))); + if (result.isPresent()) { + return result.get(); + } + } + return UNIT; + } + + private Map buildStackFrame( + final MethodNode method, + List arguments) { + + if (method.getArguments().size() != arguments.size()) { + throw new WrongNumberOfArguments(method.getArguments().size(), arguments.size()); + } + + return IntStream.range(0, method.getArguments().size()) + .boxed() + .collect(toMap( + index -> method.getArguments().get(index).getId(), + index -> arguments.get(index))); + } + + private class Executor implements StatementNodeVisitor> { + private Evaluator evaluator; + + public Executor(Evaluator evaluator) { + this.evaluator = evaluator; + } + + @Override + public Optional visit(ReturnNode returnNode) { + return Optional.of(evaluator.eval(returnNode.getValue())); + } + + @Override + public Optional visit(ExpressionStatementNode expressionStatement) { + evaluator.eval(expressionStatement.getExpression()); + return Optional.empty(); + } + } + + private class Evaluator implements ExpressionNodeVisitor { + private final Map stackFrame; + + public Evaluator(Map stackFrame) { + this.stackFrame = stackFrame; + } + + public InterpreterValue eval(ExpressionNode expression) { + return expression.accept(this); + } + + @Override + public InterpreterValue visit(LiteralNode literal) { + return literal.getValue(); + } + + @Override + public InterpreterValue visit(VariableReferenceNode variableReference) { + return stackFrame.get(variableReference.getReferentId()); + } + + @Override + public InterpreterValue visit(Assignment assignment) { + val value = eval(assignment.getValue()); + stackFrame.put(assignment.getTarget().getReferentId(), value); + return value; + } + } +} diff --git a/src/main/java/org/zwobble/couscous/interpreter/InterpreterException.java b/src/main/java/org/zwobble/couscous/interpreter/InterpreterException.java new file mode 100644 index 0000000..d6d7020 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/interpreter/InterpreterException.java @@ -0,0 +1,6 @@ +package org.zwobble.couscous.interpreter; + +public class InterpreterException extends RuntimeException { + private static final long serialVersionUID = 1L; + +} diff --git a/src/main/java/org/zwobble/couscous/interpreter/WrongNumberOfArguments.java b/src/main/java/org/zwobble/couscous/interpreter/WrongNumberOfArguments.java new file mode 100644 index 0000000..06d8079 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/interpreter/WrongNumberOfArguments.java @@ -0,0 +1,13 @@ +package org.zwobble.couscous.interpreter; + +import lombok.EqualsAndHashCode; +import lombok.Value; + +@Value +@EqualsAndHashCode(callSuper=false) +public class WrongNumberOfArguments extends InterpreterException { + private static final long serialVersionUID = 1L; + + int expected; + int actual; +} diff --git a/src/main/java/org/zwobble/couscous/values/IntegerValue.java b/src/main/java/org/zwobble/couscous/values/IntegerValue.java new file mode 100644 index 0000000..062d137 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/values/IntegerValue.java @@ -0,0 +1,8 @@ +package org.zwobble.couscous.values; + +import lombok.Value; + +@Value +public class IntegerValue implements InterpreterValue { + int value; +} diff --git a/src/main/java/org/zwobble/couscous/values/InterpreterValue.java b/src/main/java/org/zwobble/couscous/values/InterpreterValue.java new file mode 100644 index 0000000..64b0e91 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/values/InterpreterValue.java @@ -0,0 +1,5 @@ +package org.zwobble.couscous.values; + +public interface InterpreterValue { + +} diff --git a/src/main/java/org/zwobble/couscous/values/StringValue.java b/src/main/java/org/zwobble/couscous/values/StringValue.java new file mode 100644 index 0000000..1478d41 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/values/StringValue.java @@ -0,0 +1,8 @@ +package org.zwobble.couscous.values; + +import lombok.Value; + +@Value +public class StringValue implements InterpreterValue { + String value; +} diff --git a/src/main/java/org/zwobble/couscous/values/UnitValue.java b/src/main/java/org/zwobble/couscous/values/UnitValue.java new file mode 100644 index 0000000..1cf9dc7 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/values/UnitValue.java @@ -0,0 +1,11 @@ +package org.zwobble.couscous.values; + +import lombok.ToString; + +@ToString +public class UnitValue implements InterpreterValue { + public static final UnitValue UNIT = new UnitValue(); + + private UnitValue() { + } +} -- cgit v1.2.3