OLD | NEW |
(Empty) | |
| 1 PetitParser for Dart |
| 2 ==================== |
| 3 |
| 4 [](https://pub.dartl
ang.org/packages/petitparser) |
| 5 [](https:
//travis-ci.org/petitparser/dart-petitparser) |
| 6 [](https://coveralls.io/r/petitparser/dart-petitparser) |
| 7 [](https://github.com/petitparser/dart-petitparser/issues) |
| 8 [](https://gitter.im/pet
itparser/dart-petitparser) |
| 9 |
| 10 Grammars for programming languages are traditionally specified statically. They
are hard to compose and reuse due to ambiguities that inevitably arise. PetitPar
ser combines ideas from scannnerless parsing, parser combinators, parsing expres
sion grammars and packrat parsers to model grammars and parsers as objects that
can be reconfigured dynamically. |
| 11 |
| 12 This library is open source, stable and well tested. Development happens on [Git
Hub](https://github.com/petitparser/dart-petitparser). Feel free to report issue
s or create a pull-request there. General questions are best asked on [StackOver
flow](http://stackoverflow.com/questions/tagged/petitparser+dart). |
| 13 |
| 14 Up-to-date [class documentation](http://www.dartdocs.org/documentation/petitpars
er/latest/index.html) is created with every release. |
| 15 |
| 16 |
| 17 Tutorial |
| 18 -------- |
| 19 |
| 20 ### Writing a Simple Grammar |
| 21 |
| 22 Writing grammars with PetitParser is simple as writing Dart code. For example, t
o write a grammar that can parse identifiers that start with a letter followed b
y zero or more letter or digits is defined as follows: |
| 23 |
| 24 ```dart |
| 25 Parser id = letter() & (letter() | digit()).star(); |
| 26 ``` |
| 27 |
| 28 If you look at the object `id` in the debugger, you'll notice that the code abov
e builds a tree of parser objects: |
| 29 |
| 30 - Sequence: This parser accepts a sequence of parsers. |
| 31 - Predicate: This parser accepts a single letter. |
| 32 - Repeater: This parser accepts zero or more times another parser. |
| 33 - Choice: This parser accepts a single word character. |
| 34 - Predicate: This parser accepts a single letter. |
| 35 - Predicate: This parser accepts a single digit. |
| 36 |
| 37 The operators `&` and `|` are overloaded and create a sequence and a choice pars
er respectively. In some contexts it might be more convenient to use chained fun
ction calls: |
| 38 |
| 39 ```dart |
| 40 Parser id = letter().seq(letter().or(digit()).star()); |
| 41 ``` |
| 42 |
| 43 ### Parsing Some Input |
| 44 |
| 45 To actually parse a `String` (or `List`) we can use the method `Parser.parse`: |
| 46 |
| 47 ```dart |
| 48 Result id1 = id.parse('yeah'); |
| 49 Result id2 = id.parse('f12'); |
| 50 ``` |
| 51 |
| 52 The method `Parser.parse` returns a parse `Result`, which is either an instance
of `Success` or `Failure`. In both examples above we are successful and can retr
ieve the parse result using `Success.value`: |
| 53 |
| 54 ```dart |
| 55 print(id1.value); // ['y', ['e', 'a', 'h']] |
| 56 print(id2.value); // ['f', ['1', '2']] |
| 57 ``` |
| 58 |
| 59 While it seems odd to get these nested arrays with characters as a return value,
this is the default decomposition of the input into a parse tree. We'll see in
a while how that can be customized. |
| 60 |
| 61 If we try to parse something invalid we get an instance of `Failure` as an answe
r and we can retrieve a descriptive error message using `Failure.message`: |
| 62 |
| 63 ```dart |
| 64 Result id3 = id.parse('123'); |
| 65 print(id3.message); // 'letter expected' |
| 66 print(id3.position); // 0 |
| 67 ``` |
| 68 |
| 69 Trying to retrieve the parse result by calling `Failure.value` would throw the e
xception `ParserError`. `Context.isSuccess` and `Context.isFailure` can be used
to decide if the parse was successful. |
| 70 |
| 71 If you are only interested if a given string matches or not you can use the help
er method `Parser.accept`: |
| 72 |
| 73 ```dart |
| 74 print(id.accept('foo')); // true |
| 75 print(id.accept('123')); // false |
| 76 ``` |
| 77 |
| 78 ### Different Kinds of Parsers |
| 79 |
| 80 PetitParser provide a large set of ready-made parser that you can compose to con
sume and transform arbitrarily complex languages. The terminal parsers are the m
ost simple ones. We've already seen a few of those: |
| 81 |
| 82 - `char('a')` parses the character *a*. |
| 83 - `string('abc')` parses the string *abc*. |
| 84 - `any()` parses any character. |
| 85 - `digit()` parses any digit from *0* to *9*. |
| 86 - `letter()` parses any letter from *a* to *z* and *A* to *Z*. |
| 87 - `word()` parses any letter or digit. |
| 88 |
| 89 So instead of using the letter and digit predicate, we could have written our id
entifier parser like this: |
| 90 |
| 91 ```dart |
| 92 var id = letter() & word().star(); |
| 93 ``` |
| 94 |
| 95 The next set of parsers are used to combine other parsers together: |
| 96 |
| 97 - `p1 & p2` and `p1.seq(p2)` parse *p1* followed by *p2* (sequence). |
| 98 - `p1 | p2` and `p1.or(p2)` parse *p1*, if that doesn't work parse *p2* (ordered
choice). |
| 99 - `p.star()` parses *p* zero or more times. |
| 100 - `p.plus()` parses *p* one or more times. |
| 101 - `p.optional()` parses *p*, if possible. |
| 102 - `p.and()` parses *p*, but does not consume its input. |
| 103 - `p.not()` parses *p* and succeed when p fails, but does not consume its input. |
| 104 - `p.end()` parses *p* and succeed at the end of the input. |
| 105 |
| 106 To attach an action or transformation to a parser we can use the following metho
ds: |
| 107 |
| 108 - `p.map((value) => ...)` performs the transformation given the function. |
| 109 - `p.pick(n)` returns the *n*-th element of the list *p* returns. |
| 110 - `p.flatten()` creates a string from the result of *p*. |
| 111 - `p.token()` creates a token from the result of *p*. |
| 112 - `p.trim()` trims whitespaces before and after *p*. |
| 113 |
| 114 To return a string of the parsed identifier, we can modify our parser like this: |
| 115 |
| 116 ```dart |
| 117 var id = letter().seq(word().star()).flatten(); |
| 118 ``` |
| 119 |
| 120 To conveniently find all matches in a given input string you can use `Parser.mat
chesSkipping`: |
| 121 |
| 122 ```dart |
| 123 var matches = id.matchesSkipping('foo 123 bar4'); |
| 124 print(matches); // ['foo', 'bar4'] |
| 125 ``` |
| 126 |
| 127 These are the basic elements to build parsers. There are a few more well documen
ted and tested factory methods in the `Parser` class. If you want browse their d
ocumentation and tests. |
| 128 |
| 129 ### Writing a More Complicated Grammar |
| 130 |
| 131 Now we are able to write a more complicated grammar for evaluating simple |
| 132 arithmetic expressions. Within a file we start with the grammar for a |
| 133 number (actually an integer): |
| 134 |
| 135 ```dart |
| 136 var number = digit().plus().flatten().trim().map(int.parse); |
| 137 ``` |
| 138 |
| 139 Then we define the productions for addition and multiplication in order of prece
dence. Note that we instantiate the productions with undefined parsers upfront,
because they recursively refer to each other. Later on we can resolve this recur
sion by setting their reference: |
| 140 |
| 141 ```dart |
| 142 var term = undefined(); |
| 143 var prod = undefined(); |
| 144 var prim = undefined(); |
| 145 |
| 146 term.set(prod.seq(char('+').trim()).seq(term).map((values) { |
| 147 return values[0] + values[2]; |
| 148 }).or(prod)); |
| 149 prod.set(prim.seq(char('*').trim()).seq(prod).map((values) { |
| 150 return values[0] * values[2]; |
| 151 }).or(prim)); |
| 152 prim.set(char('(').trim().seq(term).seq(char(')'.trim())).map((values) { |
| 153 return values[1]; |
| 154 }).or(number)); |
| 155 ``` |
| 156 |
| 157 To make sure that our parser consumes all input we wrap it with the `end()` pars
er into the start production: |
| 158 |
| 159 ```dart |
| 160 var start = term.end(); |
| 161 ``` |
| 162 |
| 163 That's it, now we can test our parser and evaluator: |
| 164 |
| 165 ```dart |
| 166 print(start.parse('1 + 2 * 3').value); // 7 |
| 167 print(start.parse('(1 + 2) * 3').value); // 9 |
| 168 ``` |
| 169 |
| 170 As an exercise we could extend the parser to also accept negative numbers and fl
oating point numbers, not only integers. Furthermore it would be useful to suppo
rt subtraction and division as well. All these features can be added with a few
lines of PetitParser code. |
| 171 |
| 172 |
| 173 Misc |
| 174 ---- |
| 175 |
| 176 ### Examples |
| 177 |
| 178 The package comes with a large collections of grammars and language experiments
ready to explore: |
| 179 |
| 180 - `lib/dart.dart` contains an experimental Dart grammar. |
| 181 - `lib/json.dart` contains a complete JSON grammar and parser. |
| 182 - `lib/lisp.dart` contains a complete Lisp grammar, parser and evaluator: |
| 183 - `example/lisphell` contains a command line lisp interpreter. |
| 184 - `example/lispweb` contains a web based lisp interpreter. |
| 185 - `lib/smalltalk.dart` contains a complete Smalltalk grammar. |
| 186 |
| 187 Furthermore, there are various open source projects using PetitParser: |
| 188 |
| 189 - [Badger](https://github.com/badger-lang/badger) is an experimental programming
language. |
| 190 - [dart-xml](https://github.com/renggli/dart-xml) is a lightweight library for p
arsing, traversing, and querying XML documents. |
| 191 - [InQlik](https://github.com/inqlik/inqlik_cli) is a parser for QlikView load s
cript files. |
| 192 - [intl](https://github.com/dart-lang/intl) provides internationalization and lo
calization support to Dart. |
| 193 - [PowerConfig](https://github.com/kaendfinger/powerconfig.dart) is a power conf
ig implementation. |
| 194 - [RythmDart](https://github.com/freewind/RythmDart) is a rich featured, high pe
rformance template engine. |
| 195 - [SharkDart](https://github.com/freewind/SharkDart) is a small template engine. |
| 196 |
| 197 ### History |
| 198 |
| 199 PetitParser was originally implemented in [Smalltalk](http://scg.unibe.ch/resear
ch/helvetia/petitparser). Later on, as a mean to learn these languages, I reimpl
emented PetitParser in [Java](https://github.com/petitparser/java-petitparser) a
nd [Dart](https://github.com/petitparser/dart-petitparser). The implementations
are very similar in their API and the supported features. If possible, the imple
mentations adopt best practises of the target language. |
| 200 |
| 201 ### Ports |
| 202 |
| 203 - [Java](https://github.com/petitparser/java-petitparser) |
| 204 - [PHP](https://github.com/mindplay-dk/petitparserphp) |
| 205 - [Smalltalk](http://scg.unibe.ch/research/helvetia/petitparser) |
| 206 - [TypeScript](https://github.com/mindplay-dk/petitparser-ts) |
| 207 |
| 208 ### License |
| 209 |
| 210 The MIT License, see [LICENSE](https://raw.githubusercontent.com/petitparser/dar
t-petitparser/master/LICENSE). |
OLD | NEW |