Qi と Karma を使用して、いくつかの小さな言語で処理を行ってきました。ほとんどの文法はかなり小さい (20 ~ 40 のルール)。私はオートルールをほぼ排他的に使用することができたので、解析ツリーは完全にバリアント、構造体、および std::vector で構成されています。
このセットアップは、一般的なケースでうまく機能します:
1) 何かを解析する (Qi)、
2) 解析ツリーに小さな操作を行う (ビジター)、
3) 何かを出力する (カルマ)。
ただし、大きなサブツリーを移動するなど、構文ツリーに複雑な構造変更を加えたい場合はどうなるか心配です。次のおもちゃの例を考えてみましょう。
自動規則を使用する s-expr スタイルの論理式の文法...
// Inside grammar class; rule names match struct names...
pexpr %= pand | por | var | bconst;
pand %= lit("(and ") >> (pexpr % lit(" ")) >> ")";
por %= lit("(or ") >> (pexpr % lit(" ")) >> ")";
pnot %= lit("(not ") >> pexpr >> ")";
...これは、次のような解析ツリー表現につながります...
struct var {
std::string name;
};
struct bconst {
bool val;
};
struct pand;
struct por;
struct pnot;
typedef boost::variant<bconst,
var,
boost::recursive_wrapper<pand>,
boost::recursive_wrapper<por>,
boost::recursive_wrapper<pnot> > pexpr;
struct pand {
std::vector<pexpr> operands;
};
struct por {
std::vector<pexpr> operands;
};
struct pnot {
pexpr victim;
};
// Many Fusion Macros here
次のような解析ツリーがあるとします。
pand
/ ... \
por por
/ \ / \
var var var var
(省略記号は、「 の同様の形状の子がさらに多く」を意味しpand
ます。)
por
ここで、最終結果が次のようになるように、各ノードを無効にしたいとします。
pand
/ ... \
pnot pnot
| |
por por
/ \ / \
var var var var
por
直接的なアプローチは、サブツリーごとに次のようになります。
-pnot
ノードを作成します
(作成中のコピーpor
)。-ノード
内の適切なベクトル スロットを再割り当てします (ノードとそのサブツリー を
コピーします)。pand
pnot
por
別の方法として、別のベクターを構築し、そのpand
ベクターを大規模に置換 (スワップ) して、2 回目のコピーをなくすこともできます。
pnot
これはすべて、既存のノードをコピーせずにノードを挿入できるポインターベースのツリー表現と比べると面倒に思えます。私の質問:
自動規則に準拠したデータ構造を使用して、コピーの多いツリー操作を回避する方法はありますか? 弾丸をかじって、自動規則以外を使用してポインターベースの AST を構築する必要がありますか (たとえば、http://boost-spirit.com/home/2010/03/11/s-expressions-and-variants/ )?