-
Notifications
You must be signed in to change notification settings - Fork 0
/
README
126 lines (89 loc) · 4.16 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
What is rat?
=============
Rat is a packrat parser that uses a TPDL-like grammar. There are two components
involved: PEG and Packrat. PEG is responsible for loading and validating the
grammar file. Packrat uses the syntax defined by the grammar to parse input.
Grammar Syntax
--------------
The grammar syntax is a text file which may contain comments that begin with
the '#' character:
# This is a comment
A valid grammar defines at least one rule, contains no undefined rules, no
defined but unreferenced rules, and no left-recursion. The first rule defined
is the top-level or entry rule, and represents the entire syntax. Here is a
typical first rule:
first: "this"
"that"
There are several syntactical elements here. The rule name is followed by a
colon, which makes this a rule definition. Everything between the colon and the
next blank line is the rule definition. This rule is named "first", and contains
two alternate productions, each of which is a string literal ("this" and "that")
followed by a blank line which terminates the definition.
String literals always use double quotes. Character literals use single quotes,
and the above could also be represented by the more verbose and less efficient:
first: 't' 'h' 'i' 's'
't' 'h' 'a' 't'
The indentation of the second line is only important in that it is not aligned
at the left margin.
Any unquoted word represents a grammar rule that must be defined. A rule may be
followed by a quantifier, either '?', '+' or '*'. These correspond to the POSIX
wildcard semantics of zero-or-one, one-or-more, and zero-or-more matches. Here
is a grammar defining positive integers:
positive_integer: sign? digit+
sign: '+'
'-'
digit: '0'
'1'
'2'
'3'
'4'
'5'
'6'
'7'
'8'
'9'
Note the blank line between definitions, and only one production per line. Note
that 'sign' and 'digit' are leaf nodes, which contain no other rule references.
There are several built-in categories of character/token named intrinsics, and
these may be used to simplify grammar and speed parsing. The example above can
use the <digit> intrinsic:
positive_integer: sign? <digit>+
sign: '+'
'-'
Supported intrinsics are:
<digit> Numeric digits: 0 ... 9
<hex> Hex digits: 0 ... 9, a ... f, A ... F
<punct> Punctuation: . , ; : ' " ! ? etc
<alpha> Any single character that is printable, and not punctuation
or whitespace
<character> Any one character
<ws> Any single white space character
<sep> Horizontal whitespace only
<eol> Vertical whitespace only
<word> Any consecutive string of non-whitespace, non-punctuation
<token> Any consecutive string of non-whitespace
Entities are literal strings that are defined externally, and represented in the
grammar as:
<entity:thing>
This construct matches any of the externally provided entities in the 'thing'
category. Entities are provided at runtime before a grammar is loaded. This
capability allows for dynamic lists, such as user-defined reports, command, or
extensions.
Further dynamic support is available using the external intrinsic:
<external:thing>
In this case, the parsing is delegated to an external function call, identified
by the name 'thing'. External intrinsics allow complex parsing situations that
are not readily expressed in PEG.
Grammar files can grow large, and definitions may be shared across projects. To
facilitate this, grammar files may be imported using this syntax:
import <file>
Where the file is a resolvable path from the current working directory.
How to Construct a Grammar
--------------------------
A grammar is a tree structure. The easiest way to construct that tree is one
branch at a time. Start by defining the first rule which references a leaf node
and add to that. Test the syntax at every step by running something like this:
$ ./rat -d integer.peg '-23'
This will load the grammar in the integer.peg file, and if valid, will attempt
to parse the input '-23' against that grammar.
---