(私は Antlr の Noob です)... StringTemplates で文法を取得するのに苦労しています。基本的に、私は小さな DSL を書こうとしています。思い通りの文法を取得できます (正しく解析されます) が、ターゲット コードの生成をテンプレートで動作させることができません。だからここに私の文法のスニペットがあります:
grammar Pfig;
options {
output=template;
language=CSharp2;
}
conf
: globalName
;
globalName
: 'GlobalName:' ID
-> localConf(name ={$ID.text})
;
本質を理解するために、かなり単純化しました。基本的に、lex/parse が「GlobalName: Foo」に遭遇したときに、「localConf」と呼ばれる StringTemplate に基づいてテキストを吐き出したいのです。とても簡単です。
それでは、テスト アプリでパーサーを起動して、入力ファイルを処理してみましょう。
// C# processing a file with the lex/parser.
// the 'app.pfig' file just has one line that reads 'GlobalName: Bla'
using (FileStream fs = File.OpenRead("c:\\app.pfig"))
{
PfigParser parser = new PfigParser(new CommonTokenStream(
new PfigLexer(new ANTLRInputStream(fs))));
using (TextReader tr = File.OpenText("./Pfig.stg"))
{
parser.TemplateLib = new StringTemplateGroup(tr);
}
var parseResult = parser.conf();
string code = parseResult.Template.ToString(); // Fail: template is null
}
パーサー コードをステップ実行して、テキストが正しく識別され、stringTemplate が正しく適用されていることを確認できます。問題は、この 'globalName' ルールが 'conf' のサブルールであるため、直接実行されないことです。メソッドはそれを見つけて返すだけです。しかし、'Conf' メソッドの呼び出しは、サブルールからの戻り値を保持しません。これは、最終行の結果のテンプレートが null であることを意味します。
文法で「conf」ルールを取り除き、「globalName」を直接呼び出すと、機能します (スタック上の唯一のルールであるため)。しかし、明らかに複数のルールが必要です。Java でパーサーを生成しましたが、同じことを行います。
// antlr generated parser code
public PfigParser.conf_return conf() // throws RecognitionException [1]
{
PfigParser.conf_return retval = new PfigParser.conf_return();
try
{
{
PushFollow(FOLLOW_globalName_in_conf30);
globalName(); // <- it calls globalName() but doesn't keep the return.
state.followingStackPointer--;
}
retval.Stop = input.LT(-1);
}
// snip
テンプレート アプローチが Antlr でどのように機能するかについて、基本的な概念を理解していないことは簡単にわかります。これは私の問題だと確信していますが、私は自分が何を間違っているかを知るのが苦手です...私が見た例は、実際のコードのテンプレート出力を実際に示していません.