【问题标题】:Flipping the order of subrules inside a rule in a boost::spirit grammar results in segfault在 boost::spirit 语法中翻转规则内子规则的顺序会导致段错误
【发布时间】:2013-09-04 11:00:19
【问题描述】:

警告;虽然我试图将代码缩短到最低限度。我仍然必须包含很多内容,以确保提供所需的信息。

此代码编译文件并运行导致语法错误;

name = simple_name      [ qi::_val = qi::_1 ]
     | qualified_name   [ qi::_val = qi::_1 ]
     ;

此时;

name = qualified_name   [ qi::_val = qi::_1 ]
     | simple_name      [ qi::_val = qi::_1 ]
     ;

导致SIGSEGV,分段错误;

boost::detail::function::function_obj_invoker4<boost::spirit::qi::detail::parser_binder<boost::spirit::qi::alternative<boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::reference<boost::spirit::qi::rule<boost::spirit::lex::lexertl::iterator<boost::spirit::lex::lexertl::functor<boost::spirit::lex::lexertl::token<__gnu_cxx::__normal_iterator<char*, std::string>, boost::mpl::vector<std::string, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, mpl_::bool_<false>, unsigned long>, boost::spirit::lex::lexertl::detail::data, __gnu_cxx::__normal_iterator<char*, std::string>, mpl_::bool_<true>, mpl_::bool_<false> > >, Ast::name* (), boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> const>, boost::phoenix::actor<boost::proto::exprns_::expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::attribute<0> >,0l>,boost::phoenix::actor<boost::spirit::argument<0> > >, 2l> > >,boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::reference<boost::spirit::qi::rule<boost::spirit::lex::lexertl::iterator<boost::spirit::lex::lexertl::functor<boost::spirit::lex::lexertl::token<__gnu_cxx::__normal_iterator<char*, std::string>,boost::mpl::vector<std::string, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, mpl_::bool_<false>,unsigned long>, boost::spirit::lex::lexertl::detail::data, __gnu_cxx::__normal_iterator<char*,std::string>, mpl_::bool_<true>, mpl_::bool_<false> > >, Ast::name* (), ... more to come ...

在哪里;

simple_name = (tok.identifier) [ qi::_val = build_simple_name_(qi::_1) ];

和;

qualified_name = (name >> qi::raw_token(DOT) >> tok.identifier) [ qi::_val = build_qualified_name_(qi::_1, qi::_2) ] ;

所有这些规则,返回一个Ast::name*()

qi::rule<Iterator, Ast::name*()> name;
qi::rule<Iterator, Ast::name*()> simple_name;
qi::rule<Iterator, Ast::name*()> qualified_name;

辅助函数定义为;

Ast::name* build_simple_name(std::string str)
{
    return (new Ast::name_simple(Ast::identifier(str)));
}
BOOST_PHOENIX_ADAPT_FUNCTION(Ast::name*, build_simple_name_, build_simple_name, 1)

和;

Ast::name* build_qualified_name(Ast::name* name, std::string str)
{
    std::list<Ast::identifier> qualified_name = Ast::name_to_identifier_list(name);
    qualified_name.push_back(Ast::identifier(str));

    return (new Ast::name_qualified(qualified_name));
}
BOOST_PHOENIX_ADAPT_FUNCTION(Ast::name*, build_qualified_name_, build_qualified_name, 2)

使用的词法分析器定义定义为;

lex::token_def<std::string> identifier = "{JAVA_LETTER}{JAVA_LETTER_OR_DIGIT}*";

和;

('.', DOT)

模式{JAVA_LETTER}{JAVA_LETTER_OR_DIGIT} 定义为;

("DIGIT",           "[0-9]")
("LATIN1_LETTER",   "[A-Z]|[a-z]")
("JAVA_LETTER",     "{LATIN1_LETTER}|$|_")
("JAVA_LETTER_OR_DIGIT", "{JAVA_LETTER}|{DIGIT}")

我的输入,是一个简单的字符串;

package a.D;

哪些词法用于标记;

Keywords : package
Identifier : a
Delimiters : .
Identifier : D
Delimiters : ;

第一个例子(simple_name 在前),抛出语法错误为;

Syntax Error at line 1:
package a.D;
          ^^

最后一个例子只是抛出了一个段错误,错误是之前发布的。

显然第二个例子是我想要的,因为它应该在简单的表达式之前尝试匹配复杂的表达式。

有谁知道代码崩溃的原因,或者我将如何解决? - 这也应该在代码审查中?

【问题讨论】:

  • 你已经离开了递归,name依赖于qualified_name,它依赖于name等等。
  • 这是违法的吗? - 另外如果是这样的话,我怎样才能得到同样的效果,那我该如何重构呢?

标签: boost segmentation-fault boost-spirit boost-spirit-qi boost-spirit-lex


【解决方案1】:

问题是你有一个left recursive grammar 并且不能与 Boost.Spirit 一起使用。 你所拥有的基本上是:

name = identifier | name >> dot >> identifier;

如您所见here,以便在您遇到以下情况时删除左递归:

A = A >> alpha | beta;

您需要创建 2 个新的“规则”:

A = beta >> A_tail;
A_tail = eps | alpha >> A_tail;

在你的情况下:

A := name
alpha := dot >> identifier
beta := identifier

所以你的“规则”是:

name = identifier >> name_tail;
name_tail = eps | dot >> identifier >> A_tail;

如果您仔细查看name_tail,您会发现它的字面意思是:无或dot &gt;&gt; identifier 后跟无或dot &gt;&gt; identifier 等等。这意味着name_tail 是:

name_tail = *(dot >> identifier);

所以最后你的name 规则是:

name = identifier >> *(dot >> identifier);

所有这些都是正确的,但很有可能它不适用于您的属性。

【讨论】:

  • 嗯,我实际上有一个问题,因为似乎有一种确定性的方法可以删除左递归,是否有特定原因 boost::spirit 不“简单地”预处理语法,为了消除它。 - 它是不可能的、不可行的还是仅仅是一个没有人实现的功能?
  • 不知道,我实际上已经尝试过实现这一点,但没有成功。我认为这将是一件非常酷的事情。您是否能够更改您的语义操作以适应这一点,或者我应该尝试对此做些什么?
  • @Skeen 不能,因为您可以拥有违反假定的关联性法则的自定义解析器(更不用说语义操作)。当然,“他们”可以允许使用特征,但让人类进行思考要容易得多:/
  • 一般来说,我认为灵气中没有任何表达式转换(可能除了隐含的char_类的减法)
  • @Skeen 他的意思是后者,比如here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 2011-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多