mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:07:06 +00:00
Implements support for variables and evaluation of logic expressions
BUG= R=kmillikin@google.com Review-Url: https://codereview.chromium.org/2740433006 .
This commit is contained in:
parent
69b47ea010
commit
bd3cfd6979
|
@ -15,7 +15,7 @@ fail(String message) {
|
|||
main(List<String> args) {
|
||||
if (args.length == 1 && args[0].endsWith('.dill')) {
|
||||
var program = loadProgramFromBinary(args[0]);
|
||||
new Interpreter(program).evalProgram();
|
||||
new Interpreter(program).run();
|
||||
} else {
|
||||
return fail('One input binary file should be specified.');
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
Object visitStaticInvocation(StaticInvocation node, arg) {
|
||||
Value visitVariableGet(VariableGet node, env) {
|
||||
return env.lookup(node.variable);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue