Skip to content

Instantly share code, notes, and snippets.

@fge
Created May 2, 2014 22:30
Show Gist options
  • Select an option

  • Save fge/11487780 to your computer and use it in GitHub Desktop.

Select an option

Save fge/11487780 to your computer and use it in GitHub Desktop.
package org.parboiled.matchers.join;
import org.parboiled.MatcherContext;
import org.parboiled.Rule;
import org.parboiled.errors.GrammarException;
/*
* A matcher which must match exactly n times. Note that n is >= 2 (otherwise
* the builder would have returned an empty matcher or the joined rule)
*/
public final class ExactMatchesJoinMatcher
extends JoinMatcher
{
private final int nrMatches;
public ExactMatchesJoinMatcher(final Rule joined, final Rule joining,
final int nrMatches)
{
super(joined, joining);
this.nrMatches = nrMatches;
}
/**
* Tries a match on the given MatcherContext.
*
* @param context the MatcherContext
* @return true if the match was successful
*/
@Override
public <V> boolean match(final MatcherContext<V> context)
{
int matches = 0;
if (!joined.getSubContext(context).runMatcher())
return false;
matches++;
/*
* TODO: fix that...
*
* Unfortunately, we have to do a preliminary run here on joining.
* Fortunately, as a virtue of the constructor (JoinMatcherBuilder), we
* know that nrMatches is 2 or more, so we have at least one more cycle
* to run.
*
* We have to "waste" that first cycle each time so as to detect whether
* the joining rule matches empty :/
*
* This is the same story with ZeroOrMoreMatcher and OneOrMoreMatcher;
* unfortunately, due to ProxyMatcher, this cannot be done before this
* point. It can all be solved if we use a builder system instead!
*/
final int before = context.getCurrentIndex();
if (!joining.getSubContext(context).runMatcher())
return false; // Ohwell, no match...
if (before == context.getCurrentIndex())
throw new GrammarException("joining rule (%s) of a JoinMatcher" +
" cannot match an empty character sequence!", joining);
/*
* As said above, we have two or more, so if joined does not match here,
* this is an unconditional failure.
*/
if (!joined.getSubContext(context).runMatcher())
return false;
matches++;
/*
* OK, at this point we have at least two matches. But we are bound by
* the number of matches we need to achieve, so continue the "joining,
* joined" sequence until either 1. we reach the required number of
* matches or 2. we cannot reach that number.
*/
while (matches < nrMatches) {
if (!joining.getSubContext(context).runMatcher())
break;
if (!joined.getSubContext(context).runMatcher())
break;
matches++;
}
if (matches != nrMatches)
return false;
context.createNode();
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment