PEGO

Chapter 6

Text Form

The engine's printer writes a module as text: one line per rule and per node, with the children of a node indented by two spaces under it. This chapter explains how to read that text. It is a readable form, not a format to parse, and it may change before the first release. With attributes, each node line ends with the node's attributes in brackets.

Rule Lines

A rule line is rule, the rule's ID and name, then the following that apply, in this order: want; the policy flags memo, transient, plain, leader, splice and novalue; seen=n; vars=[...]; scope=[names]; twin=id; the finish, which is => term for an action, splice, or scope-value with the shape; and as Type if the rule's value is converted to a node type. The rule's body follows, indented one level.

Node Lines

Node Printed as
Lit lit "text", or lit "text"i if case-insensitive
ClassNode class [a-z]: ranges, with ^ after the bracket if negated. A code point that is not a printable ASCII character, or is -, ], \ or ^, is written \uXXXX
Any, Cut, Succeed, FailNode, Seq, Opt, Text, Drop, Recover, Missing, Stream, Terminal any, cut, succeed, fail, seq, opt, text, drop, recover, missing, stream, terminal
Anchor anchor begin-input, end-input, begin-line or end-line
Choice choice, with each alternative as an alt line, followed by guard= and the first terminal, and class for a guarded class
Repeat repeat min max, then scope=[names] and project=name if present
Look and or, if negative, not
Call call id:name, then level=n if not 0
Nest nest id:name
OperatorsRule operators, then its lines: skip, operand and op id kind assoc level=n, each with scope=[names], the shape, => action if it has one, and its body below
Capture capture slot name, then project=field if present
Pred, BindNode pred term, bind name = term
Label error "message" or diagnostic "message"
Scan scan class min max, or scan any min max
ClassRun classrun and its classes

A first terminal is a class, or a quoted code point, with i after it if case-insensitive, and +n for a depth n. The shape is written (elems,k=n,mode=n,tuple,shared), with the parts that do not apply left out.

Attributes

With attributes, a node line ends with [ and the following that apply, separated by spaces, and ]: want and the shape; nullable; first= and the first terminal; and the effects, separated by commas, named consume, fail, expect, capture[slots], bind, recover, cut, raise, call and commit.

Terms

A term is printed as follows: a string constant quoted; $slot:name for a slot; param:name, $lhs, $rhs and $op for locals; var:name; x.Name for a member; Type{Field: term, ...} for a struct, with ! after the closing brace for the final struct of a rule or line; Node(term) for a conversion; (a, b) for a tuple; f(args) for a built-in; fn(params) body for a lambda; (a op b) and opx for operations; text-eq(...), field-cmp(...), text-in(...), concat-fused(...) and projected-map(...) for the rewritten forms.

Example

The example grammar is the following.

package list

type Item = { Name node[string] }

@start
rule main = items:item+ $$

rule item Item = ws n:name ("," / ";")? => Item{Name: $n}

rule name node[string] = [a-z]+ #atomic

rule ws = [ \t]*

The module as built from the compiled grammar, printed without attributes:

rule 0 main want memo transient scope=[items] scope-value(elems,k=1,mode=2,tuple)
  seq
    capture 0 items
      repeat 1 -1
        call 1:item
    anchor end-input
rule 1 item memo transient scope=[n] => Item{Name: $0:n}!
  seq
    call 4:ws
    capture 0 n
      call 2:name
    opt
      choice
        alt
          lit ","
        alt
          lit ";"
rule 2 name want memo transient plain scope-value(k=1,mode=1)
  text
    repeat 1 -1
      class [a-z]
rule 3 ws want memo transient plain twin=4 scope-value(k=1,mode=1)
  repeat 0 -1
    class [\u0020\u0009]
rule 4 ws memo transient plain novalue
  repeat 0 -1
    class [\u0020\u0009]

Rule 1 calls rule 4, the value-free twin of ws, because it wants no value there. The action's $0:n is slot 0, named n; the final ! marks the term that makes the rule's own value.

The same module after guards and scans were introduced and analysis was run, printed with attributes (rules 0 to 2):

rule 0 main want memo transient scope=[items] scope-value(elems,k=1,mode=2,tuple)
  seq [want(elems,k=1,mode=2,tuple) consume,fail,expect,capture[0],raise,call]
    capture 0 items [want(k=1,mode=1,tuple) consume,fail,expect,capture[0],raise,call]
      repeat 1 -1 [want(k=1,mode=1) consume,fail,expect,raise,call]
        call 1:item [want(k=1,mode=1) consume,fail,expect,raise,call]
    anchor end-input [want(k=0,mode=0,tuple) nullable fail,expect]
rule 1 item memo transient scope=[n] => Item{Name: $0:n}!
  seq [consume,fail,expect,capture[0],raise,call]
    call 4:ws [nullable consume,expect,raise,call]
    capture 0 n [consume,fail,expect,capture[0],raise,call]
      call 2:name [want(k=1,mode=1) consume,fail,expect,raise,call]
    opt [nullable consume,expect]
      choice [consume,fail,expect]
        alt guard=','
          lit "," [first=',' consume,fail,expect]
        alt guard=';'
          lit ";" [first=';' consume,fail,expect]
rule 2 name want memo transient plain scope-value(k=1,mode=1)
  text [want(k=1,mode=1) consume,fail,expect]
    scan [a-z] 1 -1 [consume,fail,expect]

name shows a Scan in place of the value-free repetition under #atomic; item shows the guards of the two alternatives. The twin ws (rule 4) becomes scan [\u0020\u0009] 0 -1.