我发现很难推断宏观扩张,并且想知道测试它们的最佳实践是什么.
所以如果我有一个宏,我可以通过执行一个级别的宏扩展macroexpand-1
.
(defmacro incf-twice (n) `(progn (incf ,n) (incf ,n)))
例如
(macroexpand-1 '(incf-twice n))
评估为
(PROGN (INCF N) (INCF N))
将它变成对宏的测试似乎很简单.
(equalp (macroexpand-1 '(incf-twice n)) '(progn (incf n) (incf n)))
是否有用于组织宏测试的既定惯例?还有,是否有一个库来总结s表达式之间的差异?
通常,测试宏不是Lisp和Common Lisp的强大部分之一.Common Lisp(和Lisp方言一般)使用过程宏.宏可以依赖于运行时上下文,编译时上下文,实现等.它们也可能有副作用(比如在编译时环境中注册事物,在开发环境中注册事物等等).
所以有人可能想测试一下:
生成正确的代码
生成的代码实际上做了正确的事情
生成的代码实际上在代码上下文中有效
在复杂的宏的情况下,宏参数实际上是正确解析的.想想loop
,defstruct
...宏.
宏检测到错误形成的参数代码.再次,想想宏loop
和defstruct
.
副作用
从上面的列表可以推断,在开发宏时最好最小化所有这些问题区域.但是:那里真的有非常复杂的宏.真可怕的.特别是那些习惯于实现新域特定语言的人.
使用equalp
比较代码之类的东西仅适用于相对简单的宏.宏通常会引入新的,未加工的和唯一的符号.因此equalp
将无法与那些人合作.
示例:(rotatef a b)
看起来很简单,但扩展实际上很复杂:
CL-USER 28 > (pprint (macroexpand-1 '(rotatef a b))) (PROGN (LET* () (LET ((#:|Store-Var-1234| A)) (LET* () (LET ((#:|Store-Var-1233| B)) (PROGN (SETQ A #:|Store-Var-1233|) (SETQ B #:|Store-Var-1234|)))))) NIL)
#:|Store-Var-1233|
是一个符号,它是未处理的并由宏新创建的.
另一个具有复杂扩展的简单宏形式将是(defstruct s b)
.
因此,需要一个s表达式模式匹配器来比较扩展.有一些可用,它们在这里很有用.需要在测试模式中确保生成的符号在需要时是相同的.
还有s-expression diff工具.例如diff-sexp.