【问题标题】:boost::spirit qi parsing runtime errorboost::spirit qi 解析运行时错误
【发布时间】:2016-04-15 09:24:11
【问题描述】:

为什么在使用该语法解析字符串时出现运行时错误?

template <typename Iterator, typename Skipper>
struct grammar : qi::grammar<Iterator, QVariant(), Skipper>
{
  grammar() : grammar::base_type(object)
  {
    identifier = qi::raw[qi::lexeme[qi::alpha >> *(qi::alnum | '_' | ('-' >> qi::alnum))]];

    self = (qi::raw[qi::lexeme["self"]]);
    object = (self >> '.' >> identifier)
            |(object >> '.' >> identifier); // there is no runtime error without that line
  }
}

任何其他语法运行良好,但我想解析类似的东西:

self.foo.bar2.baz

运行时错误抛出

     qi::phrase_parse(it, str.end(), g, ascii::space, v) && it == str.end())

打电话。

【问题讨论】:

  • 规则声明的类型是什么?换句话说,你能让样本正确吗(SSCCE/MVCE)

标签: c++ boost boost-spirit boost-spirit-qi


【解决方案1】:

在我看来,object 规则作为起点,必须声明为

qi::rule<It, QVariant(), Skipper> object;

虽然我不知道 QVariant 是什么,但我知道这一点:

要使属性传播起作用,您需要使用内置的 Qi 转换启发法来实现属性类型兼容性。

对于第一个分支 (self&gt;&gt;'.'&gt;&gt;identifier),这个 /could/ 足够简单。假设identifier 合成了一个字符串兼容的属性(例如std::stringstd::vector&lt;char&gt;),那么生成的属性可以合法地分配为一个字符串。

样本

作为一个简单的例子,看看这个(我“模仿”类似于QVariant 的东西):

Live On Coliru

#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

using QVariant = boost::variant<std::string, int>;

template <typename Iterator, typename Skipper>
struct grammar : qi::grammar<Iterator, QVariant(), Skipper>
{
    grammar() : grammar::base_type(object)
    {
        identifier = qi::raw[qi::lexeme[qi::alpha >> *(qi::alnum | '_' | ('-' >> qi::alnum))]];

        self   = (qi::raw[qi::lexeme["self"]]);
        object = 
             qi::as_string [self >> '.' >> identifier]
            //|qi::as_string [object >> '.' >> identifier] // there is no runtime error without that line
            ;
    }
  private:
    qi::rule<Iterator, QVariant(), Skipper> object;
    qi::rule<Iterator, std::string(), Skipper> identifier;
    qi::rule<Iterator, std::string(), Skipper> self;
};

int main() {
    using It = std::string::const_iterator;
    std::string input = "self.foo.bar2.baz";

    It f = input.begin(), l = input.end();
    QVariant parsed;
    bool ok = qi::phrase_parse(f, l, grammar<It, qi::space_type>{}, qi::space, parsed);

    if (ok)
        std::cout << "Parsed: " << parsed << "\n";
    else
        std::cout << "Parse failed\n";

    if (f!=l)
        std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}

印刷:

Parsed: selffoo
Remaining unparsed: '.bar2.baz'

问题

第二个分支

qi::as_string [object >> '.' >> identifier]

必须综合到tuple&lt;QVariant, std::string&gt; 才能与其余的声明保持一致。 Spirit 无法自动改变它。启发式系统可能会开始抓住稻草,并尝试将绑定属性(请记住,这是神秘的 QVariant)视为一个容器。如果它在这方面成功¹,事情就会编译。显然,在运行时事情会崩溃,因为为 QVariant 的实际运行时值调用了不正确的接口。

这就是理论。

解决方案?

查看工作演示,注意'.' 被排除在外。这使我怀疑您实际上不想要任何复杂的对象取消引用的链式“列表”,而是可能只想将整个匹配的输入 视为原始字符串?在这种情况下,最简单的解决方案是将raw[] 提升一个级别,并且可能使用字符串而不是QVariant


¹ 例如因为 QVariant 接口有点草率/不安全,并且直接在变体接口上公开 .begin/.end/value_type/insert 成员?

【讨论】:

  • 感谢您的帮助。无论如何,我想,promlem 是其他任何地方,解析问题。它使用该规则运行时没有运行时错误:标识符 >> '.' >> 对象(按倒序排列)。此外,它使用前缀减规则 (exrp = '-' >> expr) 运行,但在这样的规则中存在运行时错误:expr = expr >> '-'。也许,解析器类型有一个技巧,不支持这种语法
  • 您从未告诉过使用您想要实现的目标。如果你重新开始(新问题)只是说你想得到什么(例如“我有System.Console.WriteLine,我想得到一个std::vector&lt;std::string&gt; { "System", "Console", "WriteLine" }”(但不同,因为这可能太简单了) ) 那么我们可以通过展示您如何使用 Spirit 来实现它来帮助您。见perlmonks.org/?node_id=542341
【解决方案2】:

像“A = (A >> a ) | b”这样的左递归在像 boost::spirit 这样的 LL 解析器中是不可用的。 它们应该转换为对 LL 友好的形式: A = bR R = aR | e 其中 R - 新的非终端和 e - epsilon(空终端)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多