Skip to content
kik edited this page Sep 13, 2010 · 10 revisions

これは何?

C++用のPEGパーザライブラリの作りかけ

何がすごいの?

今までにない文法の記述方法!

  • PEGの文法規則から簡単に文法を記述できる!(予定)
  • セマンティックアクションが簡単に記述できる!
  • ASTを自由に作れる!
  • テンプレートを使って、ジェネリックなルールが書ける!

今までのパーザのよくある記述方法(Boost::Spirit)だと


definition(const calc& self)
{
  using namespace calc_action;
  expr = term >> *( ('+' >> term)[&ADD]
                    | ('-' >> term)[&SUB] );
  term = fctr >> *( ('*' >> fctr)[&MUL]
                    | ('/' >> fctr)[&DIV] );
  fctr = int_p[&PUSH]
    | '(' >> expr >> ')';
}

こんなだった。演算子オーバーロードを多用して変態的なライブラリに仕上がっている。
しかし、これでセマンティックアクションを書くのは至難の業だ。
ジェネリックなルールは関数をうまく使えば書けるのか?

他のパーザもみつけてきた(pegtl)


struct grammar
         : seq< alpha, until< eol, sor< alpha, digit > > > {};

こっちは、テンプレート引数でルールを記述するぜー。これは山かっこの対応をとるのが大変死ねる。
あと、セマンティックアクションの書き方はよく知らない。
こっちはテンプレートを使えばジェネリックなルールが書けそう。大変そうだけど
これは全然だめだ。実装が楽なように記法を定めているけども、正しくは実装できる範囲で書きやすい記述方法を採用しなければならない。

使い方

構文規則・セマンティックアクション・AST

構文規則

foo ::= bar baz

があったら
struct foo {
  void action(bar x, baz y) {
  }
};

と書くだけでよい。これにより、上記の構文規則が引数の型で自動的に認識される。
セマンティックアクションの結果はfoo構造体であり、アクションの実装はactionメソッドの中に書く。
もちろん、(非)終端記号barに対応する値は引数 x で、bazに対応する値は y である。

もちろん、xとyをそのままfooの中に保持コードを実装すれば、ASTの出来上がりである。

struct integer {
  void action(digit x, const rep<digit>& y);
};

これはdigitの1回以上の繰り返しがintegerであるという規則だ。ここで、digitは数字にマッチする終端記号、repは0回以上の繰り返しだ。

選択肢

普通の文法規則にはこんな感じで選択肢が含まれている。

foo ::= bar baz | qix

もちろん、これも引数リストに書くだけで表現できる。
struct foo {
  void action(bar x, baz y, sor orr, qix z) {
    if (orr.left) {
       // x and y have valid value.
    } else {
       // z has valid value.
    }
  }
};

or があったところに引数 sor を入れるだけだ。orr.left は左側にマッチしたときに true になる。

特殊形式

他にも、0回以上の繰り返しとか色々記法があるが次の表のように記述できる。

PEG cpppeg
e* rep<e> 0回以上の繰り返し
e+ replus<e> 1回以上の繰り返し
e? opt<e> 1回以下の繰り返し
他にもあるんだけど未実装

非終端記号

もちろん、自分で定義した非終端記号に対応したクラスもactionの引数にできるぞ。
サイズが大きなクラスになっちゃったらconst T&で受け取ってもよい。

パーザの実行

文法の定義が終わったら、パーザを実行するだけだ
開始記号sに対応するクラスがsならパーザのクラスはparser<s>だ

parser<s> p;
s result;
string src = "hogehoge";
bool ok = p.parse(result, src.begin(), src.end());

完全な例

sample-1
sample-2
sample-3

Clone this wiki locally