用精神解析文件输入

Parsing file input with spirit

我最近在玩 boost::spirit,想用它来解析文件输入。我得到的是:定义一些语义动作:

data = ifstream("herpderp", ios::in);
std::string line;
auto pri = [&](auto &ctx){cout << "got this" << endl;};
auto bri = [&](auto &ctx){cout << "got that" << endl;};

实际阅读是这样的:

while(getline(data, line, '\n'))                                        
{                                                                       
    bool r = phrase_parse(line.begin(), line.end(), (int_ >> char_ >> int_ >> double_)[pri] | (int_ >> char_ >> int_)[bri], space);
}

但是问题是 - 我不知道如何访问 lambdas pribri_attr(ctx) 的内容。我知道它们按预期工作,具体取决于文件的内容,因为 cout 打印(它们交替出现)——但是它们是复合类型,正如可以从解析规则中看出的那样。如果有人能对此有所了解,我将不胜感激。

编辑: 让它按照我想要的方式工作。它需要另一个导入

#include <boost/mpl/int.hpp>

每个 lambda 看起来像这样:


auto bri = [&](auto &ctx)
{
    int    firstIntFromMatch  = at<boost::mpl::int_<0>>(_attr(ctx));
    char   charFromMatch      = at<boost::mpl::int_<1>>(_attr(ctx));
    int    secondIntFromMatch = at<boost::mpl::int_<2>>(_attr(ctx));

    doSomething(firstIntFromMatch, charFromMatch, secondIntFromMatch);
};
auto pri = [&](auto &ctx)
{
    int    firstIntFromMatch  = at<boost::mpl::int_<0>>(_attr(ctx));
    char   charFromMatch      = at<boost::mpl::int_<1>>(_attr(ctx));
    int    secondIntFromMatch = at<boost::mpl::int_<2>>(_attr(ctx));
    double doubleFromMatch    = at<boost::mpl::int_<3>>(_attr(ctx));

    doSomething(firstIntFromMatch, charFromMatch, secondIntFromMatch);
    doSomethingElse(doubleFromMatch);
};

我和@lakeweb 一起,另见

但是回答你的具体问题:属性是融合序列。包括 fusion/include/io.hpp 使您能够打印它们:

    auto pri = [&](auto &ctx){std::cout << "got this: " << _attr(ctx) << std::endl;};
    auto bri = [&](auto &ctx){std::cout << "got that: " << _attr(ctx) << std::endl;};

版画

Live On Coliru

got this: (321 a 321 3.14)
Parsed
got that: (432 b 432)
Parsed

做有用的事情

做有用的事情总是更令人兴奋。您可以 手动分解这些融合序列。定义我能想到的最简单的数据结构来接收我们的数据:

struct MyData {
    int a = 0;
    char b = 0;
    int c = 0;
    double d = 0;

    friend std::ostream& operator<<(std::ostream& os, MyData const& md) {
        return os << "MyData{" << md.a << "," << md.b << "," << md.c << "," << md.d << "}";
    }
};

现在,我们可以"enhance"(阅读:复杂化)东西来解析它:

auto pri = [&](auto &ctx) {
    auto& attr = _attr(ctx);
    std::cout << "got this: " << attr << std::endl;
    using boost::fusion::at_c;
    _val(ctx) = { at_c<0>(attr), at_c<1>(attr), at_c<2>(attr), at_c<3>(attr) };
};
auto bri = [&](auto &ctx)
{ 
    auto& attr = _attr(ctx);
    std::cout << "got that: " << attr << std::endl;
    using boost::fusion::at_c;
    _val(ctx) = { at_c<0>(attr), at_c<1>(attr), at_c<2>(attr), std::numeric_limits<double>::infinity()};
};

auto const pri_rule = x3::rule<struct _pri, MyData> {"pri_rule"} = 
        (x3::int_ >> x3::char_ >> x3::int_ >> x3::double_)[pri];
auto const bri_rule = x3::rule<struct _bri, MyData> {"bri_rule"} = 
        (x3::int_ >> x3::char_ >> x3::int_)[bri];

是的,这个 "works":

Live On Coliru

for(std::string const line : {
        "321 a 321 3.14",
        "432 b 432"
    })
{
    MyData data;

    bool r = x3::phrase_parse(
            line.begin(), line.end(),
            pri_rule | bri_rule,
            x3::space,
            data);

    if (r)
        std::cout << "Parsed " << data << "\n";
    else
        std::cout << "Failed\n";
}

版画

got this: (321 a 321 3.14)
Parsed MyData{321,a,321,3.14}
got that: (432 b 432)
Parsed MyData{432,b,432,inf}

然而这看起来非常复杂。

简化!!!

看来您只有一个可选的尾随 double_。在一点帮助下:

BOOST_FUSION_ADAPT_STRUCT(MyData, a,b,c,d);

你可以在没有任何混乱的情况下获得同样的效果:

bool r = x3::phrase_parse(
        line.begin(), line.end(),
        x3::int_ >> x3::char_ >> x3::int_ >> (x3::double_ | x3::attr(9999)),
        x3::space, data);

这将打印 Live On Coliru

Parsed MyData{321,a,321,3.14}
Parsed MyData{432,b,432,9999}

可选:可选性

如果您没有 double 的有效默认值,您可以将其设为可选:

            x3::int_ >> x3::char_ >> x3::int_ >> -x3::double_,

并且仍然可以解析它:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/optional/optional_io.hpp>
#include <iostream>

namespace x3 = boost::spirit::x3;

struct MyData {
    int a = 0;
    char b = 0;
    int c = 0;
    boost::optional<double> d;

    friend std::ostream& operator<<(std::ostream& os, MyData const& md) {
        return os << "MyData{" << md.a << "," << md.b << "," << md.c << "," << md.d << "}";
    }
};

BOOST_FUSION_ADAPT_STRUCT(MyData, a,b,c,d)

int main() {
    for(std::string const line : { "321 a 321 3.14", "432 b 432" }) {
        MyData data;

        bool r = x3::phrase_parse(
                line.begin(), line.end(),
                x3::int_ >> x3::char_ >> x3::int_ >> -x3::double_,
                x3::space, data);

        if (r)
            std::cout << "Parsed " << data << "\n";
        else
            std::cout << "Failed\n";
    }
}

打印:

Parsed MyData{321,a,321, 3.14}
Parsed MyData{432,b,432,--}