Parsing Expressions
A parsing expression is the right-hand side of a rule definition (def). It
matches a prefix of the input at the current position and either succeeds,
possibly consuming input and producing a value, or fails.
Evaluation
A parsing expression is evaluated at a position of the input. If it succeeds, the evaluation produces an end position, which is the position where the next expression of a sequence continues, and a value (or no value). If it fails, it has no effect: the position, the captures and the variables are as they were before it. The following holds for every expression:
- Determinism. The result depends only on the grammar, the start rule, the input and the position unit; there is no ambiguity.
- Order. The elements of a sequence are evaluated from left to right, and the alternatives of a choice from left to right.
- No backtracking into a success. Once an expression has succeeded, it is not evaluated again with a different result to make a later expression succeed: a choice commits to the first alternative that succeeds, and a repetition takes as many iterations as it can. This is the behavior of PEG, as opposed to context-free grammars.
- Recorded failures. A failure records what was expected at its position. The farthest failure of a parse is reported as a syntax error.
Values and the concrete syntax tree
PEGO produces a predictable and consistent concrete syntax tree (CST). When a parsing expression succeeds it produces a value, normally a node. A rule without an action produces the value of its body.
| Expression | Value |
|---|---|
Literal, character class, ., @a, _ |
A terminal: a Match node holding the matched text |
a b |
A Seq node whose children are the values of the elements that have a value |
a / b |
The value of the alternative that matched; the choice itself creates no node |
a*, a+, a{n,m} |
A List node whose children are the values of the iterations |
a? |
The value of a, or nil if a did not match |
label:a |
The value of a |
| Rule call | The value of the called rule |
The following rules determine the shape of the tree.
-
One value per rule. A rule produces a single value, whose kind is determined by the top-level expression of the rule body:
def a = "x" "y"produces aSeqnode.def b = "x" / "y" "z"produces the value of the alternative that matched (aMatchnode or aSeqnode).def c = "x"produces aMatchnode.def d = aproduces the value of ruleaunchanged.
-
Rule names. A node that a rule without an action creates in its own body is labeled with the name of that rule. Nodes received from other rules keep their own label:
def d = aproduces the node ofa, labeleda. The value of an action is final in the same way: if an action returns a node of its rule's body (-> $1), that node is not labeled by any rule. -
Expressions without a value. The following expressions have no value and never appear as children of a
Seqnode:- anchors:
^^,$$,^,$; - cut (
--), discard (-a), bottom (_|_), lookahead (&a,!a) and predicates.
An optional expression
a?that does not match does have a value,nil, which appears as a child. - anchors:
-
Suppressed construction. Atomic (
@a) and discard (-a) do not build the values of their operands, which saves work and keeps the tree small. -
Whole input. A parse succeeds only if the start rule matches the entire input; see Parsing.
The reasons for making choices transparent are recorded in docs/design/002.
Operator summary
| Syntax | Name | Consumes input |
|---|---|---|
"..." |
literal | yes |
(?...), (?^...) |
character class | yes |
. |
any character | yes |
name, name(level) |
rule call | depends on the rule |
_ |
top | no |
_|_ |
bottom | no |
^^ / $$ |
beginning / end of input | no |
^ / $ |
beginning / end of line | no |
a b |
sequence | yes |
a / b |
ordered choice | yes |
(a) |
grouping | yes |
a{n,m}, a*, a+ |
repetition | yes |
a? |
optional | yes |
&a / !a |
positive / negative lookahead | no |
@a |
atomic | yes |
-a |
discard | yes |
-- |
cut | no |
label:a |
capture | yes |
[...] |
predicate | no |
#name(...) |
attribute | depends on the attribute |
"Consumes input" states whether the expression can advance the input position.
Precedence
Operators bind in the following order, from tightest to loosest. Parentheses override the order.
| Precedence | Operators | Kind |
|---|---|---|
| 1 (tightest) | *, +, ?, {n,m}, #name(...) |
postfix |
| 2 | label:, &, !, @, - |
prefix |
| 3 | a b |
sequence |
| 4 (loosest) | a / b |
ordered choice |
For example, @(?0-9)+ is @((?0-9)+), x:a* is x:(a*), and
"a" "b" / "c" is ("a" "b") / "c". Postfix operators can be stacked
(a*?), and so can prefix operators (x:@a).
Terminals
Literals
A literal "..." matches exactly the given string and produces a terminal.
def a = "abc"
Character classes
A character class (?...) matches a single code point that is one of the listed
characters or lies in one of the listed ranges, and produces a terminal.
def a = (?0-9) // a decimal digit
def b = (?0-9a-zA-Z) // a digit or an ASCII letter
def c = (?0-9abc) // a digit, 'a', 'b' or 'c'
def d = (?\-(?\)) // '-', '(', '?' or ')'
A negated character class (?^...) matches a single code point that is not
one of the listed characters or ranges. It fails at the end of the input.
def string = "\"" @(?^"\\)* "\"" // characters other than " and \
Inside a character class:
a-zdenotes the inclusive range fromatoz. The upper bound MUST NOT be less than the lower bound.- A
-that is not between two characters (the first item, or the last item before)) is a literal-. Elsewhere, write\-. )MUST be escaped as\), and\as\\. Other characters, such as(and?, need no escape.^immediately after(?negates the class; elsewhere it is literal.- Whitespace is literal:
(? \t)matches a space or a tab. - The escape sequences of string literals are available.
- A class MUST contain at least one item and MUST NOT span lines.
Any character
. matches any single code point, including a line feed, and produces a
terminal. It fails at the end of the input.
def comment = "//" @(!"\n" .)*
Top
_ always succeeds without consuming input and produces an empty terminal (a
Match node with empty text). It is useful as a placeholder for a rule whose
body is not yet written, or to make an empty alternative explicit.
def stub = _
def a_or_empty = "a" / _ // usually written "a"?
Bottom
_|_ always fails without consuming input. Combined with the
#error attribute, it reports a descriptive error.
def hello = "hello" / _|_ #error(message="expect 'hello'")
Anchors
An anchor matches a position without consuming input and has no value.
| Syntax | Matches |
|---|---|
^^ |
at the beginning of the input |
$$ |
at the end of the input (used to ensure that all input is consumed) |
^ |
at the beginning of the input or immediately after a line feed |
$ |
at the end of the input or immediately before a line feed or a carriage return |
def a = "hello." $$
Combinators
Sequence
A sequence a b matches a and then b at the position where a ended. It fails
if any element fails.
def a = "a" "b"
Ordered choice
An ordered choice a / b tries the alternatives from left to right and commits to the
first one that succeeds. Later alternatives are tried only if the earlier ones
fail; a successful alternative is never revisited, even if the expressions after
the choice fail.
def a = "a" / "b"
Grouping
Parentheses (a) group an expression to override precedence. A group
has the value of the enclosed expression.
def a = "a" ("b" "c")
Repetition
A repetition matches a repeatedly.
| Syntax | Meaning |
|---|---|
a{n} |
exactly n times |
a{n,} |
at least n times |
a{,m} |
at most m times |
a{n,m} |
at least n and at most m times |
a* |
shorthand for a{0,} |
a+ |
shorthand for a{1,} |
Repetition is greedy and does not backtrack: it matches a as many times as
possible (up to the maximum) and fails only if fewer than the minimum matched.
m MUST NOT be less than n.
The value is a List node whose children are the values of the iterations.
Repetition stops at the first iteration that succeeds without consuming input,
so a repetition of an expression that can match the empty string does not loop
forever.
Optional
a? matches a zero times or once. Its value is the value of a, or nil if
a did not match; it is not a List. In contrast, a{0,1} is a repetition and
produces a List.
Lookahead
Lookahead tests the input that follows without consuming it, and has no value.
&a(positive lookahead) succeeds ifamatches at the current position.!a(negative lookahead) succeeds ifadoes not match at the current position.
// An identifier that does not begin with "if".
def ident = !"if" (?a-z)+
Captures and variable definitions made inside a positive lookahead remain in effect after it; only the input position is restored. A negative lookahead MUST NOT contain captures. Failures inside a lookahead are not included in the expected items of a syntax error.
Controlling construction
Atomic
@a matches a and produces a single terminal holding the matched text,
without building the internal structure of a.
// (?0-9)+ alone produces a List of single digits; @ makes it one terminal "123".
def number = @((?0-9)+)
Discard
-a matches a and discards the result: it builds neither nodes nor terminals,
and it has no value. Use it for input that the syntax requires but the tree does
not need, such as whitespace between words.
def s = " "+
def sentence = word (-s word)+
Because -- is a single token, --a is a cut followed by a, not a
discard of a discard.
Cut
A cut -- commits to the current alternative: once parsing passes a cut, the
enclosing choice does not backtrack into its other alternatives. Use it to
avoid pointless backtracking where the grammar makes the choice unambiguous.
// Matches "ab" but not "ac": after "a", the cut commits to the first
// alternative, so the second alternative is never tried.
def a = "a" -- "b" / "a" -- "c"
A cut commits the innermost enclosing choice within the same rule. A repetition or an optional expression counts as a choice between matching one more time and stopping: if an iteration fails after passing a cut, the whole repetition (or optional expression) fails instead of stopping. The effect of a cut never extends beyond the rule that contains it: it does not affect choices in the calling rule.
Capture
A capture label:a records the value of a under the name label so that
actions and predicates can refer to it as
$label. The label and the : MUST be adjacent.
def full_name = first:word " " last:word
-> new Name{First: $first, Last: $last}
Captures belong to a scope. A scope is either a rule body or a single
iteration of a repetition (*, +, {n,m}).
- A capture inside a choice or an optional expression belongs to the enclosing
scope. If it did not match, its value is
nil. - A capture inside the element of a repetition becomes a field of the value of
that iteration. If the value of the element is a
Seqnode, the fields are added to it; otherwise the value is wrapped in aSeqnode that carries the fields.
// Each element of $rest has the fields op and r.
def expr = l:term rest:(op:("+" / "-") r:term)*
In a rule without an action, the captures of the rule body that matched become
fields of the value of the rule; captures that did not match are omitted. If
that value was not created by the rule body itself (for example, it is the
value of a called rule, or the value of a capture), it is first wrapped in a
Seq node whose only child is the value. If no capture matched, the value is
unchanged.
The following restrictions apply:
- A capture made in a match that was undone by backtracking does not survive.
- Captures MUST NOT appear inside
@a,-a,!a, theskipargument of#recover, or theskipitem of a Pratt expression. - The captured expression MUST have a value:
x:&a,x:-aandx:--are errors.
Rule calls
A rule name in a parsing expression calls the rule at the current position: the body of the rule is evaluated there. If the body succeeds, so does the call, with the end position of the body and the value of the rule (the value of its action, if it has one). The rule MUST be defined; see Names.
A call is evaluated in a scope of its own. The captures made in the body of the rule are visible only in that body and in the rule's action, and the variables that the rule defines end with the call; the variables of the calling rules are visible to the called rule (see Variables). Rules MAY call themselves recursively.
A rule defined with a Pratt expression can be called with the name
of a binding level, name(level); see
Level-restricted calls.
Left recursion
A rule MAY be directly or indirectly left-recursive: it may call itself at the position where it started, before consuming any input.
def expr = expr "+" term / term
A call of a left-recursive rule R at a position p is evaluated by growing a
seed:
- While
Ris being evaluated atp, a call ofRatpfails. The body ofRis evaluated under this condition. If it fails, the call fails. - If the body succeeded, its value is the current result (the seed). The body
is evaluated again, with calls of
Ratpnow succeeding with the current result. If the body succeeds and ends farther than the current result, the new value becomes the current result and step 2 is repeated. Otherwise the current result is the value of the call.
In an indirect left recursion, one rule of the cycle (the one where the recursion is entered, decided by static analysis) is grown in this way, and the others are evaluated again in each iteration. A rule that is not left-recursive is evaluated once.
Because growing tries the alternatives in order, the recursive alternative MUST come before the base case: if the base case comes first, it matches again with the same length, nothing grows, and the input after it is left over.
def main = a $$
def a = a "x" / "y"
For the input yxx, pego parse -f sexpr prints the left-associative tree
(Seq (Seq (Seq "y"@a "x")@a "x")@a)@main
With def a = "y" / a "x", the same input fails with
1:2: syntax error: expected end of input.
The memoization that the algorithm needs is described in docs/design/003. For expressions with operator precedence and associativity, a Pratt expression declares the operators directly instead of requiring one left-recursive rule per precedence level.