From a34a00ba19f6ffb61762da4629b53b8a633f9d17 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 11 Feb 2016 21:33:25 +0000 Subject: Implement static constructor and fields in interpreter --- .../zwobble/couscous/interpreter/Environment.java | 24 ++++++++++----- .../zwobble/couscous/interpreter/Evaluator.java | 36 ++++++++++------------ .../org/zwobble/couscous/interpreter/Executor.java | 21 ++++++++----- .../zwobble/couscous/interpreter/Interpreter.java | 6 ++-- .../couscous/interpreter/values/ConcreteType.java | 28 +++++++++++------ .../interpreter/values/InstanceReceiverValue.java | 10 ++++++ .../interpreter/values/InterpreterFields.java | 36 ++++++++++++++++++++++ .../interpreter/values/ObjectInterpreterValue.java | 24 ++++----------- .../couscous/interpreter/values/ReceiverValue.java | 2 ++ .../interpreter/values/StaticReceiverValue.java | 21 +++++++++++++ .../couscous/tests/interpreter/EvaluatorTests.java | 11 +++---- 11 files changed, 148 insertions(+), 71 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/interpreter/values/InterpreterFields.java (limited to 'src') 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 types; private final Optional thisValue; private final Map stackFrame; - + private final Project project; + public Environment(Project project, Optional thisValue, Map> 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 thisValue, Map> 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 { 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 { return evalReceiver(methodCall.getReceiver()) .callMethod(environment, signature, arguments); } - - private ReceiverValue evalReceiver(Receiver receiver) { - return receiver.accept(new Receiver.Mapper() { - @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 arguments = evalArguments(call.getArguments()); return clazz.callConstructor(environment, arguments); } @@ -120,8 +105,7 @@ public class Evaluator implements ExpressionNodeMapper { @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 { .map(argument -> eval(argument)) .collect(Collectors.toList()); } + + private ReceiverValue evalReceiver(Receiver receiver) { + return receiver.accept(new Receiver.Mapper() { + @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> { public static InterpreterValue callMethod(Environment environment, CallableNode method, Optional 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 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 thisValue, PositionalArguments arguments) { @@ -46,6 +41,16 @@ public class Executor implements StatementNodeMapper> })); } + public static Optional exec(Environment environment, Iterable statements) { + for (StatementNode statement : statements) { + Optional result = exec(environment, statement); + if (result.isPresent()) { + return result; + } + } + return Optional.empty(); + } + public static Optional 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 ConcreteType.Builder builder(Class interpreterValueType, TypeName reference) { return new Builder<>(interpreterValueType, reference); } @@ -30,7 +26,7 @@ public class ConcreteType { public static ConcreteType.Builder classBuilder(String name) { return builder(ObjectInterpreterValue.class, name); } - + public static class Builder { private final ImmutableMap.Builder 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 getArgumentTypes(List arguments) { @@ -124,6 +128,7 @@ public class ConcreteType { private final TypeName name; private final Set superTypes; private final Map fields; + private final List staticConstructor; private final MethodValue constructor; private final Map methods; private final Map staticMethods; @@ -132,13 +137,14 @@ public class ConcreteType { TypeName name, Set superTypes, Map fields, - MethodValue constructor, + List staticConstructor, MethodValue constructor, Map methods, Map 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 getField(String fieldName) { return Optional.ofNullable(fields.get(fieldName)); } - + + public List getStaticConstructor() { + return staticConstructor; + } + public InterpreterValue callMethod(Environment environment, InterpreterValue receiver, MethodSignature signature, List 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 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 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 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 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 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)); -- cgit v1.2.3