2016-01-11 4 views
7

レガシーコード用のきれいなプリンタを古い言語で書く。私が翻訳を書いてC++を出力する前に、解析と解読を学ぶ予定です。私は6月にJavaとANTLRを使って深いところに投げ込まれたので、間違いなくいくつかの知識のギャップがあります。ANTLR 4 - カスタムリスナーから隠しコメントチャンネルにアクセスするには?

私は自分のカスタムリスナーのためのメソッドを書くのが楽しいと思っています。コメントもきれいに印刷したいと思っています。私のコメントは、別の隠しチャンネルにあります。

/* Comments and whitespace -- Nested comments are allowed, each is redirected to a specific channel */ 
COMMENT_1 : '(*' (COMMENT_1|COMMENT_2|.)*? '*)' -> channel(1) ; 
COMMENT_2 : '{' (COMMENT_1|COMMENT_2|.)*? '}' -> channel(1) ; 

NEWLINES : [\r\n]+        -> channel(2) ; 
WHITESPACE : [ \t]+        -> skip  ; 

私はCymbol CommentShifterの例をpで再生しています。 「Definitive ANTLR 4 Reference」の207ページを参照してください。これをリスナーのメソッドに適用する方法を理解しようとしています。

public void exitVarDecl(ParserRuleContext ctx) { 
    Token semi = ctx.getStop(); 
    int i = semi.getTokenIndex(); 
    List<Token> cmtChannel = tokens.getHiddenTokensToRight(i, CymbolLexer.COMMENTS); 
    if (cmtChannel != null) { 
     Token cmt = cmtChannel.get(0); 
     if (cmt != null) { 
      String txt = cmt.getText().substring(2); 
      String newCmt = "// " + txt.trim(); // printing comments in original format 
      rewriter.insertAfter(ctx.stop, newCmt); // at end of line 
      rewriter.replace(cmt, "\n"); 
     } 
    } 
} 

私を私はexitEveryRuleではなくexitVarDeclを使用して、この例を適応し、それがCymbol例のために働いていたが、私は自分のリスナーに適合させるとき、私は私がexitEveryRuleまたはexitSpecificThing

を使用するかどうか、nullポインタ例外を取得this answerを見て、それは有望だと思うが、私は本当に必要なのはだと思います。構文解析ツリーでリスナーのメソッドとコンテキストを実際に取得するには数か月かかりました。

CommonTokenStream.LT()CommonTokenStream.LA()、およびconsume()は私が使いたいものですが、ANTLRの本の例とは全く異なる方法を使って答えているのはなぜですか?トークンインデックスまたはトークンタイプについてはどうすればよいですか?

私はこれの背後にある論理をよりよく理解したいと思います。

答えて

2

はい、AnTLRがデータを内部的に格納する方法については回答できませんが、非表示のトークンにアクセスする方法を教えてください。私はAnTLR v4.1 for C#.NET v4.5.2を使用して自分のコンピュータでこれをテストしました。

私のコードで
LineComment 
    : '//' ~[\r\n]* 
     -> channel(1) 
    ; 

、私はこのような全体の生のトークンストリームを取得しています:

IList<IToken> lTokenList = cmnTokenStream.Get(0, cmnTokenStream.Size); 

テストするために、私はトークンリストを印刷

私はこのようになりますルールを持っています

foreach (IToken iToken in lTokenList) 
{ 
    Console.WriteLine("{0}[{1}] : {2}", 
     iToken.Channel, 
     iToken.TokenIndex, 
     iToken.Text); 
} 

このコードを実行する:次のループを使用して

void Foo() 
{ 
    // comment 
    i = 5; 
} 

は(簡潔にするために、私はまた、空白を無視している完全な文法を持っていると仮定してください)次の出力が得られます。

0[0] : void 
0[1] : Foo 
0[2] : (
0[3] :) 
0[4] : { 
1[5] : // comment 
0[6] : i 
0[7] : = 
0[8] : 6 
0[9] : ; 
0[10] : } 

あなたは、チャネルインデックスは、単一のため1で見ることができますコメントトークン。だから、のみコメントトークンにアクセスするには、このループを使用することができます。

int lCommentCount = 0; 
foreach (IToken iToken in lTokenList) 
{ 
    if (iToken.Channel == 1) 
    { 
     Console.WriteLine("{0} : {1}", 
      lCommentCount++, 
      iToken.Text); 
    } 
} 

は、次に、あなたのものは何でもしてこれらのトークンを行うことができます。複数のストリームがある場合でも動作しますが、私は65,536ストリーム以上を使用することには注意します。だから私は、彼らが唯一のインデックスにストリームを16ビットの符号なし整数を使用していると思います

Serialized ATN data element out of range. 

:私はインデックス65536をストリーミングするためにトークンルールリダイレクトと文法をコンパイルしようとしたときにANTLRは、次のエラーが発生しました。 Wierd。

関連する問題