6

私はANTLRを学び、同時にそれを現在のプロジェクトに使用しようとしています。

コードのチャンクに対してレクサーを実行し、それを CommonTokenStream に出力できるようになりました。これは正常に機能しており、ソース テキストが適切なトークンに分割されていることを確認しました。

ここで、このストリーム内の特定のトークンのテキストを変更し、変更されたソース コードを表示できるようにしたいと考えています。

たとえば、私は試しました:

import org.antlr.runtime.*;
import java.util.*;

public class LexerTest
{
    public static final int IDENTIFIER_TYPE = 4;

    public static void main(String[] args)
    {
    String input = "public static void main(String[] args) { int myVar = 0; }";
    CharStream cs = new ANTLRStringStream(input);


        JavaLexer lexer = new JavaLexer(cs);
        CommonTokenStream tokens = new CommonTokenStream();
        tokens.setTokenSource(lexer);

        int size = tokens.size();
        for(int i = 0; i < size; i++)
        {
            Token token = (Token) tokens.get(i);
            if(token.getType() == IDENTIFIER_TYPE)
            {
                token.setText("V");
            }
        }
        System.out.println(tokens.toString());
    }  
}

すべての識別子トークンのテキストを文字列リテラル「V」に設定しようとしています。

  1. tokens.toString() を呼び出したときに、トークンのテキストへの変更が反映されないのはなぜですか?

  2. さまざまなトークン タイプ ID を知るにはどうすればよいですか? デバッガーを使用して調べたところ、IDENTIFIER トークンの ID が「4」であることがわかりました (したがって、定数が一番上に表示されます)。しかし、そうでなければどうやってそれを知ったでしょうか?トークン タイプ ID をトークン名にマッピングする他の方法はありますか?


編集:

私にとって重要なことの 1 つは、トークンに元の開始位置と終了位置を持たせたいということです。つまり、変数名を「V」に変更して新しい位置を反映させたくありません。これは、トークンが元のソース テキストのどこにあったかを知るためです。

4

4 に答える 4

4

ANTLRには、文法ファイルでこれを行う方法があります。

数字とコンマで区切られた文字列で構成される文字列を解析しているとしましょう。文法は次のようになります。

grammar Foo;

parse
  :  value ( ',' value )* EOF
  ;

value
  :  Number
  |  String
  ;

String
  :  '"' ( ~( '"' | '\\' ) | '\\\\' | '\\"' )* '"'
  ;

Number
  :  '0'..'9'+
  ;

Space
  :  ( ' ' | '\t' ) {skip();}
  ;

これはすべてあなたに馴染みがあるように見えるはずです。すべての整数値を角かっこで囲みたいとしましょう。その方法は次のとおりです。

grammar Foo;

options {output=template; rewrite=true;} 

parse
  :  value ( ',' value )* EOF
  ;

value
  :  n=Number -> template(num={$n.text}) "[<num>]" 
  |  String
  ;

String
  :  '"' ( ~( '"' | '\\' ) | '\\\\' | '\\"' )* '"'
  ;

Number
  :  '0'..'9'+
  ;

Space
  :  ( ' ' | '\t' ) {skip();}
  ;

ご覧のとおり、上部にいくつかを追加し、パーサールールののoptions後に書き換えルール(の後にすべて->)を追加しました。Numbervalue

すべてをテストするには、次のクラスをコンパイルして実行します。

import org.antlr.runtime.*;

public class FooTest {
  public static void main(String[] args) throws Exception {
    String text = "12, \"34\", 56, \"a\\\"b\", 78";
    System.out.println("parsing: "+text);
    ANTLRStringStream in = new ANTLRStringStream(text);
    FooLexer lexer = new FooLexer(in);
    CommonTokenStream tokens = new TokenRewriteStream(lexer); // Note: a TokenRewriteStream!
    FooParser parser = new FooParser(tokens);
    parser.parse();
    System.out.println("tokens: "+tokens.toString());
  }
}

これは以下を生成します:

parsing: 12, "34", 56, "a\"b", 78
tokens: [12],"34",[56],"a\"b",[78]
于 2010-02-09T13:50:15.697 に答える
4

ANTLR 4 には、ツリーの観察または変換に使用できる解析ツリー リスナーと TokenStreamRewriter (名前の違いに注意してください) を使用する新しい機能があります。(TokenRewriteStream を示唆する返信は ANTLR 3 に適用され、ANTLR 4 では機能しません。)

ANTL4 では、文法の各非終端ノードに出入りするためのコールバックを備えた XXXBaseListener クラスが生成されます (例: enterClassDeclaration() )。

リスナーは次の 2 つの方法で使用できます。

  1. オブザーバーとして - メソッドを単純にオーバーライドして、入力テキストに関連する任意の出力を生成します。たとえば、enterClassDeclaration() をオーバーライドして、プログラムで宣言された各クラスの行を出力します。

  2. 元のテキストが通過するときに TokenRewriteStream を使用して元のテキストを変更するトランスフォーマーとして。これを行うには、リライターを使用してコールバック メソッドでトークンを変更 (追加、削除、置換) し、リライターと end を使用して変更されたテキストを出力します。

変換方法の例については、ANTL4 ブックの次の例を参照してください。

https://github.com/mquinn/ANTLR4/blob/master/book_code/tour/InsertSerialIDListener.java

https://github.com/mquinn/ANTLR4/blob/master/book_code/tour/InsertSerialID.java

于 2015-04-05T17:19:59.303 に答える
3

レクサーでテキストを変更するもう 1 つの例は、すべての状況でテキストをグローバルに置き換えたい場合にうまく機能しますが、特定の状況でのみトークンのテキストを置き換えたい場合がよくあります。

TokenRewriteStream を使用すると、特定のコンテキストでのみテキストを変更する柔軟性が得られます。

これは、使用していたトークン ストリーム クラスのサブクラスを使用して実行できます。CommonTokenStreamクラスを使用する代わりに、 TokenRewriteStream.

したがって、TokenRewriteStream にレクサーを消費させてから、パーサーを実行します。

文法では、通常、次のように置換します。

/** Convert "int foo() {...}" into "float foo();" */
function
:
{
    RefTokenWithIndex t(LT(1));  // copy the location of the token you want to replace
    engine.replace(t, "float");
}
type id:ID LPAREN (formalParameter (COMMA formalParameter)*)? RPAREN
    block[true]
;

ここでは、一致したトークン int をテキスト float に置き換えました。位置情報は保持されますが、「一致」するテキストは変更されています。

以前と同じコードを使用した後にトークン ストリームを確認するには。

于 2010-02-09T18:36:21.360 に答える