OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #library('Peg Parser'); | |
6 | |
7 /* | |
8 * The following functions are combinators for building Rules. | |
9 * | |
10 * A rule is one of the following | |
11 * - A String which matches the string literally. | |
12 * - A Symbol which matches the symbol's definition. | |
13 * - A list of rules with an optional reducing function, which matches a sequenc
e. | |
14 * - The result of calling one of the combinators. | |
15 * | |
16 * Some rules are 'value-generating' rules, they return an 'abstract syntax | |
17 * tree' with the match. If a rule is not value-generating [:null:] is the | |
18 * value. | |
19 * | |
20 * A Symbol is always a value-generating rule. If the value is not required, use | |
21 * [:SKIP(aSymbol):] in place of [:aSymbol:]. | |
22 * | |
23 * A String is not a value-generating rule but can be converted into one by | |
24 * using [:TEXT('string'):] in place of [:'string':]. | |
25 * | |
26 * A list or sequence is value-generating depending on the subrules. The | |
27 * sequence is value-generating if any of the subrules are value-generating or | |
28 * if there is a reducing function. If no reducing function is given, the value | |
29 * returned depends on the number of value-generating subrules. If there is | |
30 * only one value generating subrule, that provideds the value for the sequence. | |
31 * If there are more, then the value is a list of the values of the | |
32 * value-generating subrules. | |
33 */ | |
34 | |
35 /** | |
36 * Matches one character by a predicate on the character code. | |
37 * If [spec] is an int, that character is matched. | |
38 * If [spec] is a function it is used | |
39 * | |
40 * Example [: CHARCODE((code) => 48 <= code && code <= 57) :] recognizes an | |
41 * ASCII digit. | |
42 * | |
43 * CHARCODE does not generate a value. | |
44 */ | |
45 _Rule CHARCODE(spec, [name]) { | |
46 if (spec is int) | |
47 return new _CharCodeRule((code) => code == spec, name); | |
48 else | |
49 return new _CharCodeRule(spec, name); | |
50 } | |
51 | |
52 /** | |
53 * Matches one of the [characters]. | |
54 * | |
55 * CHAR does not generate a value. | |
56 */ | |
57 _Rule CHAR([characters]) { | |
58 if (characters == null) | |
59 return const _AnyCharRule(); | |
60 if (characters is int) | |
61 return CHARCODE(characters); | |
62 | |
63 // Find the range of character codes and construct an array of flags for codes | |
64 // within the range. | |
65 List<int> codes = characters.charCodes(); | |
66 codes.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); | |
67 int lo = codes[0]; | |
68 int hi = codes[codes.length - 1]; | |
69 if (lo == hi) | |
70 return CHARCODE(lo); | |
71 int len = hi - lo + 1; | |
72 var flags = new List<bool>(len); | |
73 for (int i = 0; i < len; ++i) | |
74 flags[i] = false; | |
75 for (int code in codes) | |
76 flags[code - lo] = true; | |
77 | |
78 return CHARCODE((code) => code >= lo && code <= hi && flags[code - lo]); | |
79 } | |
80 | |
81 /** | |
82 * Matches the end of the input. | |
83 * | |
84 * END does not generate a value. | |
85 */ | |
86 _Rule get END() => new _EndOfInputRule(); | |
87 | |
88 /** | |
89 * Throws an exception. | |
90 */ | |
91 _Rule ERROR(String message) => new _ErrorRule(message); | |
92 | |
93 /** | |
94 * Matches [rule] but does not consume the input. Useful for matching a right | |
95 * context. | |
96 * | |
97 * AT does not generate a value. | |
98 */ | |
99 _Rule AT(rule) => new _ContextRule(_compile(rule)); | |
100 | |
101 /** | |
102 * Matches when [rule] does not match. No input is consumed. | |
103 * | |
104 * NOT does not generate a value. | |
105 */ | |
106 _Rule NOT(rule) => new _NegativeContextRule(_compile(rule)); | |
107 | |
108 /** | |
109 * Matches [rule] but generates no value even if [rule] generates a value. | |
110 * | |
111 * SKIP never generates a value. | |
112 */ | |
113 _Rule SKIP(rule) => new _SkipRule(_compile(rule)); | |
114 | |
115 /** | |
116 * Matches [rule] in a lexical context where whitespace is not automatically | |
117 * skipped. Useful for matching what would normally be considered to be tokens. | |
118 * [name] is a user-friendly description of what is being matched and is used in | |
119 * error messages. | |
120 * | |
121 * LEX(rule) | |
122 * LEX(name, rule) | |
123 * | |
124 * LEX does not generate a value. If a value is required, wrap LEX with TEXT. | |
125 */ | |
126 _Rule LEX(arg1, [arg2]) { | |
127 if (arg2 == null) | |
128 return new _LexicalRule(arg1 is String ? arg1 : null, _compile(arg1)); | |
129 else | |
130 return new _LexicalRule(arg1, _compile(arg2)); | |
131 } | |
132 | |
133 /** | |
134 * Matches [rule] and generates a value from the matched text. If the [rule] | |
135 * matches, then TEXT(rule) matches and has a value derived from the string | |
136 * fragment that was matched. The default derived value is the string fragment. | |
137 * | |
138 * TEXT always generates a value. | |
139 */ | |
140 _Rule TEXT(rule, [extractor]) => | |
141 new _TextValueRule(_compile(rule), | |
142 extractor == null | |
143 ? (string, start, end) => string.substring(start, end) | |
144 : extractor); | |
145 | |
146 /** | |
147 * Matches an optional rule. | |
148 * | |
149 * MAYBE is a value generating matcher. | |
150 * | |
151 * If [rule] is value generating then the value is the value generated by [rule] | |
152 * if it matches, and [:null:] if it does not. | |
153 * | |
154 * If [rule] is not value generatinge then the value is [:true:] if [rule] | |
155 * matches and [:false:] if it does not. | |
156 */ | |
157 _Rule MAYBE(rule) => new _OptionalRule(_compile(rule)); | |
158 | |
159 /** | |
160 * MANY(rule) matches [rule] [min] or more times. | |
161 * [min] must be 0 or 1. | |
162 * If [separator] is provided it is used to match a separator between matches of | |
163 * [rule]. | |
164 * | |
165 * MANY is a value generating matcher. The value is a list of the matches of | |
166 * [rule]. The list may be empty if [:min == 0:]. | |
167 */ | |
168 _Rule MANY(rule, [separator = null, int min = 1]) { | |
169 assert(0 <= min && min <= 1); | |
170 return new _RepeatRule(_compile(rule), _compileOptional(separator), min); | |
171 } | |
172 | |
173 /** | |
174 * Matches [rule] zero or more times. Shorthand for [:MANY(rule, min:0):] | |
175 * TODO: retire min: parameter? | |
176 * | |
177 * MANY0 is a value generating matcher. | |
178 */ | |
179 _Rule MANY0(rule, [separator = null]) { | |
180 return new _RepeatRule(_compile(rule), _compileOptional(separator), 0); | |
181 } | |
182 | |
183 /** | |
184 * Matches [rules] in order until one succeeds. | |
185 * | |
186 * OR is value-generating. | |
187 */ | |
188 _Rule OR([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]) => | |
189 _compileMultiRule( | |
190 (a is List && b == null) // Backward compat. OR([a, b]) => OR(a, b). | |
191 ? a | |
192 : _unspread(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z), | |
193 false, | |
194 (compiledRules, valueCount, reducer) => | |
195 new _ChoiceRule(compiledRules)); | |
196 | |
197 | |
198 | |
199 _Rule SEQ([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]) => | |
200 _compile(_unspread(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z)); | |
201 | |
202 /** | |
203 * Matches [rule] | |
204 */ | |
205 _Rule MEMO(rule) => new _MemoRule(_compile(rule)); | |
206 | |
207 _Rule TAG(tag, rule) => _compile([rule, (ast) => [tag, ast]]); | |
208 | |
209 | |
210 class ParseError implements Exception { | |
211 const ParseError(String this._message); | |
212 String toString() => _message; | |
213 final String _message; | |
214 } | |
215 | |
216 /** | |
217 * A grammar is a collection of symbols and rules that may be used to parse an | |
218 * input. | |
219 */ | |
220 class Grammar { | |
221 Map<String, Symbol> _symbols; | |
222 | |
223 /** This rule may be set by the user to define whitespace. */ | |
224 _Rule _whitespace; | |
225 | |
226 _Rule get whitespace() => _whitespace; | |
227 void set whitespace(rule) { _whitespace = _compile(rule); } | |
228 | |
229 Grammar() { | |
230 _symbols = new Map<String, Symbol>(); | |
231 whitespace = CHAR(' \t\r\n'); | |
232 } | |
233 | |
234 /** | |
235 * operator [] is used to find or create symbols. Symbols may appear in rules | |
236 * to define recursive rules. | |
237 */ | |
238 Symbol operator [](String name) { | |
239 if (_symbols.containsKey(name)) | |
240 return _symbols[name]; | |
241 Symbol s = new Symbol(name, this); | |
242 _symbols[name] = s; | |
243 return s; | |
244 } | |
245 | |
246 /** | |
247 * Parses the input string and returns the parsed AST, or throws an exception | |
248 * if the input can't be parsed. | |
249 */ | |
250 parse(root, String text) { | |
251 for (var symbol in _symbols.getValues()) | |
252 if (symbol._rule == null) | |
253 print('${symbol.name} is undefined'); | |
254 | |
255 var state = new _ParserState(text, whitespace: whitespace); | |
256 var match = _compile(root).match(state, 0); | |
257 if (match == null) | |
258 return diagnose(state); | |
259 var pos = match[0]; | |
260 pos = _skip_whitespace(state, pos); | |
261 if (pos == state._end) | |
262 return match[1]; | |
263 // TODO: Make this complain about expecting end of file. | |
264 return diagnose(state); | |
265 } | |
266 | |
267 diagnose(state) { | |
268 var message = 'unexpected error'; | |
269 if (!state.max_rule.isEmpty()) { | |
270 var s = new Set(); | |
271 for (var rule in state.max_rule) | |
272 s.add(rule.description()); | |
273 var tokens = new List<String>.from(s); | |
274 tokens.sort((a, b) => | |
275 a.startsWith("'") == b.startsWith("'") | |
276 ? a.compareTo(b) | |
277 : a.startsWith("'") ? +1 : -1); | |
278 var expected = Strings.join(tokens, ' or '); | |
279 var found = state.max_pos == state._end ? 'end of file' | |
280 : "'${state._text[state.max_pos]}'"; | |
281 message = 'Expected $expected but found $found'; | |
282 } | |
283 int start = state.max_pos; | |
284 int end = start; | |
285 while (start >= 1 && state._text[start - 1] != '\n') --start; | |
286 while (end < state._text.length && state._text[end] != '\n') ++end; | |
287 var line = state._text.substring(start, end); | |
288 var indicator = ''; | |
289 for (var i = 0; i < line.length && start + i < state.max_pos; i++) | |
290 indicator = indicator + ' '; | |
291 indicator = indicator + '^'; | |
292 // TODO: Convert to an exception. | |
293 print(message); | |
294 print(line); | |
295 print(indicator); | |
296 return null; | |
297 } | |
298 } | |
299 | |
300 class Symbol { | |
301 final String name; | |
302 final Grammar grammar; | |
303 _Rule _rule; | |
304 | |
305 Symbol(this.name, this.grammar); | |
306 | |
307 void set def(rule) { | |
308 assert(_rule == null); // Assign once. | |
309 _rule = _compile(rule); | |
310 } | |
311 | |
312 toString() => _rule == null ? '<$name>' : '<$name = $_rule>'; | |
313 } | |
314 | |
315 | |
316 class _ParserState { | |
317 _ParserState(this._text, [_Rule whitespace = null]) { | |
318 _end = this._text.length; | |
319 whitespaceRule = whitespace; | |
320 max_rule = []; | |
321 } | |
322 | |
323 String _text; | |
324 int _end; | |
325 | |
326 // | |
327 bool inWhitespaceMode = false; | |
328 _Rule whitespaceRule = null; | |
329 | |
330 // Used for constructing an error message. | |
331 int inhibitExpectedTrackingDepth = 0; | |
332 int max_pos = 0; | |
333 var max_rule; | |
334 } | |
335 | |
336 /** | |
337 * An interface tag for rules. If this tag is on a rule, then the description() | |
338 * of the rule is something sensible to put in a message. | |
339 */ | |
340 interface _Expectable { | |
341 String description(); | |
342 } | |
343 | |
344 class _Rule { | |
345 const _Rule(); | |
346 // Returns null for a match failure or [pos, ast] for success. | |
347 match(_ParserState state, int pos) { | |
348 if (! state.inWhitespaceMode) { | |
349 pos = _skip_whitespace(state, pos); | |
350 } | |
351 return matchAfterWS(state, pos); | |
352 } | |
353 | |
354 // Faster entry point for matching a sub-rule that is matched to the start | |
355 // position of the super-rule. Whitespace has already been skipped so no need | |
356 // to try to skip it again. | |
357 matchAfterWS(_ParserState state, int pos) { | |
358 if (state.inhibitExpectedTrackingDepth == 0) { | |
359 // Track position for possible error messaging | |
360 if (pos > state.max_pos) { | |
361 // Store position and the rule. | |
362 state.max_pos = pos; | |
363 if (this is _Expectable) { | |
364 state.max_rule = [this]; | |
365 } else { | |
366 state.max_rule = []; | |
367 } | |
368 } else if (pos == state.max_pos) { | |
369 if (this is _Expectable) { | |
370 state.max_rule.add(this); | |
371 } | |
372 } | |
373 } | |
374 // Delegate the matching logic to the the specialized function. | |
375 return _match(state, pos); | |
376 } | |
377 | |
378 // Overridden in subclasses to match the rule. | |
379 _match(_ParserState state, int pos) => null; | |
380 | |
381 // Does the rule generate a value (AST) with the match? | |
382 bool get generatesValue() => false; | |
383 | |
384 get defaultValue() => null; | |
385 } | |
386 | |
387 int _skip_whitespace(state, pos) { | |
388 // Returns the next non-whitespace position. | |
389 // This is done by matching the optional whitespaceRule with the current text. | |
390 if (state.whitespaceRule == null) | |
391 return pos; | |
392 state.inWhitespaceMode = true; | |
393 state.inhibitExpectedTrackingDepth++; | |
394 while (true) { | |
395 var match = state.whitespaceRule.match(state, pos); | |
396 if (match == null) | |
397 break; | |
398 pos = match[0]; | |
399 } | |
400 state.inWhitespaceMode = false; | |
401 state.inhibitExpectedTrackingDepth--; | |
402 return pos; | |
403 } | |
404 | |
405 | |
406 _Rule _compileOptional(rule) { | |
407 return rule == null ? null : _compile(rule); | |
408 } | |
409 | |
410 _Rule _compile(rule) { | |
411 if (rule is _Rule) | |
412 return rule; | |
413 if (rule is String) | |
414 return new _StringRule(rule); | |
415 if (rule is Symbol) | |
416 return new _SymbolRule(rule); | |
417 if (rule is RegExp) | |
418 return new _RegExpRule(rule); | |
419 if (rule is List) { | |
420 return _compileMultiRule( | |
421 rule, true, | |
422 (compiledRules, valueCount, reducer) => | |
423 new _SequenceRule(compiledRules, valueCount, reducer)); | |
424 } | |
425 throw new Exception('Cannot compile rule: $rule'); | |
426 } | |
427 | |
428 class _EndOfInputRule extends _Rule { | |
429 _match(_ParserState state, int pos) { | |
430 if (pos == state._end) | |
431 return [pos, null]; | |
432 return null; | |
433 } | |
434 | |
435 toString() => 'END'; | |
436 } | |
437 | |
438 class _ErrorRule extends _Rule { | |
439 String message; | |
440 _ErrorRule(String this.message); | |
441 _match(_ParserState state, int pos) { | |
442 throw new ParseError(message); | |
443 } | |
444 | |
445 toString() => 'ERROR($message)'; | |
446 } | |
447 | |
448 class _CharCodeRule extends _Rule { | |
449 Function _predicate; | |
450 var _name; | |
451 _CharCodeRule(this._predicate, this._name); | |
452 _match(_ParserState state, int pos) { | |
453 if (pos == state._end) | |
454 return null; | |
455 int code = state._text.charCodeAt(pos); | |
456 if (_predicate(code)) | |
457 return [pos + 1, null]; | |
458 return null; | |
459 } | |
460 | |
461 toString() => _name == null ? 'CHARCODE($_predicate)' : 'CHARCODE($_name)'; | |
462 } | |
463 | |
464 class _AnyCharRule extends _Rule { | |
465 const _AnyCharRule(); | |
466 _match(_ParserState state, int pos) { | |
467 if (pos == state._end) | |
468 return null; | |
469 return [pos + 1, null]; | |
470 } | |
471 | |
472 toString() => 'CHAR()'; | |
473 } | |
474 | |
475 class _SymbolRule extends _Rule { | |
476 final Symbol _symbol; | |
477 _SymbolRule(Symbol this._symbol); | |
478 _match(_ParserState state, int pos) { | |
479 if (_symbol._rule == null) | |
480 throw new Exception("Symbol '${_symbol.name}' is undefined"); | |
481 return _symbol._rule.match(state, pos); | |
482 } | |
483 | |
484 bool get generatesValue() => true; | |
485 | |
486 toString() => '<${_symbol.name}>'; | |
487 } | |
488 | |
489 class _SkipRule extends _Rule { | |
490 // A rule that has no value. | |
491 _Rule _rule; | |
492 _SkipRule(_Rule this._rule); | |
493 _match(_ParserState state, int pos) { | |
494 var match = _rule.matchAfterWS(state, pos); | |
495 if (match == null) | |
496 return null; | |
497 return [match[0], null]; | |
498 } | |
499 | |
500 toString() => 'TOKEN($_rule)'; | |
501 } | |
502 | |
503 class _StringRule extends _Rule implements _Expectable { | |
504 final String _string; | |
505 int _len; | |
506 _StringRule(this._string) { | |
507 _len = _string.length; | |
508 } | |
509 | |
510 _match(_ParserState state, int pos) { | |
511 if (pos + _len > state._end) | |
512 return null; | |
513 for (int i = 0; i < _len; i++) { | |
514 if (state._text.charCodeAt(pos + i) != _string.charCodeAt(i)) | |
515 return null; | |
516 } | |
517 return [pos + _len, null]; | |
518 } | |
519 | |
520 //get defaultValue() => _string; | |
521 | |
522 toString() => '"$_string"'; | |
523 | |
524 description() => "'$_string'"; | |
525 } | |
526 | |
527 class _RegExpRule extends _Rule { | |
528 RegExp _re; | |
529 _RegExpRule(this._re) { | |
530 // There is no convenient way to match an anchored substring. | |
531 throw new Exception('RegExp matching not supported'); | |
532 } | |
533 | |
534 toString() => '"$_re"'; | |
535 } | |
536 | |
537 class _LexicalRule extends _Rule implements _Expectable { | |
538 final String _name; | |
539 final _Rule _rule; | |
540 | |
541 _LexicalRule(String this._name, _Rule this._rule); | |
542 | |
543 _match(_ParserState state, int pos) { | |
544 state.inWhitespaceMode = true; | |
545 state.inhibitExpectedTrackingDepth++; | |
546 var match = _rule.matchAfterWS(state, pos); | |
547 state.inhibitExpectedTrackingDepth--; | |
548 state.inWhitespaceMode = false; | |
549 return match; | |
550 } | |
551 | |
552 toString() => _name; | |
553 | |
554 description() => _name == null ? '?' : _name; | |
555 } | |
556 | |
557 class _TextValueRule extends _Rule { | |
558 final _Rule _rule; | |
559 final _extract; // Function | |
560 | |
561 _TextValueRule(_Rule this._rule, Function this._extract); | |
562 | |
563 _match(_ParserState state, int pos) { | |
564 var match = _rule.matchAfterWS(state, pos); | |
565 if (match == null) { | |
566 return null; | |
567 } | |
568 var endPos = match[0]; | |
569 return [endPos, _extract(state._text, pos, endPos)]; | |
570 } | |
571 | |
572 bool get generatesValue() => true; | |
573 | |
574 toString() => 'TEXT($_rule)'; | |
575 } | |
576 | |
577 _Rule _compileMultiRule(List rules, | |
578 bool allowReducer, | |
579 finish(compiledRules, valueCount, reducer)) { | |
580 int valueCount = 0; | |
581 List compiledRules = new List<_Rule>(); | |
582 Function reducer; | |
583 for (var rule in rules) { | |
584 if (reducer != null) | |
585 throw new Exception('Reducer must be last in sequence: $rule'); | |
586 if (rule is Function) { | |
587 if (allowReducer) | |
588 reducer = rule; | |
589 else | |
590 throw new Exception('Bad rule: "$rule"'); | |
591 } else { | |
592 _Rule compiledRule = _compile(rule); | |
593 if (compiledRule.generatesValue) | |
594 ++valueCount; | |
595 compiledRules.add(compiledRule); | |
596 } | |
597 } | |
598 return finish(compiledRules, valueCount, reducer); | |
599 } | |
600 | |
601 String _formatMultiRule(String functor, List rules) { | |
602 var sb = new StringBuffer(functor); | |
603 sb.add('('); | |
604 var separator = ''; | |
605 for (var rule in rules) { | |
606 sb.add(separator); | |
607 sb.add(rule); | |
608 separator = ','; | |
609 } | |
610 sb.add(')'); | |
611 return sb.toString(); | |
612 } | |
613 | |
614 class _SequenceRule extends _Rule { | |
615 // This rule matches the component rules in order. | |
616 final List<_Rule> _rules; | |
617 final int _generatingSubRules = 0; | |
618 final Function _reducer; | |
619 bool _generatesValue; | |
620 _SequenceRule(List<_Rule> this._rules, | |
621 int this._generatingSubRules, | |
622 Function this._reducer) { | |
623 _generatesValue = _generatingSubRules > 0 || _reducer != null; | |
624 } | |
625 | |
626 _match(state, pos) { | |
627 var sequence = []; | |
628 for (var rule in _rules) { | |
629 var match = rule.match(state, pos); | |
630 if (match == null) | |
631 return null; | |
632 if (rule.generatesValue) { | |
633 var ast = match[1]; | |
634 sequence.add(ast); | |
635 } | |
636 pos = match[0]; | |
637 } | |
638 if (_reducer == null) { | |
639 if (_generatingSubRules == 0) | |
640 return [pos, null]; | |
641 if (_generatingSubRules == 1) | |
642 return [pos, sequence[0]]; | |
643 return [pos, sequence]; | |
644 } else { | |
645 return [pos, _apply(_reducer, sequence)]; | |
646 } | |
647 } | |
648 | |
649 bool get generatesValue() => _generatesValue; | |
650 | |
651 toString() => _formatMultiRule('SEQ', _rules); | |
652 } | |
653 | |
654 class _ChoiceRule extends _Rule { | |
655 // This rule matches the first component rule that matches. | |
656 List<_Rule> _rules; | |
657 _ChoiceRule(List<_Rule> this._rules); | |
658 | |
659 _match(state, pos) { | |
660 for (var rule in _rules) { | |
661 var match = rule.match(state, pos); | |
662 if (match != null) { | |
663 /* | |
664 if (!rule.generatesValue) { | |
665 var value = rule.defaultValue; | |
666 if (value != null) | |
667 return [match[0], value]; | |
668 } | |
669 */ | |
670 return match; | |
671 } | |
672 } | |
673 return null; | |
674 } | |
675 | |
676 bool get generatesValue() => true; | |
677 | |
678 toString() => _formatMultiRule('OR', _rules); | |
679 } | |
680 | |
681 class _OptionalRule extends _Rule { | |
682 _Rule _rule; | |
683 _OptionalRule(_Rule this._rule); | |
684 _match(_ParserState state, int pos) { | |
685 var match = _rule.match(state, pos); | |
686 if (_rule.generatesValue) | |
687 return match == null | |
688 ? [pos, null] | |
689 : match; | |
690 return match == null | |
691 ? [pos, false] | |
692 : [match[0], true]; | |
693 } | |
694 | |
695 bool get generatesValue() => true; | |
696 | |
697 toString() => 'MAYBE($_rule)'; | |
698 } | |
699 | |
700 class _ContextRule extends _Rule { | |
701 _Rule _rule; | |
702 _ContextRule(_Rule this._rule); | |
703 _match(_ParserState state, int pos) { | |
704 // TODO: protect error state. | |
705 var match = _rule._match(state, pos); | |
706 if (match == null) | |
707 return null; | |
708 return [pos, null]; | |
709 } | |
710 | |
711 toString() => 'AT($_rule)'; | |
712 } | |
713 | |
714 class _NegativeContextRule extends _Rule { | |
715 _Rule _rule; | |
716 _NegativeContextRule(_Rule this._rule); | |
717 _match(_ParserState state, int pos) { | |
718 // TODO: protect error state. | |
719 var match = _rule._match(state, pos); | |
720 if (match == null) | |
721 return [pos, null]; | |
722 return null; | |
723 } | |
724 | |
725 toString() => 'NOT($_rule)'; | |
726 } | |
727 | |
728 class _RepeatRule extends _Rule { | |
729 // Matches zero, one or more items. | |
730 _Rule _rule; | |
731 _Rule _separator; | |
732 int _min; | |
733 | |
734 _RepeatRule(this._rule, this._separator, this._min); | |
735 | |
736 _match(state, pos) { | |
737 // First match. | |
738 var match = _rule.match(state, pos); | |
739 if (match == null) | |
740 if (_min == 0) | |
741 return [pos, []]; | |
742 else | |
743 return null; | |
744 pos = match[0]; | |
745 var result = [match[1]]; | |
746 | |
747 // Subsequent matches: | |
748 while (true) { | |
749 var newPos = pos; | |
750 if (_separator != null) { | |
751 match = _separator.match(state, pos); | |
752 if (match == null) | |
753 return [pos, result]; | |
754 newPos = match[0]; | |
755 } | |
756 match = _rule.match(state, newPos); | |
757 if (match == null) | |
758 return [pos, result]; | |
759 pos = match[0]; | |
760 result.add(match[1]); | |
761 } | |
762 } | |
763 | |
764 bool get generatesValue() => true; | |
765 | |
766 toString() => 'MANY(min:$_min, $_rule${_separator==null?'':", sep: $_separator
"})'; | |
767 } | |
768 | |
769 class _MemoRule extends _Rule { | |
770 final _Rule _rule; | |
771 | |
772 var parseInstance; | |
773 | |
774 // A map from position to result. Can this be replaced with something | |
775 // smaller? | |
776 // TODO: figure out how to discard the map and parseInstance after parsing. | |
777 Map<int,Object> map; | |
778 | |
779 _MemoRule(this._rule); | |
780 | |
781 _match(state, pos) { | |
782 // See if we are still parsing the same input. Relies on the fact that the | |
783 // input is a string and strings are immutable. | |
784 if (parseInstance !== state._text) { | |
785 map = new Map<int,Object>(); | |
786 parseInstance = state._text; | |
787 } | |
788 // TODO: does this have to check or preserve parse state (like | |
789 // inWhitespaceMode, error position info etc?) | |
790 // Stored result can be null (memoized failure). | |
791 if (map.containsKey(pos)) { | |
792 return map[pos]; | |
793 } | |
794 var match = _rule.match(state, pos); | |
795 map[pos] = match; | |
796 return match; | |
797 } | |
798 | |
799 bool get generatesValue() => _rule.generatesValue; | |
800 | |
801 toString() => 'MEMO($_rule)'; | |
802 } | |
803 | |
804 _apply(fn, List args) { | |
805 switch (args.length) { | |
806 case 0: return fn(); | |
807 case 1: return fn(args[0]); | |
808 case 2: return fn(args[0], args[1]); | |
809 case 3: return fn(args[0], args[1], args[2]); | |
810 case 4: return fn(args[0], args[1], args[2], args[3]); | |
811 case 5: return fn(args[0], args[1], args[2], args[3], args[4]); | |
812 case 6: return fn(args[0], args[1], args[2], args[3], args[4], | |
813 args[5]); | |
814 case 7: return fn(args[0], args[1], args[2], args[3], args[4], | |
815 args[5], args[6]); | |
816 case 8: return fn(args[0], args[1], args[2], args[3], args[4], | |
817 args[5], args[6], args[7]); | |
818 case 9: return fn(args[0], args[1], args[2], args[3], args[4], | |
819 args[5], args[6], args[7], args[8]); | |
820 case 10: return fn(args[0], args[1], args[2], args[3], args[4], | |
821 args[5], args[6], args[7], args[8], args[9]); | |
822 | |
823 default: | |
824 throw new Exception('Too many arguments in _apply: $args'); | |
825 } | |
826 } | |
827 | |
828 List _unspread(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z) { | |
829 List list = new List(); | |
830 add(element) { if (element != null) list.add(element); } | |
831 add(a); | |
832 add(b); | |
833 add(c); | |
834 add(d); | |
835 add(e); | |
836 add(f); | |
837 add(g); | |
838 add(h); | |
839 add(i); | |
840 add(j); | |
841 add(k); | |
842 add(l); | |
843 add(m); | |
844 add(n); | |
845 add(o); | |
846 add(p); | |
847 add(q); | |
848 add(r); | |
849 add(s); | |
850 add(t); | |
851 add(u); | |
852 add(v); | |
853 add(w); | |
854 add(x); | |
855 add(y); | |
856 add(z); | |
857 return list; | |
858 } | |
OLD | NEW |