原文地址:https://lisperator.net/pltut/
解释器(Interpreter)是在解析器理解语义之上,边解释边执行
遍历 AST,按正常顺序执行表达式即可。
Environment
Environment 在这里和作用域 scope 同义
作用域的继承其实可以用 JavaScript 的原型的继承实现
下面是环境(Environment)对象的定义:
function Environment(parent) {
this.vars = Object.create(parent ? parent.vars : null);
this.parent = parent;
}
Environment.prototype = {
extend: function () {
return new Environment(this);
},
lookup: function (name) {
var scope = this;
while (scope) {
if (Object.prototype.hasOwnProperty.call(scope.vars, name)) return scope;
scope = scope.parent;
}
},
get: function (name) {
if (name in this.vars) return this.vars[name];
throw new Error('Undefined variable ' + name);
},
set: function (name, value) {
var scope = this.lookup(name);
// let's not allow defining globals from a nested environment
if (!scope && this.parent) throw new Error('Undefined variable ' + name);
return ((scope || this).vars[name] = value);
},
def: function (name, value) {
return (this.vars[name] = value);
},
};
这样写有点不熟悉,用我比较熟悉的方式改写一下,上面的代码等价于
class Environment {
constructor(parent) {
// 全局作用域,vars 初始化为 Object.create(null)
// 子作用域,vars 初始化为 Object.create(parent.vars),在原型链上处在 parent 的下级
this.vars = Object.create(parent ? parent.vars : null);
this.parent = parent;
}
// 以当前的这个 environment 为父亲,创建一个子作用域
extend() {
return new Environment(this);
}
// 递归向父亲找存在那个 name 的作用域,scope 就是作用域的意思
lookup(name) {
var scope = this;
while (scope) {
// Object.prototype.hasOwnProperty.call(obj, name) 返回 obj 有没有 name 这个属性
if (Object.prototype.hasOwnProperty.call(scope.vars, name)) return scope;
scope = scope.parent;
}
}
get(name) {
if (name in this.vars) return this.vars[name];
throw new Error('Undefined variable ' + name);
}
// 为什么get不可以向上找,而set可以?
set(name, value) {
var scope = this.lookup(name);
// 在嵌套的作用域里不能定义全局变量
// scope == null: 有两种情况:1. 没有此作用域 2. 这是全局作用域
// this.parent != null: 说明不是全局作用域,只有全局作用域的 parent 为 null
if (!scope && this.parent) throw new Error('Undefined variable ' + name);
return ((scope || this).vars[name] = value);
}
// 在当前作用域上创建一个变量
def(name, value) {
return (this.vars[name] = value);
},
}
求值函数(evaluate function)
// exp 是 expression,表达式;env 是 environment,作用域
// 返回值
function evaluate(exp, env) {
switch (exp.type) {
// 常量节点,直接返回它们的值
case 'num':
case 'str':
case 'bool':
return exp.value;
// 变量节点,返回变量的值
case 'var':
// exp.value 是变量名
// env.get(varname) 根据变量名在当前作用域找值
return env.get(exp.value);
// 赋值节点。然后可以使用 env.set 来给变量设置值。注意变量值需要事先通过递归调用求值函数来得到。
case 'assign':
// 检查 left 是否是一个 var 节点
// 暂时不支持其他类型节点的赋值,所以如果不是,throw error
if (exp.left.type != 'var')
throw new Error('Cannot assign to ' + JSON.stringify(exp.left));
// set 最终返回的还是右值 value
return env.set(exp.left.value, evaluate(exp.right, env));
// 二元表达式节点
case 'binary':
var left = evaluate(exp.left, env);
// 把 && 和 || 逻辑运算单拿出来
// case "&&": return a !== false && b;
// case "||": return a !== false ? a : b;
if (exp.operator === '&&') {
return left !== false && evaluate(exp.right, env);
}
if (exp.operator === '||') {
return left !== false ? left : evaluate(exp.right, env);
}
// apply_op:应用一个操作符到两个操作数上
// 递归调用求值函数来计算左右操作数
return apply_op(exp.operator, left, evaluate(exp.right, env));
// 函数 lambda 节点
// 返回一个 JavaScript 闭包,用的时候 JavaScript 中可以像普通函数一样调用这个返回值
case 'lambda':
// make_lambda 返回一个闭包
return make_lambda(env, exp);
// if 节点
case 'if':
// 对条件 condition 求值
var cond = evaluate(exp.cond, env);
// 如果 condition 值不是 false,则继续对 then 分支求值
// 返回 then 分支的值
if (cond !== false) return evaluate(exp.then, env);
// 剩下的是 condition 值为 false 的情况
// 1. 如果有 else 分支,返回 else 分支的值
// 2. 如果没有 else 分支,返回 false
return exp.else ? evaluate(exp.else, env) : false;
// prog 节点,整体程序,可看作是一个表达式序列
// 返回最后一个表达式的值
case 'prog':
var val = false;
// 按顺序对每个表达式求值
exp.prog.forEach(function (exp) {
val = evaluate(exp, env);
});
// 如果表达式序列为空,则返回初始值 false
return val;
// call 节点,调用函数
case 'call':
// 对 func 属性求值,返回一个普通的 JS 函数
var func = evaluate(exp.func, env);
// 应该,接着对 args 属性求值,并传给前面返回的函数来调用
// myfun.apply(thisArg, argsArray):把 argsArray 作为参数传给 myfun 函数并执行。this 的值提供给 func 的调用。如果函数不在严格模式下,null 和 undefined 将被替换为全局对象,原始值将被转换为对象。
return func.apply(
null,
exp.args.map(function (arg) {
return evaluate(arg, env);
})
);
// 不是上面的任何情况,报错
default:
throw new Error("I don't know how to evaluate " + exp.type);
}
}
apply_op
function apply_op(op, a, b) {
// 操作数必须是数字
function num(x) {
if (typeof x != 'number') throw new Error('Expected number but got ' + x);
return x;
}
// 除数/模数不能是 0
function div(x) {
if (num(x) == 0) throw new Error('Divide by zero');
return x;
}
switch (op) {
case '+':
return num(a) + num(b);
case '-':
return num(a) - num(b);
case '*':
return num(a) * num(b);
case '/':
return num(a) / div(b);
case '%':
return num(a) % div(b);
// a() && b() 或 a() || b() 的情况被预处理掉了
// 见 evaluate
case '<':
return num(a) < num(b);
case '>':
return num(a) > num(b);
case '<=':
return num(a) <= num(b);
case '>=':
return num(a) >= num(b);
case '==':
return a === b;
case '!=':
return a !== b;
}
throw new Error("Can't apply operator " + op);
}
make_lambda
返回一个引用了环境对象和将要求值表达式的普通 JavaScript 函数
function make_lambda(env, exp) {
function lambda() {
// 形参
var names = exp.vars;
// 这里 env 是闭包创建时的 env,以该 env 为父亲创建一个新的作用域
var scope = env.extend();
// 把所有参数加到新的作用域内
for (var i = 0; i < names.length; ++i)
// 如果实参个数少于形参个数(arguments.length < names.length),多出来的参数初始化为 false
scope.def(names[i], i < arguments.length ? arguments[i] : false);
// 在新的作用域下对表达式的函数体进行求值
return evaluate(exp.body, scope);
}
return lambda;
}
原始功能函数(Primitive functions)
输入输出之类的函数
// some test code here
var code = 'sum = lambda(x, y) x + y; print(sum(2, 3));';
// remember, parse takes a TokenStream which takes an InputStream
var ast = parse(TokenStream(InputStream(code)));
// create the global environment
var globalEnv = new Environment();
// define the "print" primitive function
globalEnv.def('print', function (txt) {
console.log(txt);
});
// run the evaluator
evaluate(ast, globalEnv); // will print 5