Boost Spirit のセマンティックアクションは全か無か2020年05月22日 12時39分08秒

Boost Spirit は C++ で書かれたパーサ。C++ の様な構文で書ける。そして、目的のトークンが一致したときのデフォルトの動作は値の代入になる。その一致した時に行われる処理を明示的に指定し記述された動作が、セマンティクアクションと呼ばれている。代入の時に若干特殊な手助けが必要だったり、追加の処理を行いたい時に使われる。

言葉での説明は分かりづらいので少し簡単な例を用いて。

number = integer | real;
上の例では、数字は整数か小数のどちらかとして定義している。integer が一致したとき、または real が一致したときに、その値が number に代入される。

ここで、代入しないでデバッグの為にプリントするとしよう。

number = real[  std::cout << _1 << ];
これで、real に一致した場合は number に代入ではなく stdout に出力される。この括弧の中に明示的に指定した動作がセマンティックアクションだ。

さて、このセマンティックアクションだ、どうも右辺の一つに使った他の項にも影響が出るようだ。つまり、セマンティックアクションを使うのであれば全ての式に明示的に指定する必要がある。

上の数字の例で示す。integer の一致のみをプリントしてみたいと思い以下のようにする。

number = integer[  std::cout << _1 << ] | real;
どうも、real も integer のセマンティックアクションに引っ張られるようで、デフォルトアクションが呼ばれなくなるようだ。real にもセマンティックアクションを指定したら、期待通りの代入が起きるようになった。
number = integer[  std::cout << _1 << ] | real[ _val = _1 ];
もう一つの対応策は項を分けること。
print_integer = integer[  std::cout << _1 << ];
number = print_integer | real;

Boost Qi でセマンティックアクションを用いる場合は、しっかりと全てを明示するか項を分ける。デフォルトアクションで期待通りの動作だったら良いが、一部だけ変更するのは無理みたいだ。実はこの動作の確証は掴んでいない。しかしSpirit パーサはセマンティックアクションで何が起きているのかを追うのはとても難しい。セマンティックアクションを入れ始めて動作が変わってしまった時はここを点検。