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}