CL宏解析异常:如何避免CLSQL的[=语法被添加包前缀?
解决CLSQL宏生成函数中
[=]操作符被添加包前缀的问题 问题背景
我在Common Lisp中定义了如下宏:
(defmacro gen-update-func (func-name table id keyparam) `(defun ,func-name (db ,id) (let ((data)) (keyparams ,keyparam) (clsql:update-records ',table :database db :where [= ',id id] :av-pairs data))))
调用方式为:
(gen-update-func add-sp spender spenderid (vorname name))
macroexpand-1的展开结果符合预期:
(defun add-sp (db spenderid) (let ((data)) ;(keyparams (vorname name)) (clsql-sys:update-records 'spender :database db :where [= 'spenderid id] :av-pairs data)))
但调用生成的函数时,:where [= ',id id]被解析为:
:WHERE SPENDENDB::[= 'SPENDENDB::SPENDERID SPENDENDB::ID]
不符合预期,希望保留[=的字面量形式,不添加SPENDENDB::前缀,预期结果为:
:WHERE [= 'SPENDENDB::SPENDERID SPENDENDB::ID]
其中[=是CLSQL提供的特殊SQL语法,需要解决这个包前缀的问题。
问题原因
[=]是CLSQL提供的读取宏,在代码读取阶段就会被解析为对应包(这里是SPENDENDB)下的符号。当宏生成代码时,这个读取宏的解析时机过早,导致最终生成的SQL表达式带上了包前缀。
解决方法
方法1:使用CLSQL的sql宏显式构造表达式(推荐)
直接用CLSQL提供的sql宏来构造WHERE条件,完全绕开读取宏的包解析问题,这是最稳妥的方案:
(defmacro gen-update-func (func-name table id keyparam) `(defun ,func-name (db ,id) (let ((data)) (keyparams ,keyparam) (clsql:update-records ',table :database db :where (clsql:sql (:= (clsql:quote-sql ',id) ,id)) :av-pairs data))))
方法2:延迟读取宏的解析时机
通过将[=]相关表达式以字符串形式传递,再用clsql:sql-expression函数在运行时解析,避免宏展开阶段的包前缀添加:
(defmacro gen-update-func (func-name table id keyparam) `(defun ,func-name (db ,id) (let ((data)) (keyparams ,keyparam) (clsql:update-records ',table :database db :where (clsql:sql-expression (format nil "[= '~a ~a]" ',id ',id)) :av-pairs data))))
方法3:使用eval延迟表达式解析
通过quote阻止宏展开时的读取宏解析,在运行时用eval执行表达式(注意需确保变量上下文正确):
(defmacro gen-update-func (func-name table id keyparam) `(defun ,func-name (db ,id) (let ((data) (id-value ,id)) (keyparams ,keyparam) (clsql:update-records ',table :database db :where (eval `[= ',',id id-value]) :av-pairs data))))
内容的提问来源于stack exchange,提问作者klk2ptx
相关产品推荐
相关产品推荐

