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 |