2

ANTLR が初めてなので、文字列テンプレートがどのように機能するかを理解しようとしています。非常に単純な入力ファイルに基づいて Java コードを生成したいと考えています。柔軟なコンセプトのため、(文字列) テンプレートを使用したいと思います。Java では、通常、メンバー宣言を生成し、別の場所で初期化し、さらに別の場所で使用する必要があります。識別子名は一致する必要があるため、繰り返されます。これは、あちこちでテンプレートのインスタンス化がほとんど必要ないことを意味します。確かにそれは可能ですが、どうすればよいかわかりません。重要な「手がかり」が欠けているのではないでしょうか?

概念を調査するためのテスト プログラムを作成しました。単純な入力ファイルを取ります:

red = #FF0000
green = #00FF00
blue = #0000FF

次のような出力が生成されるはずです。

class MyColors {
  // Class members
  public java.awt.Color red;
  public java.awt.Color green;
  public java.awt.Color blue;

  // Constructor
  /* Question: How to access the right initializer value here?!? The values are not accessible at this level of the grammar*/
  public MyColors() {
    red = java.awt.Color.getColor("#FF0000");
    green = java.awt.Color.getColor("#00FF00");
    blue = java.awt.Color.getColor("#0000FF");
  }
};

...コンストラクター内の変数と初期化子の名前は、入力に従って入力されます。

私が定義した文法は次のとおりです。

grammar Test;

options {
    output=template;
}

colors: (a+=def)+ -> colorClassDef(name={$a});

def: ident '=' name -> colorDef(id={$ident.text}, name={$name.text});

ident: ID;

name: ID;

ID: ('a'..'z'|'A'..'Z'|'#'|'0'..'9')+;
WS: (' '|'\t'|'\r'|'\n')+ { skip(); };

テンプレートの定義は次のとおりです。

group Test;

colorClassDef(name, id) ::= <<
class MyColors {
// Class members
<name:{ v | public java.awt.Color <v>;
}>
// Constructor
/* Question: How to access the initializer value here?!? */
public MyColors() {
<name:{ v | <v> = java.awt.Color.getColor("<id>");
}>
}
};
>>

/* How to return both id and name here seperately, as ID should go into the declaration and name should to into the init? */
colorDef(id, name) ::= <<
<id>
>>

<id> と <name> をルール 'def' から取り出して、生成されたコードの正しい部分に含める方法を誰か提案できますか?

Returning multiple values in ANTLR ruleantlr2 return multiple valuesなど、複数の戻り値に関する複数の質問を見つけましたが、文字列テンプレートは含まれていません。私は「本」を購入し、Java バイトコード ジェネレーターを調べてみましたが、そこに答えが見つかりませんでした。すべての例は、1 ビットの入力に対して 1 ビットの出力を生成するように見えます。(後悔はありませんが、この本は寝る前に読むのに最適です;-)

誰かが私が見逃している手がかりを指摘できますか? この問題を解決する最も適切な方法は何ですか? いくつかのサンプルコードとドキュメントへのポインタをいただければ幸いです。

ありがとう、

マールテン

4

1 に答える 1

1

生成されたコードの適切な部分にそれらを含めるために、ルール「def」を取得および解除する方法を誰かが提案できますか?

これは、必要なものを取得するための単純な Java 中心のアプローチです。私が望むほど優雅ではありませんが (改善の余地があると思います)、それほど手間をかけずに問題を解決できると思います。私はいくつかの名前を変更しましたが、あなたのアプローチの精神をそのまま維持したと思います.

まずはテンプレ。colorClassDefテンプレートには、文法によって決定されるすべての情報 (すべての ID、すべての名前、および各 ID と対応する名前の関連付け) が必要であることに注意してください。テンプレートからそのすべてにアクセスする方法の 1 つを次に示します。

group Colors;

colorClassDef(ids, colors) ::= <<
class MyColors {
// Class members
<ids:{ id | public java.awt.Color <id>;
}>

// Constructor
public MyColors() {
<ids:{ id | <id> = java.awt.Color.getColor("<colors.(id)>");
}>
}
};
>>

ここでは、パラメータを使用してidsすべての受信 ID のリストを格納し、パラメータを使用colorsして ID (キー) を名前 (値) に関連付けるマップを格納しています。colorsコンストラクタ部分の場合、STは「間接プロパティ検索」構文を使用してid の名前にアクセスします<colors.(id)>colorsはマップであるためid、マップへのキーとして使用され、値がテンプレートに書き込まれます。

TemplatecolorClassDefがすべてを処理するので、 template を削除しましcolorDefた。

第二に、文法。ID とカラーマップを提供する必要があります。これを行う1つの方法を次に示します。

grammar Colors;

options {
    output=template;
}

colors 
@init {
        java.util.LinkedList<String> ids = new java.util.LinkedList<String>(); 
        java.util.HashMap<String, String> colors = new java.util.HashMap<String, String>();
    }
    : (ident '=' name 
            {ids.add($ident.text); colors.put($ident.text, $name.text);}
      )+ EOF 
        -> colorClassDef(ids={ids}, colors={colors})
    ;

ident: ID;

name: ID;

ID: ('a'..'z'|'A'..'Z'|'#'|'0'..'9')+;
WS: (' '|'\t'|'\r'|'\n')+ { skip(); };

(文法を比較的単純にするために、ルールcolorsdefをにマージしましcolorsた。)

それぞれidentがリストidsに追加され、それぞれが追加されて、対応するキーへの値としてnameマップされます。次に、テンプレートに進みます。colorsident

作品をテストするためのテストクラスは次のとおりです。

public class ColorsTest {

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

        final String code = "red = #FF0000\ngreen = #00FF00\nblue = #0000FF"; 

        process(code, "Colors.stg");

    }

    private static void process(final String code, String templateResourceName)
            throws IOException, RecognitionException, Exception {
        CharStream input = new ANTLRStringStream(code);
        ColorsLexer lexer = new ColorsLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        ColorsParser parser = new ColorsParser(tokens);

        InputStream stream = ColorsTest.class.getResourceAsStream(templateResourceName);
        Reader reader = new InputStreamReader(stream);
        parser.setTemplateLib(new StringTemplateGroup(reader));
        reader.close();
        stream.close();

        ColorsParser.colors_return result = parser.colors();

        if (parser.getNumberOfSyntaxErrors() > 0){
            throw new Exception("Syntax Errors encountered!");
        }

        System.out.println(result.toString());
    }
}

これは、質問の入力に基づくテストケースです。

入力

red = #FF0000
green = #00FF00
blue = #0000FF

出力

class MyColors {
// Class members
public java.awt.Color red;
public java.awt.Color green;
public java.awt.Color blue;


// Constructor
public MyColors() {
red = java.awt.Color.getColor("#FF0000");
green = java.awt.Color.getColor("#00FF00");
blue = java.awt.Color.getColor("#0000FF");

}
};
于 2012-12-27T22:00:52.117 に答える