JavaScript 中的 Lisp 宏引用实现
Lisp macros quotation implementation in JavaScript
我有 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;
}
}
my Macro i 只是一个函数,return Pair 的实例与要评估的输入和代码参数相同,env 是具有函数 get return 函数的 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);
}),
是否应该将引用硬编码到评估函数中,而不应按原样处理参数和 return 它们?那么每个符号的反引号会 return (引号)?
在@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);
}
...
and quasiquote 宏用常量包装输出。这只是:
function Constant(value) {
this.value = value;
}
准引用的目的是 return 可以评估为 return 新形式的代码。
例如:
`(a b ,@list (,c ,d))
... 可能展开为:
(append (list 'a 'b) list (list (list c d)))
... 这样,在评估期间,它会生成预期的列表。
您可以找到一些方法来避免分配实际上是常量的列表,例如 '(a b)
而不是 (list 'a 'b)
,但这意味着不能保证从 quasi-quotation 构建的值始终可以修改。
但是,您似乎在运行时扩展宏,在必要时评估其嵌套形式,因此在您的情况下,returned 列表每次都会不同。在那种情况下,我相信 Constant
所采用的方法应该有效。
我有 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;
}
}
my Macro i 只是一个函数,return Pair 的实例与要评估的输入和代码参数相同,env 是具有函数 get return 函数的 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);
}),
是否应该将引用硬编码到评估函数中,而不应按原样处理参数和 return 它们?那么每个符号的反引号会 return (引号)?
在@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);
}
...
and quasiquote 宏用常量包装输出。这只是:
function Constant(value) {
this.value = value;
}
准引用的目的是 return 可以评估为 return 新形式的代码。 例如:
`(a b ,@list (,c ,d))
... 可能展开为:
(append (list 'a 'b) list (list (list c d)))
... 这样,在评估期间,它会生成预期的列表。
您可以找到一些方法来避免分配实际上是常量的列表,例如 '(a b)
而不是 (list 'a 'b)
,但这意味着不能保证从 quasi-quotation 构建的值始终可以修改。
但是,您似乎在运行时扩展宏,在必要时评估其嵌套形式,因此在您的情况下,returned 列表每次都会不同。在那种情况下,我相信 Constant
所采用的方法应该有效。