; Improvements to PEG documentation (second attempt)

* doc/lispref/peg.texi: Make more use of defmac/defmacro, and try to
clarify the relationships between the various macros and functions.
* lisp/progmodes/peg.el (peg-parse): Remove claim that PEXS can also be
a single list of rules.
This commit is contained in:
Eric Abrahamsen 2024-04-15 20:14:50 -07:00
parent d39f0a165a
commit 2141caca30
2 changed files with 64 additions and 33 deletions

View file

@ -1,6 +1,6 @@
@c -*-texinfo-*-
@c This is part of the GNU Emacs Lisp Reference Manual.
@c Copyright (C) 1990--1995, 1998--1999, 2001--2023 Free Software
@c Copyright (C) 1990--1995, 1998--1999, 2001--2024 Free Software
@c Foundation, Inc.
@c See the file elisp.texi for copying conditions.
@node Parsing Expression Grammars
@ -45,34 +45,57 @@ rule. For instance:
@end example
Once defined, grammars can be used to parse text after point in the
current buffer, in the following ways:
current buffer, in a number of ways. The @code{peg-parse} macro is the
simplest:
@defmac peg-parse &rest pexs
Match @var{pexs} at point. If @var{pexs} is a list of PEG rules, the
first rule is considered the ``entry-point'':
Match @var{pexs} at point.
@end defmac
@example
@group
(peg-parse
((number sign digit (* digit))
(sign (or "+" "-" ""))
(digit [0-9])))
(number sign digit (* digit))
(sign (or "+" "-" ""))
(digit [0-9]))
@end group
@end example
@c FIXME: These two should be formally defined using @defmac and @defun.
@findex with-peg-rules
@findex peg-run
The @code{peg-parse} macro represents the simplest use of the
@acronym{PEG} library, but also the least flexible, as the rules must be
written directly into the source code. A more flexible approach
involves use of three macros in conjunction: @code{with-peg-rules}, a
@code{let}-like construct that makes a set of rules available within the
macro body; @code{peg-run}, which initiates parsing given a single rule;
and @code{peg}, which is used to wrap the entry-point rule name. In
fact, a call to @code{peg-parse} expands to just this set of calls. The
above example could be written as:
While this macro is simple it is also inflexible, as the rules must be
written directly into the source code. More flexibility can be gained
by using a combination of other functions and macros.
@defmac with-peg-rules rules &rest body
Execute @var{body} with @var{rules}, a list of @acronym{PEX}s, in
effect. Within @var{BODY}, parsing is initiated with a call to
@code{peg-run}.
@end defmac
@defun peg-run peg-matcher &optional failure-function success-function
This function accepts a single @var{peg-matcher}, which is the result of
calling @code{peg} (see below) on a named rule, usually the entry-point
of a larger grammar.
At the end of parsing, one of @var{failure-function} or
@var{success-function} is called, depending on whether the parsing
succeeded or not. If @var{success-function} is called, it is passed a
lambda form that runs all the actions collected on the stack during
parsing -- by default this lambda form is simply executed. If parsing
fails, the @var{failure-function} is called with a list of @acronym{PEG}
expressions that failed during parsing; by default this list is
discarded.
@end defun
The @var{peg-matcher} passed to @code{peg-run} is produced by a call to
@code{peg}:
@defmac peg &rest pexs
Convert @var{pexs} into a single peg-matcher suitable for passing to
@code{peg-run}.
@end defmac
The @code{peg-parse} example above expands to a set of calls to these
functions, and could be written in full as:
@example
@group
@ -84,14 +107,19 @@ above example could be written as:
@end group
@end example
This allows more explicit control over the ``entry-point'' of parsing,
and allows the combination of rules from different sources.
This approach allows more explicit control over the ``entry-point'' of
parsing, and allows the combination of rules from different sources.
@c FIXME: Use @defmac.
@findex define-peg-rule
Individual rules can also be defined using a more @code{defun}-like
syntax, using the macro @code{define-peg-rule}:
@defmac define-peg-rule name args &rest pexs
Define @var{name} as a PEG rule that accepts @var{args} and matches
@var{pexs} at point.
@end defmac
For instance:
@example
@group
(define-peg-rule digit ()
@ -99,14 +127,16 @@ syntax, using the macro @code{define-peg-rule}:
@end group
@end example
This also allows for rules that accept an argument (supplied by the
@code{funcall} PEG rule, @pxref{PEX Definitions}).
Arguments can be supplied to rules by the @code{funcall} PEG rule
(@pxref{PEX Definitions}).
@c FIXME: Use @defmac.
@findex define-peg-ruleset
Another possibility is to define a named set of rules with
@code{define-peg-ruleset}:
@defmac define-peg-ruleset name &rest rules
Define @var{name} as an identifier for @var{rules}.
@end defmac
@example
@group
(define-peg-ruleset number-grammar
@ -240,10 +270,10 @@ Returns non-@code{nil} if parsing @acronym{PEX} @var{e} from point fails
Treats the value of the Lisp expression @var{exp} as a boolean.
@end table
@c FIXME: peg-char-classes should be mentioned in the text below.
@vindex peg-char-classes
Character class matching can use the same named character classes as
in regular expressions (@pxref{Top,, Character Classes,elisp})
Character-class matching can refer to the classes named in
@code{peg-char-classes}, equivalent to character classes in regular
expressions (@pxref{Top,, Character Classes,elisp})
@node Parsing Actions
@section Parsing Actions

View file

@ -316,13 +316,14 @@ EXPS is a list of rules/expressions that failed.")
"Match PEXS at point.
PEXS is a sequence of PEG expressions, implicitly combined with `and'.
Returns STACK if the match succeed and signals an error on failure,
moving point along the way.
PEXS can also be a list of PEG rules, in which case the first rule is used."
moving point along the way."
(if (and (consp (car pexs))
(symbolp (caar pexs))
(not (ignore-errors
(not (eq 'call (car (peg-normalize (car pexs))))))))
;; `pexs' is a list of rules: use the first rule as entry point.
;; The first of `pexs' has not been defined as a rule, so assume
;; that none of them have been and they should be fed to
;; `with-peg-rules'
`(with-peg-rules ,pexs (peg-run (peg ,(caar pexs)) #'peg-signal-failure))
`(peg-run (peg ,@pexs) #'peg-signal-failure)))