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

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

Issue 8414040: Revert "Revert "PEG Parser: under review at http://codereview.chromium.org/8399029"" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: uniform comment 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 | « client/tests/dartc/dartc.status ('k') | 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 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 }
OLDNEW
« no previous file with comments | « client/tests/dartc/dartc.status ('k') | utils/peg/pegparser_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698