默认情况下,解析过程仅会移动当前缓冲区中的点位置,
解析成功时最终返回 t,失败则返回 nil。
也可以定义 解析动作(parsing actions),在解析文本的特定位置执行任意 Elisp 代码。
这些动作可以选择性地影响一个被称为 解析栈(parsing stack) 的结构,
它是解析过程返回值组成的列表。这些动作仅在解析过程最终成功时才会执行
(并返回值);如果解析失败,动作代码完全不会运行。
动作可以添加在规则定义的任意位置。 它们与解析表达式的区别在于以反引号(‘`’)开头, 后跟一个括号形式,其中必须包含一对连字符(‘--’)。 连字符左侧的符号会绑定从栈中弹出的值(在某种程度上类似于 lambda 形式的参数列表)。 连字符右侧代码产生的值会被压入栈中(类似于 lambda 的返回值)。 例如,上文的文法可以添加动作,将解析出的数字作为实际整数返回:
(with-peg-rules ((number sign digit (* digit
`(a b -- (+ (* a 10) b)))
`(sign val -- (* sign val)))
(sign (or (and "+" `(-- 1))
(and "-" `(-- -1))
(and "" `(-- 1))))
(digit [0-9] `(-- (- (char-before) ?0))))
(peg-run (peg number)))
栈中必须先存在值才能被弹出并返回 –
如果栈中没有足够的值绑定到动作左侧的项,它们会被绑定为 nil。
仅包含右侧项的动作会向栈中压入值;仅包含左侧项的动作会从栈中消耗(并丢弃)值。
解析结束时,栈中的值会以扁平列表形式返回。
要返回 PEX 匹配的字符串(而非仅移动点位置), 文法可以使用如下规则:
(one-word `(-- (point)) (+ [word]) `(start -- (buffer-substring start (point))))
上文的第一个动作将点的初始值压入栈中。
中间的 PEX 会将点移动到下一个词之后。
第二个动作从栈中弹出先前的值(绑定到变量 start),
然后使用该值从缓冲区提取子串并压入栈中。
这种模式非常常见,因此 PEG 提供了一个简写函数实现上述功能,
同时还有其他几种应对常见场景的简写: