Boost Spirit:无法让替代操作员按我预期的方式工作

Boost Spirit : can't manage to have the alternative operator working as I expect

我无法让 spirit alternative parser 工作(或者说做我期望的)。

这是我的 MCVE

#include <string>
#include <iostream>

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

using namespace boost::spirit;
namespace charset = boost::spirit::qi::standard_wide;

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

int main(int argc, char ** argv)
{
    std::string intToParse = "{int:  45}";
    std::string stringToParse = "{string:  \"foo\"}";

    using It = std::string::const_iterator;
    using Sk = qi::space_type;
    qi::rule<It, std::string(), Sk> parseString;
    qi::rule<It, int(), Sk> parseInt;
    qi::rule<It, std::string(),Sk> string_;
    
    qi::rule<It,Expr(),Sk > orParse;


    string_ =
            qi::lit('"')
            > *(charset::char_ - '"')
            > '"';
    
    parseString = qi::omit[  qi::string("string")] >  qi::lit(':') > string_;

    parseInt = qi::omit[ qi::string("int")] > qi::lit(':') > qi::uint_ ;

    orParse =  qi::omit[qi::lit('{')] >  parseString | parseInt >  '}';
    
    orParse.name_ =  "alternative parser";
    parseString.name_ = "string parser";
    parseInt.name_= "int parser";

    qi::debug(orParse);
    qi::debug(parseString);
    qi::debug(parseInt);

    Expr res2;
    qi::phrase_parse(stringToParse.cbegin(), stringToParse.cend(), orParse, qi::space, res2);
    std::cout << res2<< std::endl;


    Expr res;
    qi::phrase_parse(intToParse.cbegin(), intToParse.cend(), orParse, qi::space, res);
    std::cout << res << std::endl;
  
    return 0;
}

当第一个选择失败时,解析停止,不尝试第二个规则!我做错了什么?

这是显示我的问题的输出,对于 stringToParse,第一个替代方案失败了,但我希望执行 int 解析器,但事实并非如此!

<alternative parser>
  <try>{string:  "foo"}</try>
  <string parser>
    <try>string:  "foo"}</try>
    <success>}</success>
    <attributes>[[f, o, o]]</attributes>
  </string parser>
  <success>}</success>
  <attributes>[[f, o, o]]</attributes>
</alternative parser>
foo
<alternative parser>
  <try>{int:  45}</try>
  <string parser>
    <try>int:  45}</try>
    <fail/>
  </string parser>
  <fail/>
</alternative parser>
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::spirit::qi::expectation_failure<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > >'
  what():  boost::spirit::qi::expectation_failure
Aborted (core dumped)

operator >C++ Operator Precedence table 中比 operator | 更高,因此需要括号:

orParse =  qi::omit[qi::lit('{')] >  (parseString | parseInt) >  '}';
//                              here ^             and here ^

结果:

<alternative parser>
  <try>{string:  "foo"}</try>
  <string parser>
    <try>string:  "foo"}</try>
    <success>}</success>
    <attributes>[[f, o, o]]</attributes>
  </string parser>
  <success></success>
  <attributes>[[f, o, o]]</attributes>
</alternative parser>
foo
<alternative parser>
  <try>{int:  45}</try>
  <string parser>
    <try>int:  45}</try>
    <fail/>
  </string parser>
  <int parser>
    <try>int:  45}</try>
    <success>}</success>
    <attributes>[45]</attributes>
  </int parser>
  <success></success>
  <attributes>[45]</attributes>
</alternative parser>
45

所以,已经指出了优先级问题。请注意,您可能已经通过使用编译器的警告发现了这一点:

test.cpp|25 col 23| warning: suggest parentheses around comparison in operand of ‘|’ []8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wparentheses-Wparentheses]8;;]

还有许多其他问题。让我从有问题的开始:

在需要的地方使用词素

要么不使用船长,要么在船长不能动作的地方包裹 qi::lexeme[]。具体来说,您的 string_ 规则可能不应该跳过空格。

See also Boost spirit skipper issues

其他问题

  • 字符集用法略有不一致。这可能是也可能不是什么大问题。如果不是,我通常会使用默认值 (qiL::char_, lit, string)。

  • 为什么要手动处理调试规则的内部结构?

     BOOST_SPIRIT_DEBUG_NODES((orParse)(parseString)(parseInt))
    
  • 为什么在不公开属性的指令上省略属性?

  • 为什么要使用 qi::string 无论如何要省略该属性?使用 lit 代替

  • 记得处理错误情况和部分解析

简化和固定代码

Live On Coliru

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

namespace qi = boost::spirit::qi;
namespace charset = boost::spirit::qi::standard_wide;

using Expr = boost::variant<std::string, int >;
using It = std::string::const_iterator;
using Sk = charset::space_type;

int main()
{
    // lexeme!
    qi::rule<It, std::string()> string_ 
        = '"' > *~charset::char_('"') > '"';

    // with skipper
    Sk skip;
    qi::rule<It, Expr(),        Sk> orParse;
    qi::rule<It, std::string(), Sk> parseString;
    qi::rule<It, int(),         Sk> parseInt;

    parseString = qi::lit("string") > ':' > string_;
    parseInt    = qi::lit("int")    > ':' > qi::uint_;
    orParse     = '{' > (parseString | parseInt) > '}';
    
    BOOST_SPIRIT_DEBUG_NODES((orParse)(parseString)(parseInt))

    for (std::string const input: { "{int:  45}", "{string:  \"foo\"}" }) {
        Expr res;
        It f = input.begin(), l = input.end();

        if (phrase_parse(f, l, orParse, skip, res)) {
            std::cout << "Parsed: " << res << std::endl;
        } else {
            std::cout << "Failed" << std::endl;
        }
        if (f != l) {
            std::cout << "Remaining: " << std::string(f,l) << "\n";
        }
    }
}

版画

Parsed: 45
Parsed: foo

如果启用调试:

<orParse>
  <try>{int:  45}</try>
  <parseString>
    <try>int:  45}</try>
    <fail/>
  </parseString>
  <parseInt>
    <try>int:  45}</try>
    <success>}</success>
    <attributes>[45]</attributes>
  </parseInt>
  <success></success>
  <attributes>[45]</attributes>
</orParse>
<orParse>
  <try>{string:  "foo"}</try>
  <parseString>
    <try>string:  "foo"}</try>
    <success>}</success>
    <attributes>[[f, o, o]]</attributes>
  </parseString>
  <success></success>
  <attributes>[[f, o, o]]</attributes>
</orParse>