理解 Boost.Spirit 中的列表运算符 (%)
你能帮我理解 a % b
解析器和它扩展的 a >> 之间的区别吗?*(b >> a)
形式在 Boost.Spirit 中?即使参考手册 表示它们是等价的,
Can you help me understand the difference between the a % b
parser and its expanded a >> *(b >> a)
form in Boost.Spirit? Even though the reference manual states that they are equivalent,
列表运算符 a % b
是一种二元运算符,它匹配由 b的出现分隔的
a
的一个或多个重复的列表代码>.这相当于 a >>*(b >> a)
.
The list operator,
a % b
, is a binary operator that matches a list of one or more repetitions ofa
separated by occurrences ofb
. This is equivalent toa >> *(b >> a)
.
以下程序会根据使用的程序产生不同的结果:
the following program produces different results depending on which is used:
#include <iostream>
#include <string>
#include <vector>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
struct Record {
int id;
std::vector<int> values;
};
BOOST_FUSION_ADAPT_STRUCT(Record,
(int, id)
(std::vector<int>, values)
)
int main() {
namespace qi = boost::spirit::qi;
const auto str = std::string{"1: 2, 3, 4"};
const auto rule1 = qi::int_ >> ':' >> (qi::int_ % ',') >> qi::eoi;
const auto rule2 = qi::int_ >> ':' >> (qi::int_ >> *(',' >> qi::int_)) >> qi::eoi;
Record record1;
if (qi::phrase_parse(str.begin(), str.end(), rule1, qi::space, record1)) {
std::cout << record1.id << ": ";
for (const auto& value : record1.values) { std::cout << value << ", "; }
std::cout << '
';
} else {
std::cerr << "syntax error
";
}
Record record2;
if (qi::phrase_parse(str.begin(), str.end(), rule2, qi::space, record2)) {
std::cout << record2.id << ": ";
for (const auto& value : record2.values) { std::cout << value << ", "; }
std::cout << '
';
} else {
std::cerr << "syntax error
";
}
}
在 Coliru 上直播
1: 2, 3, 4,
1: 2,
rule1
和 rule2
的不同仅在于 rule1
使用列表运算符 ((qi::int_ % ',')
) 和 rule2
使用其扩展形式 ((qi::int_ >> *(',' >> qi::int_))
).然而,rule1
产生了 1: 2, 3, 4,
(如预期的那样)和 rule2
产生了 1: 2,
>.我无法理解 rule2
的结果:1) 为什么它与 rule1
的结果不同和 2) 为什么 3
和 4
未包含在 record2.values
中,即使 phrase_parse
以某种方式返回 true?
rule1
and rule2
are different only in that rule1
uses the list operator ((qi::int_ % ',')
) and rule2
uses its expanded form ((qi::int_ >> *(',' >> qi::int_))
). However, rule1
produced 1: 2, 3, 4,
(as expected) and rule2
produced 1: 2,
. I cannot understand the result of rule2
: 1) why is it different from that of rule1
and 2) why were 3
and 4
not included in record2.values
even though phrase_parse
returned true somehow?
推荐答案
更新 X3 版本已添加
首先,您在这里陷入了一个深陷陷阱:
First off, you fallen into a deep trap here:
Qi 规则不适用于 auto
.使用 qi::copy
或仅使用 qi::rule<>
.你的程序有未定义的行为,它确实对我来说崩溃了(valgrind 指出了悬空引用的来源).
Qi rules don't work with auto
. Use qi::copy
or just used qi::rule<>
. Your program has undefined behaviour and indeed it crashed for me (valgrind pointed out where the dangling references originated).
所以,首先:
const auto rule = qi::copy(qi::int_ >> ':' >> (qi::int_ % ',') >> qi::eoi);
现在,当你删除程序中的冗余时,你得到:
Now, when you delete the redundancy in the program, you get:
生活在 Coliru
int main() {
test(qi::copy(qi::int_ >> ':' >> (qi::int_ % ',')));
test(qi::copy(qi::int_ >> ':' >> (qi::int_ >> *(',' >> qi::int_))));
}
打印
1: 2, 3, 4,
1: 2,
原因和解决方法
成功解析的3, 4
发生了什么?
好吧,属性传播规则表明qi::int_>>*(',' >> qi::int_)
暴露了一个 tuple
int
误触发并分配"到属性引用中,而忽略了剩余的 vector
.
Well, the attribute propagation rules indicate that qi::int_ >> *(',' >> qi::int_)
exposes a tuple<int, vector<int> >
. In a bid to magically DoTheRightThing(TM) Spirit accidentally misfires and "assigngs" the int
into the attribute reference, ignoring the remaining vector<int>
.
如果要将容器属性解析为原子组",请使用qi::as<>
:
If you want to make container attributes parse as "an atomic group", use qi::as<>
:
test(qi::copy(qi::int_ >> ':' >> qi::as<Record::values_t>() [ qi::int_ >> *(',' >> qi::int_)]));
这里 as<>
作为属性兼容性启发式的障碍,语法知道你的意思:
Here as<>
acts as a barrier for the attribute compatibility heuristics and the grammar knows what you meant:
生活在 Coliru 上
#include <iostream>
#include <string>
#include <vector>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
struct Record {
int id;
using values_t = std::vector<int>;
values_t values;
};
BOOST_FUSION_ADAPT_STRUCT(Record, id, values)
namespace qi = boost::spirit::qi;
template <typename T>
void test(T const& rule) {
const std::string str = "1: 2, 3, 4";
Record record;
if (qi::phrase_parse(str.begin(), str.end(), rule >> qi::eoi, qi::space, record)) {
std::cout << record.id << ": ";
for (const auto& value : record.values) { std::cout << value << ", "; }
std::cout << '
';
} else {
std::cerr << "syntax error
";
}
}
int main() {
test(qi::copy(qi::int_ >> ':' >> (qi::int_ % ',')));
test(qi::copy(qi::int_ >> ':' >> (qi::int_ >> *(',' >> qi::int_))));
test(qi::copy(qi::int_ >> ':' >> qi::as<Record::values_t>() [ qi::int_ >> *(',' >> qi::int_)]));
}
印刷品
1: 2, 3, 4,
1: 2,
1: 2, 3, 4,
相关文章