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
代替
记得处理错误情况和部分解析
简化和固定代码
#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>
我无法让 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
代替记得处理错误情况和部分解析
简化和固定代码
#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>