package com.github.fge.grappa.matchers.repeat;
import com.github.fge.grappa.exceptions.GrappaException;
import com.github.fge.grappa.matchers.MatcherType;
import com.github.fge.grappa.matchers.base.AbstractMatcher;
import com.github.fge.grappa.matchers.base.Matcher;
import com.github.fge.grappa.parsers.BaseParser;
import com.github.fge.grappa.rules.Rule;
import com.github.fge.grappa.run.context.MatcherContext;
/**
* A matcher which repeats matching a given number of times
*
* <p>This matcher (and all its subclasses) is used, among others, in {@link
* BaseParser#repeat(Object) repeat()} but also {@code zeroOrMore()} and {@code
* oneOrMore()}, since the latter two are just specialized versions of this
* matcher.</p>
*
* <p>Note that it is forbidden for the subrule as an argument to match an
* empty input. Unfortunately, due to current limitations, this can only be
* detected at runtime.</p>
*
* <p>Example:</p>
*
* <pre>
* public Rule threeTimesHello()
* {
* return repeat("hello").times(3);
* }
* </pre>
*/
public abstract class RepeatMatcher
extends AbstractMatcher
{
private final Matcher matcher;
protected RepeatMatcher(final Rule subRule)
{
super(subRule, "repeat");
matcher = getChildren().get(0);
}
@Override
public MatcherType getType()
{
return MatcherType.COMPOSITE;
}
@Override
public <V> boolean match(final MatcherContext<V> context)
{
int cycles = 0;
int beforeMatch = context.getCurrentIndex();
int afterMatch;
while (runAgain(cycles)) {
if (!context.getSubContext(matcher).runMatcher())
break;
afterMatch = context.getCurrentIndex();
if (beforeMatch == afterMatch)
throw new GrappaException("Inner rule of a RepeatMatcher"
+ " cannot match an empty character sequence");
beforeMatch = afterMatch;
cycles++;
}
return enoughCycles(cycles);
}
protected abstract boolean enoughCycles(final int cycles);
protected abstract boolean runAgain(final int cycles);
}