ん、これはかなり良さそうに見える。無事製品化にこぎ着けて欲しい。
ん、これはかなり良さそうに見える。無事製品化にこぎ着けて欲しい。
新しい扇風機を買いました。前の扇風機は2000年7月1日購入なので11年弱使ったということになります。前々から所々プラスチック部分が劣化して割れてきてしまっていたので、暑くなってきたのを期に買い換えることにしました。10年、あっという間ですね。
久しぶりにBisonとFlexを使ったのですが、いつの間にC++用機能が搭載されたんですか? 2008年くらい? BisonもFlexも両方ともクラスが出力できるようになっていてびっくりしました。Flexの出力がnamespaceでくくれなくて残念ですが、まあ、我慢しましょう。強引にやるなら.cppを#includeでしょうが……。基本的な使い方はFlex Bison C++ Template - sourceforge.jpのソースを見たらよく分かりました。FlexLexer::yylexが引数を取らない仕様なのを回避する方法がキモ(イ)ですね。
それで、BisonをC++で使うならば、当然意味値(semantic value)にstd::stringなんかを使いたくなるわけですが、%unionを使ってしまうとunionのメンバになってしまうのでPOD以外は使えません。上のFlex Bison C++ Templateではstd::string *とポインターにして使っていますが、これでは使いにくくてかないません。
%unionをやめてYYSTYPEを直接指定すれば良いわけですが、複数の型を共存させるためにはいくつか方法が考えられます。
前者はコードを書くにせよ使うにせよ面倒なので、やりは後者にしたくなります。ちなみに、Bisonが生成するC++用コードはスタックにdequeを使うので、これらの型をちゃんと持たせられますので安心して下さい。
それでYYSTYPEをboost::variantにしたとして、次の問題は意味動作(semantic action)から意味値を参照する方法です。
たとえばunionを使った以下のような書き方があったとします。
%union {
int int_val;
char *str_val;
}
%token <int_val> INTEGER
%token <str_val> ID
%type <int_val> expr
%type <int_val> prim
%%
expr : prim '+' prim { $$ = $1 + $2; }
prim : INTEGER { $$ = $1;}
| ID { $$ = get_int_variable($1);}
これをvariantにすると……
%code requires {
#include <string>
#include <boost/variant.hpp>
#define YYSTYPE boost::variant<int, std::string>
}
%token INTEGER
%token ID
%type expr
%type prim
%%
expr : prim '+' prim { $$ = boost::get<int>($1) + boost::get<int>($2); }
prim : INTEGER { $$ = boost::get<int>($1);}
| ID { $$ = get_int_variable(boost::get<std::string>($1));}
のようになって、boost::getを多用しなければならなくなります。なんとかしてこれを改善できないものでしょうか。%unionを使っていないので%tokenや%typeの型指定が使えないのが痛い。
あれ、本当に%tokenや%typeの型指定は使えないのでしょうか。Bisonの生成したコードを眺めてみると、%token <int_val>の指定は結局のところ$$や$1のところで yyval.int_val や yysemantic_stack_[(1) - (1)].int_val のように使われるだけのようです。ということは、%token <as_int()>とすれば yyval.as_int() と展開されるのです!
以下が最終的なコードです。意味動作のところがすっきりしました。
%code requires {
#include <string>
#include <boost/variant.hpp>
struct SemanticValue {
boost::variant<int, std::string> var;
//getter
int &as_int() { return get_var_as<int>();}
std::string &as_int() { return get_var_as<std::string>();}
template<typename U>
U &get_var_as()
{
if(U *p = boost::get<U>(&var)){
return *p;
}
else{
var = U(); //use default constructor
return boost::get<U>(var);
}
}
//setter(for lex)
SemanticValue &operator=(int v) { var = v; return *this;}
SemanticValue &operator=(const string_type &v) { var = v; return *this;}
};
#define YYSTYPE SemanticValue
}
%token <as_int()> INTEGER
%token <as_string()> ID
%type <as_int()> expr
%type <as_int()> prim
%%
expr : prim '+' prim { $$ = $1 + $2; }
prim : INTEGER { $$ = $1;}
| ID { $$ = get_int_variable($1);}
まず、boost::variant<int, std::string>を包み込んだSemanticValueクラスを定義し、指定された型の参照を返すメンバ関数as_int、as_stringを用意します。そして、%tokenや%typeのところで<as_int()>や<as_string()>のように指定します。これだけで意味動作のところで$$と書けばyyval.as_int()となり、variantのint部分にアクセスできるというわけです。
get_var_asの中でデフォルトコンストラクタを呼んでいます。この部分は $$ = $1 と書いたときに yyval.as_int() = yysemantic_stack_[(1) - (1)].as_int() のように展開されることになり、as_intがそのままboost::get<int>(var)だけだと、左辺についてvariantがstd::stringだったときにbad_get例外が飛ぶからです。初期化の無駄や右辺の型チェック上の問題がありますが、まあ、今回はこのくらいで許して下さい(直後に代入すると分かっているのに初期化するのは無駄だと言うことと、右辺は必ず期待した型になるはずなので型が違う場合例外を投げる方がより安全だと言うこと)。
意味値としてvariantを使うとメモリ使用量上無駄が生じます。variantは内部に型タグを持っているでしょうから、その分は本来解析状態を使えば省くことができるはずです。ただ、その量はスタックの最大深度に比例し、文法と解析対象に依りますが、多くの用途で問題になる量では無いでしょう。
variantの中にstd::listやstd::vector等のコンテナを入れることもできます。そうするとコピーのコストが高くなるので注意が必要でしょう。意味動作のところでswapやmoveを使うことである程度回避できますが、それでもBisonが生成するyysemantic_stack_.push (yylval)等のところでコピーが発生してしまいます。気になるならshared_ptrで包むくらいでしょうか。そもそもvariantではなくshared_ptr<void>を使うという手もあるのかもしれませんけど。
モスバーガーよりフレッシュネスバーガーの方が美味しい!と言われて違和感を感じる理由が分かった。
そうだ、トマト使いすぎなんだ。バーガーメニューの半数以上にトマトが入っている。それも分厚い毒々しい赤。メニューを見た瞬間選ぶ気が失せるんだよね。
それとコーヒー。昔はちゃんと陶器(?)のコップで出してくれたけど、今は紙コップなのね。種類は増えたみたいだけど、高い割に量は少なく味もそれほどでもなく、どうかと思うね。
そうそう、それと、ネギ味噌バーガーどこ行ったのよ。
それとそれと、調べていくうちに分かったけど、禁煙席が少ないのは意図的だったのね(Wikipediaの特徴のところ参照)。こりゃひどい。これだけで敬遠する理由として十分だ。うまいまずい以前の問題だ!(by 海原雄山)
うーん、今日は暑かった。現在22:50の室温は26~29度。窓を開けていると風が心地よい。USBRH Monitorの記録では日中30度を越えていたらしい。
色々と夏の支度をしないといけないな。
最近MLに興味があります。最新コンパイラ構成技法(Modern Compiler Implementation in MLの和訳本)を読んでるからというのもあるのですが、それとは別に型についてつらつらと考えていくとMLにあたることがままあったものですから。
キーボードの掃除をしました。
今期気になるのは
くらいかな。
プリティーリズムOPがなぜか絶対可憐チルドレンを連想するのば何でだろう。
ゴールデンウィークということで、山口県へ行ってきました。湯田温泉で三泊して、そのうち一日は秋吉台へ。湯田温泉通りから秋芳洞へは中国JRバス一本で40分。久しぶりに沢山歩きました。
結果としては……、足イテーよ、マゾいよ、どうしてこう無茶な徒歩旅行の計画立てるかな、って感じ。普段ろくに歩きもしないデスクワーカーのくせに。秋芳洞から大正洞まではなんとか歩けましたが景清洞は諦めました。時間は十分残っていたのですが足の方が限界だったので。ペース配分が良くなかったかも。もう少し休みながら歩けばよかった。長者ヶ森までは楽勝楽勝と思っていたのですが、そこから大正洞までの上り下りが思いの外効きました。
今回たどったルートは、カルスト展望台→若竹山→妙見原(道間違えて戻りすぎた)→放牧場入り口→道間違えて放牧場の中に入ってしまったっぽい→XperiaとYahooMapsを頼りに何とか道に復帰→看板・交差点→冠山→長者ヶ森駐車場→ドリーネ耕作手前で道を間違えたがXperiaのおかげですぐに気がつき引き返す→ドリーネ耕作→真名ヶ岳のすぐ西→大正洞→サファリランド前。
帰りはサファリランド前→大田中央→湯田温泉通りとバスを乗り継いでホテルへ。サファリランド前14:01発のバスをすんでの所で乗り逃し3時間待ち、大田中央の乗り換えでも1時間待ち。これだから地方のバスは……。
一応撮った写真はPicasaに。初めてジオタグを有効にして写真を撮りました。
帰りの列車の中でお土産のお菓子をほおばるのは、ある種の「なごり」ですね。