3

我试图定义一个规则没有完全预定义的解析器,即它们包含一个可变部分。这对灵气没有问题,但由于X3的静态特性,我无法实现。我尝试了with 指令,不幸的是没有记录,但到目前为止还没有运气。到目前为止,我发现的唯一示例是在 lambda 表达式中。

我构建了一个简单的例子来演示这个问题:解析整数,其中分隔符作为参数给出。

#include <boost/spirit/home/x3.hpp>
#include <iostream>

namespace x3 = boost::spirit::x3;

namespace parsing {
    x3::rule<struct parser> parser {"parser"};

    //struct separator {};
    char separator(',');

    //auto parser_def = x3::int_ % x3::lit(x3::get<separator>(/* context */)); // candidate function template not viable: requires single argument 'context'
    auto parser_def = x3::int_ % x3::lit(separator);

    BOOST_SPIRIT_DEFINE(parser)
}

void parse(const std::string &data, const char separator) {
    using namespace std;

    //auto parser = x3::with<parsing::separator>(ref(separator)) [parsing::parser] >> x3::eoi;
    auto parser = parsing::parser >> x3::eoi;

    if (x3::parse(data.begin(), data.end(), parser))
        cout << "Parse succeeded\n";
    else
        cout << "Parse failed\n";
}

int main() {
    parse("1 2 3", ' ');
    parse("1,2,3", ',');
    parse("1;2;3", ';');
}

我注释掉了我尝试使用with 指令的部分。

X3目前可以做到这一点吗?有没有人这样做过?

4

2 回答 2

1

在查看了更多 X3 帖子后,我被 sehe 的这个答案启发了:https ://stackoverflow.com/a/38303379/7110782

了解 x3::_pass 让我找到了这个解决方案:

#include <boost/spirit/home/x3.hpp>
#include <iostream>

namespace x3 = boost::spirit::x3;

namespace parsing {
    x3::rule<struct parser> parser {"parser"};

    struct separator {};

    // only the separator which is currently in the context is allowed (passes)
    auto isSeparator = [](auto& ctx){ x3::_pass(ctx) = x3::_attr(ctx) == x3::get<separator>(ctx); };

    // at first match any char and then check whether it is the separator
    auto parser_def = x3::int_ % x3::char_[isSeparator];

    BOOST_SPIRIT_DEFINE(parser)
}

void parse(const std::string &data, const char separator) {
    using namespace std;

    auto parser = x3::with<parsing::separator>(ref(separator)) [parsing::parser] >> x3::eoi;

    if (x3::parse(data.begin(), data.end(), parser))
        cout << "Parse succeeded\n";
    else
        cout << "Parse failed\n";
}

int main() {
    // succeed
    parse("1 2 3", ' ');
    parse("1,2,3", ',');
    parse("1;2;3", ';');

    // fail
    parse("1,2,3", ' ');
    parse("1;2;3", ',');
}

下一步需要测试的是设置多个参数的可能性(可能通过级联 x3::with<>)。

编辑:

是的,通过级联 x3::with<> 设置多个参数似乎可行。例如:

auto parser = x3::with<parsing::separator>(ref(separator))[x3::with<parsing::separator2>(ref(separator2))[parsing::parser]] >> x3::eoi;
于 2016-11-04T08:22:18.023 回答
0

一个更简单的解决方案是,您可以拥有一个获取字符并返回类型解析器值的函数x3::rule<struct parser>

auto getParser(char sep)
{
     return x3::int_ % x3::lit(sep);
}

完整代码(Godbolt:https ://godbolt.org/z/ENxCTF )

#include <boost/spirit/home/x3.hpp>
#include <iostream>

namespace x3 = boost::spirit::x3;

namespace parsing {
    x3::rule<struct parser> parser {"parser"};

    //struct separator {};
    char separator(',');

    //auto parser_def = x3::int_ % x3::lit(x3::get<separator>(/* context */)); // candidate function template not viable: requires single argument 'context'
    auto parser_def = x3::int_ % x3::lit(separator);


    BOOST_SPIRIT_DEFINE(parser)
    auto getParser(char sep)
    {
        return x3::int_ % x3::lit(sep);
    }
}

void parse(const std::string &data, const char separator) {
    using namespace std;

    //auto parser = x3::with<parsing::separator>(ref(separator)) [parsing::parser] >> x3::eoi;
    auto parser = parsing::getParser(separator) >> x3::eoi;

    if (x3::parse(data.begin(), data.end(), parser))
        cout << "Parse succeeded\n";
    else
        cout << "Parse failed\n";
}

int main() {
    parse("1 2 3", ' ');
    parse("1,2,3", ',');
    parse("1;2;3", ';');
}
于 2019-11-14T11:45:11.113 回答