【问题标题】:Deeply-recursive qi grammars (parsers) with synthesized and inherited attributes具有综合和继承属性的深度递归 qi 语法(解析器)
【发布时间】:2014-05-09 08:30:43
【问题描述】:

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

这是一些示例代码。注意说 CIRCULAR REFERENCE 的 cmets。显然,如果我取消注释这些行,这将无法编译,因为 FooGrammar 和 BarGrammar 对象相互包含。

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

  1. 使 BarGrammar 持有指向 FooGrammar 的(智能)指针。它需要将 FooGrammar 构建为 lit("start_bar") 上的语义操作。但是我不知道 qi 内部结构是否安全——我需要维护自己的显式 FooGrammars 堆栈吗?什么时候可以安全销毁?

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

是否有任何规范的方法来制作递归语法?

.

#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;
}

提前致谢!!

【问题讨论】:

    标签: c++ boost-spirit-qi


    【解决方案1】:

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

    顶级文法(您总是开始解析的文法)持有指向所有子文法的共享指针。顶层文法的构造函数会创建所有子文法的实例,并将它们的实例作为弱指针传递。这样,每个语法只创建一个实例。感谢weak_ptr,没有通知shared_ptr。所以基本上,顶级文法拥有所有子文法,并且只是相互传递指针。

    所以在你的情况下,我会创建一个TopGrammar 类,拥有它自己的FooGrammarBarGrammar。一旦它们被构造出来,就将它们相互传递给对方。在TopGrammar 的启动规则中只需调用FooGrammar

    由于TopGrammar 拥有所有子语法,您可以确定它们的寿命是足够的。

    【讨论】:

    • 太棒了!我只需要意识到我只需要任何 grammar 的单个实例,以及我想要的任何所有权语义。语法只是告诉精神它如何在运行时动态创建嵌套的规则。我刚刚让 BarGrammar 在其构造函数中提供了对 FooGrammar 的引用。然后在顶层我创建一个 FooGrammar,然后是一个 BarGrammar(FooGrammar&),并以我想要的方式使用它们。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 2012-02-08
    • 1970-01-01
    • 2021-12-09
    • 2018-04-14
    • 1970-01-01
    相关资源
    最近更新 更多