Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(564)

Side by Side Diff: packages/petitparser/lib/src/petitparser/parser.dart

Issue 2989763002: Update charted to 0.4.8 and roll (Closed)
Patch Set: Removed Cutch from list of reviewers Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 part of petitparser;
2
3 /// Abstract base class of all parsers.
4 abstract class Parser {
5
6 /// Primitive method doing the actual parsing.
7 ///
8 /// The method is overridden in concrete subclasses to implement the
9 /// parser specific logic. The methods takes a parse [context] and
10 /// returns the resulting context, which is either a [Success] or
11 /// [Failure] context.
12 Result parseOn(Context context);
13
14 /// Returns the parse result of the [input].
15 ///
16 /// The implementation creates a default parse context on the input and calls
17 /// the internal parsing logic of the receiving parser.
18 ///
19 /// For example, `letter().plus().parse('abc')` results in an instance of
20 /// [Success], where [Result.position] is `3` and [Success.value] is
21 /// `[a, b, c]`.
22 ///
23 /// Similarly, `letter().plus().parse('123')` results in an instance of
24 /// [Failure], where [Result.position] is `0` and [Failure.message] is
25 /// ['letter expected'].
26 Result parse(input) {
27 return parseOn(new Context(input, 0));
28 }
29
30 /// Tests if the [input] can be successfully parsed.
31 ///
32 /// For example, `letter().plus().accept('abc')` returns `true`, and
33 /// `letter().plus().accept('123')` returns `false`.
34 bool accept(input) {
35 return parse(input).isSuccess;
36 }
37
38 /// Returns a list of all successful overlapping parses of the [input].
39 ///
40 /// For example, `letter().plus().matches('abc de')` results in the list
41 /// `[['a', 'b', 'c'], ['b', 'c'], ['c'], ['d', 'e'], ['e']]`. See
42 /// [Parser.matchesSkipping] to retrieve non-overlapping parse results.
43 Iterable matches(input) {
44 var list = new List();
45 and()
46 .map((each) => list.add(each))
47 .seq(any())
48 .or(any())
49 .star()
50 .parse(input);
51 return list;
52 }
53
54 /// Returns a list of all successful non-overlapping parses of the input.
55 ///
56 /// For example, `letter().plus().matchesSkipping('abc de')` results in the
57 /// list `[['a', 'b', 'c'], ['d', 'e']]`. See [Parser.matches] to retrieve
58 /// overlapping parse results.
59 Iterable matchesSkipping(input) {
60 var list = new List();
61 map((each) => list.add(each)).or(any()).star().parse(input);
62 return list;
63 }
64
65 /// Returns new parser that accepts the receiver, if possible. The resulting
66 /// parser returns the result of the receiver, or `null` if not applicable.
67 /// The returned value can be provided as an optional argument [otherwise].
68 ///
69 /// For example, the parser `letter().optional()` accepts a letter as input
70 /// and returns that letter. When given something else the parser succeeds as
71 /// well, does not consume anything and returns `null`.
72 Parser optional([otherwise]) => new OptionalParser(this, otherwise);
73
74 /// Returns a parser that accepts the receiver zero or more times. The
75 /// resulting parser returns a list of the parse results of the receiver.
76 ///
77 /// This is a greedy and blind implementation that tries to consume as much
78 /// input as possible and that does not consider what comes afterwards.
79 ///
80 /// For example, the parser `letter().star()` accepts the empty string or
81 /// any sequence of letters and returns a possibly empty list of the parsed
82 /// letters.
83 Parser star() => repeat(0, unbounded);
84
85 /// Returns a parser that parses the receiver zero or more times until it
86 /// reaches a [limit]. This is a greedy non-blind implementation of the
87 /// [Parser.star] operator. The [limit] is not consumed.
88 Parser starGreedy(Parser limit) => repeatGreedy(limit, 0, unbounded);
89
90 /// Returns a parser that parses the receiver zero or more times until it
91 /// reaches a [limit]. This is a lazy non-blind implementation of the
92 /// [Parser.star] operator. The [limit] is not consumed.
93 Parser starLazy(Parser limit) => repeatLazy(limit, 0, unbounded);
94
95 /// Returns a parser that accepts the receiver one or more times. The
96 /// resulting parser returns a list of the parse results of the receiver.
97 ///
98 /// This is a greedy and blind implementation that tries to consume as much
99 /// input as possible and that does not consider what comes afterwards.
100 ///
101 /// For example, the parser `letter().plus()` accepts any sequence of
102 /// letters and returns a list of the parsed letters.
103 Parser plus() => repeat(1, unbounded);
104
105 /// Returns a parser that parses the receiver one or more times until it
106 /// reaches [limit]. This is a greedy non-blind implementation of the
107 /// [Parser.plus] operator. The [limit] is not consumed.
108 Parser plusGreedy(Parser limit) => repeatGreedy(limit, 1, unbounded);
109
110 /// Returns a parser that parses the receiver one or more times until it
111 /// reaches a [limit]. This is a lazy non-blind implementation of the
112 /// [Parser.plus] operator. The [limit] is not consumed.
113 Parser plusLazy(Parser limit) => repeatLazy(limit, 1, unbounded);
114
115 /// Returns a parser that accepts the receiver between [min] and [max] times.
116 /// The resulting parser returns a list of the parse results of the receiver.
117 ///
118 /// This is a greedy and blind implementation that tries to consume as much
119 /// input as possible and that does not consider what comes afterwards.
120 ///
121 /// For example, the parser `letter().repeat(2, 4)` accepts a sequence of
122 /// two, three, or four letters and returns the accepted letters as a list.
123 Parser repeat(int min, int max) {
124 return new PossessiveRepeatingParser(this, min, max);
125 }
126
127 /// Returns a parser that parses the receiver at least [min] and at most [max]
128 /// times until it reaches a [limit]. This is a greedy non-blind implementatio n of
129 /// the [Parser.repeat] operator. The [limit] is not consumed.
130 Parser repeatGreedy(Parser limit, int min, int max) {
131 return new GreedyRepeatingParser(this, limit, min, max);
132 }
133
134 /// Returns a parser that parses the receiver at least [min] and at most [max]
135 /// times until it reaches a [limit]. This is a lazy non-blind implementation of
136 /// the [Parser.repeat] operator. The [limit] is not consumed.
137 Parser repeatLazy(Parser limit, int min, int max) {
138 return new LazyRepeatingParser(this, limit, min, max);
139 }
140
141 /// Returns a parser that accepts the receiver exactly [count] times. The
142 /// resulting parser returns a list of the parse results of the receiver.
143 ///
144 /// For example, the parser `letter().times(2)` accepts two letters and
145 /// returns a list of the two parsed letters.
146 Parser times(int count) => repeat(count, count);
147
148 /// Returns a parser that accepts the receiver followed by [other]. The
149 /// resulting parser returns a list of the parse result of the receiver
150 /// followed by the parse result of [other]. Calling this method on an
151 /// existing sequence code not nest this sequence into a new one, but
152 /// instead augments the existing sequence with [other].
153 ///
154 /// For example, the parser `letter().seq(digit()).seq(letter())` accepts a
155 /// letter followed by a digit and another letter. The parse result of the
156 /// input string `'a1b'` is the list `['a', '1', 'b']`.
157 Parser seq(Parser other) => new SequenceParser([this, other]);
158
159 /// Convenience operator returning a parser that accepts the receiver followed
160 /// by [other]. See [Parser.seq] for details.
161 Parser operator &(Parser other) => this.seq(other);
162
163 /// Returns a parser that accepts the receiver or [other]. The resulting
164 /// parser returns the parse result of the receiver, if the receiver fails
165 /// it returns the parse result of [other] (exclusive ordered choice).
166 ///
167 /// For example, the parser `letter().or(digit())` accepts a letter or a
168 /// digit. An example where the order matters is the following choice between
169 /// overlapping parsers: `letter().or(char('a'))`. In the example the parser
170 /// `char('a')` will never be activated, because the input is always consumed
171 /// `letter()`. This can be problematic if the author intended to attach a
172 /// production action to `char('a')`.
173 Parser or(Parser other) => new ChoiceParser([this, other]);
174
175 /// Convenience operator returning a parser that accepts the receiver or
176 /// [other]. See [Parser.or] for details.
177 Parser operator |(Parser other) => this.or(other);
178
179 /// Returns a parser (logical and-predicate) that succeeds whenever the
180 /// receiver does, but never consumes input.
181 ///
182 /// For example, the parser `char('_').and().seq(identifier)` accepts
183 /// identifiers that start with an underscore character. Since the predicate
184 /// does not consume accepted input, the parser `identifier` is given the
185 /// ability to process the complete identifier.
186 Parser and() => new AndParser(this);
187
188 /// Returns a parser (logical not-predicate) that succeeds whenever the
189 /// receiver fails, but never consumes input.
190 ///
191 /// For example, the parser `char('_').not().seq(identifier)` accepts
192 /// identifiers that do not start with an underscore character. If the parser
193 /// `char('_')` accepts the input, the negation and subsequently the
194 /// complete parser fails. Otherwise the parser `identifier` is given the
195 /// ability to process the complete identifier.
196 Parser not([String message]) => new NotParser(this, message);
197
198 /// Returns a parser that consumes any input token (character), but the
199 /// receiver.
200 ///
201 /// For example, the parser `letter().neg()` accepts any input but a letter.
202 /// The parser fails for inputs like `'a'` or `'Z'`, but succeeds for
203 /// input like `'1'`, `'_'` or `'$'`.
204 Parser neg([String message]) => not(message).seq(any()).pick(1);
205
206 /// Returns a parser that discards the result of the receiver, and returns
207 /// a sub-string of the consumed range in the string/list being parsed.
208 ///
209 /// For example, the parser `letter().plus().flatten()` returns `'abc'`
210 /// for the input `'abc'`. In contrast, the parser `letter().plus()` would
211 /// return `['a', 'b', 'c']` for the same input instead.
212 Parser flatten() => new FlattenParser(this);
213
214 /// Returns a parser that returns a [Token]. The token carries the parsed
215 /// value of the receiver [Token.value], as well as the consumed input
216 /// [Token.input] from [Token.start] to [Token.stop] of the input being
217 /// parsed.
218 ///
219 /// For example, the parser `letter().plus().token()` returns the token
220 /// `Token[start: 0, stop: 3, value: abc]` for the input `'abc'`.
221 Parser token() => new TokenParser(this);
222
223 /// Returns a parser that consumes input before and after the receiver. The
224 /// optional argument is a parser that consumes the excess input. By default
225 /// `whitespace()` is used. Two arguments can be provided to have different
226 /// parsers on the [left] and [right] side.
227 ///
228 /// For example, the parser `letter().plus().trim()` returns `['a', 'b']`
229 /// for the input `' ab\n'` and consumes the complete input string.
230 Parser trim([Parser left, Parser right]) {
231 if (left == null) left = whitespace();
232 if (right == null) right = left;
233 return new TrimmingParser(this, left, right);
234 }
235
236 /// Returns a parser that succeeds only if the receiver consumes the complete
237 /// input, otherwise return a failure with the optional [message].
238 ///
239 /// For example, the parser `letter().end()` succeeds on the input `'a'`
240 /// and fails on `'ab'`. In contrast the parser `letter()` alone would
241 /// succeed on both inputs, but not consume everything for the second input.
242 Parser end([String message = 'end of input expected']) {
243 return new EndOfInputParser(this, message);
244 }
245
246 /// Returns a parser that points to the receiver, but can be changed to point
247 /// to something else at a later point in time.
248 ///
249 /// For example, the parser `letter().settable()` behaves exactly the same
250 /// as `letter()`, but it can be replaced with another parser using
251 /// [SettableParser.set].
252 SettableParser settable() => new SettableParser(this);
253
254 /// Returns a parser that evaluates a [function] as the production action
255 /// on success of the receiver.
256 ///
257 /// For example, the parser `digit().map((char) => int.parse(char))` returns
258 /// the number `1` for the input string `'1'`. If the delegate fail, the
259 /// production action is not executed and the failure is passed on.
260 Parser map(Function function) => new ActionParser(this, function);
261
262 /// Returns a parser that transform a successful parse result by returning
263 /// the element at [index] of a list. A negative index can be used to access
264 /// the elements from the back of the list.
265 ///
266 /// For example, the parser `letter().star().pick(-1)` returns the last
267 /// letter parsed. For the input `'abc'` it returns `'c'`.
268 Parser pick(int index) {
269 return this.map((List list) {
270 return list[index < 0 ? list.length + index : index];
271 });
272 }
273
274 /// Returns a parser that transforms a successful parse result by returning
275 /// the permuted elements at [indexes] of a list. Negative indexes can be
276 /// used to access the elements from the back of the list.
277 ///
278 /// For example, the parser `letter().star().permute([0, -1])` returns the
279 /// first and last letter parsed. For the input `'abc'` it returns
280 /// `['a', 'c']`.
281 Parser permute(List<int> indexes) {
282 return this.map((List list) {
283 return indexes.map((index) {
284 return list[index < 0 ? list.length + index : index];
285 }).toList();
286 });
287 }
288
289 /// Returns a parser that consumes the receiver one or more times separated
290 /// by the [separator] parser. The resulting parser returns a flat list of
291 /// the parse results of the receiver interleaved with the parse result of the
292 /// separator parser.
293 ///
294 /// If the optional argument [includeSeparators] is set to `false`, then the
295 /// separators are not included in the parse result. If the optional argument
296 /// [optionalSeparatorAtEnd] is set to `true` the parser also accepts an
297 /// optional separator at the end.
298 ///
299 /// For example, the parser `digit().separatedBy(char('-'))` returns a parser
300 /// that consumes input like `'1-2-3'` and returns a list of the elements and
301 /// separators: `['1', '-', '2', '-', '3']`.
302 Parser separatedBy(Parser separator,
303 {bool includeSeparators: true, bool optionalSeparatorAtEnd: false}) {
304 var repeater = new SequenceParser([separator, this]).star();
305 var parser = new SequenceParser(optionalSeparatorAtEnd
306 ? [this, repeater, separator.optional(separator)]
307 : [this, repeater]);
308 return parser.map((List list) {
309 var result = new List();
310 result.add(list[0]);
311 for (var tuple in list[1]) {
312 if (includeSeparators) {
313 result.add(tuple[0]);
314 }
315 result.add(tuple[1]);
316 }
317 if (includeSeparators &&
318 optionalSeparatorAtEnd &&
319 !identical(list[2], separator)) {
320 result.add(list[2]);
321 }
322 return result;
323 });
324 }
325
326 /// Returns a shallow copy of the receiver.
327 ///
328 /// Override this method in all subclasses.
329 Parser copy();
330
331 /// Recursively tests for structural equality of two parsers.
332 ///
333 /// The code can automatically deals with recursive parsers and parsers that
334 /// refer to other parsers. This code is supposed to be overridden by parsers
335 /// that add other state.
336 bool isEqualTo(Parser other, [Set<Parser> seen]) {
337 if (seen == null) {
338 seen = new Set();
339 }
340 if (this == other || seen.contains(this)) {
341 return true;
342 }
343 seen.add(this);
344 return runtimeType == other.runtimeType &&
345 hasEqualProperties(other) &&
346 hasEqualChildren(other, seen);
347 }
348
349 /// Compare the properties of two parsers. Normally this method should not be
350 /// called directly, instead use [Parser#equals].
351 ///
352 /// Override this method in all subclasses that add new state.
353 bool hasEqualProperties(Parser other) => true;
354
355 /// Compare the children of two parsers. Normally this method should not be
356 /// called directly, instead use [Parser#equals].
357 ///
358 /// Normally this method does not need to be overridden, as this method works
359 /// generically on the returned [Parser#children].
360 bool hasEqualChildren(Parser other, Set<Parser> seen) {
361 var thisChildren = children,
362 otherChildren = other.children;
363 if (thisChildren.length != otherChildren.length) {
364 return false;
365 }
366 for (var i = 0; i < thisChildren.length; i++) {
367 if (!thisChildren[i].isEqualTo(otherChildren[i], seen)) {
368 return false;
369 }
370 }
371 return true;
372 }
373
374 /// Returns a list of directly referenced parsers.
375 ///
376 /// For example, `letter().children` returns the empty collection `[]`,
377 /// because the letter parser is a primitive or leaf parser that does not
378 /// depend or call any other parser.
379 ///
380 /// In contrast, `letter().or(digit()).children` returns a collection
381 /// containing both the `letter()` and `digit()` parser.
382 List<Parser> get children => const [];
383
384 /// Changes the receiver by replacing [source] with [target]. Does nothing
385 /// if [source] does not exist in [Parser.children].
386 ///
387 /// The following example creates a letter parser and then defines a parser
388 /// called `example` that accepts one or more letters. Eventually the parser
389 /// `example` is modified by replacing the `letter` parser with a new
390 /// parser that accepts a digit. The resulting `example` parser accepts one
391 /// or more digits.
392 ///
393 /// var letter = letter();
394 /// var example = letter.plus();
395 /// example.replace(letter, digit());
396 void replace(Parser source, Parser target) {
397 // no children, nothing to do
398 }
399 }
OLDNEW
« no previous file with comments | « packages/petitparser/lib/src/petitparser/expression.dart ('k') | packages/petitparser/lib/src/petitparser/parsers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698