[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/peg 3604f37 5/7: Add some error reporting. If a parse f
From: |
Stefan Monnier |
Subject: |
[elpa] externals/peg 3604f37 5/7: Add some error reporting. If a parse fails, move to the |
Date: |
Sun, 10 Mar 2019 17:12:10 -0400 (EDT) |
branch: externals/peg
commit 3604f37df2d9d4e96e24f8159820ef9130a292fb
Author: Helmut Eller <address@hidden>
Commit: Helmut Eller <address@hidden>
Add some error reporting. If a parse fails, move to the
right-most position and show the alternatives for that position.
Also only return a signle value: the stack. Failures are
signalled with an error. To avoid signalling errors you can add a
rule which matches everything but indicates failure in some other
way e.g. by setting a global variable.
* peg.el (peg-errors): New variable.
(peg-translate-rules): Use it. Also raise errors at the
right-most point of failure.
(peg-translate-exp): Record failures.
(peg-record-failure): New function.
(peg-merge-errors): Used for error reporting.
(peg-postprocess): Just return the stack. Errors can be indicated
by leaving something on the stack.
(peg-parse-string): Add a NOERROR argument. Hopefully more in
line with string-match. Update tests accordingly.
---
ChangeLog | 21 +++++
peg.el | 310 ++++++++++++++++++++++++++++++++++++++------------------------
2 files changed, 213 insertions(+), 118 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 5cbd460..1960686 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2009-11-04 Helmut Eller <address@hidden>
+
+ Add some error reporting. If a parse fails, move to the
+ right-most position and show the alternatives for that position.
+
+ Also only return a signle value: the stack. Failures are
+ signalled with an error. To avoid signalling errors you can add a
+ rule which matches everything but indicates failure in some other
+ way e.g. by setting a global variable.
+
+ * peg.el (peg-errors): New variable.
+ (peg-translate-rules): Use it. Also raise errors at the
+ right-most point of failure.
+ (peg-translate-exp): Record failures.
+ (peg-record-failure): New function.
+ (peg-merge-errors): Used for error reporting.
+ (peg-postprocess): Just return the stack. Errors can be indicated
+ by leaving something on the stack.
+ (peg-parse-string): Add a NOERROR argument. Hopefully more in
+ line with string-match. Update tests accordingly.
+
2009-03-06 Helmut Eller <address@hidden>
* peg.el (peg-ex-arith): Minor cleanups.
diff --git a/peg.el b/peg.el
index 11673e0..5085eab 100644
--- a/peg.el
+++ b/peg.el
@@ -159,6 +159,11 @@ Note: a PE can't \"call\" rules by name."
;; executed in a postprocessing step, not during parsing.
(defvar peg-thunks)
+;; used at runtime to track the right-most error location. It's a
+;; pair (POSITION . EXPS ...). POSITION is the buffer position and
+;; EXPS is a list of rules/expressions that failed.
+(defvar peg-errors)
+
;; The basic idea is to translate each rule to a lisp function.
;; The result looks like
;; (let ((rule1 (lambda () code-for-rule1))
@@ -176,15 +181,22 @@ Note: a PE can't \"call\" rules by name."
(dolist (rule rules)
(puthash (car rule) (peg-normalize `(and . ,(cdr rule))) peg-rules))
(peg-check-cycles peg-rules)
- `(let ((peg-thunks '()) . ,(mapcar #'car rules))
+ `(let ((peg-thunks '()) (peg-errors '(-1))
+ . ,(mapcar #'car rules))
,@(mapcar (lambda (rule)
(let ((name (car rule)))
`(setq ,name
(lambda ()
,(peg-translate-exp (gethash name peg-rules))))))
rules)
- (when (funcall ,(car (car rules)))
- (peg-postprocess peg-thunks)))))
+ (cond ((funcall ,(car (car rules)))
+ (peg-postprocess peg-thunks))
+ (t
+ (goto-char (car peg-errors))
+ (error "Parse error at %d (expecting %S)"
+ (car peg-errors)
+ (peg-merge-errors (cdr peg-errors))))))))
+
(eval-and-compile
(defun peg-method-table-name (method-name)
@@ -365,8 +377,17 @@ Note: a PE can't \"call\" rules by name."
"Return the ELisp code to match the PE EXP."
(let ((translator (or (gethash (car exp) peg-translate-methods)
(error "No translator for: %S" (car exp)))))
- (apply translator (cdr exp))))
-
+ `(or ,(apply translator (cdr exp))
+ (progn
+ (peg-record-failure ',exp) ; for error reporting
+ nil))))
+
+(defun peg-record-failure (exp)
+ (cond ((= (point) (car peg-errors))
+ (setcdr peg-errors (cons exp (cdr peg-errors))))
+ ((> (point) (car peg-errors))
+ (setq peg-errors (list (point) exp)))))
+
(peg-add-method translate and (e1 e2)
`(and ,(peg-translate-exp e1)
,(peg-translate-exp e2)))
@@ -471,12 +492,11 @@ Note: a PE can't \"call\" rules by name."
(minus (member ?- chars))
(hat (member ?^ chars)))
(dolist (c '(?\] ?- ?^))
- (setq chars (delete c chars)))
+ (setq chars (remove c chars)))
(format "[%s%s%s%s%s%s]"
(if rbracket "]" "")
(if minus "-" "")
- (mapconcat (lambda (x) (format "%c-%c" (car x) (cdr x)))
- ranges "")
+ (mapconcat (lambda (x) (format "%c-%c" (car x) (cdr x))) ranges "")
(mapconcat (lambda (c) (format "[:%s:]" c)) classes "")
(mapconcat (lambda (c) (format "%c" c)) chars "")
(if hat "^" ""))))
@@ -513,9 +533,9 @@ Note: a PE can't \"call\" rules by name."
(reverse thunks)))
(goto-char (car thunk))
(funcall (cdr thunk)))
- (list t peg-stack)))
+ peg-stack))
-;; Left recursion is presumably a common mistate when using PEGs.
+;; Left recursion is presumably a common mistake when using PEGs.
;; Here we try to detect such mistakes. Essentailly we traverse the
;; graph as long as we can without consuming input. When we find a
;; recursive call we signal an error.
@@ -594,13 +614,65 @@ input. PATH is the list of rules that we have visited so
far."
(peg-add-method detect-cycles syntax-class (p n) nil)
(peg-add-method detect-cycles action (path form) t)
+(peg-define-method-table merge-error)
+
+(defun peg-merge-errors (exps)
+ "Build a more readable error message out of failed expression."
+ (let ((merged '()))
+ (dolist (exp exps)
+ (setq merged (peg-merge-error exp merged)))
+ merged))
+
+(defun peg-merge-error (exp merged)
+ (apply (or (gethash (car exp) peg-merge-error-methods)
+ (error "No merge-error method for: %S" exp))
+ merged (cdr exp)))
+
+(peg-add-method merge-error or (merged e1 e2)
+ (peg-merge-error e2 (peg-merge-error e1 merged)))
+
+(peg-add-method merge-error and (merged e1 e2)
+ (peg-merge-error e1 merged))
+
+(peg-add-method merge-error str (merged str)
+ (add-to-list 'merged str))
+
+(peg-add-method merge-error call (merged rule)
+ (add-to-list 'merged rule))
+
+(peg-add-method merge-error char (merged char)
+ (add-to-list 'merged (string char)))
+
+(peg-add-method merge-error set (merged r c k)
+ (add-to-list 'merged (peg-make-charset-regexp r c k)))
+
+(peg-add-method merge-error range (merged from to)
+ (add-to-list 'merged (format "[%c-%c]" from to)))
+
+(peg-add-method merge-error * (merged exp)
+ (peg-merge-error exp merged))
+
+(peg-add-method merge-error any (merged)
+ (add-to-list 'merged '(any)))
+
+(peg-add-method merge-error action (merged _) merged)
+(peg-add-method merge-error null (merged) merged)
+
;;; Tests:
-(defmacro peg-parse-string (rules string)
+(defmacro peg-parse-string (rules string &optional noerror)
+ "Parse STRING according to RULES.
+If NOERROR is non-nil, push nil resp. t if the parse failed
+resp. succeded instead of signaling an error."
`(with-temp-buffer
(insert ,string)
(goto-char (point-min))
- (peg-parse . ,rules)))
+ ,(if noerror
+ (let ((entry (make-symbol "entry"))
+ (start (caar rules)))
+ `(peg-parse (entry (or (and ,start `(-- t)) ""))
+ . ,rules))
+ `(peg-parse . ,rules))))
;; We can't expand the macro at compile time, because it needs helper
;; functions which aren't available yet. Delay the expansion to
@@ -610,83 +682,82 @@ input. PATH is the list of rules that we have visited so
far."
(defun peg-test ()
(interactive)
- (assert (peg-parse-string ((s "a")) "a"))
- (assert (not (peg-parse-string ((s "a")) "b")))
- (assert (peg-parse-string ((s (not "a"))) "b"))
- (assert (not (peg-parse-string ((s (not "a"))) "a")))
- (assert (peg-parse-string ((s (if "a"))) "a"))
- (assert (not (peg-parse-string ((s (if "a"))) "b")))
- (assert (peg-parse-string ((s "ab")) "ab"))
- (assert (not (peg-parse-string ((s "ab")) "ba")))
- (assert (not (peg-parse-string ((s "ab")) "a")))
- (assert (peg-parse-string ((s (range ?0 ?9))) "0"))
- (assert (not (peg-parse-string ((s (range ?0 ?9))) "a")))
- (assert (peg-parse-string ((s [0-9])) "0"))
- (assert (not (peg-parse-string ((s [0-9])) "a")))
- (assert (not (peg-parse-string ((s [0-9])) "")))
- (assert (peg-parse-string ((s (any))) "0"))
- (assert (not (peg-parse-string ((s (any))) "")))
- (assert (peg-parse-string ((s (eob))) ""))
- (assert (peg-parse-string ((s (not (eob)))) "a"))
- (assert (peg-parse-string ((s (or "a" "b"))) "a"))
- (assert (peg-parse-string ((s (or "a" "b"))) "b"))
- (assert (not (peg-parse-string ((s (or "a" "b"))) "c")))
- (assert (peg-parse-string ((s (and "a" "b"))) "ab"))
- (assert (peg-parse-string ((s (and "a" "b"))) "abc"))
- (assert (not (peg-parse-string ((s (and "a" "b"))) "ba")))
- (assert (peg-parse-string ((s (and "a" "b" "c"))) "abc"))
- (assert (peg-parse-string ((s (* "a") "b" (eob))) "b"))
- (assert (peg-parse-string ((s (* "a") "b" (eob))) "ab"))
- (assert (peg-parse-string ((s (* "a") "b" (eob))) "aaab"))
- (assert (not (peg-parse-string ((s (* "a") "b" (eob))) "abc")))
- (assert (peg-parse-string ((s "")) "abc"))
- (assert (peg-parse-string ((s "" (eob))) ""))
- (assert (peg-parse-string ((s (opt "a") "b")) "abc"))
- (assert (peg-parse-string ((s (opt "a") "b")) "bc"))
- (assert (not (peg-parse-string ((s (or))) "ab")))
- (assert (peg-parse-string ((s (and))) "ab"))
- (assert (peg-parse-string ((s (and))) ""))
- (assert (peg-parse-string ((s ["^"])) "^"))
- (assert (peg-parse-string ((s ["^a"])) "a"))
- (assert (peg-parse-string ((s ["-"])) "-"))
- (assert (peg-parse-string ((s ["]-"])) "]"))
- (assert (peg-parse-string ((s ["^]"])) "^"))
- (assert (peg-parse-string ((s [alpha])) "z"))
- (assert (not (peg-parse-string ((s [alpha])) "0")))
- (assert (not (peg-parse-string ((s [alpha])) "")))
- (assert (not (peg-parse-string ((s ["][:alpha:]"])) "z")))
- (assert (peg-parse-string ((s (bob))) ""))
- (assert (peg-parse-string ((s (bos))) "x"))
- (assert (not (peg-parse-string ((s (bos))) " x")))
- (assert (peg-parse-string ((s "x" (eos))) "x"))
- (assert (peg-parse-string ((s (syntax-class whitespace))) " "))
- (assert (peg-parse-string ((s (= "foo"))) "foo"))
- (assert (let ((f "foo")) (peg-parse-string ((s (= f))) "foo")))
- (assert (not (peg-parse-string ((s (= "foo"))) "xfoo")))
- (assert (equal (peg-parse-string ((s `(-- 1 2))) "") '(t (2 1))))
- (assert (equal (peg-parse-string ((s `(-- 1 2) `(a b -- a b))) "")
- '(t (2 1))))
+ (assert (peg-parse-string ((s "a")) "a" t))
+ (assert (not (peg-parse-string ((s "a")) "b" t)))
+ (assert (peg-parse-string ((s (not "a"))) "b" t))
+ (assert (not (peg-parse-string ((s (not "a"))) "a" t)))
+ (assert (peg-parse-string ((s (if "a"))) "a" t))
+ (assert (not (peg-parse-string ((s (if "a"))) "b" t)))
+ (assert (peg-parse-string ((s "ab")) "ab" t))
+ (assert (not (peg-parse-string ((s "ab")) "ba" t)))
+ (assert (not (peg-parse-string ((s "ab")) "a" t)))
+ (assert (peg-parse-string ((s (range ?0 ?9))) "0" t))
+ (assert (not (peg-parse-string ((s (range ?0 ?9))) "a" t)))
+ (assert (peg-parse-string ((s [0-9])) "0" t))
+ (assert (not (peg-parse-string ((s [0-9])) "a" t)))
+ (assert (not (peg-parse-string ((s [0-9])) "" t)))
+ (assert (peg-parse-string ((s (any))) "0" t))
+ (assert (not (peg-parse-string ((s (any))) "" t)))
+ (assert (peg-parse-string ((s (eob))) "" t))
+ (assert (peg-parse-string ((s (not (eob)))) "a" t))
+ (assert (peg-parse-string ((s (or "a" "b"))) "a" t))
+ (assert (peg-parse-string ((s (or "a" "b"))) "b" t))
+ (assert (not (peg-parse-string ((s (or "a" "b"))) "c" t)))
+ (assert (peg-parse-string ((s (and "a" "b"))) "ab" t))
+ (assert (peg-parse-string ((s (and "a" "b"))) "abc" t))
+ (assert (not (peg-parse-string ((s (and "a" "b"))) "ba" t)))
+ (assert (peg-parse-string ((s (and "a" "b" "c"))) "abc" t))
+ (assert (peg-parse-string ((s (* "a") "b" (eob))) "b" t))
+ (assert (peg-parse-string ((s (* "a") "b" (eob))) "ab" t))
+ (assert (peg-parse-string ((s (* "a") "b" (eob))) "aaab" t))
+ (assert (not (peg-parse-string ((s (* "a") "b" (eob))) "abc" t)))
+ (assert (peg-parse-string ((s "")) "abc" t))
+ (assert (peg-parse-string ((s "" (eob))) "" t))
+ (assert (peg-parse-string ((s (opt "a") "b")) "abc" t))
+ (assert (peg-parse-string ((s (opt "a") "b")) "bc" t))
+ (assert (not (peg-parse-string ((s (or))) "ab" t)))
+ (assert (peg-parse-string ((s (and))) "ab" t))
+ (assert (peg-parse-string ((s (and))) "" t))
+ (assert (peg-parse-string ((s ["^"])) "^" t))
+ (assert (peg-parse-string ((s ["^a"])) "a" t))
+ (assert (peg-parse-string ((s ["-"])) "-" t))
+ (assert (peg-parse-string ((s ["]-"])) "]" t))
+ (assert (peg-parse-string ((s ["^]"])) "^" t))
+ (assert (peg-parse-string ((s [alpha])) "z" t))
+ (assert (not (peg-parse-string ((s [alpha])) "0" t)))
+ (assert (not (peg-parse-string ((s [alpha])) "" t)))
+ (assert (not (peg-parse-string ((s ["][:alpha:]"])) "z" t)))
+ (assert (peg-parse-string ((s (bob))) "" t))
+ (assert (peg-parse-string ((s (bos))) "x" t))
+ (assert (not (peg-parse-string ((s (bos))) " x" t)))
+ (assert (peg-parse-string ((s "x" (eos))) "x" t))
+ (assert (peg-parse-string ((s (syntax-class whitespace))) " " t))
+ (assert (peg-parse-string ((s (= "foo"))) "foo" t))
+ (assert (let ((f "foo")) (peg-parse-string ((s (= f))) "foo" t)))
+ (assert (not (peg-parse-string ((s (= "foo"))) "xfoo" t)))
+ (assert (equal (peg-parse-string ((s `(-- 1 2))) "") '(2 1)))
+ (assert (equal (peg-parse-string ((s `(-- 1 2) `(a b -- a b))) "") '(2 1)))
(assert (equal (peg-parse-string ((s (or (and (any) s)
(substring [0-9]))))
"ab0cd1ef2gh")
- '(t ("2"))))
+ '("2")))
(assert (equal (peg-parse-string ((s (list x y))
(x `(-- 1))
(y `(-- 2)))
"")
- '(t ((1 2)))))
+ '((1 2))))
(assert (equal (peg-parse-string ((s (list (* x)))
(x "x" `(-- 'x)))
"xxx")
- '(t ((x x x)))))
+ '((x x x))))
(assert (equal (peg-parse-string ((s (region (* x)))
(x "x" `(-- 'x)))
"xxx")
- '(t (4 x x x 1))))
+ '(4 x x x 1)))
(assert (equal (peg-parse-string ((s (region (list (* x))))
(x "x" `(-- 'x 'y)))
"xxx")
- '(t (4 (x y x y x y) 1))))
+ '(4 (x y x y x y) 1)))
(assert (equal (with-temp-buffer
(save-excursion (insert "abcdef"))
(list
@@ -695,7 +766,7 @@ input. PATH is the list of rules that we have visited so
far."
(replace "de" "y")
"f"))
(buffer-string)))
- '((t nil) "axyf")))
+ '(nil "axyf")))
)
(when (featurep 'cl)
@@ -718,28 +789,27 @@ input. PATH is the list of rules that we have visited so
far."
(digit [0-9])))
;; peg-ex-parse-int recognizes integers and computes the corresponding
-;; value. The grammer is the same as for `peg-ex-recognize-int' added
-;; with parsing actions. Unfortunaletly, the actions add quite a bit
-;; of clutter.
+;; value. The grammer is the same as for `peg-ex-recognize-int'
+;; augmented with parsing actions. Unfortunaletly, the actions add
+;; quite a bit of clutter.
;;
-;; The action for the sign rule pushes t on the stack for a minus sign
-;; and nil for plus or no sign.
+;; The actions for the sign rule push -1 on the stack for a minus sign
+;; and 1 for plus or no sign.
;;
;; The action for the digit rule pushes the value for a single digit.
;;
;; The action `(a b -- (+ (* a 10) b)), takes two items from the stack
-;; and pushes the first digit times 10 added to second digit.
+;; and pushes the first digit times 10 added to the second digit.
;;
-;; The action `(minus val -- (if minus (- val) val)), negates the
-;; value if the minus flag is true.
+;; The action `(sign val -- (* sign val)), multiplies val with the
+;; sign (1 or -1).
(defun peg-ex-parse-int ()
- (peg-parse (number sign
- digit
- (* digit `(a b -- (+ (* a 10) b)))
- `(minus val -- (if minus (- val) val)))
- (sign (or (and "+" `(-- nil))
- (and "-" `(-- t))
- (and "" `(-- nil))))
+ (peg-parse (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)))))
;; Put point after the ) and press C-x C-e
@@ -748,8 +818,7 @@ input. PATH is the list of rules that we have visited so
far."
;; Parse arithmetic expressions and compute the result as side effect.
(defun peg-ex-arith ()
(peg-parse
- (expr (or (and _ sum eol)
- (and (* (not eol) (any)) eol error)))
+ (expr _ sum eol)
(sum product (* (or (and "+" _ product `(a b -- (+ a b)))
(and "-" _ product `(a b -- (- a b))))))
(product value (* (or (and "*" _ value `(a b -- (* a b)))
@@ -758,10 +827,10 @@ input. PATH is the list of rules that we have visited so
far."
(and "(" _ sum ")" _)))
(number (+ [0-9]) _)
(_ (* [" \t"]))
- (eol (or "\n" "\r\n" "\r"))
- (error (action (error "Parse error at: %s" (point))))))
+ (eol (or "\n" "\r\n" "\r"))))
;; (peg-ex-arith) 1 + 2 * 3 * (4 + 5)
+;; (peg-ex-arith) 1 + 2 ^ 3 * (4 + 5)
;; Parse URI according to RFC 2396.
(defun peg-ex-uri ()
@@ -825,8 +894,9 @@ input. PATH is the list of rules that we have visited so
far."
(lowalpha [a-z])
(upalpha [A-Z])
(digit [0-9])))
-;; (peg-ex-uri)file:/bar/baz.html?foo=df#x
+
;; (peg-ex-uri)http://address@hidden:8080/bar/baz.html?x=1#foo
+;; (peg-ex-uri)file:/bar/baz.html?foo=df#x
;; Split STRING where SEPARATOR occurs.
(defun peg-ex-split (string separator)
@@ -837,30 +907,6 @@ input. PATH is the list of rules that we have visited so
far."
;; (peg-ex-split "-abc-cd-" "-")
-;; Find the last digit in a string.
-(defun peg-ex-last-digit (string)
- (peg-parse-string ((s (or (and (any) s)
- (substring [0-9]))))
- string))
-
-;; (peg-ex-last-digit "ab0cd1ef2gh")
-;; (peg-ex-last-digit (make-string 50 ?-))
-;; (peg-ex-last-digit (make-string 1000 ?-))
-
-;; Find the last digit without recursion.
-(defun peg-ex-last-digit2 (string)
- (peg-parse-string ((s `(-- nil)
- (+ (* (not digit) (any))
- (substring digit)
- `(d1 d2 -- d2)))
- (digit [0-9]))
- string))
-
-;; (peg-ex-last-digit2 "ab0cd1ef2gh")
-;; (peg-ex-last-digit2 (concat (make-string 500000 ?-) "8a9b"))
-;; (peg-ex-last-digit2 (make-string 500000 ?-))
-;; (peg-ex-last-digit2 (make-string 500000 ?5))
-
;; Parse a lisp style Sexp.
;; [To keep the example short, ' and . are handled as ordinary symbol.]
(defun peg-ex-lisp ()
@@ -896,6 +942,34 @@ input. PATH is the list of rules that we have visited so
far."
"y"
(action (foo))))))))
+;; Some efficecy problems:
+
+;; Find the last digit in a string.
+;; Recursive definition with excessive stack usage.
+(defun peg-ex-last-digit (string)
+ (peg-parse-string ((s (or (and (any) s)
+ (substring [0-9]))))
+ string))
+
+;; (peg-ex-last-digit "ab0cd1ef2gh")
+;; (peg-ex-last-digit (make-string 50 ?-))
+;; (peg-ex-last-digit (make-string 1000 ?-))
+
+;; Find the last digit without recursion. Doesn't run out of stack,
+;; but probably still too inefficient for large inputs.
+(defun peg-ex-last-digit2 (string)
+ (peg-parse-string ((s `(-- nil)
+ (+ (* (not digit) (any))
+ (substring digit)
+ `(d1 d2 -- d2)))
+ (digit [0-9]))
+ string))
+
+;; (peg-ex-last-digit2 "ab0cd1ef2gh")
+;; (peg-ex-last-digit2 (concat (make-string 500000 ?-) "8a9b"))
+;; (peg-ex-last-digit2 (make-string 500000 ?-))
+;; (peg-ex-last-digit2 (make-string 500000 ?5))
+
)) ; end of eval-when-load
(provide 'peg)
- [elpa] branch externals/peg created (now 081efec), Stefan Monnier, 2019/03/10
- [elpa] externals/peg a4286cb 2/7: Mention mailing list., Stefan Monnier, 2019/03/10
- [elpa] externals/peg 47b737c 4/7: * peg.el (peg-ex-arith): Minor cleanups., Stefan Monnier, 2019/03/10
- [elpa] externals/peg 3313a38 6/7: * peg.el (merge-error): Add a method for (NOT X)., Stefan Monnier, 2019/03/10
- [elpa] externals/peg cb46f6f 3/7: Introduce an operator to match string literals., Stefan Monnier, 2019/03/10
- [elpa] externals/peg 081efec 7/7: * peg.el: Cleanup whitespace., Stefan Monnier, 2019/03/10
- [elpa] externals/peg 3604f37 5/7: Add some error reporting. If a parse fails, move to the,
Stefan Monnier <=
- [elpa] externals/peg 585f4de 1/7: Create hg repo., Stefan Monnier, 2019/03/10