【问题标题】:Lisp macros quotation implementation in JavaScriptJavaScript 中的 Lisp 宏引用实现
【发布时间】:2018-02-22 16:28:46
【问题描述】:

我有 basic scheme like lisp in JavaScript 并且有反引号和引号宏的问题,如果它们是数组的第一个元素,它们会评估符号,例如

> `(foo 10)

报错foo not found

它适用于这样的代码

> (define x '(1 2 3))
> (print `(1 2 ,@x 4 5))

我的 eval 函数如下所示:

function evaluate(code, env) {
  env = env || global_env;
  var value;
  if (typeof code === 'undefined') {
    return;
  }
  var first = code.car;
  var rest = code.cdr;
  if (first instanceof Pair) {
    value = evaluate(first, env);
  }
  if (typeof first === 'function') {
    value = first;
  }
  if (first instanceof Symbol) {
    value = env.get(first);
    if (value instanceof Macro) {
      return evaluate(value.invoke(rest, env), env);
    } else if (typeof value !== 'function') {
      throw new Error('Unknown function `' + first.name + '\'');
    }
  }
  if (typeof value === 'function') {
    var args = [];
    var node = rest;
    while (true) {
      if (node instanceof Pair) {
        args.push(evaluate(node.car, env));
        node = node.cdr;
      } else {
        break;
      }
    }
    var promises = args.filter((arg) => arg instanceof Promise);
    if (promises.length) {
      return Promise.all(args).then((args) => {
        return value.apply(env, args);
      });
    }
    return value.apply(env, args);
  } else if (code instanceof Symbol) {
    value = env.get(code);
    if (value === 'undefined') {
      throw new Error('Unbound variable `' + code.name + '\'');
    }
    return value;
  } else {
    return code;
  }
}

我的宏我只是一个返回 Pair 实例的函数,它与要评估的输入和代码参数相同,env 是一个 Environment 实例,它具有返回函数或变量的函数。

quasiquote 的宏如下所示:

  quasiquote: new Macro(function(arg) {
    var env = this;
    function recur(pair) {
      if (pair instanceof Pair) {
        var eval_pair;
        if (Symbol.is(pair.car.car, 'unquote-splicing')) {
          eval_pair = evaluate(pair.car.cdr.car, env);
          if (!eval_pair instanceof Pair) {
            throw new Error('Value of unquote-splicing need to be pair')
          }
          if (pair.cdr instanceof Pair) {
            if (eval_pair instanceof Pair) {
              eval_pair.cdr.append(recur(pair.cdr));
            } else {
              eval_pair = new Pair(eval_pair, recur(pair.cdr));
            }
          }
          return eval_pair;
        }
        if (Symbol.is(pair.car, 'unquote-splicing')) {
          eval_pair = evaluate(pair.cdr.car, env);
          if (!eval_pair instanceof Pair) {
            throw new Error('Value of unquote-splicing need to be pair')
          }
          return eval_pair;
        }
        if (Symbol.is(pair.car, 'unquote')) {
          return evaluate(pair.cdr.car, env);
        }
        var car = pair.car;
        if (car instanceof Pair) {
          car = recur(car);
        }
        var cdr = pair.cdr;
        if (cdr instanceof Pair) {
          cdr = recur(cdr);
        }
        return new Pair(car, cdr);
      }
      return pair;
    }
    return recur(arg.car);
  }),

是否应该将引号硬编码到评估函数中,不应该处理参数并按原样返回?然后反引号会为每个符号返回(引号符号)?

【问题讨论】:

    标签: javascript macros lisp language-implementation


    【解决方案1】:

    在@WillNess 的评论帮助下,我能够解决问题,我需要在解析器中添加特殊情况。

      if (first instanceof Symbol) {
        value = env.get(first);
        if (value instanceof Macro) {
          value = value.invoke(rest, env);
          if (value instanceof Constant) {
            return value.value;
          }
          return evaluate(value, env);
        }
      ...
    

    和 quasiquote 宏用常量包装输出。这只是:

    function Constant(value) {
       this.value = value;
    }
    

    【讨论】:

      【解决方案2】:

      quasiquote 的目的是返回可以被评估以返回新表单的代码。 例如:

      `(a b ,@list (,c ,d))
      

      ... 可能扩展为:

      (append (list 'a 'b) list (list (list c d)))
      

      ... 这样,在评估期间,它会生成预期的列表。 您可以找到一些方法来避免分配实际上是常量的列表,例如 '(a b) 而不是 (list 'a 'b),但这意味着不能保证从准引用构建的值始终是可修改的。

      但是,您似乎在运行时扩展宏,并在必要时评估其嵌套形式,因此在您的情况下,每次返回的列表都会不同。在这种情况下,我相信Constant 采用的方法应该可行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-07-17
        • 2014-09-03
        • 2013-05-06
        • 2014-10-30
        • 2011-03-28
        • 1970-01-01
        • 2012-04-06
        • 1970-01-01
        相关资源
        最近更新 更多