aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble/couscous/interpreter
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-10-06 16:24:37 +0100
committerMichael Williamson <mike@zwobble.org>2015-10-06 16:24:37 +0100
commite6f463257720bee91a1b4954d97684dbb3398cbf (patch)
treec8d1f04dfe8928d33361108aefee7e7572dae1c7 /src/main/java/org/zwobble/couscous/interpreter
parent9685c8fb7fa14d5b75d828f8443dd948715ac4af (diff)
Add support for constructors and fields
Diffstat (limited to 'src/main/java/org/zwobble/couscous/interpreter')
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Environment.java15
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Evaluator.java34
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Executor.java20
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Interpreter.java3
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java12
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java66
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java1
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java17
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java6
12 files changed, 165 insertions, 27 deletions
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Environment.java b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
index 6da54ce..0bbc808 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Environment.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
@@ -19,16 +19,25 @@ import lombok.val;
public class Environment {
private final Project project;
+ private final Optional<InterpreterValue> thisValue;
private final Map<Integer, VariableEntry> stackFrame;
- public Environment(Project project, Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
+ public Environment(
+ Project project,
+ Optional<InterpreterValue> thisValue,
+ Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
this.project = project;
+ this.thisValue = thisValue;
this.stackFrame = stackFrame.entrySet()
.stream()
.collect(toMap(
entry -> entry.getKey().getId(),
entry -> VariableEntry.of(entry.getKey().getType(), entry.getValue())));
}
+
+ public Optional<InterpreterValue> getThis() {
+ return thisValue;
+ }
public InterpreterValue get(int variableId) {
checkVariableIsInScope(variableId);
@@ -51,8 +60,8 @@ public class Environment {
return project.findClass(className);
}
- public Environment withStackFrame(Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
- return new Environment(project, stackFrame);
+ public Environment withStackFrame(Optional<InterpreterValue> thisValue, Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
+ return new Environment(project, thisValue, stackFrame);
}
private void checkVariableIsInScope(int variableId) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
index ee67da0..18d7109 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
@@ -6,15 +6,20 @@ import java.util.stream.Collectors;
import org.zwobble.couscous.ast.AssignmentNode;
import org.zwobble.couscous.ast.ConstructorCallNode;
import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.FieldAccessNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.MethodCallNode;
import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
+import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
+import org.zwobble.couscous.ast.visitors.AssignableExpressionNodeVisitor;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
import org.zwobble.couscous.interpreter.values.BooleanInterpreterValue;
import org.zwobble.couscous.interpreter.values.InterpreterValue;
import org.zwobble.couscous.interpreter.values.InterpreterValues;
+import org.zwobble.couscous.interpreter.values.ObjectInterpreterValue;
+import org.zwobble.couscous.util.Casts;
import lombok.val;
@@ -44,9 +49,28 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
}
@Override
+ public InterpreterValue visit(ThisReferenceNode reference) {
+ return environment.getThis().get();
+ }
+
+ @Override
public InterpreterValue visit(AssignmentNode assignment) {
val value = eval(assignment.getValue());
- environment.put(assignment.getTarget().getReferentId(), value);
+ assignment.getTarget().accept(new AssignableExpressionNodeVisitor() {
+ @Override
+ public void visit(FieldAccessNode fieldAccess) {
+ val maybeLeft = Casts.tryCast(
+ ObjectInterpreterValue.class,
+ eval(fieldAccess.getLeft()));
+ val left = maybeLeft.orElseThrow(() -> new UnsupportedOperationException());
+ left.setField(fieldAccess.getFieldName(), value);
+ }
+
+ @Override
+ public void visit(VariableReferenceNode reference) {
+ environment.put(reference.getReferentId(), value);
+ }
+ });
return value;
}
@@ -84,6 +108,14 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
return clazz.callConstructor(environment, arguments);
}
+ @Override
+ public InterpreterValue visit(FieldAccessNode fieldAccess) {
+ // TODO: handle unbound fields
+ // TODO: handle non-existent fields
+ val left = eval(fieldAccess.getLeft());
+ return left.getField(fieldAccess.getFieldName());
+ }
+
private List<InterpreterValue> evalArguments(List<ExpressionNode> arguments) {
return arguments
.stream()
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
index e9304cb..5c26244 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Executor.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
@@ -4,9 +4,9 @@ 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.LocalVariableDeclarationNode;
-import org.zwobble.couscous.ast.MethodNode;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.StatementNode;
import org.zwobble.couscous.ast.VariableNode;
@@ -19,8 +19,13 @@ import static org.zwobble.couscous.interpreter.Evaluator.eval;
import lombok.val;
public class Executor implements StatementNodeMapper<Optional<InterpreterValue>> {
- public static InterpreterValue callMethod(Environment environment, MethodNode method, PositionalArguments arguments) {
- val innerEnvironment = buildEnvironment(environment, method, arguments);
+ public static InterpreterValue callMethod(
+ Environment environment,
+ CallableNode method,
+ Optional<InterpreterValue> thisValue,
+ PositionalArguments arguments) {
+
+ val innerEnvironment = buildEnvironment(environment, method, thisValue, arguments);
for (val statement : method.getBody()) {
val result = exec(innerEnvironment, statement);
@@ -32,9 +37,10 @@ public class Executor implements StatementNodeMapper<Optional<InterpreterValue>>
}
private static Environment buildEnvironment(
- final Environment environment,
- final MethodNode method,
- PositionalArguments arguments) {
+ final Environment environment,
+ final CallableNode method,
+ Optional<InterpreterValue> thisValue,
+ PositionalArguments arguments) {
val stackFrame = new StackFrameBuilder();
for (int index = 0; index < method.getArguments().size(); index++) {
@@ -44,7 +50,7 @@ public class Executor implements StatementNodeMapper<Optional<InterpreterValue>>
findDeclarations(method.getBody()).forEach(declaration ->
stackFrame.declare(declaration));
- return environment.withStackFrame(stackFrame.build());
+ return environment.withStackFrame(thisValue, stackFrame.build());
}
private static Stream<VariableNode> findDeclarations(List<StatementNode> body) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
index 38ea2ac..88e9d0e 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
@@ -1,6 +1,7 @@
package org.zwobble.couscous.interpreter;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import org.zwobble.couscous.Project;
@@ -28,7 +29,7 @@ public class Interpreter {
.stream()
.<ExpressionNode>map(argument -> literal(argument.toPrimitiveValue().get()))
.collect(Collectors.toList());
- return eval(new Environment(project, ImmutableMap.of()),
+ return eval(new Environment(project, Optional.empty(), ImmutableMap.of()),
staticMethodCall(className, methodName, argumentExpressions));
}
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java b/src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java
new file mode 100644
index 0000000..31feaab
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java
@@ -0,0 +1,12 @@
+package org.zwobble.couscous.interpreter;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+@Value
+@EqualsAndHashCode(callSuper=false)
+public class NoSuchField extends InterpreterException {
+ private static final long serialVersionUID = 1L;
+
+ String fieldName;
+}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java
index b98eb20..06e8bee 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -30,4 +31,9 @@ public class BooleanInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.value(value));
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java b/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java
index 70ba96a..bd63d7f 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java
@@ -2,10 +2,12 @@ package org.zwobble.couscous.interpreter.values;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.FormalArgumentNode;
import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.interpreter.PositionalArguments;
import org.zwobble.couscous.interpreter.Environment;
@@ -15,6 +17,7 @@ import org.zwobble.couscous.interpreter.UnexpectedValueType;
import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
import org.zwobble.couscous.util.Casts;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import static java.util.stream.Collectors.toMap;
@@ -57,22 +60,38 @@ public class ConcreteType {
}
public ConcreteType build() {
- return new ConcreteType(name, methods.build(), staticMethods.build());
+ return new ConcreteType(
+ name,
+ new MethodValue(
+ ImmutableList.of(),
+ (environment, arguments) -> InterpreterValues.UNIT),
+ methods.build(),
+ staticMethods.build());
}
}
public static ConcreteType fromNode(ClassNode classNode) {
+ val constructor = new MethodValue(
+ getArgumentTypes(classNode.getConstructor().getArguments()),
+ (environment, arguments) -> Executor.callMethod(
+ environment,
+ classNode.getConstructor(),
+ Optional.of(arguments.getReceiver()),
+ arguments.getPositionalArguments()));
+
val methods = classNode.getMethods().stream()
.filter(method -> !method.isStatic())
.collect(toMap(
method -> method.getName(),
method -> {
- List<TypeName> argumentTypes = method.getArguments()
- .stream()
- .map(arg -> arg.getType())
- .collect(Collectors.toList());
+ val argumentTypes = getArgumentTypes(method.getArguments());
+
return new MethodValue(argumentTypes, (environment, arguments) -> {
- return Executor.callMethod(environment, method, arguments.getPositionalArguments());
+ return Executor.callMethod(
+ environment,
+ method,
+ Optional.of(arguments.getReceiver()),
+ arguments.getPositionalArguments());
});
}));
@@ -82,29 +101,42 @@ public class ConcreteType {
.collect(toMap(
method -> method.getName(),
method -> {
- List<TypeName> argumentTypes = method.getArguments()
- .stream()
- .map(arg -> arg.getType())
- .collect(Collectors.toList());
+ val argumentTypes = getArgumentTypes(method.getArguments());
+
return new StaticMethodValue(argumentTypes, (environment, arguments) -> {
- return Executor.callMethod(environment, method, arguments);
+ return Executor.callMethod(
+ environment,
+ method,
+ Optional.empty(),
+ arguments);
});
}));
return new ConcreteType(
classNode.getName(),
+ constructor,
methods,
staticMethods);
}
+
+ private static List<TypeName> getArgumentTypes(List<FormalArgumentNode> arguments) {
+ return arguments
+ .stream()
+ .map(arg -> arg.getType())
+ .collect(Collectors.toList());
+ }
- private TypeName name;
- private Map<String, MethodValue> methods;
- private Map<String, StaticMethodValue> staticMethods;
+ private final TypeName name;
+ private final MethodValue constructor;
+ private final Map<String, MethodValue> methods;
+ private final Map<String, StaticMethodValue> staticMethods;
public ConcreteType(
TypeName name,
+ MethodValue constructor,
Map<String, MethodValue> methods,
Map<String, StaticMethodValue> staticMethods) {
this.name = name;
+ this.constructor = constructor;
this.methods = methods;
this.staticMethods = staticMethods;
}
@@ -136,7 +168,11 @@ public class ConcreteType {
public InterpreterValue callConstructor(
Environment environment,
List<InterpreterValue> arguments) {
- return new ObjectInterpreterValue(this);
+ val object = new ObjectInterpreterValue(this);
+ constructor.apply(
+ environment,
+ MethodCallArguments.of(object, new PositionalArguments(arguments)));
+ return object;
}
private static <T extends Callable> T findMethod(Map<String, T> methods, String methodName, List<InterpreterValue> arguments) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java
index 92ba306..1effe6a 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -34,4 +35,9 @@ public class IntegerInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.value(value));
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java
index 6c3c2ec..78d27d2 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java
@@ -7,4 +7,5 @@ import org.zwobble.couscous.values.PrimitiveValue;
public interface InterpreterValue {
ConcreteType getType();
Optional<PrimitiveValue> toPrimitiveValue();
+ InterpreterValue getField(String fieldName);
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
index 4383353..acb6ef7 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
@@ -1,14 +1,18 @@
package org.zwobble.couscous.interpreter.values;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Optional;
import org.zwobble.couscous.values.PrimitiveValue;
public class ObjectInterpreterValue implements InterpreterValue {
private final ConcreteType type;
+ private final Map<String, InterpreterValue> fields;
public ObjectInterpreterValue(ConcreteType type) {
this.type = type;
+ this.fields = new HashMap<>();
}
@Override
@@ -20,4 +24,17 @@ public class ObjectInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.empty();
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ if (fields.containsKey(fieldName)) {
+ return fields.get(fieldName);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void setField(String fieldName, InterpreterValue value) {
+ fields.put(fieldName, value);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java
index aa3432a..a4dde19 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -39,4 +40,9 @@ public class StringInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.value(value));
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java
index 253932f..e221fee 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -26,4 +27,9 @@ public class UnitInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.UNIT);
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}