具有合成和继承属性的深度递归qi语法(解析器)

时间:2014-05-09 08:30:43

标签: c++ boost-spirit-qi

我正在使用spirit :: qi语法构造并返回非平凡对象作为其合成属性。问题是我希望语法递归地相互依赖。使用递归规则是很简单的,但我想要递归的语法

这是一些示例代码。请注意有关CIRCULAR REFERENCE的注释。显然,如果我取消注释这些行,则无法编译,因为FooG​​rammar和BarGrammar对象相互包含。

我可以想出两种方法来达到预期的效果,但我似乎无法让它们与qi一起使用:

  1. 让BarGrammar保持一个指向FooGrammar的(智能)指针。它需要构造FooGrammar作为lit(“start_bar”)上的语义动作。但我不知道qi内部是否知道这是否安全 - 我是否需要维护我自己的FooGrammars显式堆栈?他们什么时候可以安全地被摧毁?

  2. BarGrammar可以将FooGrammar作为其qi :: locals之一。但由于某些原因,如果我尝试将qi :: locals中的语法传递为继承属性,我就无法编译它。

  3. 有没有任何规范的方法来制作递归语法?

    #include <boost/fusion/include/std_pair.hpp>
    #include <boost/optional.hpp>
    #include <boost/ptr_container/ptr_vector.hpp>
    #include <boost/shared_ptr.hpp>
    #include <boost/spirit/include/phoenix_bind.hpp>
    #include <boost/spirit/include/phoenix_core.hpp>
    #include <boost/spirit/include/phoenix_object.hpp>
    #include <boost/spirit/include/phoenix_operator.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/variant.hpp>
    namespace phoenix = boost::phoenix;
    namespace spirit = boost::spirit;
    namespace ascii = spirit::ascii;
    namespace qi = spirit::qi;
    using ascii::space_type;
    using phoenix::ref;
    using qi::grammar;
    using qi::lit;
    using qi::_r1;
    using qi::_val;
    using qi::lit;
    using qi::omit;
    using qi::rule;
    
    #include <iostream>
    #include <string>
    using std::cout;
    using std::endl;
    using std::string;
    
    typedef int Reffy;
    
    struct Foo {
      int foo;
    };
    
    struct Bar {
      int bar;
    };
    
    template <typename Iterator>
    struct BarGrammar;
    
    template <typename Iterator>
    struct FooGrammar : grammar<Iterator, Foo(Reffy&), space_type> {
      FooGrammar() : FooGrammar::base_type(foo_) {
        foo_ = lit("start_foo")[_val = phoenix::construct<Foo>()]
          > -bar_(_r1)
          > lit("end_foo");
        ;
      }
    
      BarGrammar<Iterator> bar_;
      rule<Iterator, Foo(Reffy&), space_type> foo_;
    };
    
    template <typename Iterator>
    struct BarGrammar : grammar<Iterator, Bar(Reffy&), space_type> {
      BarGrammar() : BarGrammar::base_type(bar_) {
        bar_ = lit("start_bar")[_val = phoenix::construct<Bar>()]
          //> -foo_(_r1)    // CIRCULAR REFERENCE
          > lit("end_bar");
      }
    
      //FooGrammar<Iterator> foo_;    // CIRCULAR REFERENCE
      rule<Iterator, Bar(Reffy&), space_type> bar_;
    };
    
    int main(int argc, char *argv[]) {
      Reffy reffy(0);
    
      FooGrammar<string::iterator> foog;
      rule<string::iterator, space_type> top_rule = omit[ foog(ref(reffy)) ];
    
      string input("start_foo start_bar end_bar end_foo");
      string::iterator begin = input.begin();
      string::iterator end = input.end();
      if(qi::phrase_parse(begin, end, top_rule, ascii::space)) {
        cout << "passed" << endl;
      } else {
        cout << "failed" << endl;
      }
    
      return 0;
    }
    

    提前致谢!!

1 个答案:

答案 0 :(得分:2)

在qi中实现SQL解析器时遇到了同样的问题。我有语句,字段,表格等单独的语法,但是字段和表格有时也可能是语句(子查询)。就我而言,我解决了这样的问题:

顶级语法(你总是开始解析的语法)拥有所有子语法的共享指针。顶级语法的构造函数将创建所有子语法的实例,并将它们的实例作为弱指针传递给它们。这样,每个语法只创建一个实例。由于shared_ptr,没有圆形weak_ptr。基本上,顶级语法拥有所有子语法,只是将指针传递给彼此。

因此,在您的情况下,我会创建一个TopGrammar类,让它拥有FooGrammarBarGrammar。一旦它们被构造,将它们指向彼此。在TopGrammar的启动规则中,只需调用FooGrammar

由于TopGrammar拥有所有子文件,因此您确信它们的生命周期已足够。