吐嘈一下,emacs30 的 PEG 官方文档示例运行报错

哈哈,这个问题我也发现了,我是这么干的:

(put 'define-peg-rule 'lisp-indent-function 'defun)

和你的 setf 效果一样的

3 个赞

大佬,你研究出来带参数的规则,是如何调用的了吗,我试了好久也没搞明白 :joy:

终于成功了 :joy: 文档要是能给个完整的例子,也不用这么折腾,,

(define-peg-ruleset myrules
  (sign  () (or "+" "-" ""))
  (digit () [0-9])
  (nat   () digit (* digit))
  (int   () sign digit (* digit))
  (float () int "." nat)
  (not-char (peg) (and (not (funcall peg)) (any)))
  (not-char+ (char) (+ (not-char char))))

;; 从当前位置匹配所有非 "[" 位置的字符。
(with-peg-rules (myrules)
  (peg-run (peg (not-char+ (peg "[")))))

最后两个规则:not-char 用来匹配非参数的单个字符;not-char+ 用来匹配非参数的多个字符;其中 not-char+ 复用了 not-char 的规则。

3 个赞

不太理解,PEG 的功效是什么?

缩进不如用SMIE,内置很多major-mode都在用,比较稳定,PEG感觉像是给font-lock用的

+1

简单说就是:按照预先定义的规则来匹配和操作文本,类似正则表达式。官方文档说它:

PEGs are more expressive than regexps and potentially easier to use.

解析表达式文法(Parsing Expression Grammars,PEG)是一种用于描述语法规则的形式化工具,其核心作用是定义语言的结构并指导解析器如何将输入文本转换为结构化的语法树 。与传统的正则表达式和上下文无关文法(CFG)相比,PEG在表达能力、语法简洁性和实现效率上具有独特优势。

我觉得它最大的价值在语法怪异的 action 部分,就是对部分匹配到文本提供了灵活操作的机制。具体有什么用处还有待发掘,毕竟这是一个偏底层的包。可能需要再此基础上做进一步的封装才更好用。

www、中午睡觉去了。

由于某个规则要调用函数,它接受的也只能是函数,所以要包一层 peg 才行。除了你上面的用 with-peg-rules ,分别定义 define-peg-rule 然后套起来用也行:

(define-peg-rule minifycss--comment ()
  "/*" (* (not "*/") (any)) "*/")
(define-peg-rule two (x)
  (funcall x) (funcall x))
(with-temp-buffer
  (insert "/* *//**/")
  (goto-char (point-min))
  (peg-run (peg (two (peg minifycss--comment)))))
;;=> t

(with-temp-buffer
  (insert "/* *//**")
  (goto-char (point-min))
  (peg-run (peg (two (peg minifycss--comment)))))
;;=> nil

peg-parse 看上去似乎不用这些 peg 来包装,然而:

(with-temp-buffer
  (insert "/* *//**/")
  (goto-char (point-min))
  (peg-parse (two minifycss--comment)))
;; => t

(with-temp-buffer
  (insert "/**/") ;; single comment!
  (goto-char (point-min))
  (peg-parse (two minifycss--comment)))
;;=> t

以下 peg-parse 似乎能够正常工作,但这似乎就没有 peg-parse 的便利性了:

(with-temp-buffer
  (insert "/* *//**/")
  (goto-char (point-min))
  (peg-parse (peg (two (peg minifycss--comment)))))
;;=> t
(with-temp-buffer
  (insert "/* *///")
  (goto-char (point-min))
  (peg-parse (peg (two (peg minifycss--comment)))))
;;=> ERROR

感觉这种用法和 peg-parse 用起来似乎比较容易犯错… 也许这是一个 bug,因为注释是这么写的:

;;;; Rule argument and indirect calls:
;;
;; Rules can take arguments and those arguments can themselves be PEGs.
;; For example:
;;
;;     (define-peg-rule 2-or-more (peg)
;;       (funcall peg)
;;       (funcall peg)
;;       (* (funcall peg)))
;;
;;     ... (peg-parse
;;          ...
;;          (2-or-more (peg foo))
;;          ...
;;          (2-or-more (peg bar))
;;          ...)

感觉也许需要一点研究然后再报个 bug :rofl:

看源码, peg-parse 的用法应该是这样的:

(with-temp-buffer
  (insert "test:/* *//**/")
  (goto-char (point-min))
  (peg-parse (any-name prefix (two (peg minifycss--comment)))
             (prefix "test:")))

参数仍然是多个 PEX 表达式,最终是取第一个表达式的第一个symbol: any-name 作为函数的名字组装规则。只能说是比 (peg-run (peg … )) 的写法稍微简洁了一些,但是不多 :joy:

peg-parse 也不支持匹配成功或失败时的回调函数,而且搜索失败就会报错,不知道为啥要这样设计?有时候即使搜索成功也会有 warning。

原来如此,但我感觉这不管是在文档还是注释中都不怎么直观,我想想怎么说去 :imp:

这块注释里面的例子确实是错的,让人误以为是

;;; 错误的例子
(with-temp-buffer
  (insert "/* *//**/")
  (goto-char (point-min))
  (peg-parse (two minifycss--comment)))

这种写法。

3 个赞

我又仔细研究了一下源码, peg-parse 宏是想兼容 with-peg-rulespeg-run 两种用法,根据参数形式不同来确定使用哪种。

  • with-peg-rules 的参数形式:每个列表的第一个参数是变量名,后面是多个 pexs,变量可以引用;我称它为 rules 形式
  • peg-run 的参数形式是:多个 pexs,这种就是 pexs 形式

如果第一个参数都是 cons,没法区别这两种。当第一个参数不是cons时,就是使用 pexs 格式,否则是 rules 格式。

下面的第二个例子,第一个参数写成空字符串就可以使用啦。

;; peg-parse 参数为 rules 形式
(with-temp-buffer
  (insert "avioeuiewrhvi/* *//**/vhioehvioe")
  (goto-char (point-min))
  (peg-parse (text (+ (not two-comments) (any)) two-comments)
             (two-comments (two (peg minifycss--comment)))))

;; peg-parse 参数为 pexs 形式,第一个参数不能为 cons
(with-temp-buffer
  (insert "/* *//**/")
  (goto-char (point-min))
  (peg-parse "" (two (peg minifycss--comment))))
2 个赞

嗯,这就说的通了,如果第一个元素是 list 形式就表示想要使用 (with (xx1 xx2) (peg-run xx1)) 之类的,如果第一个元素是符号就表示用 (and 把 PEX 列表连起来:

(peg-parse p1 p2 p3) 
;; expands 
(peg-run (peg p1 p2 p3 ...))

但是这些信息应该在文档里面给出来 :rage:,果然我还是应该再发个邮件

2 个赞

之前一直以为 PEG 是 Python Expression Grammar 的意思 (汗

SMIE 也是难用无比,这两个(SMIE和PEG)更像是实验性的feature,先不管三七二十一塞进来看看 话说现在还有什么是tree sitter搞不定的吗?

能否不要回复的时候带脏字?

2 个赞

抱歉,已改正

1 个赞

peg和递归下降是等价的,但是看上去更像不带lookahead的LL (0) parser?从形状上看,peg文法是一个向右折叠的二叉树,每个结点的左子树一定没有左子树。

2 个赞

tree sitter 搞不定的上下文相关语法当然有,不过 PEG 单独也搞不定。

2 个赞

Fixed.

连修三次,估计困麻了 :rofl:

OK, I think I managed to push a proper fix to `emacs-30`, tho
very laboriously.  Clearly lack of sleep does not impact my
cognitive abilities.


        Stefan

#76555