Boost::Spirit::Qi autorules — avoiding repeated copying of AST data structures

社会主义新天地 提交于 2019-12-23 14:35:12

问题


I've been using Qi and Karma to do some processing on several small languages. Most of the grammars are pretty small (20-40 rules). I've been able to use autorules almost exclusively, so my parse trees consist entirely of variants, structs, and std::vectors.

This setup works great for the common case:
1) parse something (Qi),
2) make minor manipulations to the parse tree (visitor), and
3) output something (Karma).

However, I'm concerned about what will happen if I want to make complex structural changes to a syntax tree, like moving big subtrees around. Consider the following toy example:

A grammar for s-expr-style logical expressions that uses autorules...

// 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 >> ")";

... which leads to parse tree representation that looks like this...

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

Suppose I have a parse tree that looks something like this:

       pand
      / ... \
   por      por
   / \      / \
 var var   var var

(The ellipsis means 'many more children of similar shape for pand.')

Now, suppose that I want negate each of the por nodes, so that the end result is:

       pand
      / ... \
   pnot     pnot
    |        |
   por      por
   / \      / \
 var var   var var

The direct approach would be, for each por subtree:
- create pnot node
(copies por in construction);
- re-assign the appropriate vector slot in the pand node
(copies pnot node and its por subtree).

Alternatively, I could construct a separate vector, and then replace (swap) the pand vector wholesale, eliminating a second round of copying.

All of this seems cumbersome compared to a pointer-based tree representation, which would allow for the pnot nodes to be inserted without any copying of existing nodes. My question:

Is there a way to avoid copy-heavy tree manipulations with autorule-compliant data structures? Should I bite the bullet and just use non-autorules to build a pointer-based AST (e.g., http://boost-spirit.com/home/2010/03/11/s-expressions-and-variants/)?


回答1:


Copying the subtrees shouldn't be as expensive as you assume as the recursive_variant is essentially a wrapper around a shared_ptr. I believe, it's not only the best, but also the easiest solution.



来源:https://stackoverflow.com/questions/4643356/boostspiritqi-autorules-avoiding-repeated-copying-of-ast-data-structures

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!