Created
May 2, 2014 22:30
-
-
Save fge/11487780 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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