【编译器实现笔记】3. 解释器(Interpreter)

原文地址: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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值