- Documentation
- Contribute
- Home
Treetop grammars are written in a custom language based on parsing expression grammars. Literature on the subject of parsing expression grammars (PEGs) is useful in writing Treetop grammars.
PEGs have no separate lexical analyser (since the algorithm has the same time-complexity guarantees as the best lexical analysers) so all whitespace and other lexical niceties (like comments) must be explicitly handled in the grammar. A further benefit is that multiple PEG grammars may be seamlessly composed into a single parser.
Treetop grammars look like this:
require "my_stuff"
grammar GrammarName
include Module::Submodule
rule rule_name
...
end
rule rule_name
...
end
...
end
The main keywords are:
grammar
: This introduces a new grammar. It is followed by a constant name to which the grammar will be bound when it is loaded.
include
: This causes the generated parser to include the referenced Ruby module (which may be another parser)
require
: This must be at the start of the file, and is passed through to the emitted Ruby grammar
rule
: This defines a parsing rule within the grammar. It is followed by a name by which this rule can be referenced within other rules. It is then followed by a parsing expression defining the rule.
A grammar may be surrounded by one or more nested module
or class
statements, which provides a namespace for the generated Ruby parser. Note that you cannot specify a superclass for a class, so if your class has a superclass, it must be declared elsewhere and loaded first.
Treetop will emit a module called GrammarName
and a parser class called GrammarNameParser
(in the namespace, if specified).
Each rule associates a name with a parsing expression. Parsing expressions are a generalization of vanilla regular expressions. Their key feature is the ability to reference other expressions in the grammar by name.
Treetop parsers will try to match the first rule defined in the grammar, unless you pass an optional parameter to set a different top rule.
Strings are surrounded in double or single quotes and must be matched exactly.
"foo"
'foo'
Character classes are surrounded by brackets. Their semantics are identical to those used in Ruby's regular expressions.
[a-zA-Z]
[0-9]
The anything symbol is represented by a dot (.
) and matches any single character.
An empty string matches at any position and consumes no input. It's useful when you wish to treat a single symbol as part of a sequence, for example when an alternate rule will be processed using shared code.
rule alts ( foo bar / baz '' ) { def value elements.map{|e| e.text_value } end } end
Nonterminal symbols are unquoted references to other named rules. They are equivalent to an inline substitution of the named expression.
rule foo
"the dog " bar
end
rule bar
"jumped"
end
The above grammar is equivalent to:
rule foo
"the dog jumped"
end
Parsers attempt to match ordered choices in left-to-right order, and stop after the first successful match.
"foobar" / "foo" / "bar"
Note that if "foo"
in the above expression came first, "foobar"
would never be matched.
Note also that the above rule will match "bar"
as a prefix of "barbie"
.
Care is required when it's desired to match language keywords exactly.
Sequences are a space-separated list of parsing expressions. They have higher precedence than choices, so choices must be parenthesized to be used as the elements of a sequence.
"foo" "bar" ("baz" / "bop")
Parsers will greedily match an expression zero or more times if it is followed by the star (*
) symbol.
'foo'*
matches the empty string, "foo"
, "foofoo"
, etc.Parsers will greedily match an expression one or more times if it is followed by the plus (+
) symbol.
'foo'+
does not match the empty string, but matches "foo"
, "foofoo"
, etc.An expression can be declared optional by following it with a question mark (?
).
'foo'?
matches "foo"
or the empty string.A generalised repetition count (minimum, maximum) is also available.
'foo' 2..
matches 'foo'
two or more times'foo' 3..5
matches 'foo'
from three to five times'foo' ..4
matches 'foo'
from zero to four timesLookahead assertions can be used to make parsing expressions context-sensitive. The parser will look ahead into the buffer and attempt to match an expression without consuming input.
Preceding an expression with an ampersand (&)
indicates that it must match, but no input will be consumed in the process of determining whether this is true.
"foo" &"bar"
matches "foobar"
but only consumes up to the end "foo"
. It will not match "foobaz"
.Preceding an expression with a bang (!)
indicates that the expression must not match, but no input will be consumed in the process of determining whether this is true.
"foo" !"bar"
matches "foobaz"
but only consumes up to the end "foo"
. It will not match "foobar"
.Note that a lookahead assertion may be used on any rule, not just a string terminal.
rule things
thing (!(disallowed / ',') following)*
end
Here's a common use case:
rule word
[a-zA-Z]+
end
rule conjunction
primitive ('and' ' '+ primitive)*
end
rule primitive
(!'and' word ' '+)*
end
Here's the easiest way to handle C-style comments:
rule c_comment
'/*'
(
!'*/'
(. / "\n")
)*
'*/'
end
Sometimes you must execute Ruby code during parsing in order to decide how to proceed. This is an advanced feature, and must be used with great care, because it can change the way a Treetop parser backtracks in a way that breaks the parsing algorithm. See the notes below on how to use this feature safely.
The code block is the body of a Ruby lambda block, and should return true or false, to cause this parse rule to continue or fail (for positive sempreds), fail or continue (for negative sempreds).
&{ ... }
Evaluate the block and fail this rule if the result is false or nil!{ ... }
Evaluate the block and fail this rule if the result is not false or nilThe lambda is passed a single argument which is the array of syntax nodes matched so far in the
current sequence. Note that because the current rule has not yet succeeded, no syntax node has
yet been constructed, and so the lambda block is being run in a context where the names
of the
preceding rules (or as assigned by labels) are not available to access the sub-rules.
rule id
[a-zA-Z][a-zA-Z0-9]*
{
def is_reserved
ReservedSymbols.include? text_value
end
}
end
rule foo_rule
foo id &{|seq| seq[1].is_reserved } baz
end
Match "foo id baz" only if id.is_reserved
. Note that id
cannot be referenced by name from foo_rule
,
since that rule has not yet succeeded, but id
has completed and so its added methods are available.
rule test_it
foo bar &{|s| debugger; true } baz
end
Match foo
then bar
, stop to enter the debugger (make sure you have said require "ruby-debug"
somewhere),
then continue by trying to match baz
.
Treetop, like other PEG parsers, achieves its performance guarantee by remembering which rules it has tried at which locations in the input, and what the result was. This process, called memoization, requires that the rule would produce the same result (if run again) as it produced the first time when the result was remembered. If you violate this principle in your semantic predicates, be prepared to fight Cerberus before you're allowed out of Hades again.
There's an example of how to use semantic predicates to parse a language with white-space indented blocks in the examples directory.