19.2.15.2 规范列表

如果宏调用的部分参数会被求值,而另一部分不会,则 Edebug 规范必须使用 规范列表(specification list)。 规范列表中的某些元素匹配一个或多个参数,另一些则会修改其后所有元素的处理方式。 后者被称为 规范关键字(specification keywords),是以 ‘&’ 开头的符号(例如 &optional)。

规范列表可以包含子列表,用于匹配本身就是列表的参数;也可以包含用于分组的向量。 子列表和分组将规范列表划分为多层级结构。规范关键字仅对其所在子列表或分组中的剩余元素生效。

当规范列表包含多选或重复结构时,与实际宏调用进行匹配可能需要**回溯**。 更多细节见 see 规范中的回溯

Edebug 规范具备正则表达式匹配的能力,外加一些上下文无关文法结构: 匹配括号平衡的子列表、对表达式的递归处理,以及通过间接规范实现的递归。

下表是规范列表中可使用的元素及其含义(相关示例见 see 规范示例):

sexp

单个不求值的 Lisp 对象,不进行插桩。 如果宏在宏展开时对某个参数求值,应当为该参数使用 sexp 而非 form

form

单个会被求值的表达式,会被插桩。 如果你的宏在表达式求值前用 lambda 包裹它,请改用 def-form。见下方 def-form

place

广义变量。See 广义变量

body

&rest form 的简写。见下方 &rest。 如果你的宏在代码体求值前用 lambda 包裹它,请改用 def-body。见下方 def-body

lambda-expr

未被引用的 lambda 表达式。

&optional

规范列表中此后的所有元素均为可选;一旦某个元素不匹配,Edebug 就在当前层级停止匹配。

若只需让部分元素可选,后面跟随必选元素,可使用 [&optional specs…]。 若要指定多个元素要么全部匹配、要么全都不匹配,可使用 &optional [specs…]。 参见 defun 示例。

&rest

规范列表中此后的所有元素可以重复零次或多次。 但在最后一次重复时,即使表达式提前结束而未能匹配所有元素,也不算错误。

若只需让部分元素重复,可使用 [&rest specs…]。 若要指定每次重复时多个元素都必须全部匹配,可使用 &rest [specs…]

&or

规范列表中此后的每个元素都是一个候选项。 必须匹配其中一个,否则 &or 规范失败。

&or 后的每个列表元素都是一个独立候选项。 若要将多个列表元素作为一个候选项,可用 […] 包裹。

&not

此后的每个元素都按类似 &or 的方式作为候选项匹配, 但只要有一个匹配,规范就失败; 如果全都不匹配,则虽不匹配任何内容,但 &not 规范成功。

&define

表明该规范用于定义型表达式。 Edebug 对定义型表达式的定义是:包含一个或多个代码表达式, 这些代码会被保存并在定义型表达式执行之后才运行。

定义型表达式本身不会被插桩(即 Edebug 不会在其前后暂停), 但其内部的表达式通常会被插桩。 &define 关键字应当作为列表规范的第一个元素。

nil

在当前参数列表层级没有更多参数可匹配时成功,否则失败。 参见子列表规范和反引号示例。

gate

不匹配任何参数,但在匹配当前层级剩余规范时,禁止通过 gate 回溯。 主要用于生成更精确的语法错误信息。 详情见 规范中的回溯,另见 let 示例。

&error

&error 后需跟随一个字符串作为错误信息; 它会中止插桩,并在迷你缓冲区显示该信息。

&interpose

由一个函数控制剩余代码的解析。 形式为 &interpose spec fun args..., 表示 Edebug 先用 spec 匹配代码, 然后以匹配到的代码、解析函数 pf 以及 args... 为参数调用 fun。 解析函数接受一个参数,指定用于解析剩余代码的规范列表,必须被精确调用一次, 并返回 fun 应当返回的插桩后代码。 例如 (&interpose symbolp pcase--match-pat-args) 匹配首元素为符号的表达式, 然后由 pcase--match-pat-args 根据该头部符号查找对应的规范, 并传给它收到的解析函数 pf

other-symbol

规范列表中的其他符号可以是谓词或间接规范。

如果该符号已有 Edebug 规范,这个**间接规范**可以是一个列表规范(用于替换该符号), 或是一个用于处理参数的函数。 该规范可用 def-edebug-elem-spec 定义:

Function: def-edebug-elem-spec element specification

定义用于替换符号 element 的规范 specificationspecification 必须是列表。

否则,该符号应当是一个谓词。 谓词会以当前参数为参数被调用,若返回 nil,则规范失败,该参数不插桩。

常用谓词包括 symbolpintegerpstringpvectorpatom

[elements…]

由元素组成的向量将这些元素归为一个 分组规范(group specification)。 其含义与向量本身无关。

"string"

参数必须是名为 string 的符号。 等价于引号符号 'symbol(符号名为 string),但推荐使用字符串形式。

(vector elements…)

参数必须是向量,且其元素匹配规范中的 elements。 参见反引号示例。

(elements…)

其他普通列表为 子列表规范(sublist specification),参数必须是列表,且其元素匹配规范 elements

子列表规范可以是点分列表,对应的参数列表也可以是点分列表。 或者,点分列表规范的最后一个 CDR 可以是另一个子列表规范 (通过分组或间接规范,例如 (spec . [(more specs…)])), 其元素匹配非点分列表的参数。 这在递归规范中非常有用,如反引号示例。 关于终止此类递归,参见上面 nil 规范的说明。

注意,写成 (specs . nil) 的子列表规范等价于 (specs), 而 (specs . (sublist-elements…)) 等价于 (specs sublist-elements…)

下面是仅可出现在 &define 之后的附加规范列表。参见 defun 示例。

&name

从代码中提取当前定义型表达式的名称。 形式为 &name [prestring] spec [poststring] fun args..., 表示 Edebug 先用 spec 匹配代码, 然后以当前名称、args...prestring、匹配到的代码和 poststring 拼接后的结果为参数调用 fun。 若 fun 省略,则默认使用拼接函数(在旧名称与新名称之间用 @ 连接)。

name

参数是一个符号,作为定义型表达式的名称。 是 [&name symbolp] 的简写。

定义型表达式不要求必须有名称字段,也可以有多个名称字段。

arg

参数是一个符号,作为定义型表达式的参数名。 但 lambda 列表关键字(以 ‘&’ 开头的符号)不允许使用。

lambda-list

匹配一个 lambda 列表,即 lambda 表达式的参数列表。

def-body

参数是定义中的代码体。 与上面的 body 类似,但定义体必须用另一个 Edebug 调用进行插桩, 以查找与该定义关联的信息。 对定义中最高层级的表达式列表使用 def-body

def-form

参数是定义中单个最高层级的表达式。 与 def-body 类似,但用于匹配单个表达式而非表达式列表。 作为特殊情况,def-form 还表示该表达式执行时不输出跟踪信息。 参见 interactive 示例。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike