代码之家  ›  专栏  ›  技术社区  ›  jcubic

Lisp宏报价在JavaScript中的实现

  •  1
  • jcubic  · 技术社区  · 8 年前

    我有 basic scheme like lisp in JavaScript 如果反引号和引号宏有问题,它们会在符号位于数组的第一个元素时求值,如

    > `(foo 10)
    

    给出错误 foo not found

    它适用于这样的代码

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

    我的评估函数如下所示:

    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是一个环境实例,它有一个返回函数或变量的函数get。

    Quasikote的宏如下所示:

      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);
      }),
    

    quote是否应该硬编码到evaluate函数中,该函数不应该处理参数并按原样返回它们?那么反引号将为每个符号返回(引号符号)?

    2 回复  |  直到 8 年前
        1
  •  1
  •   jcubic    8 年前

      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);
        }
      ...
    

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

    function Constant(value) {
       this.value = value;
    }
    
        2
  •  1
  •   coredump    8 年前

    Quasikote的目的是返回可以通过计算返回新表单的代码。

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

    ... 可能会扩展为:

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

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

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