You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写支持非标准调用语法的Common Lisp宏?

好问题!要在Common Lisp里实现@route(*app*, "/", :GET)这种类Python装饰器风格的语法,核心是用**读取器宏(Reader Macro)**来扩展Lisp的原生语法。下面是具体步骤和可运行的代码示例:

1. 核心思路:语法转换

我们需要让Lisp的读取器把非标准的@route(...)语法,自动转换成你已经定义好的define-route宏调用格式。简单来说就是:
@route(*app*, "/", :GET) → (define-route (*app* "/" :GET))

2. 实现读取器宏函数

首先写一个函数,用来处理@开头的表达式,负责解析符号、清理参数并转换为标准Lisp形式:

(defun read-at-notation (stream char)
  (declare (ignore char))
  ;; 读取@后面的符号(比如route)
  (let* ((macro-short-name (read stream t nil t))
         ;; 读取括号内的所有内容,得到带逗号的参数列表
         (raw-args (read-delimited-list #\) stream t))
         ;; 移除参数中的逗号(Lisp不需要逗号分隔参数)
         (clean-args (remove-if (lambda (x) (eq x ',')) raw-args)))
    ;; 拼接成完整的宏名(route → DEFINE-ROUTE)
    (let ((full-macro-name (intern (concatenate 'string "DEFINE-" (string macro-short-name))
                                   *package*)))
      ;; 返回标准宏调用形式:(define-route (arg1 arg2 arg3))
      (list full-macro-name
            (if (= (length clean-args) 1)
                (car clean-args)
                clean-args)))))

3. 注册读取器宏

把@字符绑定到上面的处理函数,让Lisp读取器识别这个新语法:

(set-macro-character #\@ #'read-at-notation)

4. 测试你的新语法

现在你就可以用简洁的@route语法来定义路由了,和你原来的逻辑完全兼容:

@route(*app*, "/", :GET)
  (print "Welcome to ningle using GET!")

Lisp读取器会自动把它转换成你熟悉的标准宏调用:

(define-route (*app* "/" :GET)
  (print "Welcome to ningle using GET!"))

额外优化:支持关键字参数风格

如果想更贴近Python装饰器的写法,比如@route(app=*app*, uri="/", method=:GET),可以修改读取器宏函数来解析键值对:

(defun read-at-notation (stream char)
  (declare (ignore char))
  (let* ((macro-short-name (read stream t nil t))
         (raw-args (read-delimited-list #\) stream t))
         (clean-args (remove-if (lambda (x) (eq x ',')) raw-args))
         ;; 把app=*app*转换成:app *app*这种关键字参数形式
         (parsed-args (loop for (key val) on clean-args by #'cddr
                            collect (intern (string key) "KEYWORD")
                            collect val)))
    (let ((full-macro-name (intern (concatenate 'string "DEFINE-" (string macro-short-name))
                                   *package*)))
      (list full-macro-name parsed-args))))

此时你可以这样写路由:

@route(app=*app*, uri="/", method=:GET)
  (print "Welcome with keyword args!")

它会被转换成:

(define-route (:app *app* :uri "/" :method :GET)
  (print "Welcome with keyword args!"))

当然,你需要调整原有的define-route宏来支持关键字参数的解构逻辑。

注意事项

  • 读取器宏是全局生效的,要注意和其他库的语法冲突(如果有其他库也用@做读取器宏);
  • 字符串中的逗号不会被误处理,因为Lisp读取器会完整读取字符串内容;
  • 如果只想在特定包中启用该语法,可以在读取器函数中加入包判断逻辑。

内容的提问来源于stack exchange,提问作者MadPhysicist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:55:01