3

Antlr v3 (正確には 3.0.1) で生成された文法の解析プロセスをログに記録できるようにしたいと考えています。DebugTreeParser を使用しようとしましたが、何もしません。メソッドが呼び出されないようです。

理想的には、試行/実行されたルールのトレースである、次のようなものを出力できるようにしたいと考えています。

 parsing: program (token: Foo)
 parsing: statements (token: Foo)
 parsing: statement (token: Foo)
 parsing: block (token: Foo)
 parsed: block -> false (at 0)
 parsing: method call (token: Foo)
 parsing: variable (token: Foo)
 parsed: variable -> true (at 1)
 ...

ここに私の解析コードがあります:

        CharStream cs = new ANTLRReaderStream(script);
        MyLexer lex = new MyLexer(cs);
        CommonTokenStream tokens = new CommonTokenStream(lex);
        MyParser parser = new MyParser(tokens);
        return new Program(makeProgram((Tree) parser.program().getTree()));

Antlr Wiki にある解決策を試しました。

 ...
 ParseTreeBuilder builder = new ParseTreeBuilder("prog");

 MyParser parser = new MyParser(tokens);
 parser.setTreeAdaptor(new DebugTreeAdaptor(builder, parser.getTreeAdaptor()));

しかし、ビルダーは興味深いものを何も出力しません。

デバッグ互換のパーサーを生成するために、ソース文法で有効にするオプションがあるのではないでしょうか?

4

1 に答える 1

3

まず、-debugコマンドライン オプションを使用して文法を生成します。これが完了すると、トークン パーサーは追加のデバッグ中心のコンストラクターを使用できるようになり、カスタムDebugEventListenerまたは組み込みのコンストラクターを使用できるようになります。カスタム ロギングを使用するため、カスタムDebugEventListeners を使用して開始するためのソリューションの例を次に示します。

テストに使用する文法は次のとおりです。問題が含まれる場合があります。

DebugMe.g

grammar DebugMe;


compilationUnit : statements EOF;
statements      : statement+;
statement       : block | call | decl;
block           : LCUR statements RCUR;    
call            : ID LPAR arglist? RPAR SEMI;
arglist         : ID (COMMA ID)*;    
decl            : VAR ID EQ expr SEMI;
expr            : add_expr;     
    
add_expr        : primary_expr ((PLUS|MINUS) primary_expr)*;    
primary_expr    : STRING | ID | INT | LPAR expr RPAR;    
    
VAR: 'var';   
ID: ('a'..'z'|'A'..'Z')+;
INT: ('0'..'9')+;
STRING: '"' ~('\r'|'\n'|'"')* '"';
SEMI: ';';
LPAR: '(';
RPAR: ')';
LCUR: '{';
RCUR: '}';
PLUS: '+';
MINUS: '-';    
COMMA: ',';
EQ: '=';
WS: (' '|'\t'|'\f'|'\r'|'\n') {skip();};

これが私が使用するテストプログラムです。の実装を省略したことに注意してくださいnewEventListener

TestDebugMeGrammar.java

public class TestDebugMeGrammar {

    public static void main(String[] args) throws Exception {

        CharStream input = new ANTLRStringStream("var x = 3; print(x);");

        DebugMeLexer lexer = new DebugMeLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        DebugMeParser parser = new DebugMeParser(tokens, newEventListener());

        parser.compilationUnit();
    }
    //...
}

DebugEventListenergetがパーサーによってどのように呼び出されるかについてあまり詳しくないのでProxy、最小限の手間ですべての呼び出しをダンプする単純な実装から始めます。

//TestDebugMeGrammar.java

    private static DebugEventListener newEventListener() {
        return (DebugEventListener) Proxy.newProxyInstance(TestDebugMeGrammar.class.getClassLoader(),
                new Class[] { DebugEventListener.class },
                new DebugListenerHandler());
    }

    public static class DebugListenerHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {

            // simply print out the method call.
            System.out.print(method.getName());

            if (args != null && args.length > 0) {
                System.out.print(": ");
                for (int i = 0, count = args.length; i < count; ++i) {
                    Object arg = args[i];
                    if (arg == null) {
                        System.out.printf("<(null)> ");
                    } else {
                        System.out.printf("<%s> ", arg.toString());
                    }
                }
            }
            
            System.out.println();
            return null;
        }
    }

出力は広範囲ですが、リスナーが聞く内容をよく理解できます。

enterRule: <DebugMe.g> <compilationUnit> 
commence
location: <4> <1> 
enterAlt: <1> 
location: <5> <7> 
enterRule: <DebugMe.g> <statements> 
location: <7> <1> 
enterAlt: <1> 
location: <8> <7> 
enterSubRule: <1> 
enterDecision: <1> <false> 
LT: <1> <[@0,0:2='var',<11>,1:0]> 
exitDecision: <1> 
enterAlt: <1> 
location: <8> <7> 
enterRule: <DebugMe.g> <statement> 
location: <10> <1> 
enterDecision: <2> <false> 
LT: <1> <[@0,0:2='var',<11>,1:0]> 
exitDecision: <2> 
enterAlt: <3> 
location: <13> <7> 
enterRule: <DebugMe.g> <decl> 
...

これは、上記から収集したものに基づいた、焦点を絞った小さなリスナーです。出力はあなたが望むものにより近く、あなたにとって有用な出発点として役立つかもしれません.

//TestDebugMeGrammar.g
    //redefinition
    private static DebugEventListener newEventListener() {
        return new SimpleDebugEventListener();
    }

    private static class SimpleDebugEventListener extends
            BlankDebugEventListener {
        
        private Token lastToken;
        @Override
        public void LT(int i, Object t) {
            System.out.println("Read object \"" + t + "\"");
        }

        @Override
        public void LT(int i, Token t) {
            if (!t.equals(lastToken)){
                System.out.println("Read input \"" + t.getText() + "\"");
                lastToken = t;
            }
        }

        @Override
        //public void enterRule(String ruleName) { // <-- ANTLR 3.0.1
        public void enterRule(String grammarFileName, String ruleName) { //<-- ANTLR 3.4
            System.out.println("Entered rule " + ruleName);
        }

        @Override
        //public void exitRule(String ruleName) { // <-- ANTLR 3.0.1
        public void exitRule(String grammarFileName, String ruleName) { //<-- ANTLR 3.4
            System.out.println("Exited rule " + ruleName);
        }

        @Override
        public void consumeToken(Token token) {
            System.out.println("Consumed \"" + token.getText() + "\"");
        }
    }

出力は次のとおりです。

Entered rule compilationUnit
Entered rule statements
Read input "var"
Entered rule statement
Entered rule decl
Consumed "var"
Read input "x"
Consumed "x"
Read input "="
Consumed "="
Entered rule expr
Entered rule add_expr
Entered rule primary_expr
Read input "3"
Consumed "3"
Exited rule primary_expr
Read input ";"
Exited rule add_expr
Exited rule expr
Consumed ";"
Exited rule decl
Exited rule statement
Read input "print"
Entered rule statement
Entered rule call
Consumed "print"
Read input "("
Consumed "("
Read input "x"
Entered rule arglist
Consumed "x"
Read input ")"
Exited rule arglist
Consumed ")"
Read input ";"
Consumed ";"
Exited rule call
Exited rule statement
Read input "<EOF>"
Exited rule statements
Consumed "<EOF>"
Exited rule compilationUnit

私は当初、ANTLR 3.4 を使用して上記のコードをテストし、実行しました。仕様に従って、ANTLR 3.0.1で再テストしましたが、これを機能させるために必要な唯一の変更はSimpleDebugEventListenerクラスです。コードを更新して、変更が必要な場所とその変更内容を示しました。


楽しみのためSimpleDebugEventListenerに、ログの目標により近いと思われる出力を出力するように変更したものを次に示します。

    private static class SimpleDebugEventListener extends
            BlankDebugEventListener {

        private LinkedList<String> activeRules = new LinkedList<String>();

        @Override
        public void enterRule(String grammar, String ruleName) {  //ANTLR 3.4
            activeRules.add(ruleName);
        }

        @Override
        public void exitRule(String grammar, String ruleName) { //ANTLR 3.4
            activeRules.removeLast();
        }

        @Override
        public void consumeToken(Token token) {
            System.out.printf("%s consumed \"%s\"%n", formatRules(),
                    token.getText());
        }

        private String formatRules() {
            if (activeRules.size() == 1) {
                return activeRules.getLast();
            } else { 
                StringBuilder builder = new StringBuilder();
                boolean first = true;
                for (String rule : activeRules){
                    if (!first){
                        builder.append(" -> ");
                    } else { 
                        first = false;
                    }
                    builder.append(rule);
                }
                
                return builder.toString();
            }
        }
    }

出力:

compilationUnit -> statements -> statement -> decl consumed "var"
compilationUnit -> statements -> statement -> decl consumed "x"
compilationUnit -> statements -> statement -> decl consumed "="
compilationUnit -> statements -> statement -> decl -> expr -> add_expr -> primary_expr consumed "3"
compilationUnit -> statements -> statement -> decl consumed ";"
compilationUnit -> statements -> statement -> call consumed "print"
compilationUnit -> statements -> statement -> call consumed "("
compilationUnit -> statements -> statement -> call -> arglist consumed "x"
compilationUnit -> statements -> statement -> call consumed ")"
compilationUnit -> statements -> statement -> call consumed ";"
compilationUnit consumed "<EOF>"
于 2012-12-07T00:03:29.743 に答える