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 kernel.interpreter; | 4 library kernel.interpreter; |
| 5 | 5 |
| 6 import '../ast.dart'; | 6 import '../ast.dart'; |
| 7 import '../ast.dart' as ast show Class; | 7 import '../ast.dart' as ast show Class; |
| 8 | 8 |
| 9 class NotImplemented { | 9 class NotImplemented { |
| 10 String message; | 10 String message; |
| 11 | 11 |
| 12 NotImplemented(this.message); | 12 NotImplemented(this.message); |
| 13 | 13 |
| 14 String toString() => message; | 14 String toString() => message; |
| 15 } | 15 } |
| 16 | 16 |
| 17 class Interpreter { | 17 class Interpreter { |
| 18 Program program; | 18 Program program; |
| 19 StatementExecuter visitor = new StatementExecuter(); | 19 StatementExecuter visitor = new StatementExecuter(); |
| 20 Environment env = new Environment.empty(); | |
| 21 | 20 |
| 22 Interpreter(this.program); | 21 Interpreter(this.program); |
| 23 | 22 |
| 24 void run() { | 23 void run() { |
| 25 assert(program.libraries.isEmpty); | 24 assert(program.libraries.isEmpty); |
| 26 Procedure mainMethod = program.mainMethod; | 25 Procedure mainMethod = program.mainMethod; |
| 27 Statement statementBlock = mainMethod.function.body; | 26 Statement statementBlock = mainMethod.function.body; |
| 28 visitor.exec(statementBlock, env); | 27 Continuation cont = new Continuation(statementBlock, new State.empty()); |
| 28 visitor.trampolinedExecution(cont); | |
| 29 } | 29 } |
| 30 } | 30 } |
| 31 | 31 |
| 32 class InvalidExpressionError { | |
| 33 InvalidExpression expression; | |
| 34 | |
| 35 InvalidExpressionError(this.expression); | |
| 36 | |
| 37 String toString() => | |
| 38 'Invalid expression at ${expression.location.toString()}'; | |
| 39 } | |
| 40 | |
| 41 class Binding { | 32 class Binding { |
| 42 final VariableDeclaration variable; | 33 final VariableDeclaration variable; |
| 43 Value value; | 34 Value value; |
| 44 | 35 |
| 45 Binding(this.variable, this.value); | 36 Binding(this.variable, this.value); |
| 46 } | 37 } |
| 47 | 38 |
| 48 class Environment { | 39 class Environment { |
| 49 final List<Binding> bindings = <Binding>[]; | 40 final List<Binding> bindings = <Binding>[]; |
| 50 final Environment parent; | 41 final Environment parent; |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 75 assert(contains(variable)); | 66 assert(contains(variable)); |
| 76 lookupBinding(variable).value = value; | 67 lookupBinding(variable).value = value; |
| 77 } | 68 } |
| 78 | 69 |
| 79 void expand(VariableDeclaration variable, Value value) { | 70 void expand(VariableDeclaration variable, Value value) { |
| 80 assert(!contains(variable)); | 71 assert(!contains(variable)); |
| 81 bindings.add(new Binding(variable, value)); | 72 bindings.add(new Binding(variable, value)); |
| 82 } | 73 } |
| 83 } | 74 } |
| 84 | 75 |
| 76 /// Evaluate expressions. | |
| 85 class Evaluator extends ExpressionVisitor1<Value> { | 77 class Evaluator extends ExpressionVisitor1<Value> { |
| 86 Value eval(Expression expr, Environment env) => expr.accept1(this, env); | 78 Value eval(Expression expr, Environment env) => expr.accept1(this, env); |
| 87 | 79 |
| 88 Value defaultExpression(Expression node, env) { | 80 Value defaultExpression(Expression node, env) { |
| 89 throw new NotImplemented('Evaluation for expressions of type ' | 81 throw new NotImplemented('Evaluation for expressions of type ' |
| 90 '${node.runtimeType} is not implemented.'); | 82 '${node.runtimeType} is not implemented.'); |
| 91 } | 83 } |
| 92 | 84 |
| 93 Value visitInvalidExpression1(InvalidExpression node, env) { | 85 Value visitInvalidExpression1(InvalidExpression node, env) { |
| 94 throw new InvalidExpressionError(node); | 86 throw 'Invalid expression at ${node.location.toString()}'; |
| 95 } | 87 } |
| 96 | 88 |
| 97 Value visitVariableGet(VariableGet node, env) { | 89 Value visitVariableGet(VariableGet node, env) { |
| 98 return env.lookup(node.variable); | 90 return env.lookup(node.variable); |
| 99 } | 91 } |
| 100 | 92 |
| 101 Value visitVariableSet(VariableSet node, env) { | 93 Value visitVariableSet(VariableSet node, env) { |
| 102 return env.assign(node.variable, eval(node.value, env)); | 94 return env.assign(node.variable, eval(node.value, env)); |
| 103 } | 95 } |
| 104 | 96 |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 216 Value visitNullLiteral(NullLiteral node, env) => Value.nullInstance; | 208 Value visitNullLiteral(NullLiteral node, env) => Value.nullInstance; |
| 217 | 209 |
| 218 Value visitLet(Let node, env) { | 210 Value visitLet(Let node, env) { |
| 219 var value = eval(node.variable.initializer, env); | 211 var value = eval(node.variable.initializer, env); |
| 220 var letEnv = new Environment(env); | 212 var letEnv = new Environment(env); |
| 221 letEnv.expand(node.variable, value); | 213 letEnv.expand(node.variable, value); |
| 222 return eval(node.body, letEnv); | 214 return eval(node.body, letEnv); |
| 223 } | 215 } |
| 224 } | 216 } |
| 225 | 217 |
| 218 /// Represents a state which consists of current environment, continuation to be | |
| 219 /// applied and the current label. | |
| 220 class State { | |
| 221 final Environment env; | |
| 222 final Continuation continuation; | |
| 223 Label label; | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
List these in the same order as the constructor ar
zhivkag
2017/04/05 10:00:19
Done.
| |
| 224 | |
| 225 State(this.env, this.label, this.continuation); | |
| 226 State.withEnvironment(Environment env, State s) | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
This is basically a static function that we have t
zhivkag
2017/04/05 10:00:19
Done.
| |
| 227 : this(env, s.label, s.continuation); | |
| 228 | |
| 229 State.withContinuation(Continuation cont, State s) | |
| 230 : this(s.env, s.label, cont); | |
| 231 | |
| 232 State.empty() | |
|
Kevin Millikin (Google)
2017/04/05 08:21:48
Empty state seems ambiguous. Perhaps State.initia
zhivkag
2017/04/05 10:00:20
Done.
| |
| 233 : this(new Environment.empty(), new Label.empty(), | |
| 234 const Continuation.empty()); | |
| 235 | |
| 236 void addLabel(Statement s) { | |
| 237 label = new Label(s, continuation, label); | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
Suggest to make label be final and use a construct
zhivkag
2017/04/05 10:00:19
Done.
| |
| 238 } | |
| 239 | |
| 240 Label lookupLabel(LabeledStatement s) { | |
| 241 return label.lookupLabel(s); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 /// Represent the continuation for execution of statement. | |
| 246 class Continuation { | |
| 247 final Statement statement; | |
| 248 final State state; | |
| 249 | |
| 250 Continuation(this.statement, this.state); | |
| 251 const Continuation.empty() | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
This is the initial continuation, right? I think
zhivkag
2017/04/05 10:00:19
Yes, we shouldn't apply this continuation. exec sh
| |
| 252 : statement = null, | |
| 253 state = null; | |
| 254 } | |
| 255 | |
| 256 /// Represents a labeled statement, the corresponding continuation and the | |
| 257 /// enclosing label. | |
| 258 class Label { | |
| 259 final LabeledStatement statement; | |
| 260 final Continuation continuation; | |
| 261 final Label enclosingLabel; | |
| 262 | |
| 263 Label(this.statement, this.continuation, this.enclosingLabel); | |
| 264 Label.empty() | |
|
Kevin Millikin (Google)
2017/04/05 08:21:48
Does it make sense to use null as the sentinel for
zhivkag
2017/04/05 10:00:19
Done.
| |
| 265 : statement = null, | |
| 266 continuation = new Continuation.empty(), | |
| 267 enclosingLabel = null; | |
| 268 | |
| 269 Label lookupLabel(LabeledStatement s) { | |
| 270 if (identical(s, statement)) return this; | |
| 271 return enclosingLabel.lookupLabel(s); | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
In a well-formed program we will not get that encl
zhivkag
2017/04/05 10:00:19
Done.
| |
| 272 } | |
| 273 } | |
| 274 | |
| 226 /// Executes statements. | 275 /// Executes statements. |
| 227 class StatementExecuter extends StatementVisitor1 { | 276 /// |
| 277 /// Execution of a statement completes in one of the following ways: | |
| 278 /// - it completes normally, in which case the execution proceeds to applying | |
| 279 /// the next continuation | |
| 280 /// - it breaks with a label, in which case the corresponding continuation is | |
| 281 /// returned and applied | |
| 282 /// - it returns with or without value, TBD | |
| 283 /// - it throws, TBD | |
| 284 class StatementExecuter extends StatementVisitor1<Continuation> { | |
| 228 Evaluator evaluator = new Evaluator(); | 285 Evaluator evaluator = new Evaluator(); |
| 229 | 286 |
| 230 exec(Statement statement, env) => statement.accept1(this, env); | 287 void trampolinedExecution(Continuation continuation) { |
| 231 eval(Expression expression, env) => evaluator.eval(expression, env); | 288 while (!identical(continuation, const Continuation.empty())) { |
| 289 continuation = exec(continuation.statement, continuation.state); | |
| 290 } | |
| 291 } | |
| 232 | 292 |
| 233 defaultStatement(Statement node, env) { | 293 Continuation exec(Statement statement, state) => |
| 294 statement.accept1(this, state); | |
| 295 Value eval(Expression expression, env) => evaluator.eval(expression, env); | |
| 296 | |
| 297 Continuation defaultStatement(Statement node, state) { | |
| 234 throw notImplemented( | 298 throw notImplemented( |
| 235 m: "Execution is not implemented for statement:\n$node "); | 299 m: "Execution is not implemented for statement:\n$node "); |
| 236 } | 300 } |
| 237 | 301 |
| 238 visitInvalidStatement(InvalidStatement node, env) { | 302 Continuation visitInvalidStatement(InvalidStatement node, state) { |
| 239 throw "Invalid statement at ${node.location}"; | 303 throw "Invalid statement at ${node.location}"; |
| 240 } | 304 } |
| 241 | 305 |
| 242 visitExpressionStatement(ExpressionStatement node, env) { | 306 Continuation visitExpressionStatement(ExpressionStatement node, state) { |
| 243 return eval(node.expression, env); | 307 eval(node.expression, state.env); |
| 308 return state.continuation; | |
| 244 } | 309 } |
| 245 | 310 |
| 246 visitBlock(Block node, env) { | 311 Continuation visitBlock(Block node, state) { |
| 247 Environment blockEnv = new Environment(env); | 312 // Block statement introduces a new environment. |
| 248 for (Statement s in node.statements) { | 313 Environment blockEnv = new Environment(state.env); |
| 249 exec(s, blockEnv); | 314 State blockState = new State.withEnvironment(blockEnv, state); |
| 315 Continuation cont; | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
Maybe it's a little hard to reason about this code
zhivkag
2017/04/05 10:00:19
Done.
| |
| 316 for (Statement s in node.statements.reversed) { | |
| 317 cont = new Continuation(s, blockState); | |
| 318 blockState = new State.withContinuation(cont, blockState); | |
| 250 } | 319 } |
| 320 return cont != null ? cont : state.continuation; | |
| 251 } | 321 } |
| 252 | 322 |
| 253 visitEmptyStatement(EmptyStatement node, env) {} | 323 Continuation visitEmptyStatement(EmptyStatement node, state) { |
| 254 | 324 return state.continuation; |
| 255 visitIfStatement(IfStatement node, env) { | |
| 256 Value condition = eval(node.condition, env).toBoolean(); | |
| 257 if (identical(Value.trueInstance, condition)) { | |
| 258 exec(node.then, env); | |
| 259 } else { | |
| 260 exec(node.otherwise, env); | |
| 261 } | |
| 262 } | 325 } |
| 263 | 326 |
| 264 visitVariableDeclaration(VariableDeclaration node, env) { | 327 Continuation visitIfStatement(IfStatement node, state) { |
| 328 Value cond = eval(node.condition, state.env).toBoolean(); | |
| 329 if (identical(Value.trueInstance, cond)) { | |
| 330 return new Continuation(node.then, state); | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
Here we can (should?) have:
return exec(node.then
zhivkag
2017/04/05 10:00:19
Acknowledged.
| |
| 331 } else if (node.otherwise != null) { | |
| 332 return new Continuation(node.otherwise, state); | |
| 333 } | |
| 334 return state.continuation; | |
| 335 } | |
| 336 | |
| 337 Continuation visitLabeledStatement(LabeledStatement node, state) { | |
| 338 state.addLabel(node); | |
| 339 return new Continuation(node.body, state); | |
| 340 } | |
| 341 | |
| 342 Continuation visitBreakStatement(BreakStatement node, state) { | |
| 343 return state.lookupLabel(node.target).continuation; | |
| 344 } | |
| 345 | |
| 346 Continuation visitWhileStatement(WhileStatement node, state) { | |
| 347 Value cond = eval(node.condition, state.env).toBoolean(); | |
| 348 if (identical(Value.trueInstance, cond)) { | |
| 349 // Add continuation for the While statement to the linked list. | |
| 350 Continuation c = new Continuation(node, state); | |
| 351 // Continuation for the body of the loop. | |
| 352 return new Continuation(node.body, new State.withContinuation(c, state)); | |
|
Kevin Millikin (Google)
2017/04/05 08:21:47
return exec(node.body, state.withContinuation(c));
zhivkag
2017/04/05 10:00:19
Acknowledged.
| |
| 353 } | |
| 354 return state.continuation; | |
| 355 } | |
| 356 | |
| 357 Continuation visitDoStatement(DoStatement node, state) { | |
| 358 WhileStatement whileStatement = | |
| 359 new WhileStatement(node.condition, node.body); | |
| 360 Continuation c = new Continuation(whileStatement, state); | |
| 361 return new Continuation(node.body, new State.withContinuation(c, state)); | |
| 362 } | |
| 363 | |
| 364 Continuation visitVariableDeclaration(VariableDeclaration node, state) { | |
| 265 Value value = node.initializer != null | 365 Value value = node.initializer != null |
| 266 ? eval(node.initializer, env) | 366 ? eval(node.initializer, state.env) |
| 267 : Value.nullInstance; | 367 : Value.nullInstance; |
| 268 env.expand(node, value); | 368 state.env.expand(node, value); |
| 369 return state.continuation; | |
| 269 } | 370 } |
| 270 } | 371 } |
| 271 | 372 |
| 272 typedef Value Getter(Value receiver); | 373 typedef Value Getter(Value receiver); |
| 273 typedef void Setter(Value receiver, Value value); | 374 typedef void Setter(Value receiver, Value value); |
| 274 | 375 |
| 275 // TODO(zhivkag): Change misleading name. | |
| 276 // This is representation of a class in the interpreter, not a declaration. | |
| 277 class Class { | 376 class Class { |
| 278 static final Map<Reference, Class> _classes = <Reference, Class>{}; | 377 static final Map<Reference, Class> _classes = <Reference, Class>{}; |
| 279 | 378 |
| 280 Class superclass; | 379 Class superclass; |
| 281 List<Field> instanceFields = <Field>[]; | 380 List<Field> instanceFields = <Field>[]; |
| 282 List<Field> staticFields = <Field>[]; | 381 List<Field> staticFields = <Field>[]; |
| 283 // Implicit getters and setters for instance Fields. | 382 // Implicit getters and setters for instance Fields. |
| 284 Map<Name, Getter> getters = <Name, Getter>{}; | 383 Map<Name, Getter> getters = <Name, Getter>{}; |
| 285 Map<Name, Setter> setters = <Name, Setter>{}; | 384 Map<Name, Setter> setters = <Name, Setter>{}; |
| 286 // The initializers of static fields are evaluated the first time the field | 385 // The initializers of static fields are evaluated the first time the field |
| (...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 490 | 589 |
| 491 class NullValue extends LiteralValue { | 590 class NullValue extends LiteralValue { |
| 492 Object get value => null; | 591 Object get value => null; |
| 493 | 592 |
| 494 const NullValue(); | 593 const NullValue(); |
| 495 } | 594 } |
| 496 | 595 |
| 497 notImplemented({String m, Object obj}) { | 596 notImplemented({String m, Object obj}) { |
| 498 throw new NotImplemented(m ?? 'Evaluation for $obj is not implemented'); | 597 throw new NotImplemented(m ?? 'Evaluation for $obj is not implemented'); |
| 499 } | 598 } |
| OLD | NEW |