8

ASTツリーを使用してAntlr 3を使用して複雑な文法を開発しました。ANTLR はレクサーとパーサーを生成します。問題は、たとえば、ユーザーが無効な構文を入力すると、文法が「;」を想定していることです。ユーザーがこれを入力しないと、Eclipse IDE で次の例外が発生します。

 line 1:24 mismatched input '<EOF>' expecting ';'

この例外をキャッチしようとするが、例外がキャッチされないため、この例外をどのように処理できますか。これはまったく例外ですか?この例外がキャッチされない理由がわかりません。調べてみましたが、Antlr の Web サイトはしばらくダウンしているようです。

私は以下を見て、Java の "$" での ANTLR 例外処理を見て、その例に従いましたが、Lexer が RuntimeException() を追加してコードを生成すると、到達不能なコードが得られます。

どうすればよいかわかりません。

パーサーから構文エラーの数を取得しようとすると、0 が表示されます。

編集:

見ることで機能する解決策を見つけました:ANTLRは無効な入力でエラーをスローしません

ただし、例外メッセージを取得しようとすると、null になります。すべてを正しく設定しましたか? サンプル文法を参照してください:

grammar i;

options {
output=AST;
}

@header {
package com.data;
}

@rulecatch {
    catch(RecognitionException e) {
        throw e;
   }
}

// by having these below it makes no difference
/**@parser::members {
    @Override
    public void reportError(RecognitionException e) {
        throw new RuntimeException("Exception : " + " " + e.getMessage());
    }
}

@lexer::members {
    @Override
    public void reportError(RecognitionException e) {
       throw new RuntimeException("Exception : " + " " + e.getMessage());
    }
}*/

編集:

私がこれまでに持っているものを見てください:

grammar i;

options {
output=AST;
}

@header {
package com.data;
}

@rulecatch {
    // ANTLR does not generate its normal rule try/catch
    catch(RecognitionException e) {
        throw e;
    }
}

@parser::members {
    @Override
    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        throw new RuntimeException(hdr + ":" + msg);
    }
}

@lexer::members {
    @Override
    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        throw new RuntimeException(hdr + ":" + msg);
    }
}

operatorLogic   : 'AND' | 'OR';
value       : STRING;
query       : (select)*;
select      : 'SELECT'^ functions 'FROM table' filters?';';
operator    : '=' | '!=' | '<' | '>' | '<=' | '>=';
filters : 'WHERE'^ conditions;
members : STRING operator value;
conditions  : (members (operatorLogic members)*);
functions   : '*';
STRING  : ('a'..'z'|'A'..'Z')+;
WS      : (' '|'\t'|'\f'|'\n'|'\r')+ {skip();}; // handle white space between keywords

public class Processor {

public Processor() {

}

/**
 * This method builds the MQL Parser.
 * @param args the args.
 * @return the built IParser.
 */
private IParser buildMQLParser(String query) {
    CharStream cs = new ANTLRStringStream(query);
    // the input needs to be lexed
    ILexer lexer = new ILexer(cs);
          CommonTokenStream tokens = new CommonTokenStream();
    IParser parser = new IParser(tokens);
    tokens.setTokenSource(lexer);
    // use the ASTTreeAdaptor so that the grammar is aware to build tree in AST format
    parser.setTreeAdaptor((TreeAdaptor) new ASTTreeAdaptor().getASTTreeAdaptor());
return parser;
}

/**
 * This method parses the MQL query.
 * @param query the query.
 */
public void parseMQL(String query) {
    IParser parser = buildMQLParser(query);
    CommonTree commonTree = null;
    try {
                     commonTree = (CommonTree) parser.query().getTree();
                    }
    catch(Exception e) {
        System.out.println("Exception :" + " " + e.getMessage());
    }
}
}

public class ASTTreeAdaptor {

public ASTTreeAdaptor() {

}

/**
 * This method is used to create a TreeAdaptor.
 * @return a treeAdaptor.
 */
public Object getASTTreeAdaptor() {
    TreeAdaptor treeAdaptor = new CommonTreeAdaptor() {
        public Object create(Token payload) {
        return new CommonTree(payload);
        }
    };
    return treeAdaptor; 
}
}

したがって、次のように入力すると: SELECT * FROM table

「;」なし 私は MismatchedTokenException を取得します:

catch(Exception e) {
     System.out.println("Exception : " + " " e);
}

私がしようとすると:

e.getMessage();

null を返します。

4

2 に答える 2

5

displayRecognitionError代わりにオーバーライドしてみてください:

@parser::members { 
   ...

    @Override    
    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        throw new RuntimeException(hdr + ":" + msg);
    }
    ...
}
//same code in @lexer::members

中止するのではなくエラーを追跡したい場合は、それらを追跡するためのハンドラー インターフェイスを作成できます。

@parser::members { 
   ...
    private YourErrorTrackerInterface errorTracker;

    //getter/setter for errorTracker here        

    @Override    
    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        if (errorTracker != null){
          errorTracker.addError(e, tokenNames, hdr, msg);
        }
    }
    ...
}
//same code in @lexer::members

その後、エラー トラッカーは、例外をスローするか続行するかを決定できます。


上記のコードを使用すると、「回復可能な」エラー、つまり ANTLR がスキップできるエラーを追跡できます。SELECT * FROM table(末尾の なし)など、回復不能なエラーが発生するシナリオがまだあります ;。その場合、その中parseMQLまたはその周辺のどこかで例外をキャッチする必要があります。(独自の回復コードを作成することもできますが、お勧めしません。)

parseMQLこれは、2 つの異なるタイプの解析エラーを示す変更です。への呼び出しを削除したことに注意してください。これは、すべての例外がそれを埋めることからgetMessage派生したわけではないためです。RecognitionException

public void parseMQL(String query) {
    iParser parser = buildMQLParser(query);
    CommonTree commonTree = null;
    try {
        commonTree = (CommonTree) parser.query().getTree();
    } catch (MismatchedTokenException e){
        //not production-quality code, just forming a useful message
        String expected = e.expecting == -1 ? "<EOF>" : iParser.tokenNames[e.expecting];
        String found = e.getUnexpectedType() == -1 ? "<EOF>" : iParser.tokenNames[e.getUnexpectedType()];

        System.out.println("Fatal mismatched token exception: expected " + expected + " but was " + found);   

    } catch (RecognitionException e) {
        System.out.println("Fatal recognition exception " + e.getClass().getName()
                + " : " + e);

    } catch (Exception e) {
        System.out.println("Other exception : " + e.getMessage());
    }
}

入力SELECT * FROM tableにより、「致命的な不一致トークンの例外: 予想される ';'」というメッセージが生成されます。しかし <EOF> でした」。この例外は、ANTLR によって直接生成されました。

入力SELECT FROM table;により、「その他の例外: 行 1:7: 'FROM テーブル' に '*' がありません」というメッセージが生成されます。この例外は、上記のコードによって生成されました。

于 2012-11-29T17:11:55.007 に答える
1

私の理解が正しければ、言語の構文エラーを処理する必要があります。これが、私のプロジェクトでこの設定を行う方法です。

/**
 * Adapter need for ANTL to recognize our custom nodes
 * 
 * @author Greg
 */
public class PhantomTreeAdaptor extends CommonTreeAdaptor{

    @Override
    public Object create(Token payload){
        return new ASTNode(payload);
    }

    @Override
    public Object dupNode(Object old){
        return (old == null) ? null : ((ASTNode) old).dupNode();
    }

    @Override
    public Object errorNode(TokenStream input, Token start, Token stop, RecognitionException e){
        return new ASTErrorNode(input, start, stop, e);
    }
}

ここにエラーノードがあります

/**
 * This is our custom Error node used by the adapter.
 * 
 * @author Greg
 */
public class ASTErrorNode extends ASTNode {

    org.antlr.runtime.tree.CommonErrorNode delegate;

    public ASTErrorNode(TokenStream input, Token start, Token stop, RecognitionException e) {

        delegate = new CommonErrorNode(input, start, stop, e);

    }

    public boolean isNil() {
        return delegate.isNil();
    }

    public int getType() {
        return delegate.getType();
    }

    public String getText() {
        return delegate.getText();
    }

    public String toString() {

        return delegate.toString();
    }

}

そして、これがすべてを接着する方法です。

 final PhantomSQLLexer lex = new PhantomSQLLexer(input);

        final CommonTokenStream tokens = new CommonTokenStream(lex);
        final PhantomSQLParser g = new PhantomSQLParser(tokens);
        g.setTreeAdaptor(new PhantomTreeAdaptor());
        final start_rule_return r = g.start_rule();
        if (g.getNumberOfSyntaxErrors() == 0) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("tree=" + ((Tree) r.tree).toStringTree());
                LOGGER.debug("-------------------------------------------");
            }
            final ASTNode root = r.tree;
            exec(root);            
        }
        else {
            LOGGER.debug("Error parsing input");
        }

単純にレクサーとパーサーを作成し、パーサーをカスタム ツリー アダプター (PhantomTreeAdaptor) でセットアップしました。そこから、カスタム コードにエラーがあるかどうかを確認できます。

于 2012-11-29T12:29:57.353 に答える