如何解决如何创建一个可选的解析器,该解析器能够有条件地删除合成项
我正在尝试创建一个可选的解析器规则。根据第一个属性的值,我想有选择地发出数据。
示例,用于输入:
x,2,3
y,3,4
x,5,6
如果第一个字符是y
,则该行应被丢弃。否则将被处理。在此示例中,如果第3个属性为>= 4
,则为true。综合属性应为std::pair<bool,unsigned int>
,其中unsigned int
值为第二个属性。
解析器是:
using namespace qi = boost::spirit::qi;
using Data = std::pair<bool,unsigned>;
BOOST_PHOENIX_ADAPT_FUNCTION(Data,make_pair,std::make_pair,2);
class DataParser :
public qi::grammar<
std::string::iterator,boost::spirit::char_encoding::ascii,boost::spirit::ascii::space_type,std::vector<Data>()
>
{
qi::rule<iterator_type,encoding_type,bool()> type;
qi::rule<iterator_type,bool()> side;
// doesn't compile: qi::rule<iterator_type,boost::optional<Data>()> line;
qi::rule<iterator_type,qi::locals<bool,unsigned,bool>,Data()> line;
qi::rule<iterator_type,sig_type> start;
public:
DataParser()
: base_type(start)
{
using namespace qi::labels;
type = qi::char_[_val = _1 == 'x'];
side = qi::int_[_val = _1 >= 4];
line %= (qi::omit[type[_a = _1]] >> ',' >> qi::omit[qi::uint_[_b = _1]] >> ',' >> qi::omit[side[_c = _1]])[if_(_a)[_val = make_pair(_c,_b)]];
// doesn't compile: line %= (qi::omit[type[_a = _1]] >> ',_b)].else_[_val = qi::unused]];
// doesn't compile: line %= (type >> ',' >> qi::uint_ >> ',' >> side)[if_(_1)[_val = make_pair(_3,_2)]];
// doesn't compile: line %= (type >> ',_2)].else_[_val = unused]];
start = *line;
}
};
我得到:[[false,2],[false,0],[true,5]]
我想得到的地方:[[false,5]]
(第二个条目应被丢弃)。
我尝试使用boost::optional<Data>
来设置data
规则,还尝试将unused
分配给_val
,但没有任何效果。
在解决问题并接受答案后进行编辑
新规则现在是:
using Data = std::pair<bool,2);
class DataParser :
public qi::grammar<
std::string::iterator,boost::spirit::ascii::blank_type,std::vector<Data>()
>
{
using Items = boost::fusion::vector<bool,bool>;
qi::rule<iterator_type,bool()> side;
qi::rule<iterator_type,Items()> line;
qi::rule<iterator_type,sig_type> start;
public:
DataParser()
: base_type(start)
{
using namespace qi::labels;
namespace px = boost::phoenix;
type = qi::char_[_val = _1 == 'x'];
side = qi::int_[_val = _1 >= 4];
line = type >> ',' >> side;
start = line[if_(_1)[px::push_back(_val,make_pair(_3,_2))]] % qi::eol;
}
};
关键在于使用语义动作来决定是否应通过使用上一条规则的所有属性(在这种情况下为line
来添加综合属性)。
解决方法
好的。您使用许多电动工具。但是请记住,with great power comes...。
尤其是qi :: locals,phoenix,语义动作:它们都使生活变得复杂,因此只能将它们用作最后的手段(或者当它们很自然地适应时,很少出现¹)。
直接思考,
start = *line;
line = // ....
当你说
如果第一个字符是y,则应将该行丢弃。否则它将被处理。
您可以直接表达 :
line = !qi::lit('y') >> // ...
或者,说出可以接受的入门者:
line = qi::omit[ qi::char_("xz") ] >> // ...
完成。
直接向前映射
在这里,我将通过重新排序pair<unsigned,bool>
使其与输入顺序匹配来作弊。现在一切都可以直接使用,无需任何“魔术”:
line = !qi::lit('y') >> qi::omit[qi::alnum] >> ',' >> qi::int_ >> ',' >> side;
ignore = +(qi::char_ - qi::eol);
start = qi::skip(qi::blank) [ (line | ignore) % qi::eol ];
但是,它将导致您注意到虚假的输入: Live On Compiler Explorer
Parsed: {(2,false),(0,(5,true)}
改进
现在,您可以通过更改eol
来解决问题,以免吃掉似乎不包含有效数据行的后续行。但是,它变得笨拙,我们仍然希望翻转这对成员。
所以,这是我认为手枪可以方便的地方:
public:
DataParser() : DataParser::base_type(start) {
using namespace qi::labels;
start = qi::skip(qi::blank) [
(qi::char_ >> ',' >> qi::uint_ >> ',' >> qi::int_) [
_pass = process(_val,_1,_2,_3) ]
% qi::eol ];
}
private:
struct process_f {
template <typename... T>
bool operator()(Datas& into,char id,unsigned type,int side) const {
switch(id) {
case 'z': case 'x':
into.emplace_back(side >= 4,type);
break;
case 'y': // ignore
break;
case 'a':
return false; // fail the rule
}
return true;
}
};
boost::phoenix::function<action_f> process;
您可以看到,现在有一个很好的关注点分离。您解析(char,int,int)
并有条件地对其进行处理。与您的尝试相比,这就是保持相对简单的原因。
实时演示
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <fmt/ranges.h>
namespace qi = boost::spirit::qi;
using Data = std::pair<bool,unsigned>;
using Datas = std::vector<Data>;
template <typename It>
class DataParser : public qi::grammar<It,Datas()> {
using Skipper = qi::blank_type;
qi::rule<It,Datas(),Skipper> line;
qi::rule<It,Datas()> start;
public:
DataParser() : DataParser::base_type(start) {
using namespace qi::labels;
start = qi::skip(qi::blank) [
(qi::char_ >> ',type);
break;
case 'y': // ignore
break;
case 'a':
return false; // fail the rule
}
return true;
}
};
boost::phoenix::function<process_f> process;
};
int main() {
using It = std::string::const_iterator;
DataParser<It> p;
for (std::string const input : {
"x,2,3\ny,3,4\nx,5,6",})
{
auto f = begin(input),l = end(input);
Datas d;
auto ok = qi::parse(f,l,p,d);
if (ok) {
fmt::print("Parsed: {}\n",d);
} else {
fmt::print("Parsed failed\n",d);
}
if (f!=l) {
fmt::print("Remaining unparsed: '{}'\n",std::string(f,l));
}
}
}
打印
Parsed: {(false,2),(true,5)}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。