aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Environment.java24
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Evaluator.java36
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Executor.java21
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Interpreter.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java28
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/InstanceReceiverValue.java10
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/InterpreterFields.java36
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java24
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/ReceiverValue.java2
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/StaticReceiverValue.java21
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java11
11 files changed, 148 insertions, 71 deletions
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Environment.java b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
index e57f8e5..daee3ed 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Environment.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
@@ -1,24 +1,29 @@
package org.zwobble.couscous.interpreter;
-import java.util.Map;
-import java.util.Optional;
import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.ast.VariableDeclaration;
import org.zwobble.couscous.ast.VariableNode;
import org.zwobble.couscous.ast.identifiers.Identifier;
import org.zwobble.couscous.interpreter.errors.UnboundVariable;
import org.zwobble.couscous.interpreter.errors.VariableNotInScope;
-import org.zwobble.couscous.interpreter.values.ConcreteType;
import org.zwobble.couscous.interpreter.values.InterpreterValue;
+import org.zwobble.couscous.interpreter.values.StaticReceiverValue;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
import static java.util.stream.Collectors.toMap;
public class Environment {
- private final Project project;
+ private final Map<TypeName, StaticReceiverValue> types;
private final Optional<InterpreterValue> thisValue;
private final Map<Identifier, VariableEntry> stackFrame;
-
+ private final Project project;
+
public Environment(Project project, Optional<InterpreterValue> thisValue, Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
this.project = project;
+ this.types = new HashMap<>();
this.thisValue = thisValue;
this.stackFrame = stackFrame.entrySet().stream()
.collect(toMap(
@@ -46,8 +51,13 @@ public class Environment {
put(variable.getDeclaration().getId(), value);
}
- public ConcreteType findClass(TypeName className) {
- return project.findClass(className);
+ public StaticReceiverValue findClass(TypeName className) {
+ if (!types.containsKey(className)) {
+ StaticReceiverValue value = new StaticReceiverValue(project.findClass(className));
+ types.put(className, value);
+ value.callStaticConstructor(this);
+ }
+ return types.get(className);
}
public Environment withStackFrame(Optional<InterpreterValue> thisValue, Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
index 6c40960..1d88c6b 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
@@ -55,8 +55,7 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
assignment.getTarget().accept(new AssignableExpressionNodeVisitor(){
@Override
public void visit(FieldAccessNode fieldAccess) {
- // TODO: handle static receiver
- InterpreterValue left = eval(((InstanceReceiver)fieldAccess.getLeft()).getExpression());
+ ReceiverValue left = evalReceiver(fieldAccess.getLeft());
left.setField(fieldAccess.getFieldName(), value);
}
@@ -91,24 +90,10 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
return evalReceiver(methodCall.getReceiver())
.callMethod(environment, signature, arguments);
}
-
- private ReceiverValue evalReceiver(Receiver receiver) {
- return receiver.accept(new Receiver.Mapper<ReceiverValue>() {
- @Override
- public ReceiverValue visit(ExpressionNode receiver) {
- return new InstanceReceiverValue(eval(receiver));
- }
-
- @Override
- public ReceiverValue visit(TypeName receiver) {
- return new StaticReceiverValue(environment.findClass(receiver));
- }
- });
- }
@Override
public InterpreterValue visit(ConstructorCallNode call) {
- ConcreteType clazz = environment.findClass(call.getType());
+ StaticReceiverValue clazz = environment.findClass(call.getType());
List<InterpreterValue> arguments = evalArguments(call.getArguments());
return clazz.callConstructor(environment, arguments);
}
@@ -120,8 +105,7 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
@Override
public InterpreterValue visit(FieldAccessNode fieldAccess) {
- // TODO: handle static receiver
- InterpreterValue left = eval(((InstanceReceiver)fieldAccess.getLeft()).getExpression());
+ ReceiverValue left = evalReceiver(fieldAccess.getLeft());
return left.getField(fieldAccess.getFieldName());
}
@@ -173,4 +157,18 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
.map(argument -> eval(argument))
.collect(Collectors.toList());
}
+
+ private ReceiverValue evalReceiver(Receiver receiver) {
+ return receiver.accept(new Receiver.Mapper<ReceiverValue>() {
+ @Override
+ public ReceiverValue visit(ExpressionNode receiver) {
+ return new InstanceReceiverValue(eval(receiver));
+ }
+
+ @Override
+ public ReceiverValue visit(TypeName receiver) {
+ return environment.findClass(receiver);
+ }
+ });
+ }
} \ No newline at end of file
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
index 6de4828..60f7d9c 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Executor.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
@@ -16,14 +16,9 @@ import static org.zwobble.couscous.interpreter.Evaluator.evalCondition;
public class Executor implements StatementNodeMapper<Optional<InterpreterValue>> {
public static InterpreterValue callMethod(Environment environment, CallableNode method, Optional<InterpreterValue> 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()) {
- final java.util.Optional<org.zwobble.couscous.interpreter.values.InterpreterValue> result = exec(innerEnvironment, statement);
- if (result.isPresent()) {
- return result.get();
- }
- }
- return UnitInterpreterValue.UNIT;
+ Environment innerEnvironment = buildEnvironment(environment, method, thisValue, arguments);
+ return exec(innerEnvironment, method.getBody())
+ .orElse(UnitInterpreterValue.UNIT);
}
private static Environment buildEnvironment(final Environment environment, final CallableNode method, Optional<InterpreterValue> thisValue, PositionalArguments arguments) {
@@ -46,6 +41,16 @@ public class Executor implements StatementNodeMapper<Optional<InterpreterValue>>
}));
}
+ public static Optional<InterpreterValue> exec(Environment environment, Iterable<StatementNode> statements) {
+ for (StatementNode statement : statements) {
+ Optional<InterpreterValue> result = exec(environment, statement);
+ if (result.isPresent()) {
+ return result;
+ }
+ }
+ return Optional.empty();
+ }
+
public static Optional<InterpreterValue> exec(Environment environment, StatementNode statement) {
return statement.accept(new Executor(environment));
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
index 5493e52..8ec461a 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
@@ -3,8 +3,8 @@ package org.zwobble.couscous.interpreter;
import com.google.common.collect.ImmutableMap;
import org.zwobble.couscous.ast.MethodSignature;
import org.zwobble.couscous.ast.TypeName;
-import org.zwobble.couscous.interpreter.values.ConcreteType;
import org.zwobble.couscous.interpreter.values.InterpreterValue;
+import org.zwobble.couscous.interpreter.values.StaticReceiverValue;
import java.util.List;
import java.util.Optional;
@@ -24,11 +24,11 @@ public class Interpreter {
Optional.empty(),
ImmutableMap.of());
- ConcreteType clazz = environment.findClass(className);
+ StaticReceiverValue clazz = environment.findClass(className);
MethodSignature signature = new MethodSignature(
methodName,
eagerMap(arguments, argument -> argument.getType().getName()),
returnType);
- return clazz.callStaticMethod(environment, signature, arguments);
+ return clazz.callMethod(environment, signature, arguments);
}
} \ No newline at end of file
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 41c0445..3a4443f 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java
@@ -1,10 +1,7 @@
package org.zwobble.couscous.interpreter.values;
import com.google.common.collect.ImmutableMap;
-import org.zwobble.couscous.ast.ClassNode;
-import org.zwobble.couscous.ast.FormalArgumentNode;
-import org.zwobble.couscous.ast.MethodSignature;
-import org.zwobble.couscous.ast.TypeName;
+import org.zwobble.couscous.ast.*;
import org.zwobble.couscous.interpreter.*;
import org.zwobble.couscous.interpreter.errors.NoSuchMethod;
import org.zwobble.couscous.interpreter.errors.WrongNumberOfArguments;
@@ -18,7 +15,6 @@ import static java.util.stream.Collectors.toMap;
import static org.zwobble.couscous.util.ExtraLists.list;
public class ConcreteType {
-
public static <T> ConcreteType.Builder<T> builder(Class<T> interpreterValueType, TypeName reference) {
return new Builder<>(interpreterValueType, reference);
}
@@ -30,7 +26,7 @@ public class ConcreteType {
public static ConcreteType.Builder<ObjectInterpreterValue> classBuilder(String name) {
return builder(ObjectInterpreterValue.class, name);
}
-
+
public static class Builder<T> {
private final ImmutableMap.Builder<String, FieldValue> fields = ImmutableMap.builder();
private MethodValue constructor = new MethodValue(list(), (environment, arguments) -> InterpreterValues.UNIT);
@@ -88,6 +84,7 @@ public class ConcreteType {
name,
Collections.emptySet(),
fields.build(),
+ list(),
constructor,
methods.build(),
staticMethods.build());
@@ -115,7 +112,14 @@ public class ConcreteType {
return Executor.callMethod(environment, method, Optional.empty(), arguments);
});
}));
- return new ConcreteType(classNode.getName(), classNode.getSuperTypes(), fields, constructor, methods, staticMethods);
+ return new ConcreteType(
+ classNode.getName(),
+ classNode.getSuperTypes(),
+ fields,
+ classNode.getStaticConstructor(),
+ constructor,
+ methods,
+ staticMethods);
}
private static List<TypeName> getArgumentTypes(List<FormalArgumentNode> arguments) {
@@ -124,6 +128,7 @@ public class ConcreteType {
private final TypeName name;
private final Set<TypeName> superTypes;
private final Map<String, FieldValue> fields;
+ private final List<StatementNode> staticConstructor;
private final MethodValue constructor;
private final Map<MethodSignature, MethodValue> methods;
private final Map<MethodSignature, StaticMethodValue> staticMethods;
@@ -132,13 +137,14 @@ public class ConcreteType {
TypeName name,
Set<TypeName> superTypes,
Map<String, FieldValue> fields,
- MethodValue constructor,
+ List<StatementNode> staticConstructor, MethodValue constructor,
Map<MethodSignature, MethodValue> methods,
Map<MethodSignature, StaticMethodValue> staticMethods)
{
this.name = name;
this.superTypes = superTypes;
this.fields = fields;
+ this.staticConstructor = staticConstructor;
this.constructor = constructor;
this.methods = methods;
this.staticMethods = staticMethods;
@@ -155,7 +161,11 @@ public class ConcreteType {
public Optional<FieldValue> getField(String fieldName) {
return Optional.ofNullable(fields.get(fieldName));
}
-
+
+ public List<StatementNode> getStaticConstructor() {
+ return staticConstructor;
+ }
+
public InterpreterValue callMethod(Environment environment, InterpreterValue receiver, MethodSignature signature, List<InterpreterValue> arguments) {
MethodValue method = findMethod(methods, signature, arguments);
return method.apply(environment, MethodCallArguments.of(receiver, new PositionalArguments(arguments)));
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/InstanceReceiverValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/InstanceReceiverValue.java
index 3c3d932..8179394 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/InstanceReceiverValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/InstanceReceiverValue.java
@@ -17,4 +17,14 @@ public class InstanceReceiverValue implements ReceiverValue {
ConcreteType type = value.getType();
return type.callMethod(environment, value, signature, arguments);
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ return value.getField(fieldName);
+ }
+
+ @Override
+ public void setField(String fieldName, InterpreterValue value) {
+ this.value.setField(fieldName, value);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterFields.java b/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterFields.java
new file mode 100644
index 0000000..fc082a5
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterFields.java
@@ -0,0 +1,36 @@
+package org.zwobble.couscous.interpreter.values;
+
+import org.zwobble.couscous.interpreter.InterpreterTypes;
+import org.zwobble.couscous.interpreter.errors.NoSuchField;
+import org.zwobble.couscous.interpreter.errors.UnboundField;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class InterpreterFields {
+ // TODO: distinguish static and non-static fields
+
+ private final ConcreteType type;
+ private final Map<String, InterpreterValue> fields;
+
+ public InterpreterFields(ConcreteType type) {
+ this.type = type;
+ this.fields = new HashMap<>();
+ }
+
+ public InterpreterValue getField(String fieldName) {
+ type.getField(fieldName).orElseThrow(() -> new NoSuchField(fieldName));
+ if (fields.containsKey(fieldName)) {
+ return fields.get(fieldName);
+ } else {
+ throw new UnboundField(fieldName);
+ }
+ }
+
+ public void setField(String fieldName, InterpreterValue value) {
+ FieldValue field = type.getField(fieldName)
+ .orElseThrow(() -> new NoSuchField(fieldName));
+ InterpreterTypes.checkIsInstance(field.getType(), value);
+ fields.put(fieldName, value);
+ }
+}
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 566ff80..e46ae45 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
@@ -1,20 +1,16 @@
package org.zwobble.couscous.interpreter.values;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import org.zwobble.couscous.interpreter.InterpreterTypes;
-import org.zwobble.couscous.interpreter.errors.NoSuchField;
-import org.zwobble.couscous.interpreter.errors.UnboundField;
import org.zwobble.couscous.values.PrimitiveValue;
+import java.util.Optional;
+
public class ObjectInterpreterValue implements InterpreterValue {
private final ConcreteType type;
- private final Map<String, InterpreterValue> fields;
+ private final InterpreterFields fields;
public ObjectInterpreterValue(ConcreteType type) {
this.type = type;
- this.fields = new HashMap<>();
+ this.fields = new InterpreterFields(type);
}
@Override
@@ -29,19 +25,11 @@ public class ObjectInterpreterValue implements InterpreterValue {
@Override
public InterpreterValue getField(String fieldName) {
- type.getField(fieldName).orElseThrow(() -> new NoSuchField(fieldName));
- if (fields.containsKey(fieldName)) {
- return fields.get(fieldName);
- } else {
- throw new UnboundField(fieldName);
- }
+ return fields.getField(fieldName);
}
@Override
public void setField(String fieldName, InterpreterValue value) {
- FieldValue field = type.getField(fieldName)
- .orElseThrow(() -> new NoSuchField(fieldName));
- InterpreterTypes.checkIsInstance(field.getType(), value);
- fields.put(fieldName, value);
+ fields.setField(fieldName, value);
}
} \ No newline at end of file
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/ReceiverValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/ReceiverValue.java
index 5b24f95..5d6d804 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/ReceiverValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/ReceiverValue.java
@@ -7,4 +7,6 @@ import java.util.List;
public interface ReceiverValue {
InterpreterValue callMethod(Environment environment, MethodSignature signature, List<InterpreterValue> arguments);
+ InterpreterValue getField(String fieldName);
+ void setField(String fieldName, InterpreterValue value);
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/StaticReceiverValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/StaticReceiverValue.java
index f7ce1f6..9844e33 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/StaticReceiverValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/StaticReceiverValue.java
@@ -2,18 +2,39 @@ package org.zwobble.couscous.interpreter.values;
import org.zwobble.couscous.ast.MethodSignature;
import org.zwobble.couscous.interpreter.Environment;
+import org.zwobble.couscous.interpreter.Executor;
import java.util.List;
public class StaticReceiverValue implements ReceiverValue {
private final ConcreteType type;
+ private final InterpreterFields fields;
public StaticReceiverValue(ConcreteType type) {
this.type = type;
+ this.fields = new InterpreterFields(type);
}
@Override
public InterpreterValue callMethod(Environment environment, MethodSignature signature, List<InterpreterValue> arguments) {
return type.callStaticMethod(environment, signature, arguments);
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ return fields.getField(fieldName);
+ }
+
+ @Override
+ public void setField(String fieldName, InterpreterValue value) {
+ fields.setField(fieldName, value);
+ }
+
+ public void callStaticConstructor(Environment environment) {
+ Executor.exec(environment, type.getStaticConstructor());
+ }
+
+ public InterpreterValue callConstructor(Environment environment, List<InterpreterValue> arguments) {
+ return type.callConstructor(environment, arguments);
+ }
}
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 21bc88f..9d5e18d 100644
--- a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
@@ -1,13 +1,15 @@
package org.zwobble.couscous.tests.interpreter;
import com.google.common.collect.ImmutableMap;
-import org.junit.Ignore;
import org.junit.Test;
import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionNode;
import org.zwobble.couscous.ast.FormalArgumentNode;
import org.zwobble.couscous.ast.MethodSignature;
-import org.zwobble.couscous.interpreter.*;
+import org.zwobble.couscous.interpreter.Environment;
+import org.zwobble.couscous.interpreter.JavaProject;
+import org.zwobble.couscous.interpreter.MapBackedProject;
+import org.zwobble.couscous.interpreter.StackFrameBuilder;
import org.zwobble.couscous.interpreter.errors.*;
import org.zwobble.couscous.interpreter.values.IntegerInterpreterValue;
import org.zwobble.couscous.interpreter.values.InterpreterValue;
@@ -39,11 +41,6 @@ import static org.zwobble.couscous.tests.util.ExtraAsserts.assertThrows;
import static org.zwobble.couscous.util.ExtraLists.list;
public class EvaluatorTests extends BackendEvalTests {
- @Ignore
- @Test
- public void staticConstructorIsExecutedOnReference() {
- }
-
@Test
public void valueOfAssignmentExpressionIsNewValue() {
FormalArgumentNode arg = formalArg(var(ANY_ID, "x", StringValue.REF));