diff options
| author | Michael Williamson <mike@zwobble.org> | 2016-02-11 21:33:25 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2016-02-11 21:33:25 +0000 |
| commit | a34a00ba19f6ffb61762da4629b53b8a633f9d17 (patch) | |
| tree | 6ae34a92c0dfe9a24df0b7e90da2da263a61f708 /src/main/java/org/zwobble | |
| parent | 6d02b3e3e6f8da9837a46efda4733bdd89e38aaf (diff) | |
Implement static constructor and fields in interpreter
Diffstat (limited to 'src/main/java/org/zwobble')
10 files changed, 144 insertions, 64 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); + } } |
