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.parserunners;
018
019import org.parboiled.Rule;
020import org.parboiled.buffers.InputBuffer;
021import org.parboiled.common.Preconditions;
022import org.parboiled.errors.InvalidInputError;
023import org.parboiled.support.ParsingResult;
024import org.parboiled.support.ParsingResult;
025
026import static org.parboiled.common.Preconditions.checkArgNotNull;
027
028/**
029 * A {@link ParseRunner} implementation that properly reports the first {@link InvalidInputError} if the input
030 * does not conform to the rule grammar.
031 * It performs exactly as the {@link BasicParseRunner} on valid input, however, on invalid input two more parsing
032 * runs are initiated: one for recording the first parse error and one for collecting the error report information.
033 */
034public class ReportingParseRunner<V> extends AbstractParseRunner<V> {
035
036    /**
037     * Create a new ReportingParseRunner instance with the given rule and input text and returns the result of
038     * its {@link #run(String)} method invocation.
039     *
040     * @param rule  the parser rule to run
041     * @param input the input text to run on
042     * @return the ParsingResult for the parsing run
043     * @deprecated  As of 0.11.0 you should use the "regular" constructor and one of the "run" methods rather than
044     * this static method. This method will be removed in one of the coming releases.
045     */
046    @Deprecated
047    public static <V> ParsingResult<V> run(Rule rule, String input) {
048        checkArgNotNull(rule, "rule");
049        checkArgNotNull(input, "input");
050        return new ReportingParseRunner<V>(rule).run(input);
051    }
052
053    /**
054     * Creates a new ReportingParseRunner instance for the given rule.
055     *
056     * @param rule the parser rule
057     */
058    public ReportingParseRunner(Rule rule) {
059        super(rule);
060    }
061
062    public ParsingResult<V> run(InputBuffer inputBuffer) {
063        checkArgNotNull(inputBuffer, "inputBuffer");
064        resetValueStack();
065
066        // first, run a basic match
067        ParsingResult<V> result = runBasicMatch(inputBuffer);
068        if (result.matched) return result; // all good
069
070        // ok, we have a parse error, so determine the error location
071        resetValueStack();
072        result = runLocatingMatch(inputBuffer);
073        Preconditions.checkState(!result.matched); // we failed before so we should really be failing again
074        Preconditions.checkState(result.parseErrors.size() >= 1); // may be more than one in case of custom ActionExceptions
075        
076        // finally perform a third, reporting run (now that we know the error location)
077        resetValueStack();
078        result = runReportingMatch(inputBuffer, result.parseErrors.get(0).getStartIndex());
079        Preconditions.checkState(!result.matched); // we failed before so we should really be failing again
080        return result;
081    }
082
083    protected ParsingResult<V> runBasicMatch(InputBuffer inputBuffer) {
084        ParseRunner<V> basicRunner = new BasicParseRunner<V>(getRootMatcher())
085            .withParseErrors(getParseErrors())
086            .withValueStack(getValueStack());
087        return basicRunner.run(inputBuffer);
088    }
089
090    protected ParsingResult<V> runLocatingMatch(InputBuffer inputBuffer) {
091        ParseRunner<V> locatingRunner = new ErrorLocatingParseRunner<V>(getRootMatcher())
092                .withValueStack(getValueStack());
093        return locatingRunner.run(inputBuffer);
094    }
095
096    protected ParsingResult<V> runReportingMatch(InputBuffer inputBuffer, int errorIndex) {
097        ParseRunner<V> reportingRunner = new ErrorReportingParseRunner<V>(getRootMatcher(), errorIndex)
098                .withParseErrors(getParseErrors())
099                .withValueStack(getValueStack());
100        return reportingRunner.run(inputBuffer);
101    }
102}
103