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 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 } |
OLD | NEW |