Chromium Code Reviews| 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..c0e7038b2f7954690fa3a2ac17196b41a0a42240 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 { |
| @@ -23,17 +22,25 @@ class Interpreter { |
| 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. |
| + // Evaluates only ExpressionStatements and VariableDeclarations in the top |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
Evaluates ==> Executes.
zhivkag
2017/03/15 11:48:53
Done.
|
| + // 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 exprEval = new ExpressionEval(); |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
So far ExpressionEval doesn't have any state, so I
zhivkag
2017/03/15 11:48:53
Done.
|
| + var env = new Environment.empty(); |
| + |
| + for (Statement s in statementBlock.statements) { |
| + if (s is ExpressionStatement) { |
| + s.expression.accept1(exprEval, env); |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
It reads more nicely to have a method
Value eval(
zhivkag
2017/03/15 11:48:52
Done.
|
| + } else if (s is VariableDeclaration) { |
| + env.expand(s, s.initializer.accept1(exprEval, env)); |
| + } 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 +50,180 @@ 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 { |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
Ultimately we should have really detailed comments
zhivkag
2017/03/15 11:48:53
Acknowledged.
|
| + 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>[]; |
| + Environment parent; |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
final Environment parent;
zhivkag
2017/03/15 11:48:52
Done.
|
| + |
| + Environment.empty(); |
| + Environment(this.parent); |
| + |
| + Binding lookupBinding(VariableDeclaration variable) { |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
I'd make this the recursive function (and never re
zhivkag
2017/03/15 11:48:52
Done.
|
| + for (Binding b in bindings) { |
| + if (b.variable == variable) return b; |
| + } |
| + return null; |
| + } |
| + |
| + Value lookup(VariableDeclaration variable) { |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
Then this is:
return lookupBinding(variable).valu
zhivkag
2017/03/15 11:48:52
Done.
|
| + return lookupBinding(variable)?.value ?? |
| + parent?.lookup(variable) ?? |
| + error(variable); |
| + } |
| + |
| + void assign(VariableDeclaration variable, Value value) { |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
And this is:
lookupBinding(variable).value = valu
zhivkag
2017/03/15 11:48:53
Done.
|
| + var b = lookupBinding(variable); |
| + if (b != null) { |
| + b.value = value; |
| + } else if (parent != null) { |
| + parent.assign(variable, value); |
| + } else { |
| + error(variable); |
| + } |
| + } |
| + |
| + void expand(VariableDeclaration variable, Value value) { |
| + if (lookupBinding(variable) != null) { |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
Variable declarations should be unique. We could
zhivkag
2017/03/15 11:48:52
Done.
|
| + error(variable); |
| + } |
| + bindings.add(new Binding(variable, value)); |
| + } |
| +} |
| + |
| +class ExpressionEval extends ExpressionVisitor1<Value> { |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
class Evaluator?
zhivkag
2017/03/15 11:48:52
Done.
|
| + Value defaultExpression(Expression node, arg) { |
| 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, arg) { |
| + throw new InvalidExpressionError(node); |
| + } |
| + |
| + Value visitVariableGet(VariableGet node, arg) { |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
arg ==> env.
zhivkag
2017/03/15 11:48:53
Done.
|
| + return arg.lookup(node.variable); |
| + } |
| - Object visitStaticInvocation(StaticInvocation node, arg) { |
| + Value visitVariableSet(VariableSet node, arg) { |
| + return arg.assign(node.variable, node.value.accept1(this, arg)); |
| + } |
| + |
| + Value visitStaticInvocation(StaticInvocation node, arg) { |
| if ('print' == node.name.toString()) { |
| // Special evaluation of print. |
| var res = node.arguments.positional[0].accept1(this, arg); |
| - print(res); |
| + print(res.value); |
| + return null; |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
Not null, but new NullValue().
We should make can
zhivkag
2017/03/15 11:48:52
Acknowledged.
|
| } else { |
| throw new NotImplemented('Support for statement type ' |
| '${node.runtimeType} is not implemented'); |
| } |
| } |
| + Value visitNot(Not node, arg) { |
| + return new BoolValue(!node.operand.accept1(this, arg).asBool); |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
I think I'd do the boolean conversion a little dif
zhivkag
2017/03/15 11:48:53
Acknowledged.
|
| + } |
| + |
| + Value visitLogicalExpression(LogicalExpression node, arg) { |
| + bool left = node.left.accept1(this, arg).asBool; |
| + |
| + if ('||' == node.operator) { |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
We might want a more literal interpretation of the
zhivkag
2017/03/15 11:48:52
Acknowledged.
|
| + return left |
| + ? new BoolValue(true) |
| + : new BoolValue(node.right.accept1(this, arg).asBool); |
| + } else { |
| + assert('&&' == node.operator); |
| + return !left |
| + ? new BoolValue(false) |
| + : new BoolValue(node.right.accept1(this, arg).asBool); |
| + } |
| + } |
| + |
| + Value visitConditionalExpression(ConditionalExpression node, arg) { |
| + if (node.condition.accept1(this, arg).asBool) { |
| + return node.then.accept1(this, arg); |
| + } else { |
| + return node.otherwise.accept1(this, arg); |
| + } |
| + } |
| + |
| + Value visitStringConcatenation(StringConcatenation node, arg) { |
| + StringBuffer res = new StringBuffer(); |
| + for (Expression e in node.expressions) { |
| + res.write(e.accept1(this, arg).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, arg) => |
| + new StringValue(node.value); |
| + Value visitIntLiteral(IntLiteral node, arg) => new IntValue(node.value); |
| + Value visitDoubleLiteral(DoubleLiteral node, arg) => |
| + new DoubleValue(node.value); |
| + Value visitBoolLiteral(BoolLiteral node, arg) => new BoolValue(node.value); |
| + Value visitNullLiteral(NullLiteral node, arg) => new NullValue(); |
| + |
| + Value visitLet(Let node, arg) { |
| + var letEnv = new Environment(arg); |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
It might be more clear to name the value instead o
zhivkag
2017/03/15 11:48:52
Acknowledged.
|
| + letEnv.expand(node.variable, node.variable.initializer.accept1(this, arg)); |
| + return node.body.accept1(this, 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); |
| } |