我有一个普遍的问题,我应该如何去创建正确的宏扩展函数或宏。
这是我在 LIPS 解释器中的宏定义(你可以在这里测试它https://jcubic.github.io/lips/)
function macro_expand(single) {
return async function(code, args) {
var env = args['env'] = this;
async function traverse(node) {
if (node instanceof Pair && node.car instanceof Symbol) {
try {
var value = env.get(node.car);
if (value instanceof Macro && value.defmacro) {
var result = await value.invoke(node.cdr, args, true);
if (result instanceof Pair) {
return result;
}
}
} catch (e) {
// ignore variables
}
}
var car = node.car;
if (car instanceof Pair) {
car = await traverse(car);
}
var cdr = node.cdr;
if (cdr instanceof Pair) {
cdr = await traverse(cdr);
}
var pair = new Pair(car, cdr);
return pair;
}
var new_code = code;
if (single) {
return quote((await traverse(code)).car);
} else {
while (true) {
new_code = await traverse(code);
if (code.toString() === new_code.toString()) {
break;
}
code = new_code;
}
return quote(new_code.car);
}
};
}
问题是这是虚拟宏扩展并忽略有关变量的错误,因此它无法评估宏准引用,因为它会抛出找不到变量的异常。所以我最终在我的扩展列表中得到了 quasiquote(注意:最新版本的代码甚至不尝试扩展 quasiquote,因为它被标记为不可扩展)。
编写宏扩展的方法是什么?使用宏扩展功能时,我应该扩展评估功能以使其工作不同吗?
我正在测试 biwascheme 如何创建这个函数,https: //www.biwascheme.org/但它也不能像我期望的那样工作:
它扩展:
biwascheme> (define-macro (foo name . body) `(let ((x ,(symbol->string name))) `(print ,x)))
biwascheme> (macroexpand '(foo bar))
=> ((lambda (x) (cons (quote print) (cons x (quote ())))) "bar")
biwascheme>
我希望它扩展到:
(let ((x "bar")) (quasiquote (print (unquote x))))
我的 lisp 回报:
lips> (define-macro (foo name . body)
`(let ((x ,(symbol->string name))) `(print ,x)))
;; macroexpand is a macro
lips> (macroexpand (foo bar))
(quasiquote (let ((x (unquote (symbol->string name))))
(quasiquote (print (unquote x)))))
即使我设置quasiquote
为可扩展,它也不会扩展准引号,因为它找不到名称,所以它会抛出被宏扩展忽略的异常。
任何代码甚至伪代码都将有助于在我的 LISP 中编写此函数或宏。
编辑:
我已经开始更新我的代码以将宏扩展合并到评估函数中,并在定义宏宏中进行了一项更改。调用宏扩展时,它不是第一次调用代码,这就是问题所在。:
前:
var rest = __doc__ ? macro.cdr.cdr : macro.cdr;
if (macro_expand) {
return rest.car;
}
var pair = rest.reduce(function(result, node) {
return evaluate(node, { env, dynamic_scope, error });
});
后:
var rest = __doc__ ? macro.cdr.cdr : macro.cdr;
var pair = rest.reduce(function(result, node) {
return evaluate(node, eval_args);
});
if (macro_expand) {
return quote(pair);
}
现在它可以正常工作了,所以我的 expand_macro 宏工作正常,这就是你应该如何编写 macro_expand。
EDIT2:我进一步重构了代码,事实证明我不需要在define-macro宏中使用macro_exapnd代码,只需取消引用该对(删除数据标志)。