-
Notifications
You must be signed in to change notification settings - Fork 0
Usage
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 > > > {};
こっちは、テンプレート引数でルールを記述するぜー。これは山かっこの対応をとるのが大変死ねる。
あと、セマンティックアクションの書き方はよく知らない。
こっちはテンプレートを使えばジェネリックなルールが書けそう。大変そうだけど
これは全然だめだ。実装が楽なように記法を定めているけども、正しくは実装できる範囲で書きやすい記述方法を採用しなければならない。
構文規則
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());