Author Archives: misohena

2011-05-13

BisonでBoost.Variantを使う

久しぶりに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を直接指定すれば良いわけですが、複数の型を共存させるためにはいくつか方法が考えられます。

  • YYSTYPEをboost::shared_ptr<SemanticValueBase>にし、SemanticValueBaseを継承したclass SVInt, class SVDouble, class SVStringを作る。
  • YYSTYPEをboost::variant<int, double, std::string>にする。

前者はコードを書くにせよ使うにせよ面倒なので、やりは後者にしたくなります。ちなみに、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>を使うという手もあるのかもしれませんけど。

2011-05-10

モスバーガーとフレッシュネスバーガー

モスバーガーよりフレッシュネスバーガーの方が美味しい!と言われて違和感を感じる理由が分かった。

そうだ、トマト使いすぎなんだ。バーガーメニューの半数以上にトマトが入っている。それも分厚い毒々しい赤。メニューを見た瞬間選ぶ気が失せるんだよね。

それとコーヒー。昔はちゃんと陶器(?)のコップで出してくれたけど、今は紙コップなのね。種類は増えたみたいだけど、高い割に量は少なく味もそれほどでもなく、どうかと思うね。

そうそう、それと、ネギ味噌バーガーどこ行ったのよ。

それとそれと、調べていくうちに分かったけど、禁煙席が少ないのは意図的だったのね(Wikipediaの特徴のところ参照)。こりゃひどい。これだけで敬遠する理由として十分だ。うまいまずい以前の問題だ!(by 海原雄山)

2011-05-08

ML(Programming Language)

最近MLに興味があります。最新コンパイラ構成技法(Modern Compiler Implementation in MLの和訳本)を読んでるからというのもあるのですが、それとは別に型についてつらつらと考えていくとMLにあたることがままあったものですから。

2011-05-02

山口県 秋吉台

ゴールデンウィークということで、山口県へ行ってきました。湯田温泉で三泊して、そのうち一日は秋吉台へ。湯田温泉通りから秋芳洞へは中国JRバス一本で40分。久しぶりに沢山歩きました。

結果としては……、足イテーよ、マゾいよ、どうしてこう無茶な徒歩旅行の計画立てるかな、って感じ。普段ろくに歩きもしないデスクワーカーのくせに。秋芳洞から大正洞まではなんとか歩けましたが景清洞は諦めました。時間は十分残っていたのですが足の方が限界だったので。ペース配分が良くなかったかも。もう少し休みながら歩けばよかった。長者ヶ森までは楽勝楽勝と思っていたのですが、そこから大正洞までの上り下りが思いの外効きました。

今回たどったルートは、カルスト展望台→若竹山→妙見原(道間違えて戻りすぎた)→放牧場入り口→道間違えて放牧場の中に入ってしまったっぽい→XperiaとYahooMapsを頼りに何とか道に復帰→看板・交差点→冠山→長者ヶ森駐車場→ドリーネ耕作手前で道を間違えたがXperiaのおかげですぐに気がつき引き返す→ドリーネ耕作→真名ヶ岳のすぐ西→大正洞→サファリランド前。

帰りはサファリランド前→大田中央→湯田温泉通りとバスを乗り継いでホテルへ。サファリランド前14:01発のバスをすんでの所で乗り逃し3時間待ち、大田中央の乗り換えでも1時間待ち。これだから地方のバスは……。

一応撮った写真はPicasaに。初めてジオタグを有効にして写真を撮りました。

2011-04-24

kindleでオライリーサイズの本を読む

オライリーの本(B5より少し高さが小さいサイズ)がどの程度読めるか試してみた。

全体:

20110424_orelly_kindle_1_th.jpg

コードが書いてある部分:

20110424_orelly_kindle_2_th.jpg

脚注が書いてある部分:

20110424_orelly_kindle_3_th.jpg

(ScanSnap S1500でグレースケール300dpi取り込み、Acrobat Proでモバイル用最適化出力(96ppi、画質高)、ClearScanなし(ClearScanすると開くのにモーレツに時間がかかるページが出たので))

まあ、一応読める。ざっと見た感じ、つぶれたり霞んだりしている部分は推測で読める。100%読めるかどうかは読んだことがない本で試さないと分からないけど。

Kindle3だとこのサイズまでが限界だろう。額縁が少なくてより大きい表示領域を持つリーダーの一刻も早い登場が待たれる。

2011-04-24

すべての本はKindleで読みたい

Kindleを使っていると、すべての本はKindleで読みたくなる。なぜならば、紙の本はページをめくったり押さえたりするのが面倒だから。しおり挟むの面倒だから。重い本を何冊も鞄に入れたくないから。別にKindleでなくても別の電子書籍端末でも良いんだけど、とにかく紙の本を扱いたくなくなる。

でもそれを阻む最大の問題は、Kindle3の画面サイズだ。