c++な構文解析機超訳

超訳なので、間違いがあるかもしれませんの。

10.1 C++パーサー

10.1.1 C++ Bisonインターフェイス

LALRパーサーを c++ で作るときは スケルトンディレクティブ '%skeleton "lalr1.c"’ を使ってくれ。コマンドラインオプション --skeleton=lalr1.c でもおk。

実行したら bison は、各々のエントリを yy 名前空間上に作るぜ。'%define namespace' ディレクティブを使ったら変更できる。

いくつかのクラスも作成される。

position.hh
location.hh

位置トラッキング用のポジションとロケーションの定義ファイル。C++ロケーション値のとこもみてくれや。

stack.hh

パーサーが使うスタックの補助クラス。

file.hh
file.cc

(入力ファイルの拡張子が .yy のときな) C++パーサークラスの、宣言と定義。2個のファイルのベース名と拡張子はCパーサーとおんなじルールで作成される。
ヘッダが必要なときは -d/--defines オプション指定するか、%defines ディレクティブな。

こいつらは Doxygen でドキュメント化できるし、ちゃんとしたドキュメントほしかったら doxygen 実行しる。

10.1.2 C++で定義されてる値

%union ディレクティブはCでちゃんと動くように作ってある。具体的には、ちゃんとした union を生成する。C++だといくつかの特性がある。

  • YYSTYPEが定義されてるけど、それはお勧めできない。パーサーにカプセルされた yy::parser::semantic_type を使うようにしてくれ。
  • POD (Plain Old Data) じゃないと使われへん。C++は union の中でコンストラクタを持ったクラスの実体化を禁止しとる。ポインタやったらいけるで。

オブジェクトをポインタでおいといたら、メモリは自動的に再生されへんで:%destructor ディレクティブを使ってもそれはメモリリークを避けるためだけやしね。

10.1.3 C++ロケーション値

%locations ディレクティブを使ったら C++ パーサーにロケーショントラッキング機能が追加されるで。詳しいのはロケーション概要を見といてくれ。2個の補助クラス position (ファイル中の位置を示す)と、location(ポジションのペアから成るレンジ)が作成される。(いくつかのファイルにまたがる可能性がある)

10.1.4 C++パーサーインターフェイス

出力される output.hh と output.cc の、名前空間 yy 内にパーサークラスの宣言と定義がある。クラス名のデフォルトは parser やけど、%define parser_class_name "name" で変えられる。クラスのインターフェイスの詳細は後で書くけど、%parse-param を使うと拡張できる。こいつの動作は、パーサークラスにメンバーを追加して、コンストラクタに引数を追加するちょっとの処理。

  • 型 parser: semantic_type
  • 型 parser: location_type

意味値と位置値。

  • 関数 parser: parser (type1 arg1, ...)

新しいパーサーオブジェクトを作成する。引数なしがデフォルト、%parser-param {type1 arg1} が使われてたらこうなる。

  • 関数 parser: int parse ()

構文解析の実行、0がかえってきたら成功で、1だったらその他。

  • 関数 parser: std::ostream& debug_stream ()
  • 関数 parser: void set_debug_stream (std::ostream& o)

パース追跡時に使われるストリームの設定/取得。デフォルトは std::cerr。

  • 関数 parser: debug_level_type debug_level ()
  • 関数 parser: void set_debug_level (debug_level l)

追跡レベルの設定/取得。0だと追跡なし、0じゃなかったら、フルに追跡じゃ。

  • 関数 parser: void error (const location_type& l, const std::string& m)

このメンバ関数はユーザー側で作らなあかん。パーサーはエラーをどこで起きたかを l、内容を m として呼び出す。

10.1.5 C++スキャナインターフェイス

パーサーはスキャナを yylex として呼び出す。Cパーサーに対してC++パーサーは %define api.pure ディレクティブを使ってなくても常にピュアだで。
インターフェイスは次のようになってる。

  • 関数 parser: int yylex (semantic_type& yylval, location_type& yylloc, type1 arg1, ...)

戻り値は次のトークンを返す。意味値と位置値が yylval と yylloc。%lex-param {type1 arg1} が定義されていれば、追加の引数もね。