aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-09-29 20:14:34 +0100
committerMichael Williamson <mike@zwobble.org>2015-09-29 20:14:34 +0100
commitccac9247fe4663874be8026ab9cc968ecec7157b (patch)
tree6e15ebf9eb130034c45f84372ce4d3776f401328 /src/main
Initial commit
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/zwobble/couscous/Project.java7
-rw-r--r--src/main/java/org/zwobble/couscous/ast/Assignment.java16
-rw-r--r--src/main/java/org/zwobble/couscous/ast/CallNode.java5
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNode.java14
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ExpressionNode.java7
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ExpressionStatementNode.java15
-rw-r--r--src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java9
-rw-r--r--src/main/java/org/zwobble/couscous/ast/LiteralNode.java26
-rw-r--r--src/main/java/org/zwobble/couscous/ast/MethodNode.java22
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ReturnNode.java15
-rw-r--r--src/main/java/org/zwobble/couscous/ast/StatementNode.java7
-rw-r--r--src/main/java/org/zwobble/couscous/ast/VariableNode.java5
-rw-r--r--src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java19
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java11
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/StatementNodeVisitor.java9
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Interpreter.java110
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/InterpreterException.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/WrongNumberOfArguments.java13
-rw-r--r--src/main/java/org/zwobble/couscous/values/IntegerValue.java8
-rw-r--r--src/main/java/org/zwobble/couscous/values/InterpreterValue.java5
-rw-r--r--src/main/java/org/zwobble/couscous/values/StringValue.java8
-rw-r--r--src/main/java/org/zwobble/couscous/values/UnitValue.java11
22 files changed, 348 insertions, 0 deletions
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> T accept(ExpressionNodeVisitor<T> 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<MethodNode> 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> T accept(ExpressionNodeVisitor<T> 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> T accept(StatementNodeVisitor<T> 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> T accept(ExpressionNodeVisitor<T> 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<FormalArgumentNode> arguments;
+ @Singular("statement")
+ List<StatementNode> 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> T accept(StatementNodeVisitor<T> 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> T accept(StatementNodeVisitor<T> 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> T accept(ExpressionNodeVisitor<T> 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> {
+ 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> {
+ 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<InterpreterValue> 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<Integer, InterpreterValue> buildStackFrame(
+ final MethodNode method,
+ List<InterpreterValue> 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<Optional<InterpreterValue>> {
+ private Evaluator evaluator;
+
+ public Executor(Evaluator evaluator) {
+ this.evaluator = evaluator;
+ }
+
+ @Override
+ public Optional<InterpreterValue> visit(ReturnNode returnNode) {
+ return Optional.of(evaluator.eval(returnNode.getValue()));
+ }
+
+ @Override
+ public Optional<InterpreterValue> visit(ExpressionStatementNode expressionStatement) {
+ evaluator.eval(expressionStatement.getExpression());
+ return Optional.empty();
+ }
+ }
+
+ private class Evaluator implements ExpressionNodeVisitor<InterpreterValue> {
+ private final Map<Integer, InterpreterValue> stackFrame;
+
+ public Evaluator(Map<Integer, InterpreterValue> 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() {
+ }
+}