boost::spirit::qi 规则减少解析错误
boost::spirit::qi rule reduce parsing error
我尝试使用 boost::spirit::qi
解析 integer
和 double
对的列表。 integer
在此列表之前和之后。此列表的示例是:
20 1 1.3 2 2.3 30
我要创建的结构是:
typedef std::vector< std::pair<int, double> > vec_t;
struct list_s{
int first;
std::vector< std::pair<int, double> > list;
int last;
};
我还使用 qi::on_error
向我的解析器添加了错误处理并将所有序列 >>
转换为预期的 >
.
我的实现是:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
typedef std::vector< std::pair<int, double> > vec_t;
struct list_s{
int first;
vec_t list;
int last;
};
BOOST_FUSION_ADAPT_STRUCT(
list_s,
(int, first)
(vec_t, list)
(int, last)
)
template <typename Iterator>
struct list_parser : qi::grammar<Iterator, list_s(), ascii::space_type >
{
public:
list_parser() : list_parser::base_type(r) {
r = qi::eps >>
(
qi::int_
> *( qi::int_ > qi::double_ )
> qi::int_
);
r.name("r");
qi::on_error<qi::fail>
(
r
, std::cout
<< phoenix::val("\nError! Expecting ")
<< qi::labels::_4
<< phoenix::val(" here: \"")
<< phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2)
<< phoenix::val("\"\n\n")
);
}
private:
qi::rule<Iterator, list_s(), ascii::space_type > r;
};
int main() {
std::string str("20 1 1.3 2 2.3 30");
list_s r;
list_parser<std::string::const_iterator> p;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
if( qi::phrase_parse(iter, end, p, ascii::space, r) ){
std::cout << r.first << std::endl;
for(auto& i : r.list) std::cout << i.first << " , " << i.second << std::endl;
std::cout << r.last << std::endl;
}
return 0;
}
当运行这个例子我得到
Error! Expecting <real> here: ""
我知道我可以将列表解析规则更改为 sequence
*( qi::int_ >> qi::double_ )
克服以上错误。但是随后 20 1 1.3 2 error 30
的解析也成功了。结果将是:
20
1 , 1.3
2
有什么解决这个问题的建议吗?
期望点创造 non-backtracking 期望。
所以你不能接受失败后的替代分支。
这意味着您的最后一个整数将与 (int_ > double_)
中的整数匹配。然而,表达式 需要 double_
后面的 (>
),因为它没有,所以它会抛出。
用 >
代替 >>
.
事实上,看起来你只是想确保整个 input/line 被消耗掉,所以让你的期望 explicit:
r = (
qi::int_
> *( qi::int_ >> qi::double_ )
> qi::int_
)
> qi::eoi
;
演示
Note: also enabled rule debug (implicitly names the rule):
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapted.hpp>
#include <iostream>
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
typedef std::vector<std::pair<int, double>> vec_t;
struct list_s{
int first;
vec_t list;
int last;
};
BOOST_FUSION_ADAPT_STRUCT(list_s, first, list, last)
template <typename Iterator>
struct list_parser : qi::grammar<Iterator, list_s(), ascii::space_type>
{
public:
list_parser() : list_parser::base_type(r) {
r = (
qi::int_
> *( qi::int_ >> qi::double_ )
> qi::int_
)
> qi::eoi
;
BOOST_SPIRIT_DEBUG_NODES((r))
qi::on_error<qi::fail>
(
r
, std::cout
<< phoenix::val("\nError! Expecting ")
<< qi::labels::_4
<< phoenix::val(" here: \"")
<< phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2)
<< phoenix::val("\"\n\n")
);
}
private:
qi::rule<Iterator, list_s(), ascii::space_type > r;
};
int main() {
std::string str("20 1 1.3 2 2.3 30");
using It = std::string::const_iterator;
list_parser<It> p;
It iter = str.begin(), end = str.end();
list_s data;
if (qi::phrase_parse(iter, end, p, ascii::space, data)){
std::cout << data.first << "\n";
for(auto& i : data.list)
std::cout << i.first << ", " << i.second << "\n";
std::cout << data.last << "\n";
}
}
打印:
20
1, 1.3
2, 2.3
30
调试输出:
<r>
<try>20 1 1.3 2 2.3 30</try>
<success></success>
<attributes>[[20, [[1, 1.3], [2, 2.3]], 30]]</attributes>
</r>
对于错误输入:
Error! Expecting <eoi> here: "error 30"
有调试输出:
<r>
<try>20 1 1.3 2 error 30</try>
<fail/>
</r>
我尝试使用 boost::spirit::qi
解析 integer
和 double
对的列表。 integer
在此列表之前和之后。此列表的示例是:
20 1 1.3 2 2.3 30
我要创建的结构是:
typedef std::vector< std::pair<int, double> > vec_t;
struct list_s{
int first;
std::vector< std::pair<int, double> > list;
int last;
};
我还使用 qi::on_error
向我的解析器添加了错误处理并将所有序列 >>
转换为预期的 >
.
我的实现是:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
typedef std::vector< std::pair<int, double> > vec_t;
struct list_s{
int first;
vec_t list;
int last;
};
BOOST_FUSION_ADAPT_STRUCT(
list_s,
(int, first)
(vec_t, list)
(int, last)
)
template <typename Iterator>
struct list_parser : qi::grammar<Iterator, list_s(), ascii::space_type >
{
public:
list_parser() : list_parser::base_type(r) {
r = qi::eps >>
(
qi::int_
> *( qi::int_ > qi::double_ )
> qi::int_
);
r.name("r");
qi::on_error<qi::fail>
(
r
, std::cout
<< phoenix::val("\nError! Expecting ")
<< qi::labels::_4
<< phoenix::val(" here: \"")
<< phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2)
<< phoenix::val("\"\n\n")
);
}
private:
qi::rule<Iterator, list_s(), ascii::space_type > r;
};
int main() {
std::string str("20 1 1.3 2 2.3 30");
list_s r;
list_parser<std::string::const_iterator> p;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
if( qi::phrase_parse(iter, end, p, ascii::space, r) ){
std::cout << r.first << std::endl;
for(auto& i : r.list) std::cout << i.first << " , " << i.second << std::endl;
std::cout << r.last << std::endl;
}
return 0;
}
当运行这个例子我得到
Error! Expecting <real> here: ""
我知道我可以将列表解析规则更改为 sequence
*( qi::int_ >> qi::double_ )
克服以上错误。但是随后 20 1 1.3 2 error 30
的解析也成功了。结果将是:
20
1 , 1.3
2
有什么解决这个问题的建议吗?
期望点创造 non-backtracking 期望。
所以你不能接受失败后的替代分支。
这意味着您的最后一个整数将与 (int_ > double_)
中的整数匹配。然而,表达式 需要 double_
后面的 (>
),因为它没有,所以它会抛出。
用 >
代替 >>
.
事实上,看起来你只是想确保整个 input/line 被消耗掉,所以让你的期望 explicit:
r = (
qi::int_
> *( qi::int_ >> qi::double_ )
> qi::int_
)
> qi::eoi
;
演示
Note: also enabled rule debug (implicitly names the rule):
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapted.hpp>
#include <iostream>
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
typedef std::vector<std::pair<int, double>> vec_t;
struct list_s{
int first;
vec_t list;
int last;
};
BOOST_FUSION_ADAPT_STRUCT(list_s, first, list, last)
template <typename Iterator>
struct list_parser : qi::grammar<Iterator, list_s(), ascii::space_type>
{
public:
list_parser() : list_parser::base_type(r) {
r = (
qi::int_
> *( qi::int_ >> qi::double_ )
> qi::int_
)
> qi::eoi
;
BOOST_SPIRIT_DEBUG_NODES((r))
qi::on_error<qi::fail>
(
r
, std::cout
<< phoenix::val("\nError! Expecting ")
<< qi::labels::_4
<< phoenix::val(" here: \"")
<< phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2)
<< phoenix::val("\"\n\n")
);
}
private:
qi::rule<Iterator, list_s(), ascii::space_type > r;
};
int main() {
std::string str("20 1 1.3 2 2.3 30");
using It = std::string::const_iterator;
list_parser<It> p;
It iter = str.begin(), end = str.end();
list_s data;
if (qi::phrase_parse(iter, end, p, ascii::space, data)){
std::cout << data.first << "\n";
for(auto& i : data.list)
std::cout << i.first << ", " << i.second << "\n";
std::cout << data.last << "\n";
}
}
打印:
20
1, 1.3
2, 2.3
30
调试输出:
<r>
<try>20 1 1.3 2 2.3 30</try>
<success></success>
<attributes>[[20, [[1, 1.3], [2, 2.3]], 30]]</attributes>
</r>
对于错误输入:
Error! Expecting <eoi> here: "error 30"
有调试输出:
<r>
<try>20 1 1.3 2 error 30</try>
<fail/>
</r>