微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何创建一个可选的解析器,该解析器能够有条件地删除合成项

如何解决如何创建一个可选的解析器,该解析器能够有条件地删除合成项

我正在尝试创建一个可选的解析器规则。根据第一个属性的值,我想有选择地发出数据。

示例,用于输入:

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)并有条件地对其进行处理。与您的尝试相比,这就是保持相对简单的原因。

实时演示

Live On Compiler Explorer

#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)}

¹Boost Spirit: "Semantic actions are evil"?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?