| Index: pkg/kernel/lib/interpreter/interpreter.dart
|
| diff --git a/pkg/kernel/lib/interpreter/interpreter.dart b/pkg/kernel/lib/interpreter/interpreter.dart
|
| index f4f87794487782fad8e299b6762d142050ce8dcd..c6c5881a1a2c8591e210b371ed056ebbc6343a8f 100644
|
| --- a/pkg/kernel/lib/interpreter/interpreter.dart
|
| +++ b/pkg/kernel/lib/interpreter/interpreter.dart
|
| @@ -3,7 +3,6 @@
|
| // BSD-style license that can be found in the LICENSE file.
|
| library kernerl.interpreter;
|
|
|
| -import 'dart:collection';
|
| import '../ast.dart';
|
|
|
| class NotImplemented {
|
| @@ -16,24 +15,33 @@ class NotImplemented {
|
|
|
| class Interpreter {
|
| Program program;
|
| + Evaluator evaluator = new Evaluator();
|
|
|
| Interpreter(this.program);
|
|
|
| - void evalProgram() {
|
| + void run() {
|
| assert(program.libraries.isEmpty);
|
| Procedure mainMethod = program.mainMethod;
|
| Statement statementBlock = mainMethod.function.body;
|
| - // Evaluate only statement with one expression, ExpressionStatement, which
|
| - // is StaticInvocation of the method print.
|
| + // Executes only ExpressionStatements and VariableDeclarations in the top
|
| + // BlockStatement.
|
| if (statementBlock is Block) {
|
| - Statement statement = statementBlock.statements.first;
|
| - if (statement is ExpressionStatement) {
|
| - statement.expression.accept1(new ExpressionEval1(),
|
| - new ExpressionState(new HashMap<String, Object>()));
|
| + var env = new Environment.empty();
|
| +
|
| + for (Statement s in statementBlock.statements) {
|
| + if (s is ExpressionStatement) {
|
| + evaluator.eval(s.expression, env);
|
| + } else if (s is VariableDeclaration) {
|
| + var value = evaluator.eval(s.initializer ?? new NullLiteral(), env);
|
| + env.expand(s, value);
|
| + } else {
|
| + throw new NotImplemented('Evaluation for statement type '
|
| + '${s.runtimeType} is not implemented.');
|
| + }
|
| }
|
| } else {
|
| throw new NotImplemented('Evaluation for statement type '
|
| - '${statementBlock.runtimeType} is not implemented');
|
| + '${statementBlock.runtimeType} is not implemented.');
|
| }
|
| }
|
| }
|
| @@ -43,41 +51,181 @@ class InvalidExpressionError {
|
|
|
| InvalidExpressionError(this.expression);
|
|
|
| - String toString() => 'Invalid expression at '
|
| - '${expression.location.toString()}';
|
| + String toString() =>
|
| + 'Invalid expression at ${expression.location.toString()}';
|
| }
|
|
|
| -class ExpressionState {
|
| - Map<String, Object> environment;
|
| +class Binding {
|
| + final VariableDeclaration variable;
|
| + Value value;
|
|
|
| - ExpressionState(this.environment);
|
| + Binding(this.variable, this.value);
|
| }
|
|
|
| -class ExpressionEval1 extends ExpressionVisitor1<Object> {
|
| - @override
|
| - Object defaultExpression(Expression node, arg) {
|
| +class Environment {
|
| + final List<Binding> bindings = <Binding>[];
|
| + final Environment parent;
|
| +
|
| + Environment.empty() : parent = null;
|
| + Environment(this.parent);
|
| +
|
| + bool contains(VariableDeclaration variable) {
|
| + for (Binding b in bindings.reversed) {
|
| + if (identical(b.variable, variable)) return true;
|
| + }
|
| + return parent?.contains(variable) ?? false;
|
| + }
|
| +
|
| + Binding lookupBinding(VariableDeclaration variable) {
|
| + assert(contains(variable));
|
| + for (Binding b in bindings) {
|
| + if (identical(b.variable, variable)) return b;
|
| + }
|
| + return parent.lookupBinding(variable);
|
| + }
|
| +
|
| + Value lookup(VariableDeclaration variable) {
|
| + return lookupBinding(variable).value;
|
| + }
|
| +
|
| + void assign(VariableDeclaration variable, Value value) {
|
| + assert(contains(variable));
|
| + lookupBinding(variable).value = value;
|
| + }
|
| +
|
| + void expand(VariableDeclaration variable, Value value) {
|
| + assert(!contains(variable));
|
| + bindings.add(new Binding(variable, value));
|
| + }
|
| +}
|
| +
|
| +class Evaluator extends ExpressionVisitor1<Value> {
|
| + Value eval(Expression expr, Environment env) => expr.accept1(this, env);
|
| +
|
| + Value defaultExpression(Expression node, env) {
|
| throw new NotImplemented('Evaluation for expressions of type '
|
| '${node.runtimeType} is not implemented.');
|
| }
|
|
|
| - Object visitInvalidExpression1(InvalidExpression node, arg) =>
|
| - throw new InvalidExpressionError(node);
|
| + Value visitInvalidExpression1(InvalidExpression node, env) {
|
| + throw new InvalidExpressionError(node);
|
| + }
|
| +
|
| + Value visitVariableGet(VariableGet node, env) {
|
| + return env.lookup(node.variable);
|
| + }
|
|
|
| - Object visitStaticInvocation(StaticInvocation node, arg) {
|
| + Value visitVariableSet(VariableSet node, env) {
|
| + return env.assign(node.variable, eval(node.value, env));
|
| + }
|
| +
|
| + Value visitStaticInvocation(StaticInvocation node, env) {
|
| if ('print' == node.name.toString()) {
|
| // Special evaluation of print.
|
| - var res = node.arguments.positional[0].accept1(this, arg);
|
| - print(res);
|
| + var res = eval(node.arguments.positional[0], env);
|
| + print(res.value);
|
| + return new NullValue();
|
| } else {
|
| throw new NotImplemented('Support for statement type '
|
| '${node.runtimeType} is not implemented');
|
| }
|
| }
|
|
|
| + Value visitNot(Not node, env) {
|
| + return new BoolValue(!eval(node.operand, env).asBool);
|
| + }
|
| +
|
| + Value visitLogicalExpression(LogicalExpression node, env) {
|
| + if ('||' == node.operator) {
|
| + bool left = eval(node.left, env).asBool;
|
| + return left
|
| + ? new BoolValue(true)
|
| + : new BoolValue(eval(node.right, env).asBool);
|
| + } else {
|
| + assert('&&' == node.operator);
|
| + bool left = eval(node.left, env).asBool;
|
| + return !left
|
| + ? new BoolValue(false)
|
| + : new BoolValue(eval(node.right, env).asBool);
|
| + }
|
| + }
|
| +
|
| + Value visitConditionalExpression(ConditionalExpression node, env) {
|
| + if (eval(node.condition, env).asBool) {
|
| + return eval(node.then, env);
|
| + } else {
|
| + return eval(node.otherwise, env);
|
| + }
|
| + }
|
| +
|
| + Value visitStringConcatenation(StringConcatenation node, env) {
|
| + StringBuffer res = new StringBuffer();
|
| + for (Expression e in node.expressions) {
|
| + res.write(eval(e, env).value);
|
| + }
|
| + return new StringValue(res.toString());
|
| + }
|
| +
|
| // Evaluation of BasicLiterals.
|
| - Object visitStringLiteral(StringLiteral node, arg) => node.value;
|
| - Object visitIntLiteral(IntLiteral node, arg) => node.value;
|
| - Object visitDoubleLiteral(DoubleLiteral node, arg) => node.value;
|
| - Object visitBoolLiteral(BoolLiteral node, arg) => node.value;
|
| - Object visitNullLiteral(NullLiteral node, arg) => node.value;
|
| + Value visitStringLiteral(StringLiteral node, env) =>
|
| + new StringValue(node.value);
|
| + Value visitIntLiteral(IntLiteral node, env) => new IntValue(node.value);
|
| + Value visitDoubleLiteral(DoubleLiteral node, env) =>
|
| + new DoubleValue(node.value);
|
| + Value visitBoolLiteral(BoolLiteral node, env) => new BoolValue(node.value);
|
| + Value visitNullLiteral(NullLiteral node, env) => new NullValue();
|
| +
|
| + Value visitLet(Let node, env) {
|
| + var value = eval(node.variable.initializer, env);
|
| + var letEnv = new Environment(env);
|
| + letEnv.expand(node.variable, value);
|
| + return eval(node.body, letEnv);
|
| + }
|
| +}
|
| +
|
| +abstract class Value {
|
| + Object get value;
|
| + bool get asBool;
|
| +}
|
| +
|
| +class StringValue extends Value {
|
| + String value;
|
| +
|
| + bool get asBool => false;
|
| +
|
| + StringValue(this.value);
|
| +}
|
| +
|
| +class IntValue extends Value {
|
| + int value;
|
| +
|
| + bool get asBool => false;
|
| +
|
| + IntValue(this.value);
|
| +}
|
| +
|
| +class DoubleValue extends Value {
|
| + double value;
|
| +
|
| + bool get asBool => false;
|
| +
|
| + DoubleValue(this.value);
|
| +}
|
| +
|
| +class BoolValue extends Value {
|
| + bool value;
|
| +
|
| + bool get asBool => value;
|
| +
|
| + BoolValue(this.value);
|
| +}
|
| +
|
| +class NullValue extends Value {
|
| + Object get value => null;
|
| + bool get asBool => false;
|
| +}
|
| +
|
| +Object error(obj) {
|
| + // TODO: Implement accordingly with support for error handling.
|
| + throw new ArgumentError(obj);
|
| }
|
|
|