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

Side by Side Diff: utils/peg/pegparser.dart

Issue 8399029: A PEG parser in Dart (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add copyright Created 9 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | utils/peg/pegparser_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 new _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 List<bool> 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), _compile_optional(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), _compile_optional(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 = OR([' ', '\t', '\r', '\n']);
232 this.whitespace = CHAR(' \t\r\n');
233 }
234
235 /**
236 * operator [] is used to find or create symbols. Symbols may appear in rules
237 * to define recursive rules.
238 */
239 Symbol operator [](String name) {
240 if (_symbols.containsKey(name))
241 return _symbols[name];
242 Symbol s = new Symbol(name, this);
243 _symbols[name] = s;
244 return s;
245 }
246
247 /**
248 * Parses the input string and returns the parsed AST, or throws an exception
249 * if the input can't be parsed.
250 */
251 parse(root, String text) {
252 for (var symbol in _symbols.getValues())
253 if (symbol._rule == null)
254 print('${symbol.name} is undefined');
255
256 var state = new _ParserState(text, whitespace: whitespace);
257 var match = _compile(root).match(state, 0);
258 if (match == null)
259 return diagnose(state);
260 var pos = match[0];
261 pos = _skip_whitespace(state, pos);
262 if (pos == state._end)
263 return match[1];
264 return diagnose(state);
265 }
266
267 diagnose(state) {
268 // TODO: locate error and describe.
269 print('Parse failed at position ${state.max_pos}');
270 var message = 'unexpected error';
271 if (state.max_rule.isEmpty()) {
272 var s = new Set();
273 for (var rule in state.max_rule)
274 s.add(rule.description());
275 var tokens = new List<String>.from(s);
276 tokens.sort((a, b) => a.startsWith("'")==b.startsWith("'") ? 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 print(message);
293 print(line);
294 print(indicator);
295 return null;
296 }
297 }
298
299 class Symbol {
300 final String name;
301 final Grammar grammar;
302 _Rule _rule;
303
304 Symbol(this.name, this.grammar);
305
306 void set def(rule) {
307 assert(_rule == null); // Assign once.
308 _rule = _compile(rule);
309 }
310
311 toString() => _rule == null ? '<$name>' : '<$name = $_rule>';
312 }
313
314
315 class _ParserState {
316 _ParserState(this._text, [_Rule whitespace = null]) {
317 _end = this._text.length;
318 whitespace_rule = whitespace;
319 max_rule = [];
320 }
321
322 String _text;
323 int _end;
324
325 //
326 bool in_whitespace_mode = false;
327 _Rule whitespace_rule = null;
328
329 // Used for constructing an error message.
330 int inhibitExpectedTrackingDepth = 0;
331 int max_pos = 0;
332 var max_rule;
333 }
334
335 /**
336 * An interface tag for rules. If this tag is on a rule, then the description()
337 * of the rule is something sensible to put in a message.
338 */
339 interface _Expectable {
340 String description();
341 }
342
343 class _Rule {
344 // Returns null for a match failure or [pos, ast] for success.
345 match(_ParserState state, int pos) {
346 if (! state.in_whitespace_mode) {
347 pos = _skip_whitespace(state, pos);
348 }
349 return matchAfterWS(state, pos);
350 }
351
352 // Faster entry point for matching a sub-rule that is matched to the start
353 // position of the super-rule. Whitespace has already been skipped so no need
354 // to try to skip it again.
355 matchAfterWS(_ParserState state, int pos) {
356 if (state.inhibitExpectedTrackingDepth == 0) {
357 // Track position for possible error messaging
358 if (pos > state.max_pos) {
359 // Store position and the rule.
360 state.max_pos = pos;
361 if (this is _Expectable) {
362 state.max_rule = [this];
363 } else {
364 state.max_rule = [];
365 }
366 } else if (pos == state.max_pos) {
367 if (this is _Expectable) {
368 state.max_rule.add(this);
369 }
370 }
371 }
372 // Delegate the matching logic to the the specialized function.
373 return _match(state, pos);
374 }
375
376 // Overridden in subclasses to match the rule.
377 _match(_ParserState state, int pos) => null;
378
379 // Does the rule generate a value (AST) with the match?
380 bool get generatesValue() => false;
381
382 get defaultValue() => null;
383 }
384
385 int _skip_whitespace(state, pos) {
386 // Returns the next non-whitespace position.
387 // This is done by matching the optional whitespace_rule with the current text .
388 if (state.whitespace_rule == null)
389 return pos;
390 state.in_whitespace_mode = true;
391 state.inhibitExpectedTrackingDepth++;
392 while (true) {
393 var match = state.whitespace_rule.match(state, pos);
394 if (match == null)
395 break;
396 pos = match[0];
397 }
398 state.in_whitespace_mode = false;
399 state.inhibitExpectedTrackingDepth--;
400 return pos;
401 }
402
403
404 _Rule _compile_optional(rule) {
405 return rule == null ? null : _compile(rule);
406 }
407
408 _Rule _compile(rule) {
409 if (rule is _Rule)
410 return rule;
411 if (rule is String)
412 return new _StringRule(rule);
413 if (rule is Symbol)
414 return new _SymbolRule(rule);
415 if (rule is RegExp)
416 return new _RegExpRule(rule);
417 if (rule is List) {
418 return _compileMultiRule(
419 rule, true,
420 (compiledRules, valueCount, reducer) =>
421 new _SequenceRule(compiledRules, valueCount, reducer));
422 }
423 throw new Exception('Cannot compile rule: $rule');
424 }
425
426 class _EndOfInputRule extends _Rule {
427 _match(_ParserState state, int pos) {
428 if (pos == state._end)
429 return [pos, null];
430 return null;
431 }
432
433 toString() => 'END';
434 }
435
436 class _ErrorRule extends _Rule {
437 String message;
438 _ErrorRule(String this.message);
439 _match(_ParserState state, int pos) {
440 throw new ParseError(message);
441 }
442
443 toString() => 'ERROR($message)';
444 }
445
446 class _CharCodeRule extends _Rule {
447 Function _predicate;
448 var _name;
449 _CharCodeRule(Function this._predicate, this._name);
450 _match(_ParserState state, int pos) {
451 if (pos == state._end)
452 return null;
453 int code = state._text.charCodeAt(pos);
454 if (_predicate(code))
455 return [pos + 1, null];
456 return null;
457 }
458
459 toString() => _name == null ? 'CHARCODE($_predicate)' : 'CHARCODE($_name)';
460 }
461
462 class _AnyCharRule extends _Rule {
463 _match(_ParserState state, int pos) {
464 if (pos == state._end)
465 return null;
466 return [pos + 1, null];
467 }
468
469 toString() => 'CHAR()';
470 }
471
472 class _SymbolRule extends _Rule {
473 final Symbol _symbol;
474 _SymbolRule(Symbol this._symbol);
475 _match(_ParserState state, int pos) {
476 if (_symbol._rule == null)
477 throw new Exception("Symbol '${_symbol.name}' is undefined");
478 return _symbol._rule.match(state, pos);
479 }
480
481 bool get generatesValue() => true;
482
483 toString() => '<${_symbol.name}>';
484 }
485
486 class _SkipRule extends _Rule {
487 // A rule that has no value.
488 _Rule _rule;
489 _SkipRule(_Rule this._rule);
490 _match(_ParserState state, int pos) {
491 var match = _rule.matchAfterWS(state, pos);
492 if (match == null)
493 return null;
494 return [match[0], null];
495 }
496
497 toString() => 'TOKEN($_rule)';
498 }
499
500 class _StringRule extends _Rule implements _Expectable {
501 final String _string;
502 int _len;
503 _StringRule(this._string) {
504 _len = _string.length;
505 }
506
507 _match(_ParserState state, int pos) {
508 if (pos + _len > state._end)
509 return null;
510 for (int i = 0; i < _len; i++) {
511 if (state._text.charCodeAt(pos + i) != _string.charCodeAt(i))
512 return null;
513 }
514 return [pos + _len, null];
515 }
516
517 //get defaultValue() => _string;
518
519 toString() => '"$_string"';
520
521 description() => "'$_string'";
522 }
523
524 class _RegExpRule extends _Rule {
525 RegExp _re;
526 _RegExpRule(this._re) {
527 // There is no convenient way to match an anchored substring.
528 throw new Exception('RegExp matching not supported');
529 }
530
531 toString() => '"$_re"';
532 }
533
534 class _LexicalRule extends _Rule implements _Expectable {
535 final String _name;
536 final _Rule _rule;
537
538 _LexicalRule(String this._name, _Rule this._rule);
539
540 _match(_ParserState state, int pos) {
541 state.in_whitespace_mode = true;
542 state.inhibitExpectedTrackingDepth++;
543 var match = _rule.matchAfterWS(state, pos);
544 state.inhibitExpectedTrackingDepth--;
545 state.in_whitespace_mode = false;
546 return match;
547 }
548
549 toString() => _name;
550
551 description() => _name == null ? '?' : _name;
552 }
553
554 class _TextValueRule extends _Rule {
555 final _Rule _rule;
556 final _extract; // Function
557
558 _TextValueRule(_Rule this._rule, Function this._extract);
559
560 _match(_ParserState state, int pos) {
561 var match = _rule.matchAfterWS(state, pos);
562 if (match == null) {
563 return null;
564 }
565 var endPos = match[0];
566 return [endPos, _extract(state._text, pos, endPos)];
567 }
568
569 bool get generatesValue() => true;
570
571 toString() => 'TEXT($_rule)';
572 }
573
574 _Rule _compileMultiRule(List rules,
575 bool allowReducer,
576 finish(compiledRules, valueCount, reducer)) {
577 int valueCount = 0;
578 List compiledRules = new List<_Rule>();
579 Function reducer;
580 for (var rule in rules) {
581 if (reducer != null)
582 throw new Exception('Reducer must be last in sequence: $rule');
583 if (rule is Function) {
584 if (allowReducer)
585 reducer = rule;
586 else
587 throw new Exception('Bad rule: "$rule"');
588 } else {
589 _Rule compiledRule = _compile(rule);
590 if (compiledRule.generatesValue)
591 ++valueCount;
592 compiledRules.add(compiledRule);
593 }
594 }
595 return finish(compiledRules, valueCount, reducer);
596 }
597
598 String _formatMultiRule(String functor, List rules) {
599 var sb = new StringBuffer(functor);
600 sb.add('(');
601 var separator = '';
602 for (var rule in rules) {
603 sb.add(separator);
604 sb.add(rule);
605 separator = ',';
606 }
607 sb.add(')');
608 return sb.toString();
609 }
610
611 class _SequenceRule extends _Rule {
612 // This rule matches the component rules in order.
613 final List<_Rule> _rules;
614 final int _generatingSubRules = 0;
615 final Function _reducer;
616 bool _generatesValue;
617 _SequenceRule(List<_Rule> this._rules,
618 int this._generatingSubRules,
619 Function this._reducer) {
620 _generatesValue = _generatingSubRules > 0 || _reducer != null;
621 }
622
623 _match(state, pos) {
624 var sequence = [];
625 for (var rule in _rules) {
626 var match = rule.match(state, pos);
627 if (match == null)
628 return null;
629 if (rule.generatesValue) {
630 var ast = match[1];
631 sequence.add(ast);
632 }
633 pos = match[0];
634 }
635 if (_reducer == null) {
636 if (_generatingSubRules == 0)
637 return [pos, null];
638 if (_generatingSubRules == 1)
639 return [pos, sequence[0]];
640 return [pos, sequence];
641 } else {
642 return [pos, _apply(_reducer, sequence)];
643 }
644 }
645
646 bool get generatesValue() => _generatesValue;
647
648 toString() => _formatMultiRule('SEQ', _rules);
649 }
650
651 class _ChoiceRule extends _Rule {
652 // This rule matches the first component rule that matches.
653 List<_Rule> _rules;
654 _ChoiceRule(List<_Rule> this._rules);
655
656 _match(state, pos) {
657 for (var rule in _rules) {
658 var match = rule.match(state, pos);
659 if (match != null) {
660 /*
661 if (!rule.generatesValue) {
662 var value = rule.defaultValue;
663 if (value != null)
664 return [match[0], value];
665 }
666 */
667 return match;
668 }
669 }
670 return null;
671 }
672
673 bool get generatesValue() => true;
674
675 toString() => _formatMultiRule('OR', _rules);
676 }
677
678 class _OptionalRule extends _Rule {
679 _Rule _rule;
680 _OptionalRule(_Rule this._rule);
681 _match(_ParserState state, int pos) {
682 var match = _rule.match(state, pos);
683 if (_rule.generatesValue)
684 if (match == null)
685 return [pos, null];
686 else
687 return match;
688 if (match == null)
689 return [pos, false];
690 else
691 return [match[0], true];
692 }
693
694 bool get generatesValue() => true;
695
696 toString() => 'MAYBE($_rule)';
697 }
698
699 class _ContextRule extends _Rule {
700 _Rule _rule;
701 _ContextRule(_Rule this._rule);
702 _match(_ParserState state, int pos) {
703 // TODO: protect error state.
704 var match = _rule._match(state, pos);
705 if (match == null)
706 return null;
707 return [pos, null];
708 }
709
710 toString() => 'AT($_rule)';
711 }
712
713 class _NegativeContextRule extends _Rule {
714 _Rule _rule;
715 _NegativeContextRule(_Rule this._rule);
716 _match(_ParserState state, int pos) {
717 // TODO: protect error state.
718 var match = _rule._match(state, pos);
719 if (match == null)
720 return [pos, null];
721 return null;
722 }
723
724 toString() => 'NOT($_rule)';
725 }
726
727 class _RepeatRule extends _Rule {
728 // Matches zero, one or more items.
729 _Rule _rule;
730 _Rule _separator;
731 int _min;
732
733 _RepeatRule(this._rule, this._separator, this._min);
734
735 _match(state, pos) {
736 // First match.
737 var match = _rule.match(state, pos);
738 if (match == null)
739 if (_min == 0)
740 return [pos, []];
741 else
742 return null;
743 pos = match[0];
744 var result = [match[1]];
745
746 // Subsequent matches:
747 while (true) {
748 var newPos = pos;
749 if (_separator != null) {
750 match = _separator.match(state, pos);
751 if (match == null)
752 return [pos, result];
753 newPos = match[0];
754 }
755 match = _rule.match(state, newPos);
756 if (match == null)
757 return [pos, result];
758 pos = match[0];
759 result.add(match[1]);
760 }
761 }
762
763 bool get generatesValue() => true;
764
765 toString() => 'MANY(min:$_min, $_rule${_separator==null?'':", sep: $_separator "})';
766 }
767
768 class _MemoRule extends _Rule {
769 final _Rule _rule;
770
771 var parseInstance;
772
773 // A map from position to result. Can this be replaced with something
774 // smaller?
775 // TODO: figure out how to discard the map and parseInstance after parsing.
776 Map<int,Object> map;
777
778 _MemoRule(this._rule);
779
780 _match(state, pos) {
781 // See if we are still parsing the same input. Relies on the fact that the
782 // input is a string ans string are immutable.
783 if (parseInstance !== state._text) {
784 map = new Map<int,Object>();
785 parseInstance = state._text;
786 }
787 // TODO: does this have to check or preserve parse state (like
788 // in_whitespace_mode, error position info etc?)
789 if (map.containsKey(pos)) {
790 //print('Found $pos $_rule => ${map[pos][0]}');
791 return map[pos];
792 }
793 var match = _rule.match(state, pos);
794 map[pos] = match;
795 return match;
796 }
797
798 bool get generatesValue() => _rule.generatesValue;
799
800 toString() => 'MEMO($_rule)';
801 }
802
803 _apply(fn, List args) {
804 switch (args.length) {
805 case 0: return fn();
806 case 1: return fn(args[0]);
807 case 2: return fn(args[0], args[1]);
808 case 3: return fn(args[0], args[1], args[2]);
809 case 4: return fn(args[0], args[1], args[2], args[3]);
810 case 5: return fn(args[0], args[1], args[2], args[3], args[4]);
811 case 6: return fn(args[0], args[1], args[2], args[3], args[4],
812 args[5]);
813 case 7: return fn(args[0], args[1], args[2], args[3], args[4],
814 args[5], args[6]);
815 case 8: return fn(args[0], args[1], args[2], args[3], args[4],
816 args[5], args[6], args[7]);
817 case 9: return fn(args[0], args[1], args[2], args[3], args[4],
818 args[5], args[6], args[7], args[8]);
819 case 10: return fn(args[0], args[1], args[2], args[3], args[4],
820 args[5], args[6], args[7], args[8], args[9]);
821
822 default:
823 throw new Exception('Too many arguments in _apply: $args');
824 }
825 }
826
827 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) {
828 List list = new List();
829 add(element) { if (element != null) list.add(element); }
830 add(a);
831 add(b);
832 add(c);
833 add(d);
834 add(e);
835 add(f);
836 add(g);
837 add(h);
838 add(i);
839 add(j);
840 add(k);
841 add(l);
842 add(m);
843 add(n);
844 add(o);
845 add(p);
846 add(q);
847 add(r);
848 add(s);
849 add(t);
850 add(u);
851 add(v);
852 add(w);
853 add(x);
854 add(y);
855 add(z);
856 return list;
857 }
OLDNEW
« no previous file with comments | « no previous file | utils/peg/pegparser_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698