Lisp宏在JavaScript中的实现

Lisp宏在JavaScript中的实现,javascript,macros,lisp,language-implementation,Javascript,Macros,Lisp,Language Implementation,我对反引号和引号宏有问题,如果它们是数组的第一个元素,它们会计算符号,就像 > `(foo 10) 未找到给定错误foo 它适用于这样的代码 > (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') {

我对反引号和引号宏有问题,如果它们是数组的第一个元素,它们会计算符号,就像

> `(foo 10)
未找到给定错误
foo

它适用于这样的代码

> (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;
  }
}
  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);
  }),
我的宏我只是一个返回Pair实例的函数,它与要计算的输入和代码参数相同,env是一个环境实例,它有一个返回函数或变量的函数get

QUISQUOTE的宏如下所示:

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;
  }
}
  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函数中,该函数不应该处理参数并按原样返回它们?然后backquote将为每个符号返回(quote symbol)

在@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);
    }
  ...
和QUISQUOTE宏使用常量包装输出。这只是:

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

quasiquote的目的是返回可以通过计算返回新表单的代码。 例如:

`(a b ,@list (,c ,d))
。。。可能会扩展为:

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

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