2016-03-10 3 views
12

私はANTLR 4の文法を持っており、そこからレクサーとパーサーを構築しました。今私はエラーに遭遇するまで解析するような方法でそのパーサーをインスタンス化しようとしています。エラーが発生した場合は、解析を継続すべきではありませんが、問題に関する有益な情報を提供する必要があります。理想的には機械可読の場所と人間が読めるメッセージです。ここで有用なメッセージで解析エラーを中止します

は私が現時点で持っているものです。

grammar Toy; 

@parser::members { 

    public static void main(String[] args) { 
     for (String arg: args) 
      System.out.println(arg + " => " + parse(arg)); 
    } 

    public static String parse(String code) { 
     ErrorListener errorListener = new ErrorListener(); 
     CharStream cstream = new ANTLRInputStream(code); 
     ToyLexer lexer = new ToyLexer(cstream); 
     lexer.removeErrorListeners(); 
     lexer.addErrorListener(errorListener); 
     TokenStream tstream = new CommonTokenStream(lexer); 
     ToyParser parser = new ToyParser(tstream); 
     parser.removeErrorListeners(); 
     parser.addErrorListener(errorListener); 
     parser.setErrorHandler(new BailErrorStrategy()); 
     try { 
      String res = parser.top().str; 
      if (errorListener.message != null) 
       return "Parsed, but " + errorListener.toString(); 
      return res; 
     } catch (ParseCancellationException e) { 
      if (errorListener.message != null) 
       return "Failed, because " + errorListener.toString(); 
      throw e; 
     } 
    } 

    static class ErrorListener extends BaseErrorListener { 

     String message = null; 
     int start = -2, stop = -2, line = -2; 

     @Override 
     public void syntaxError(Recognizer<?, ?> recognizer, 
           Object offendingSymbol, 
           int line, 
           int charPositionInLine, 
           String msg, 
           RecognitionException e) { 
      if (message != null) return; 
      if (offendingSymbol instanceof Token) { 
       Token t = (Token) offendingSymbol; 
       start = t.getStartIndex(); 
       stop = t.getStopIndex(); 
      } else if (recognizer instanceof ToyLexer) { 
       ToyLexer lexer = (ToyLexer)recognizer; 
       start = lexer._tokenStartCharIndex; 
       stop = lexer._input.index(); 
      } 
      this.line = line; 
      message = msg; 
     } 

     @Override public String toString() { 
      return start + "-" + stop + " l." + line + ": " + message; 
     } 
    } 

} 

top returns [String str]: e* EOF {$str = "All went well.";}; 
e: 'a' 'b' | 'a' 'c' e; 

保存これはToy.gに、これらのコマンドを試してみてください。一方で

> java -jar antlr-4.5.2-complete.jar Toy.g 
> javac -cp antlr-4.5.2-complete.jar Toy*.java 
> java -cp .:tools/antlr-4.5.2-complete.jar ToyParser ab acab acc axb abc 
ab => All went well. 
acab => All went well. 
acc => Failed, because 2-2 l.1: no viable alternative at input 'c' 
axb => Parsed, but 1-1 l.1: token recognition error at: 'x' 
Exception in thread "main" org.antlr.v4.runtime.misc.ParseCancellationException 
    at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:90) 
    at org.antlr.v4.runtime.Parser.match(Parser.java:229) 
    at ToyParser.top(ToyParser.java:187) 
    at ToyParser.parse(ToyParser.java:95) 
    at ToyParser.main(ToyParser.java:80) 
Caused by: org.antlr.v4.runtime.InputMismatchException 
    at org.antlr.v4.runtime.BailErrorStrategy.recoverInline(BailErrorStrategy.java:85) 
    ... 4 more 

、私は私はと感じてすでに過度にやっている。シンプルで一般的な作業でなければならないコードの量を見てもわかりませんが、シンプルな解決法がないのかどうか疑問に思います。一方で、それでも十分ではないように見える理由は2つあります。まず、レクサーエラーを報告することができましたが、残りのストリームでパーサーが続行されることはありません。これは入力axbParsed, but文字列による証です。第二に、私はまだスタックトレースで示されているようにエラーリスナーに報告されないエラーが残っています。

私はBailErrorStrategyをインストールしない場合、私はより多くの有用な出力を得る:

acc => Parsed, but 2-2 l.1: mismatched input 'c' expecting 'a' 
axb => Parsed, but 1-1 l.1: token recognition error at: 'x' 
abc => Parsed, but 2-2 l.1: extraneous input 'c' expecting {<EOF>, 'a'} 

は、エラーメッセージのこの種を取得するが、それでもエラーに救済する方法はありますか?私はsee from the sourcesextraneous inputというメッセージが実際にDefaultErrorStrategyによって生成されていることがわかります。明らかに、問題の修正方法が分かっています。私はそれをやらなければならないし、、つまりBailErrorStrategyという自分の変種を書いてからスローするのですか?

答えて

7

同じ状況で私はDefaultErrorStrategyを拡張してreport*のメソッドをオーバーライドしました。それはかなり簡単です(あなたもANTLRErrorStrategyを使用することができます)

Hereフェイルファースト戦略の例があります。私はあなたの場合、同じ方法ですべてのエラーを収集し、詳細なレポートを作成することができると思います。

+0

ありがとう!つまり、エラーメッセージのフォーマットはすべて自分で行う必要がありますか? [DefaultErrorStrategy'ソース](https://github.com/antlr/antlr4/blob/4.5/runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy)を見てください。java)は、エラー書式とエラー記者に通知するための呼び出しの間に得る方法がないように見えます。 – MvG

+0

@MvG残念ながらはい。これは私が出会った唯一の信頼できる解決策です – vsminkov

1

1つのアプローチでは、エラー戦略の代わりにエラーリスナーを変更することがあります。一つは、一緒に、次のリスナーにデフォルトの戦略を使用することができます。

class ErrorListener extends BaseErrorListener { 
    @Override 
    public void syntaxError(Recognizer<?, ?> recognizer, 
          Object offendingSymbol, 
          int line, 
          int charPositionInLine, 
          String msg, 
          RecognitionException e) { 
     throw new ParseException(msg, e, line); 
    } 
} 

class ParseException extends RuntimeException { 
    int line; 
    public ParseException(String message, Throwable cause, int line) { 
     super(message, cause); 
     this.line = line; 
    } 
} 

この方法は、彼らが出力するためのものであるとして、エラーがフォーマットされますが、報告された最初のエラーは、コンパイルという名前の例外をスローすることによって中止するようになります。これはチェックされていない例外なので、忘れてしまった場合にコンパイラが警告しないので、必ずキャッチするようにしてください。

 int start = 0, stop = -1; 
     if (offendingSymbol instanceof Token) { 
      Token t = (Token) offendingSymbol; 
      start = t.getStartIndex(); 
      stop = t.getStopIndex(); 
     } else if (recognizer instanceof Lexer) { 
      Lexer lexer = (Lexer)recognizer; 
      start = lexer._tokenStartCharIndex; 
      stop = lexer._input.index(); 
     } 
:行番号に加えて、あなたはまた、入力の問題のある部分のソース・テキストのオフセットが必要な場合は、機械読み取り可能な場所に関して

は、このようなコードはsyntaxErrorメソッド内に動作するようです

関連する問題