001/* 002 * Copyright (C) 2009-2011 Mathias Doenitz 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.parboiled.parser; 018 019import org.parboiled.Action; 020import org.parboiled.Rule; 021import org.parboiled.annotations.Cached; 022import org.parboiled.annotations.DontExtend; 023import org.parboiled.annotations.DontLabel; 024import org.parboiled.annotations.SkipActionsInPredicates; 025import org.parboiled.annotations.SuppressNode; 026import org.parboiled.annotations.SuppressSubnodes; 027import org.parboiled.common.Utils; 028import org.parboiled.errors.GrammarException; 029import org.parboiled.matchers.ActionMatcher; 030import org.parboiled.matchers.AnyMatcher; 031import org.parboiled.matchers.AnyOfMatcher; 032import org.parboiled.matchers.CharIgnoreCaseMatcher; 033import org.parboiled.matchers.CharMatcher; 034import org.parboiled.matchers.CharRangeMatcher; 035import org.parboiled.matchers.EmptyMatcher; 036import org.parboiled.matchers.FirstOfMatcher; 037import org.parboiled.matchers.FirstOfStringsMatcher; 038import org.parboiled.matchers.NothingMatcher; 039import org.parboiled.matchers.OneOrMoreMatcher; 040import org.parboiled.matchers.OptionalMatcher; 041import org.parboiled.matchers.SequenceMatcher; 042import org.parboiled.matchers.StringMatcher; 043import org.parboiled.matchers.TestMatcher; 044import org.parboiled.matchers.TestNotMatcher; 045import org.parboiled.matchers.ZeroOrMoreMatcher; 046import org.parboiled.support.Characters; 047import org.parboiled.support.Chars; 048import org.parboiled.support.Checks; 049import org.parboiled.matchers.ActionMatcher; 050import org.parboiled.matchers.AnyMatcher; 051import org.parboiled.matchers.AnyOfMatcher; 052import org.parboiled.matchers.CharIgnoreCaseMatcher; 053import org.parboiled.matchers.CharMatcher; 054import org.parboiled.matchers.CharRangeMatcher; 055import org.parboiled.matchers.EmptyMatcher; 056import org.parboiled.matchers.FirstOfMatcher; 057import org.parboiled.matchers.FirstOfStringsMatcher; 058import org.parboiled.matchers.NothingMatcher; 059import org.parboiled.matchers.OneOrMoreMatcher; 060import org.parboiled.matchers.OptionalMatcher; 061import org.parboiled.matchers.SequenceMatcher; 062import org.parboiled.matchers.StringMatcher; 063import org.parboiled.matchers.TestMatcher; 064import org.parboiled.matchers.TestNotMatcher; 065import org.parboiled.matchers.ZeroOrMoreMatcher; 066import org.parboiled.support.Chars; 067import org.parboiled.support.Checks; 068 069import java.util.Arrays; 070 071import static org.parboiled.common.Preconditions.checkArgNotNull; 072import static org.parboiled.common.Preconditions.checkArgument; 073 074/** 075 * Base class of all parboiled parsers. Defines the basic rule creation methods. 076 * 077 * @param <V> the type of the parser values 078 */ 079@SuppressWarnings( {"UnusedDeclaration"}) 080public abstract class BaseParser<V> extends BaseActions<V> { 081 082 /** 083 * Matches the {@link Chars#EOI} (end of input) character. 084 */ 085 public static final Rule EOI = new CharMatcher(Chars.EOI); 086 087 /** 088 * Matches the special {@link Chars#INDENT} character produces by the org.parboiled.buffers.IndentDedentInputBuffer 089 */ 090 public static final Rule INDENT = new CharMatcher(Chars.INDENT); 091 092 /** 093 * Matches the special {@link Chars#DEDENT} character produces by the org.parboiled.buffers.IndentDedentInputBuffer 094 */ 095 public static final Rule DEDENT = new CharMatcher(Chars.DEDENT); 096 097 /** 098 * Matches any character except {@link Chars#EOI}. 099 */ 100 public static final Rule ANY = new AnyMatcher(); 101 102 /** 103 * Matches nothing and always succeeds. 104 */ 105 public static final Rule EMPTY = new EmptyMatcher(); 106 107 /** 108 * Matches nothing and always fails. 109 */ 110 public static final Rule NOTHING = new NothingMatcher(); 111 112 /** 113 * Creates a new instance of this parsers class using the no-arg constructor. If no no-arg constructor 114 * exists this method will fail with a java.lang.NoSuchMethodError. 115 * Using this method is faster than using {@link Parboiled#createParser(Class, Object...)} for creating 116 * new parser instances since this method does not use reflection. 117 * 118 * @param <P> the parser class 119 * @return a new parser instance 120 */ 121 public <P extends BaseParser<V>> P newInstance() { 122 throw new UnsupportedOperationException( 123 "Illegal parser instance, you have to use Parboiled.createParser(...) to create your parser instance!"); 124 } 125 126 /** 127 * Explicitly creates a rule matching the given character. Normally you can just specify the character literal 128 * directly in you rule description. However, if you don't want to go through {@link #fromCharLiteral(char)}, 129 * e.g. because you redefined it, you can also use this wrapper. 130 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 131 * argument will yield the same rule instance.</p> 132 * 133 * @param c the char to match 134 * @return a new rule 135 */ 136 @Cached 137 @DontLabel 138 public Rule Ch(char c) { 139 return new CharMatcher(c); 140 } 141 142 /** 143 * Explicitly creates a rule matching the given character case-independently. 144 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 145 * argument will yield the same rule instance.</p> 146 * 147 * @param c the char to match independently of its case 148 * @return a new rule 149 */ 150 @Cached 151 @DontLabel 152 public Rule IgnoreCase(char c) { 153 if (Character.isLowerCase(c) == Character.isUpperCase(c)) { 154 return Ch(c); 155 } 156 return new CharIgnoreCaseMatcher(c); 157 } 158 159 /** 160 * Creates a rule matching a range of characters from cLow to cHigh (both inclusively). 161 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 162 * arguments will yield the same rule instance.</p> 163 * 164 * @param cLow the start char of the range (inclusively) 165 * @param cHigh the end char of the range (inclusively) 166 * @return a new rule 167 */ 168 @Cached 169 @DontLabel 170 public Rule CharRange(char cLow, char cHigh) { 171 return cLow == cHigh ? Ch(cLow) : new CharRangeMatcher(cLow, cHigh); 172 } 173 174 /** 175 * Creates a new rule that matches any of the characters in the given string. 176 * <p>Note: This methods provides caching, which means that multiple invocations with the same 177 * argument will yield the same rule instance.</p> 178 * 179 * @param characters the characters 180 * @return a new rule 181 */ 182 @DontLabel 183 public Rule AnyOf(String characters) { 184 checkArgNotNull(characters, "characters"); 185 return AnyOf(characters.toCharArray()); 186 } 187 188 /** 189 * Creates a new rule that matches any of the characters in the given char array. 190 * <p>Note: This methods provides caching, which means that multiple invocations with the same 191 * argument will yield the same rule instance.</p> 192 * 193 * @param characters the characters 194 * @return a new rule 195 */ 196 @DontLabel 197 public Rule AnyOf(char[] characters) { 198 checkArgNotNull(characters, "characters"); 199 checkArgument(characters.length > 0); 200 return characters.length == 1 ? Ch(characters[0]) : AnyOf(Characters.of(characters)); 201 } 202 203 /** 204 * Creates a new rule that matches any of the given characters. 205 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 206 * argument will yield the same rule instance.</p> 207 * 208 * @param characters the characters 209 * @return a new rule 210 */ 211 @Cached 212 @DontLabel 213 public Rule AnyOf(Characters characters) { 214 checkArgNotNull(characters, "characters"); 215 if (!characters.isSubtractive() && characters.getChars().length == 1) { 216 return Ch(characters.getChars()[0]); 217 } 218 if (characters.equals(Characters.NONE)) return NOTHING; 219 return new AnyOfMatcher(characters); 220 } 221 222 /** 223 * Creates a new rule that matches all characters except the ones in the given string and EOI. 224 * <p>Note: This methods provides caching, which means that multiple invocations with the same 225 * argument will yield the same rule instance.</p> 226 * 227 * @param characters the characters 228 * @return a new rule 229 */ 230 @DontLabel 231 public Rule NoneOf(String characters) { 232 checkArgNotNull(characters, "characters"); 233 return NoneOf(characters.toCharArray()); 234 } 235 236 /** 237 * Creates a new rule that matches all characters except the ones in the given char array and EOI. 238 * <p>Note: This methods provides caching, which means that multiple invocations with the same 239 * argument will yield the same rule instance.</p> 240 * 241 * @param characters the characters 242 * @return a new rule 243 */ 244 @DontLabel 245 public Rule NoneOf(char[] characters) { 246 checkArgNotNull(characters, "characters"); 247 checkArgument(characters.length > 0); 248 249 // make sure to always exclude EOI as well 250 boolean containsEOI = false; 251 for (char c : characters) if (c == Chars.EOI) { containsEOI = true; break; } 252 if (!containsEOI) { 253 char[] withEOI = new char[characters.length + 1]; 254 System.arraycopy(characters, 0, withEOI, 0, characters.length); 255 withEOI[characters.length] = Chars.EOI; 256 characters = withEOI; 257 } 258 259 return AnyOf(Characters.allBut(characters)); 260 } 261 262 /** 263 * Explicitly creates a rule matching the given string. Normally you can just specify the string literal 264 * directly in you rule description. However, if you want to not go through {@link #fromStringLiteral(String)}, 265 * e.g. because you redefined it, you can also use this wrapper. 266 * <p>Note: This methods provides caching, which means that multiple invocations with the same 267 * argument will yield the same rule instance.</p> 268 * 269 * @param string the String to match 270 * @return a new rule 271 */ 272 @DontLabel 273 public Rule String(String string) { 274 checkArgNotNull(string, "string"); 275 return String(string.toCharArray()); 276 } 277 278 /** 279 * Explicitly creates a rule matching the given string. Normally you can just specify the string literal 280 * directly in you rule description. However, if you want to not go through {@link #fromStringLiteral(String)}, 281 * e.g. because you redefined it, you can also use this wrapper. 282 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 283 * argument will yield the same rule instance.</p> 284 * 285 * @param characters the characters of the string to match 286 * @return a new rule 287 */ 288 @Cached 289 @SuppressSubnodes 290 @DontLabel 291 public Rule String(char... characters) { 292 if (characters.length == 1) return Ch(characters[0]); // optimize one-char strings 293 Rule[] matchers = new Rule[characters.length]; 294 for (int i = 0; i < characters.length; i++) { 295 matchers[i] = Ch(characters[i]); 296 } 297 return new StringMatcher(matchers, characters); 298 } 299 300 /** 301 * Explicitly creates a rule matching the given string in a case-independent fashion. 302 * <p>Note: This methods provides caching, which means that multiple invocations with the same 303 * argument will yield the same rule instance.</p> 304 * 305 * @param string the string to match 306 * @return a new rule 307 */ 308 @DontLabel 309 public Rule IgnoreCase(String string) { 310 checkArgNotNull(string, "string"); 311 return IgnoreCase(string.toCharArray()); 312 } 313 314 /** 315 * Explicitly creates a rule matching the given string in a case-independent fashion. 316 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 317 * argument will yield the same rule instance.</p> 318 * 319 * @param characters the characters of the string to match 320 * @return a new rule 321 */ 322 @Cached 323 @SuppressSubnodes 324 @DontLabel 325 public Rule IgnoreCase(char... characters) { 326 if (characters.length == 1) return IgnoreCase(characters[0]); // optimize one-char strings 327 Rule[] matchers = new Rule[characters.length]; 328 for (int i = 0; i < characters.length; i++) { 329 matchers[i] = IgnoreCase(characters[i]); 330 } 331 return ((SequenceMatcher) Sequence(matchers)).label('"' + String.valueOf(characters) + '"'); 332 } 333 334 /** 335 * Creates a new rule that successively tries all of the given subrules and succeeds when the first one of 336 * its subrules matches. If all subrules fail this rule fails as well. 337 * <p>Note: This methods provides caching, which means that multiple invocations with the same 338 * arguments will yield the same rule instance.</p> 339 * 340 * @param rule the first subrule 341 * @param rule2 the second subrule 342 * @param moreRules the other subrules 343 * @return a new rule 344 */ 345 @DontLabel 346 public Rule FirstOf(Object rule, Object rule2, Object... moreRules) { 347 checkArgNotNull(moreRules, "moreRules"); 348 return FirstOf(Utils.arrayOf(rule, rule2, moreRules)); 349 } 350 351 /** 352 * Creates a new rule that successively tries all of the given subrules and succeeds when the first one of 353 * its subrules matches. If all subrules fail this rule fails as well. 354 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 355 * argument will yield the same rule instance.</p> 356 * 357 * @param rules the subrules 358 * @return a new rule 359 */ 360 @Cached 361 @DontLabel 362 public Rule FirstOf(Object[] rules) { 363 checkArgNotNull(rules, "rules"); 364 if (rules.length == 1) { 365 return toRule(rules[0]); 366 } 367 Rule[] convertedRules = toRules(rules); 368 char[][] chars = new char[rules.length][]; 369 for (int i = 0, convertedRulesLength = convertedRules.length; i < convertedRulesLength; i++) { 370 Object rule = convertedRules[i]; 371 if (rule instanceof StringMatcher) { 372 chars[i] = ((StringMatcher) rule).characters; 373 } else { 374 return new FirstOfMatcher(convertedRules); 375 } 376 } 377 return new FirstOfStringsMatcher(convertedRules, chars); 378 } 379 380 /** 381 * Creates a new rule that tries repeated matches of its subrule and succeeds if the subrule matches at least once. 382 * If the subrule does not match at least once this rule fails. 383 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 384 * argument will yield the same rule instance.</p> 385 * 386 * @param rule the subrule 387 * @return a new rule 388 */ 389 @Cached 390 @DontLabel 391 public Rule OneOrMore(Object rule) { 392 return new OneOrMoreMatcher(toRule(rule)); 393 } 394 395 /** 396 * Creates a new rule that tries repeated matches of a sequence of the given subrules and succeeds if the sequence 397 * matches at least once. If the sequence does not match at least once this rule fails. 398 * <p>Note: This methods provides caching, which means that multiple invocations with the same 399 * arguments will yield the same rule instance.</p> 400 * 401 * @param rule the first subrule 402 * @param rule2 the second subrule 403 * @param moreRules the other subrules 404 * @return a new rule 405 */ 406 @DontLabel 407 public Rule OneOrMore(Object rule, Object rule2, Object... moreRules) { 408 checkArgNotNull(moreRules, "moreRules"); 409 return OneOrMore(Sequence(rule, rule2, moreRules)); 410 } 411 412 /** 413 * Creates a new rule that tries a match on its subrule and always succeeds, independently of the matching 414 * success of its sub rule. 415 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 416 * argument will yield the same rule instance.</p> 417 * 418 * @param rule the subrule 419 * @return a new rule 420 */ 421 @Cached 422 @DontLabel 423 public Rule Optional(Object rule) { 424 return new OptionalMatcher(toRule(rule)); 425 } 426 427 /** 428 * Creates a new rule that tries a match on the sequence of the given subrules and always succeeds, independently 429 * of the matching success of its sub sequence. 430 * <p>Note: This methods provides caching, which means that multiple invocations with the same 431 * arguments will yield the same rule instance.</p> 432 * 433 * @param rule the first subrule 434 * @param rule2 the second subrule 435 * @param moreRules the other subrules 436 * @return a new rule 437 */ 438 @DontLabel 439 public Rule Optional(Object rule, Object rule2, Object... moreRules) { 440 checkArgNotNull(moreRules, "moreRules"); 441 return Optional(Sequence(rule, rule2, moreRules)); 442 } 443 444 /** 445 * Creates a new rule that only succeeds if all of its subrule succeed, one after the other. 446 * <p>Note: This methods provides caching, which means that multiple invocations with the same 447 * arguments will yield the same rule instance.</p> 448 * 449 * @param rule the first subrule 450 * @param rule2 the second subrule 451 * @param moreRules the other subrules 452 * @return a new rule 453 */ 454 @DontLabel 455 public Rule Sequence(Object rule, Object rule2, Object... moreRules) { 456 checkArgNotNull(moreRules, "moreRules"); 457 return Sequence(Utils.arrayOf(rule, rule2, moreRules)); 458 } 459 460 /** 461 * Creates a new rule that only succeeds if all of its subrule succeed, one after the other. 462 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 463 * arguments will yield the same rule instance.</p> 464 * 465 * @param rules the sub rules 466 * @return a new rule 467 */ 468 @Cached 469 @DontLabel 470 public Rule Sequence(Object[] rules) { 471 checkArgNotNull(rules, "rules"); 472 return rules.length == 1 ? toRule(rules[0]) : new SequenceMatcher(toRules(rules)); 473 } 474 475 /** 476 * <p>Creates a new rule that acts as a syntactic predicate, i.e. tests the given sub rule against the current 477 * input position without actually matching any characters. Succeeds if the sub rule succeeds and fails if the 478 * sub rule rails. Since this rule does not actually consume any input it will never create a parse tree node.</p> 479 * <p>Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse 480 * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the 481 * presence of certain parse tree structures in their context. 482 * Also see {@link SkipActionsInPredicates}</p> 483 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 484 * argument will yield the same rule instance.</p> 485 * 486 * @param rule the subrule 487 * @return a new rule 488 */ 489 @Cached 490 @SuppressNode 491 @DontLabel 492 public Rule Test(Object rule) { 493 Rule subMatcher = toRule(rule); 494 return new TestMatcher(subMatcher); 495 } 496 497 /** 498 * <p>Creates a new rule that acts as a syntactic predicate, i.e. tests the sequence of the given sub rule against 499 * the current input position without actually matching any characters. Succeeds if the sub sequence succeeds and 500 * fails if the sub sequence rails. Since this rule does not actually consume any input it will never create a 501 * parse tree node.</p> 502 * <p>Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse 503 * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the 504 * presence of certain parse tree structures in their context. 505 * Also see {@link SkipActionsInPredicates}</p> 506 * <p>Note: This methods provides caching, which means that multiple invocations with the same 507 * arguments will yield the same rule instance.</p> 508 * 509 * @param rule the first subrule 510 * @param rule2 the second subrule 511 * @param moreRules the other subrules 512 * @return a new rule 513 */ 514 @DontLabel 515 public Rule Test(Object rule, Object rule2, Object... moreRules) { 516 checkArgNotNull(moreRules, "moreRules"); 517 return Test(Sequence(rule, rule2, moreRules)); 518 } 519 520 /** 521 * <p>Creates a new rule that acts as an inverse syntactic predicate, i.e. tests the given sub rule against the 522 * current input position without actually matching any characters. Succeeds if the sub rule fails and fails if the 523 * sub rule succeeds. Since this rule does not actually consume any input it will never create a parse tree node.</p> 524 * <p>Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse 525 * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the 526 * presence of certain parse tree structures in their context. 527 * Also see {@link SkipActionsInPredicates}</p> 528 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 529 * argument will yield the same rule instance.</p> 530 * 531 * @param rule the subrule 532 * @return a new rule 533 */ 534 @Cached 535 @SuppressNode 536 @DontLabel 537 public Rule TestNot(Object rule) { 538 Rule subMatcher = toRule(rule); 539 return new TestNotMatcher(subMatcher); 540 } 541 542 /** 543 * <p>Creates a new rule that acts as an inverse syntactic predicate, i.e. tests the sequence of the given sub rules 544 * against the current input position without actually matching any characters. Succeeds if the sub sequence fails 545 * and fails if the sub sequence succeeds. Since this rule does not actually consume any input it will never create 546 * a parse tree node.</p> 547 * <p>Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse 548 * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the 549 * presence of certain parse tree structures in their context. 550 * Also see {@link SkipActionsInPredicates}</p> 551 * <p>Note: This methods provides caching, which means that multiple invocations with the same 552 * arguments will yield the same rule instance.</p> 553 * 554 * @param rule the first subrule 555 * @param rule2 the second subrule 556 * @param moreRules the other subrules 557 * @return a new rule 558 */ 559 @DontLabel 560 public Rule TestNot(Object rule, Object rule2, Object... moreRules) { 561 checkArgNotNull(moreRules, "moreRules"); 562 return TestNot(Sequence(rule, rule2, moreRules)); 563 } 564 565 /** 566 * Creates a new rule that tries repeated matches of its subrule. 567 * Succeeds always, even if the subrule doesn't match even once. 568 * <p>Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same 569 * argument will yield the same rule instance.</p> 570 * 571 * @param rule the subrule 572 * @return a new rule 573 */ 574 @Cached 575 @DontLabel 576 public Rule ZeroOrMore(Object rule) { 577 return new ZeroOrMoreMatcher(toRule(rule)); 578 } 579 580 /** 581 * Creates a new rule that tries repeated matches of the sequence of the given sub rules. 582 * Succeeds always, even if the sub sequence doesn't match even once. 583 * <p>Note: This methods provides caching, which means that multiple invocations with the same 584 * arguments will yield the same rule instance.</p> 585 * 586 * @param rule the first subrule 587 * @param rule2 the second subrule 588 * @param moreRules the other subrules 589 * @return a new rule 590 */ 591 @DontLabel 592 public Rule ZeroOrMore(Object rule, Object rule2, Object... moreRules) { 593 checkArgNotNull(moreRules, "moreRules"); 594 return ZeroOrMore(Sequence(rule, rule2, moreRules)); 595 } 596 597 /** 598 * Creates a new rule that repeatedly matches a given sub rule a certain fixed number of times. 599 * <p>Note: This methods provides caching, which means that multiple invocations with the same 600 * arguments will yield the same rule instance.</p> 601 * 602 * @param repetitions The number of repetitions to match. Must be >= 0. 603 * @param rule the sub rule to match repeatedly. 604 * @return a new rule 605 */ 606 @Cached 607 @DontLabel 608 public Rule NTimes(int repetitions, Object rule) { 609 return NTimes(repetitions, rule, null); 610 } 611 612 /** 613 * Creates a new rule that repeatedly matches a given sub rule a certain fixed number of times, optionally 614 * separated by a given separator rule. 615 * <p>Note: This methods provides caching, which means that multiple invocations with the same 616 * arguments will yield the same rule instance.</p> 617 * 618 * @param repetitions The number of repetitions to match. Must be >= 0. 619 * @param rule the sub rule to match repeatedly. 620 * @param separator the separator to match, if null the individual sub rules will be matched without separator. 621 * @return a new rule 622 */ 623 @Cached 624 @DontLabel 625 public Rule NTimes(int repetitions, Object rule, Object separator) { 626 checkArgNotNull(rule, "rule"); 627 checkArgument(repetitions >= 0, "repetitions must be non-negative"); 628 switch (repetitions) { 629 case 0: return EMPTY; 630 case 1: return toRule(rule); 631 default: 632 Object[] rules = new Object[separator == null ? repetitions : repetitions * 2 - 1]; 633 if (separator != null) { 634 for (int i = 0; i < rules.length; i++) 635 rules[i] = i % 2 == 0 ? rule : separator; 636 } else Arrays.fill(rules, rule); 637 return Sequence(rules); 638 } 639 } 640 641 ///************************* "MAGIC" METHODS ***************************/// 642 643 /** 644 * Explicitly marks the wrapped expression as an action expression. 645 * parboiled transforms the wrapped expression into an {@link Action} instance during parser construction. 646 * 647 * @param expression the expression to turn into an Action 648 * @return the Action wrapping the given expression 649 */ 650 public static Action ACTION(boolean expression) { 651 throw new UnsupportedOperationException("ACTION(...) calls can only be used in Rule creating parser methods"); 652 } 653 654 ///************************* HELPER METHODS ***************************/// 655 656 /** 657 * Used internally to convert the given character literal to a parser rule. 658 * You can override this method, e.g. for specifying a Sequence that automatically matches all trailing 659 * whitespace after the character. 660 * 661 * @param c the character 662 * @return the rule 663 */ 664 @DontExtend 665 protected Rule fromCharLiteral(char c) { 666 return Ch(c); 667 } 668 669 /** 670 * Used internally to convert the given string literal to a parser rule. 671 * You can override this method, e.g. for specifying a Sequence that automatically matches all trailing 672 * whitespace after the string. 673 * 674 * @param string the string 675 * @return the rule 676 */ 677 @DontExtend 678 protected Rule fromStringLiteral(String string) { 679 checkArgNotNull(string, "string"); 680 return fromCharArray(string.toCharArray()); 681 } 682 683 /** 684 * Used internally to convert the given char array to a parser rule. 685 * You can override this method, e.g. for specifying a Sequence that automatically matches all trailing 686 * whitespace after the characters. 687 * 688 * @param array the char array 689 * @return the rule 690 */ 691 @DontExtend 692 protected Rule fromCharArray(char[] array) { 693 checkArgNotNull(array, "array"); 694 return String(array); 695 } 696 697 /** 698 * Converts the given object array to an array of rules. 699 * 700 * @param objects the objects to convert 701 * @return the rules corresponding to the given objects 702 */ 703 @DontExtend 704 public Rule[] toRules(Object... objects) { 705 checkArgNotNull(objects, "objects"); 706 Rule[] rules = new Rule[objects.length]; 707 for (int i = 0; i < objects.length; i++) { 708 rules[i] = toRule(objects[i]); 709 } 710 return rules; 711 } 712 713 /** 714 * Converts the given object to a rule. 715 * This method can be overriden to enable the use of custom objects directly in rule specifications. 716 * 717 * @param obj the object to convert 718 * @return the rule corresponding to the given object 719 */ 720 @DontExtend 721 public Rule toRule(Object obj) { 722 if (obj instanceof Rule) return (Rule) obj; 723 if (obj instanceof Character) return fromCharLiteral((Character) obj); 724 if (obj instanceof String) return fromStringLiteral((String) obj); 725 if (obj instanceof char[]) return fromCharArray((char[]) obj); 726 if (obj instanceof Action) { 727 Action action = (Action) obj; 728 return new ActionMatcher(action); 729 } 730 Checks.ensure(!(obj instanceof Boolean), "Rule specification contains an unwrapped Boolean value, " + 731 "if you were trying to specify a parser action wrap the expression with ACTION(...)"); 732 733 throw new GrammarException("'" + obj + "' cannot be automatically converted to a parser Rule"); 734 } 735 736}