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); |
} |