aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/couscous/Project.java4
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNode.java1
-rw-r--r--src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java12
-rw-r--r--src/main/java/org/zwobble/couscous/ast/LiteralNode.java4
-rw-r--r--src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java4
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Environment.java15
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Executor.java34
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Interpreter.java68
-rw-r--r--src/main/java/org/zwobble/couscous/values/ConcreteType.java59
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java10
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java24
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java19
12 files changed, 163 insertions, 91 deletions
diff --git a/src/main/java/org/zwobble/couscous/Project.java b/src/main/java/org/zwobble/couscous/Project.java
index 6afac6e..56b439f 100644
--- a/src/main/java/org/zwobble/couscous/Project.java
+++ b/src/main/java/org/zwobble/couscous/Project.java
@@ -1,7 +1,7 @@
package org.zwobble.couscous;
-import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.values.ConcreteType;
public interface Project {
- ClassNode findClass(String name);
+ ConcreteType<?> findClass(String name);
}
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
index ffa5165..6dd7b12 100644
--- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
@@ -9,6 +9,7 @@ import lombok.Value;
@Builder
@Value
public class ClassNode {
+ String name;
@Singular
List<MethodNode> methods;
}
diff --git a/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java b/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java
index da3a838..72a5f51 100644
--- a/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java
@@ -1,9 +1,21 @@
package org.zwobble.couscous.ast;
+import java.util.function.Supplier;
+
+import org.zwobble.couscous.values.ConcreteType;
+
+import lombok.AccessLevel;
+import lombok.Getter;
import lombok.Value;
@Value
public class FormalArgumentNode implements VariableNode {
int id;
+ @Getter(value=AccessLevel.NONE)
+ Supplier<ConcreteType<?>> type;
String name;
+
+ public ConcreteType<?> getType() {
+ return type.get();
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java
index cc57aef..0869653 100644
--- a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java
@@ -10,6 +10,10 @@ import lombok.Value;
@Value
public class LiteralNode implements ExpressionNode {
+ public static LiteralNode literal(InterpreterValue value) {
+ return new LiteralNode(value);
+ }
+
public static LiteralNode literal(String value) {
return new LiteralNode(new StringValue(value));
}
diff --git a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
index 390c37f..4f1e9ff 100644
--- a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
@@ -14,6 +14,10 @@ public class StaticMethodCallNode implements ExpressionNode {
return new StaticMethodCallNode(className, methodName, asList(arguments));
}
+ public static StaticMethodCallNode staticMethodCall(String className, String methodName, List<ExpressionNode> arguments) {
+ return new StaticMethodCallNode(className, methodName, arguments);
+ }
+
String className;
String methodName;
List<ExpressionNode> arguments;
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Environment.java b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
index ea4a5e4..76aa56b 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Environment.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
@@ -3,14 +3,16 @@ package org.zwobble.couscous.interpreter;
import java.util.HashMap;
import java.util.Map;
+import org.zwobble.couscous.Project;
import org.zwobble.couscous.values.ConcreteType;
-import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.InterpreterValue;
public class Environment {
- private Map<Integer, InterpreterValue> stackFrame;
+ private final Map<Integer, InterpreterValue> stackFrame;
+ private final Project project;
- public Environment(Map<Integer, InterpreterValue> stackFrame) {
+ public Environment(Project project, Map<Integer, InterpreterValue> stackFrame) {
+ this.project = project;
this.stackFrame = new HashMap<>(stackFrame);
}
@@ -23,10 +25,7 @@ public class Environment {
}
public ConcreteType<?> findClass(String className) {
- if (className == "java.lang.Integer") {
- return IntegerValue.TYPE;
- } else {
- throw new UnsupportedOperationException();
- }
+ // TODO: handle missing classes
+ return project.findClass(className);
}
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
new file mode 100644
index 0000000..633bf6b
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
@@ -0,0 +1,34 @@
+package org.zwobble.couscous.interpreter;
+
+import java.util.Optional;
+
+import org.zwobble.couscous.ast.ExpressionStatementNode;
+import org.zwobble.couscous.ast.ReturnNode;
+import org.zwobble.couscous.ast.StatementNode;
+import org.zwobble.couscous.ast.visitors.StatementNodeVisitor;
+import org.zwobble.couscous.values.InterpreterValue;
+
+import static org.zwobble.couscous.interpreter.Evaluator.eval;
+
+public class Executor implements StatementNodeVisitor<Optional<InterpreterValue>> {
+ public static Optional<InterpreterValue> exec(Environment environment, StatementNode statement) {
+ return statement.accept(new Executor(environment));
+ }
+
+ private final Environment environment;
+
+ private Executor(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ public Optional<InterpreterValue> visit(ReturnNode returnNode) {
+ return Optional.of(eval(environment, returnNode.getValue()));
+ }
+
+ @Override
+ public Optional<InterpreterValue> visit(ExpressionStatementNode expressionStatement) {
+ eval(environment, expressionStatement.getExpression());
+ return Optional.empty();
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
index bc82fee..884a082 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
@@ -1,20 +1,17 @@
package org.zwobble.couscous.interpreter;
import java.util.List;
-import java.util.Optional;
-import java.util.stream.IntStream;
+import java.util.stream.Collectors;
import org.zwobble.couscous.Project;
-import org.zwobble.couscous.ast.ExpressionStatementNode;
-import org.zwobble.couscous.ast.MethodNode;
-import org.zwobble.couscous.ast.ReturnNode;
-import org.zwobble.couscous.ast.visitors.StatementNodeVisitor;
+import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.values.InterpreterValue;
-import static com.google.common.collect.Iterables.find;
-import static java.util.stream.Collectors.toMap;
+import com.google.common.collect.ImmutableMap;
+
+import static org.zwobble.couscous.ast.StaticMethodCallNode.staticMethodCall;
import static org.zwobble.couscous.interpreter.Evaluator.eval;
-import static org.zwobble.couscous.values.UnitValue.UNIT;
import lombok.val;
@@ -26,52 +23,11 @@ public class Interpreter {
}
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 environment = buildEnvironment(method, arguments);
-
- for (val statement : method.getBody()) {
- val result = statement.accept(new Executor(environment));
- if (result.isPresent()) {
- return result.get();
- }
- }
- return UNIT;
- }
-
- private Environment buildEnvironment(
- final MethodNode method,
- List<InterpreterValue> arguments) {
-
- if (method.getArguments().size() != arguments.size()) {
- throw new WrongNumberOfArguments(method.getArguments().size(), arguments.size());
- }
-
- val stackFrame = IntStream.range(0, method.getArguments().size())
- .boxed()
- .collect(toMap(
- index -> method.getArguments().get(index).getId(),
- index -> arguments.get(index)));
- return new Environment(stackFrame);
- }
-
- private class Executor implements StatementNodeVisitor<Optional<InterpreterValue>> {
- private Environment environment;
-
- public Executor(Environment environment) {
- this.environment = environment;
- }
-
- @Override
- public Optional<InterpreterValue> visit(ReturnNode returnNode) {
- return Optional.of(eval(environment, returnNode.getValue()));
- }
-
- @Override
- public Optional<InterpreterValue> visit(ExpressionStatementNode expressionStatement) {
- eval(environment, expressionStatement.getExpression());
- return Optional.empty();
- }
+ val argumentExpressions = arguments
+ .stream()
+ .<ExpressionNode>map(LiteralNode::literal)
+ .collect(Collectors.toList());
+ return eval(new Environment(project, ImmutableMap.of()),
+ staticMethodCall(className, methodName, argumentExpressions));
}
}
diff --git a/src/main/java/org/zwobble/couscous/values/ConcreteType.java b/src/main/java/org/zwobble/couscous/values/ConcreteType.java
index db5209e..6aea1db 100644
--- a/src/main/java/org/zwobble/couscous/values/ConcreteType.java
+++ b/src/main/java/org/zwobble/couscous/values/ConcreteType.java
@@ -5,14 +5,23 @@ import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.zwobble.couscous.Project;
+import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.MethodNode;
import org.zwobble.couscous.interpreter.Arguments;
+import org.zwobble.couscous.interpreter.Environment;
import org.zwobble.couscous.interpreter.NoSuchMethod;
import org.zwobble.couscous.interpreter.UnexpectedValueType;
import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
import com.google.common.collect.ImmutableMap;
+import static java.util.stream.Collectors.toMap;
+import static org.zwobble.couscous.interpreter.Executor.exec;
+
import lombok.val;
public class ConcreteType<T> {
@@ -47,15 +56,61 @@ public class ConcreteType<T> {
return new ConcreteType<T>(name, methods.build(), staticMethods.build());
}
}
+
+ public static ConcreteType<?> fromNode(ClassNode classNode) {
+ val staticMethods = classNode.getMethods()
+ .stream()
+ .filter(method -> method.isStatic())
+ .collect(toMap(
+ method -> method.getName(),
+ method -> {
+ Supplier<List<ConcreteType<?>>> argumentTypes = () -> method.getArguments()
+ .stream()
+ .<ConcreteType<?>>map(arg -> arg.getType())
+ .collect(Collectors.toList());
+ return new StaticMethodValue(argumentTypes, arguments -> {
+ val environment = buildEnvironment(method, arguments);
+
+ for (val statement : method.getBody()) {
+ val result = exec(environment, statement);
+ if (result.isPresent()) {
+ return result.get();
+ }
+ }
+ return UnitValue.UNIT;
+ });
+ }));
+ return new ConcreteType<Void>(
+ classNode.getName(),
+ ImmutableMap.of(),
+ staticMethods);
+ }
+
+ private static Environment buildEnvironment(
+ final MethodNode method,
+ Arguments arguments) {
+
+ val stackFrame = IntStream.range(0, method.getArguments().size())
+ .boxed()
+ .collect(toMap(
+ index -> method.getArguments().get(index).getId(),
+ index -> arguments.get(index)));
+ return new Environment(new Project() {
+ @Override
+ public ConcreteType<?> findClass(String name) {
+ throw new UnsupportedOperationException();
+ }
+ }, stackFrame);
+ }
private String name;
private Map<String, MethodValue<T>> methods;
- private ImmutableMap<String, StaticMethodValue> staticMethods;
+ private Map<String, StaticMethodValue> staticMethods;
public ConcreteType(
String name,
Map<String, MethodValue<T>> methods,
- ImmutableMap<String, StaticMethodValue> staticMethods) {
+ Map<String, StaticMethodValue> staticMethods) {
this.name = name;
this.methods = methods;
this.staticMethods = staticMethods;
diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
index d17e7fc..72a3ab4 100644
--- a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
@@ -26,8 +26,10 @@ import lombok.val;
public class EvaluatorTests {
@Test
public void valueOfAssignmentExpressionIsNewValue() {
- val arg = new FormalArgumentNode(42, "x");
- val environment = new Environment(ImmutableMap.of(arg.getId(), new StringValue("[initial value]")));
+ val arg = new FormalArgumentNode(42, () -> StringValue.TYPE, "x");
+ val environment = new Environment(
+ new MapBackedProject(ImmutableMap.of()),
+ ImmutableMap.of(arg.getId(), new StringValue("[initial value]")));
val result = eval(environment, assign(arg, literal("[updated value]")));
assertEquals(new StringValue("[updated value]"), result);
}
@@ -100,6 +102,8 @@ public class EvaluatorTests {
}
private static Environment emptyEnvironment() {
- return new Environment(ImmutableMap.of());
+ return new Environment(
+ new MapBackedProject(ImmutableMap.of("java.lang.Integer", IntegerValue.TYPE)),
+ ImmutableMap.of());
}
}
diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java
index bfdadf8..c764261 100644
--- a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java
@@ -1,9 +1,6 @@
package org.zwobble.couscous.tests.interpreter;
-import java.util.Map;
-
import org.junit.Test;
-import org.zwobble.couscous.Project;
import org.zwobble.couscous.ast.Assignment;
import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
@@ -13,6 +10,7 @@ import org.zwobble.couscous.ast.MethodNode;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.interpreter.Interpreter;
import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
+import org.zwobble.couscous.values.ConcreteType;
import org.zwobble.couscous.values.InterpreterValue;
import org.zwobble.couscous.values.StringValue;
@@ -47,7 +45,7 @@ public class InterpreterTests {
@Test
public void canPassValueToMethod() {
- val arg = new FormalArgumentNode(42, "x");
+ val arg = new FormalArgumentNode(42, () -> StringValue.TYPE, "x");
val method = staticMethod("hello")
.argument(arg)
.statement(new ReturnNode(reference(arg)));
@@ -68,7 +66,7 @@ public class InterpreterTests {
@Test
public void canReassignValueToArgument() {
- val arg = new FormalArgumentNode(42, "x");
+ val arg = new FormalArgumentNode(42, () -> StringValue.TYPE, "x");
val method = staticMethod("hello")
.argument(arg)
.statement(new ExpressionStatementNode(new Assignment(reference(arg), LiteralNode.literal("[updated value]"))))
@@ -85,22 +83,8 @@ public class InterpreterTests {
.build();
val className = "com.example.Program";
val interpreter = new Interpreter(new MapBackedProject(ImmutableMap.of(
- className, classNode)));
+ className, ConcreteType.fromNode(classNode))));
return interpreter.run(className, method.getName(), asList(arguments));
}
-
- private static class MapBackedProject implements Project {
- private Map<String, ClassNode> classes;
-
- public MapBackedProject(Map<String, ClassNode> classes) {
- this.classes = classes;
- }
-
- @Override
- public ClassNode findClass(String name) {
- return classes.get(name);
- }
-
- }
}
diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java b/src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java
new file mode 100644
index 0000000..0874afb
--- /dev/null
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java
@@ -0,0 +1,19 @@
+package org.zwobble.couscous.tests.interpreter;
+
+import java.util.Map;
+
+import org.zwobble.couscous.Project;
+import org.zwobble.couscous.values.ConcreteType;
+
+public class MapBackedProject implements Project {
+ private Map<String, ConcreteType<?>> classes;
+
+ public MapBackedProject(Map<String, ConcreteType<?>> classes) {
+ this.classes = classes;
+ }
+
+ @Override
+ public ConcreteType<?> findClass(String name) {
+ return classes.get(name);
+ }
+}