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