Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 library kernerl.interpreter; | 4 library kernerl.interpreter; |
| 5 | 5 |
| 6 import 'dart:collection'; | |
| 7 import '../ast.dart'; | 6 import '../ast.dart'; |
| 8 | 7 |
| 9 class NotImplemented { | 8 class NotImplemented { |
| 10 String message; | 9 String message; |
| 11 | 10 |
| 12 NotImplemented(this.message); | 11 NotImplemented(this.message); |
| 13 | 12 |
| 14 String toString() => message; | 13 String toString() => message; |
| 15 } | 14 } |
| 16 | 15 |
| 17 class Interpreter { | 16 class Interpreter { |
| 18 Program program; | 17 Program program; |
| 19 | 18 |
| 20 Interpreter(this.program); | 19 Interpreter(this.program); |
| 21 | 20 |
| 22 void evalProgram() { | 21 void evalProgram() { |
|
Kevin Millikin (Google)
2017/03/15 10:01:23
Program's don't produce values, and maybe we shoul
zhivkag
2017/03/15 11:48:52
Done.
| |
| 23 assert(program.libraries.isEmpty); | 22 assert(program.libraries.isEmpty); |
| 24 Procedure mainMethod = program.mainMethod; | 23 Procedure mainMethod = program.mainMethod; |
| 25 Statement statementBlock = mainMethod.function.body; | 24 Statement statementBlock = mainMethod.function.body; |
| 26 // Evaluate only statement with one expression, ExpressionStatement, which | 25 // 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.
| |
| 27 // is StaticInvocation of the method print. | 26 // BlockStatement. |
| 28 if (statementBlock is Block) { | 27 if (statementBlock is Block) { |
| 29 Statement statement = statementBlock.statements.first; | 28 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.
| |
| 30 if (statement is ExpressionStatement) { | 29 var env = new Environment.empty(); |
| 31 statement.expression.accept1(new ExpressionEval1(), | 30 |
| 32 new ExpressionState(new HashMap<String, Object>())); | 31 for (Statement s in statementBlock.statements) { |
| 32 if (s is ExpressionStatement) { | |
| 33 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.
| |
| 34 } else if (s is VariableDeclaration) { | |
| 35 env.expand(s, s.initializer.accept1(exprEval, env)); | |
| 36 } else { | |
| 37 throw new NotImplemented('Evaluation for statement type ' | |
| 38 '${s.runtimeType} is not implemented.'); | |
| 39 } | |
| 33 } | 40 } |
| 34 } else { | 41 } else { |
| 35 throw new NotImplemented('Evaluation for statement type ' | 42 throw new NotImplemented('Evaluation for statement type ' |
| 36 '${statementBlock.runtimeType} is not implemented'); | 43 '${statementBlock.runtimeType} is not implemented.'); |
| 37 } | 44 } |
| 38 } | 45 } |
| 39 } | 46 } |
| 40 | 47 |
| 41 class InvalidExpressionError { | 48 class InvalidExpressionError { |
| 42 InvalidExpression expression; | 49 InvalidExpression expression; |
| 43 | 50 |
| 44 InvalidExpressionError(this.expression); | 51 InvalidExpressionError(this.expression); |
| 45 | 52 |
| 46 String toString() => 'Invalid expression at ' | 53 String toString() => |
| 47 '${expression.location.toString()}'; | 54 'Invalid expression at ${expression.location.toString()}'; |
| 48 } | 55 } |
| 49 | 56 |
| 50 class ExpressionState { | 57 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.
| |
| 51 Map<String, Object> environment; | 58 final VariableDeclaration variable; |
| 59 Value value; | |
| 52 | 60 |
| 53 ExpressionState(this.environment); | 61 Binding(this.variable, this.value); |
| 54 } | 62 } |
| 55 | 63 |
| 56 class ExpressionEval1 extends ExpressionVisitor1<Object> { | 64 class Environment { |
| 57 @override | 65 final List<Binding> bindings = <Binding>[]; |
| 58 Object defaultExpression(Expression node, arg) { | 66 Environment parent; |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
final Environment parent;
zhivkag
2017/03/15 11:48:52
Done.
| |
| 67 | |
| 68 Environment.empty(); | |
| 69 Environment(this.parent); | |
| 70 | |
| 71 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.
| |
| 72 for (Binding b in bindings) { | |
| 73 if (b.variable == variable) return b; | |
| 74 } | |
| 75 return null; | |
| 76 } | |
| 77 | |
| 78 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.
| |
| 79 return lookupBinding(variable)?.value ?? | |
| 80 parent?.lookup(variable) ?? | |
| 81 error(variable); | |
| 82 } | |
| 83 | |
| 84 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.
| |
| 85 var b = lookupBinding(variable); | |
| 86 if (b != null) { | |
| 87 b.value = value; | |
| 88 } else if (parent != null) { | |
| 89 parent.assign(variable, value); | |
| 90 } else { | |
| 91 error(variable); | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 void expand(VariableDeclaration variable, Value value) { | |
| 96 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.
| |
| 97 error(variable); | |
| 98 } | |
| 99 bindings.add(new Binding(variable, value)); | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 class ExpressionEval extends ExpressionVisitor1<Value> { | |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
class Evaluator?
zhivkag
2017/03/15 11:48:52
Done.
| |
| 104 Value defaultExpression(Expression node, arg) { | |
| 59 throw new NotImplemented('Evaluation for expressions of type ' | 105 throw new NotImplemented('Evaluation for expressions of type ' |
| 60 '${node.runtimeType} is not implemented.'); | 106 '${node.runtimeType} is not implemented.'); |
| 61 } | 107 } |
| 62 | 108 |
| 63 Object visitInvalidExpression1(InvalidExpression node, arg) => | 109 Value visitInvalidExpression1(InvalidExpression node, arg) { |
| 64 throw new InvalidExpressionError(node); | 110 throw new InvalidExpressionError(node); |
| 111 } | |
| 65 | 112 |
| 66 Object visitStaticInvocation(StaticInvocation node, arg) { | 113 Value visitVariableGet(VariableGet node, arg) { |
|
Kevin Millikin (Google)
2017/03/15 10:01:24
arg ==> env.
zhivkag
2017/03/15 11:48:53
Done.
| |
| 114 return arg.lookup(node.variable); | |
| 115 } | |
| 116 | |
| 117 Value visitVariableSet(VariableSet node, arg) { | |
| 118 return arg.assign(node.variable, node.value.accept1(this, arg)); | |
| 119 } | |
| 120 | |
| 121 Value visitStaticInvocation(StaticInvocation node, arg) { | |
| 67 if ('print' == node.name.toString()) { | 122 if ('print' == node.name.toString()) { |
| 68 // Special evaluation of print. | 123 // Special evaluation of print. |
| 69 var res = node.arguments.positional[0].accept1(this, arg); | 124 var res = node.arguments.positional[0].accept1(this, arg); |
| 70 print(res); | 125 print(res.value); |
| 126 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.
| |
| 71 } else { | 127 } else { |
| 72 throw new NotImplemented('Support for statement type ' | 128 throw new NotImplemented('Support for statement type ' |
| 73 '${node.runtimeType} is not implemented'); | 129 '${node.runtimeType} is not implemented'); |
| 74 } | 130 } |
| 75 } | 131 } |
| 76 | 132 |
| 133 Value visitNot(Not node, arg) { | |
| 134 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.
| |
| 135 } | |
| 136 | |
| 137 Value visitLogicalExpression(LogicalExpression node, arg) { | |
| 138 bool left = node.left.accept1(this, arg).asBool; | |
| 139 | |
| 140 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.
| |
| 141 return left | |
| 142 ? new BoolValue(true) | |
| 143 : new BoolValue(node.right.accept1(this, arg).asBool); | |
| 144 } else { | |
| 145 assert('&&' == node.operator); | |
| 146 return !left | |
| 147 ? new BoolValue(false) | |
| 148 : new BoolValue(node.right.accept1(this, arg).asBool); | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 Value visitConditionalExpression(ConditionalExpression node, arg) { | |
| 153 if (node.condition.accept1(this, arg).asBool) { | |
| 154 return node.then.accept1(this, arg); | |
| 155 } else { | |
| 156 return node.otherwise.accept1(this, arg); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 Value visitStringConcatenation(StringConcatenation node, arg) { | |
| 161 StringBuffer res = new StringBuffer(); | |
| 162 for (Expression e in node.expressions) { | |
| 163 res.write(e.accept1(this, arg).value); | |
| 164 } | |
| 165 return new StringValue(res.toString()); | |
| 166 } | |
| 167 | |
| 77 // Evaluation of BasicLiterals. | 168 // Evaluation of BasicLiterals. |
| 78 Object visitStringLiteral(StringLiteral node, arg) => node.value; | 169 Value visitStringLiteral(StringLiteral node, arg) => |
| 79 Object visitIntLiteral(IntLiteral node, arg) => node.value; | 170 new StringValue(node.value); |
| 80 Object visitDoubleLiteral(DoubleLiteral node, arg) => node.value; | 171 Value visitIntLiteral(IntLiteral node, arg) => new IntValue(node.value); |
| 81 Object visitBoolLiteral(BoolLiteral node, arg) => node.value; | 172 Value visitDoubleLiteral(DoubleLiteral node, arg) => |
| 82 Object visitNullLiteral(NullLiteral node, arg) => node.value; | 173 new DoubleValue(node.value); |
| 174 Value visitBoolLiteral(BoolLiteral node, arg) => new BoolValue(node.value); | |
| 175 Value visitNullLiteral(NullLiteral node, arg) => new NullValue(); | |
| 176 | |
| 177 Value visitLet(Let node, arg) { | |
| 178 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.
| |
| 179 letEnv.expand(node.variable, node.variable.initializer.accept1(this, arg)); | |
| 180 return node.body.accept1(this, letEnv); | |
| 181 } | |
| 83 } | 182 } |
| 183 | |
| 184 abstract class Value { | |
| 185 Object get value; | |
| 186 bool get asBool; | |
| 187 } | |
| 188 | |
| 189 class StringValue extends Value { | |
| 190 String value; | |
| 191 | |
| 192 bool get asBool => false; | |
| 193 | |
| 194 StringValue(this.value); | |
| 195 } | |
| 196 | |
| 197 class IntValue extends Value { | |
| 198 int value; | |
| 199 | |
| 200 bool get asBool => false; | |
| 201 | |
| 202 IntValue(this.value); | |
| 203 } | |
| 204 | |
| 205 class DoubleValue extends Value { | |
| 206 double value; | |
| 207 | |
| 208 bool get asBool => false; | |
| 209 | |
| 210 DoubleValue(this.value); | |
| 211 } | |
| 212 | |
| 213 class BoolValue extends Value { | |
| 214 bool value; | |
| 215 | |
| 216 bool get asBool => value; | |
| 217 | |
| 218 BoolValue(this.value); | |
| 219 } | |
| 220 | |
| 221 class NullValue extends Value { | |
| 222 Object get value => null; | |
| 223 bool get asBool => false; | |
| 224 } | |
| 225 | |
| 226 Object error(obj) { | |
| 227 // TODO: Implement accordingly with support for error handling. | |
| 228 throw new ArgumentError(obj); | |
| 229 } | |
| OLD | NEW |