というわけで、StringTemplateで繰り返し処理をやってみようと思う。
これができれば、必要な定義情報を食わせて、いっぱいのものを吐き出す処理とかがガシガシ組めそう。
WPFの画面のコードの雛形とかガンガン吐けるものとか作れるといいなぁ。(夢
繰り返しをやること自体は超簡単。
group loop;
foo(val) ::= <<
$val$
>>
こんなテンプレートで、もう繰り返し処理ができてるようなもんだ。
実際↓のコードを実行するとわかる。
using System;
using System.IO;
using Antlr.StringTemplate;
using Antlr.StringTemplate.Language;
namespace StringTemplateLoopTest
{
class Program
{
static void Main(string[] args)
{
var loop = new StringTemplateGroup(
new StreamReader(
typeof(Program).Assembly.GetManifestResourceStream("StringTemplateLoopTest.loop.stg")),
typeof(DefaultTemplateLexer));
var template = loop.GetInstanceOf("foo");
template.SetAttribute("val", "一郎");
template.SetAttribute("val", "二郎");
template.SetAttribute("val", "三郎");
Console.WriteLine(template);
}
}
}
valに対して、一郎、二郎、三郎の三つの値をセットしてるのがわかる。
これを実行すると↓のような結果になる。
一郎二郎三郎
繰り返されてる。でもイマイチ。
ここで、テンプレートを↓のようにします。separatorで区切り文字を指定できる!!
group loop;
foo(val) ::= <<
$!一郎, 二郎, 三郎という出力になる!$
$val; separator=", "$
>>
ちなみに$!から!$で囲まれた部分はコメントになります。セパレータを適用すると素敵な感じになった。
もちろんこれだけじゃ、実際には物足りない。
ということで、任意の形に整形することももちろんできる。テンプレートを↓のように書き換える。
group loop;
foo(val) ::= <<
$val:{$i$: $it$$\n$}$
>>
実行すると下の結果が得られる。
1: 一郎
2: 二郎
3: 三郎
ちょっと自分で見てもわかりにくいので分解してみよう。
まず、$val:{適用するテンプレート}$という大枠があります。
valを、{ }で囲んだテンプレートに適用するってことになります。
テンプレートの中身はどうなってるかというと、$i$: $it$$\n$になってる。
$i$は、ループの1はじまりのインデックスになる。
$it$は、ループの中の現在の要素になる。
$\n$は、改行になる。
改行の部分は、separatorにもっていくこともできる。
group loop;
foo(val) ::= <<
$! 改行をセパレータに移動 !$
$val:{$i$: $it$}; separator="\n"$
>>
因みに、separator以外にもnullっていうオプションを指定することもできるっぽい。
nullの要素の時に、何を出力するかってことみたい。
ちなみに、nullを指定するためには、C#の方のコードから配列(リスト)でデータを渡す必要があった。
ということで、C#のコードを↓のように変更。
using System;
using System.IO;
using Antlr.StringTemplate;
using Antlr.StringTemplate.Language;
namespace StringTemplateLoopTest
{
class Program
{
static void Main(string[] args)
{
var loop = new StringTemplateGroup(
new StreamReader(
typeof(Program).Assembly.GetManifestResourceStream("StringTemplateLoopTest.loop.stg")),
typeof(DefaultTemplateLexer));
var template = loop.GetInstanceOf("foo");
// 配列で渡すように変更
template.SetAttribute("val", new[]{"一郎", null, "三郎"});
Console.WriteLine(template);
}
}
}
二郎さんには悲しいがいなくなってもらった。
んで、テンプレートも改造。
group loop;
foo(val) ::= <<
$! nullの時はn/aという値を使うようにする !$
$val:{$i$: $it$}; null="n/a", separator="\n"$
>>
実行すると、
1: 一郎
2: n/a
3: 三郎
という結果になる。ばっちり。
因みに、今まで使ってきた{ }で囲った部分は、匿名テンプレート?っていうみたい。
つまり、ちゃんと名前のついたテンプレートを{ }のかわりに使うこともできる!
group loop;
foo(val) ::= <<
$! boldという名前のテンプレートとして切り出し !$
$val:bold(); null="n/a", separator="\n"$
>>
bold(i, it) ::= "$i$: $it$"
なかなかいい感じ。