一个简单的X3语法的难以理解的编译错误

Unintelligible compilation error for a simple X3 grammar

我有一个非常简单的语法,我尝试使用 boost spirit x3 来实现,但没有成功。

它不编译,并且由于库中使用的所有模板和复杂概念(我知道,它更像是一个“header”),编译错误消息太长了明白了。

我试图评论部分代码以缩小罪魁祸首,但没有成功,因为它归结为几个部分,反正我没有看到任何错误。

Edit2:第一个错误消息确实在 push_front_impl.hpp 中突出显示:
::REQUESTED_PUSH_FRONT_SPECIALISATION_FOR_SEQUENCE_DOES_NOT_EXIST::*

我怀疑关键字 autop2 语句与 ulong_long...但没有信心。 需要你们的帮助...精灵精英!

下面是重现编译错误的最小代码片段。 编辑:使用 boost 1.70 和 visual studio 2019 v16.1.6

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

int main(void)
{
       std::string input = \
             "\"nodes\":{ {\"type\":\"bb\", \"id\" : 123456567, \"label\" : \"0x12023049\"}," \
                         "{\"type\":\"bb\", \"id\" : 123123123, \"label\" : \"0x01223234\"}," \
                         "{\"type\":\"ib\", \"id\" : 223092343, \"label\" : \"0x03020343\"}}";
       std::istringstream iss(input);
       namespace x3 = boost::spirit::x3;
       using x3::char_;
       using x3::ulong_long;
       using x3::lit;
 
       auto q = lit('\"'); /* q => quote */
 
       auto p1 = q >> lit("type") >> q >> lit(':') >> q >> (lit("bb") | lit("ib")) >> q;
       auto p2 = q >> lit("id") >> q >> lit(':') >> ulong_long;
       auto p3 = q >> lit("label") >> q >> lit(':') >> q >> (+x3::alpha) >> q;
       auto node =  lit('{') >> p1 >> lit(',') >> p2 >> lit(',') >> p3 >> lit('}');
       auto nodes = q >> lit("nodes") >> q >> lit(':') >> lit('{') >> node % lit(',') >> lit('}');
 
       boost::spirit::istream_iterator f(iss >> std::noskipws), l{};
       bool b = x3::phrase_parse(f, l, nodes, x3::space);
 
       return 0;
}

您的特定 platform/version 可能缺少间接包含(如果我不得不猜测这可能是由于使用 Qi 的 istream 迭代器支持 header 引起的)。

如果这不是问题,我的注意力被 where T = boost::mpl::aux::vector_tag<20> 吸引了(/HT @Rup - 数字 20 看起来很可疑,好像它可能是某种限制。

要么我们可以找到超出限制的内容,看看我们是否可以提高它,但为了帮助您和解析器,我将采用“不科学”的方法。

简化表达式

我在您的解析器表达式中看到很多 (lot) 个您不需要的 lit() 节点。我怀疑所有引用的结构都需要是词位,而不是在各处煞费苦心地重复引号,也许将其打包如下:

auto q = [](auto p) { return x3::lexeme['"' >> x3::as_parser(p) >> '"']; };
auto type  = q("type")  >> ':' >> q(bb_ib);
auto id    = q("id")    >> ':' >> x3::ulong_long;
auto label = q("label") >> ':' >> q(+x3::alnum);

备注:

  • 我改进了命名,这样阅读起来更自然:

    auto node = '{' >> type >> ',' >> id >> ',' >> label >> '}';
    
  • 我将 alpha 更改为 alnum 所以它实际上会匹配您的样本输入

  • 假设:表达式在结构上被简化为更具层次性 - 序列由更少的 >>-ed 术语组成 - 希望这消除了潜在的 mpl::vector 大小限制。

我遗漏了一个 bb_ib 部分,因为当您想要实际将已解析的值分配给属性时它会发生变化。让我们这样做:

属性

struct Node {
    enum Type { bb, ib } type;
    uint64_t id;
    std::string label;
};

如您所见,我选择了一个枚举来表示 type。最自然的解析方式是使用 symbols<>

struct bb_ib_sym : x3::symbols<Node::Type> {
    bb_ib_sym() { this->add("bb", Node::bb)("ib", Node::ib); }
} bb_ib;

现在你可以解析成一个向量 Node:

演示

Live On Coliru

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>

struct Node {
    enum Type { bb, ib } type;
    uint64_t id;
    std::string label;
};

namespace { // debug output
    inline std::ostream& operator<<(std::ostream& os, Node::Type t) {
        switch (t) {
            case Node::bb: return os << "bb";
            case Node::ib: return os << "ib";
        }
        return os << "?";
    }
    inline std::ostream& operator<<(std::ostream& os, Node const& n) {
        return os << "Node{" << n.type << ", " << n.id << ", " << std::quoted(n.label) << "}";
    }
}

// attribute propagation
BOOST_FUSION_ADAPT_STRUCT(Node, type, id, label)

int main() {
    std::string input = R"("nodes": {
    {
        "type": "bb",
        "id": 123456567,
        "label": "0x12023049"
    },
    {
        "type": "bb",
        "id": 123123123,
        "label": "0x01223234"
    },
    {
        "type": "ib",
        "id": 223092343,
        "label": "0x03020343"
    }
})";

    namespace x3 = boost::spirit::x3;
    struct bb_ib_sym : x3::symbols<Node::Type> {
        bb_ib_sym() { this->add("bb", Node::bb)("ib", Node::ib); }
    } bb_ib;

    auto q = [](auto p) { return x3::lexeme['"' >> x3::as_parser(p) >> '"']; };
    auto type  = q("type")  >> ':' >> q(bb_ib);
    auto id    = q("id")    >> ':' >> x3::ulong_long;
    auto label = q("label") >> ':' >> q(+x3::alnum);
    auto node
        = x3::rule<Node, Node> {"node"}
        = '{' >> type >> ',' >> id >> ',' >> label >> '}';
    auto nodes = q("nodes") >> ':' >> '{' >> node % ',' >> '}';

    std::vector<Node> parsed;
    auto f = begin(input);
    auto l = end(input);
    if (x3::phrase_parse(f, l, nodes, x3::space, parsed)) {
        for (Node& node : parsed) {
            std::cout << node << "\n";
        }
    } else {
        std::cout << "Parse failed\n";
    }
    if (f!=l) {
        std::cout << "Remaining input: " << std::quoted(std::string(f, l)) << "\n";
    }
}

版画

Node{bb, 123456567, "0x12023049"}
Node{bb, 123123123, "0x01223234"}
Node{ib, 223092343, "0x03020343"}

这是一个已知的 MPL 限制 (Issue with X3 and MS VS2017, https://github.com/boostorg/spirit/issues/515) + bug/difference of implementation for MSVC/ICC compilers (https://github.com/boostorg/mpl/issues/43)。

我在没有使用 MPL (https://github.com/boostorg/spirit/pull/607) 的情况下重写了一个有问题的部分,它将在 Boost 1.74 中发布,在那之前你应该能够解决问题:

#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_VECTOR_SIZE 50

或者,您可以将语法的不同部分包装到规则中,这将减少序列解析器链。


请注意,q >> lit("x") >> q >> lit(':') >> ... 可能不是您真正想要的,它(带有船长)将允许解析 " x ":。如果您不希望使用 lit("\"x\"") >> lit(':') >> ...