boost::spirit 将可选项解析为适配 Fusion 结构。

3
如果有一个结构
struct price
{
   int chicken;
   int cow;
   bool in_stock;
   std::string place;
};

使用 boost::fusion 进行适配。 如果需要解析它,但是 in_stock 和 place 是可选的。例如:
template <typename it, typename skipper = qi::space_type>
struct p : qi::grammar<it, price(), skipper>
{
    p() : p::base_type(p_instance)
    {
        using namespace qi;
        psr %= int_ > int_ > -bool_ > -('"' > char_ % ',' > '"');
    }
  private:
      qi::rule<it,price(),skipper> limit;
};

然而,这种方法是行不通的。如果输入为"2 3 \"Chili\"",则会抛出异常。

有什么解决方法吗?

1个回答

7

首先

我想你可能使用了太多的>期望符号,这些应该改为>>序列运算符。

我建议您让语法更加明确,例如通过以下方式:

bool_ | attr(false)

为了确保如果bool_失败,将会暴露一个结果属性值为false。这样,暴露的属性就是bool,而不是boost::optional<bool>,这更符合目标结构(price)的兼容性。
演示:
template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, price(), Skipper>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;

        in_stock = bool_ | attr(false);
        start    = int_ > int_ > in_stock > -('"' >> char_ % ',' > '"');
    }

  private:
    qi::rule<It, price(), Skipper> start;
    qi::rule<It, bool()> in_stock;
};

其次

接下来,

('"' > char_ % ',' > '"')

将只匹配像"a""b,c,d"等字符串。如果您想要解析"Chili",您可能应该将其更改为类似以下内容:

    place    = '"' >> *~char_('"') > '"';
    start    = int_ > int_ > in_stock > (place | attr(""));

这意味着解析任何由'"'分隔的字符串 (~char_("abc) 表示:除 [abc] 以外的任何字符).

示例解决方案

下面是完整演示:

  • 结构的Fusion适应
  • 已调整的语法
  • 使用BOOST_SPIRIT_DEBUG进行调试
  • 捕获qi::expectation_failure以在解析失败时打印诊断信息
  • 使用Spirit Karma以人类可读的形式打印解析结果

测试程序输出如下:

代码

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi    = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx   = boost::phoenix;

struct price
{
    int chicken;
    int cow;
    bool in_stock;
    std::string place;
};

BOOST_FUSION_ADAPT_STRUCT(price, 
        (int, chicken)
        (int, cow)
        (bool, in_stock)
        (std::string, place))

template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, price(), Skipper>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;

        in_stock = bool_ | attr(false);
#if 0
        start    = int_ > int_ > in_stock > -('"' >> char_ % ',' > '"');
#else
        place    = '"' >> *~char_('"') > '"';
        start    = int_ > int_ > in_stock > (place | attr(""));
#endif

        BOOST_SPIRIT_DEBUG_NODE(start);
        BOOST_SPIRIT_DEBUG_NODE(place);
        BOOST_SPIRIT_DEBUG_NODE(in_stock);
    }

  private:
    qi::rule<It, price(), Skipper> start;
    //
    qi::rule<It, bool()>            in_stock;
    qi::rule<It, std::string()>     place; // no skipper
};

bool doParse(const std::string& input)
{
    typedef std::string::const_iterator It;
    auto f(begin(input)), l(end(input));

    parser<It, qi::space_type> p;
    price data;

    try
    {
        bool ok = qi::phrase_parse(f,l,p,qi::space,data);
        if (ok)   
        {
            std::cout << "parse success: '" << input << "'\n";
            std::cout << "data: " << karma::format_delimited(karma::eps <<
                    "chicken:"  << karma::int_  << 
                    "cow:"      << karma::int_  << 
                    "in_stock:" << karma::bool_ << 
                    "place:"    << karma::auto_,
                    " ", data) << "\n";
        }
        else      std::cerr << "parse failed: '" << std::string(f,l) << "'\n";

        if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
        return ok;
    } catch(const qi::expectation_failure<It>& e)
    {
        std::string frag(e.first, e.last);
        std::cerr << e.what() << "'" << frag << "'\n";
    }

    return false;
}

int main()
{
    doParse("1 2 true \"a,b,c,d,e,f\"");
    doParse("3 4 \"a,b,c,d,e,f\"");
    doParse("5 6");
    doParse("7 8 false");
}

输出

<start>
  <try>1 2 true "a,b,c,d,e,</try>
  <in_stock>
    <try>true "a,b,c,d,e,f"</try>
    <success> "a,b,c,d,e,f"</success>
    <attributes>[1]</attributes>
  </in_stock>
  <place>
    <try>"a,b,c,d,e,f"</try>
    <success></success>
    <attributes>[[a, ,, b, ,, c, ,, d, ,, e, ,, f]]</attributes>
  </place>
  <success></success>
  <attributes>[[1, 2, 1, [a, ,, b, ,, c, ,, d, ,, e, ,, f]]]</attributes>
</start>
parse success: '1 2 true "a,b,c,d,e,f"'
data:  chicken: 1 cow: 2 in_stock: true place: a,b,c,d,e,f 
<start>
  <try>3 4 "a,b,c,d,e,f"</try>
  <in_stock>
    <try>"a,b,c,d,e,f"</try>
    <success>"a,b,c,d,e,f"</success>
    <attributes>[0]</attributes>
  </in_stock>
  <place>
    <try>"a,b,c,d,e,f"</try>
    <success></success>
    <attributes>[[a, ,, b, ,, c, ,, d, ,, e, ,, f]]</attributes>
  </place>
  <success></success>
  <attributes>[[3, 4, 0, [a, ,, b, ,, c, ,, d, ,, e, ,, f]]]</attributes>
</start>
parse success: '3 4 "a,b,c,d,e,f"'
data:  chicken: 3 cow: 4 in_stock: false place: a,b,c,d,e,f 
<start>
  <try>5 6</try>
  <in_stock>
    <try></try>
    <success></success>
    <attributes>[0]</attributes>
  </in_stock>
  <place>
    <try></try>
    <fail/>
  </place>
  <success></success>
  <attributes>[[5, 6, 0, []]]</attributes>
</start>
parse success: '5 6'
data:  chicken: 5 cow: 6 in_stock: false place:  
<start>
  <try>7 8 false</try>
  <in_stock>
    <try>false</try>
    <success></success>
    <attributes>[0]</attributes>
  </in_stock>
  <place>
    <try></try>
    <fail/>
  </place>
  <success></success>
  <attributes>[[7, 8, 0, []]]</attributes>
</start>
parse success: '7 8 false'
data:  chicken: 7 cow: 8 in_stock: false place:  

你知道在Phoenix Ref中,通过设置规则和语法的起点之间有什么区别吗? - alexander.sivak
除了调试设施、错误处理设施和潜在的解析器上下文之外,没有什么主要区别。不过我是即兴想出这些的。请参见文档中关于非终端符号的部分中两个概念之间的差异。基本上,规则提供了比语法更丰富的接口,而语法旨在进行模块化。请参见此处有关规则表达式语义的更详细介绍。 - sehe
我对调试和异常很感兴趣。有一个未被捕获的异常(帖子#1),同时spirit调试器没有启用调试(语法没有定义默认处理程序)。 - alexander.sivak
你想告诉我什么?我认为我的示例代码已经涵盖了所有内容。如果你这次遇到了其他问题,可能需要开一个新的问题。 - sehe
@user2496553 我相信这是英语障碍,但我不明白。我的代码中没有其他异常。如果您有,请发布一个新的问题。别忘了提到抛出的异常类型。在那之前,祝你好运(我不知道“语法是语法的开始(失败)”是什么意思)。 - sehe
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接