BisonとFlexをC++とboost::variantで使う

2011-05-18 : Bison 2.4.2 / Flex 2.5.35 / boost 1.46.1 / Cygwin / Visual Studio 2008使用

簡易電卓ソースコード直接閲覧: src/

ソースコード解説

作成したファイルはmain.cpplexer.hlexer.lparser_semantic_action.hparser.yの5つです。lexer.hlexer.lによってExampleFlexLexerクラスとexample::Lexerクラスの二つが作られます。parser_semantic_action.hではParserSemanticValueクラスとParserSemanticActionクラスを定義しています。parser.yによってexample::Parserクラスが作られます。main関数ではこれらを結びつけて実行します。

generated/ディレクトリ以下はBisonやFlexが生成したファイルです。上で説明したクラスの他に、example::position、example::location、example::stackといったクラスが生成されています。positionクラスはファイル名とその中の位置(行数、桁数)を保持します。locationはpositionを二つ持ち、トークンの範囲(開始位置と終了位置)を表します。stackはstd::dequeをラップしただけのクラステンプレートでgenerated/parser.hppの中で使われています。

BisonとFlexをC++で使う方法については、Flex Bison C++ Templateを参考にしました。C++用のオプションを有効にするだけで無く、位置情報(location、position)を追跡するためにlex側のアクションで更新したり、その位置情報や意味値(yylval)をLexerとParserとの間で受け渡すためにFlexLexerクラスを継承した別のLexerクラスを作らなければならなかったりします。面倒ですね。位置の追跡方法についてはlexer.lparser.yの中をlocで検索してみて下さい。位置情報と意味値の受け渡しについては、lexer.hlexer.lparser.yの中をyylvalやyyllocで検索してみて下さい。generated/の中のコードと合わせてみると理解が深まると思います。

意味値(semantic value)の型は、parser_semantic_action.h内のParserSemanticValueクラスです。このクラスは内部にboost::variant<i32_type, f64_type, str_type, expr_type, expr_list_type>を一つだけ持ちます。そして、メンバ関数as_i32()やas_string()でvariant内への参照を返すようにしておきます。parser.y内に「#define YYSTYPE example::ParserSemanticValue」と記述することで、この型が意味値として使われます。

そして、parser.y内、%tokenや%typeの型指定部分に関数呼び出しを書くことで、意味動作部の$$や$1を適切な型で扱えるようになります。このあたりの説明は日記:BisonでBoost.Variantを使うに書いておきました。

parser.yにはC言語のexpressionにあたる文法を一通り書きました。簡易電卓としては使わないものが多いですが、拡張する際の参考のために書きました。

parser.yの意味動作部では、ごちゃごちゃ書くのが嫌だったのでParserSemanticActionクラスのメンバ関数を呼び出すだけにしています。意味動作を変えたいときはparser.yを変更せずに、parser_semantic_action.hを差し替えるだけで良いようにしてみました。現在は整数の計算を直接実行しているだけです。構文木を作成したい場合はParserSemanticValue::expr_typeを構文木ノードの型に変更して、ParserSemanticActionの各メンバ関数で構文木を組みたてていくことになるでしょう。

ファイル一覧

クラス一覧


AKIYAMA Kouhei
Last modified: 2011-05-18 19:59:45+0900