c++ - 如果我的语法中有 "or"('|' ),如何使用 boost spirit 将数据解析为 C++ Struct

标签 c++ struct boost-spirit

我有以下 C++ 结构:

struct Dimension {
    enum Type { PARENT, CHILD, PIXEL };

    Type mWidth_type = Type::PIXEL;
    int mWidth = 0;

    Type mHeight_type = Type::PIXEL;
    int mHeight = 0;
};

我的语法是这样的:

+(char_ - "{") >> "{" >>
-(lit("width") >> ":" >> (int_ | lit("_parent") | lit("_child")) >> ";") >>
-(lit("height") >> ":" >> (int_ | lit("_parent") | lit("_child")) >> ";") >>
"}"

我有一个层次结构,其中一些节点可能采用父节点或子节点的宽度或/和高度。所以在我的逻辑中,我首先检查每个节点的 Dimension 类型。如果它是 PIXEL,我得到值,否则我从父节点或子节点询问值。因此,在我的文件中,我可以有以下可能性(高度相同):

width: 10;

在这种情况下,我想使用默认枚举 PIXEL 保留类型并设置 mWidth 的值。

widht: _parent;

在这种情况下,我想将 Type 设置为 PARENT 并将 mWidth 保留为默认值 0。

width: _child;

在这种情况下,我想将 Type 设置为 CHILD 并将 mWidth 保留为默认值 0。

如何将其解析为结构?如果我的尺寸只能采用数字,那么我就可以继续,但我被卡住了,因为这是一个不同的情况。非常感谢任何提示、想法和帮助!

编辑1:

下面是需要解析成上述结构的文本文件示例:

.struct1 {
    width: 12;
    height: 50;
}

.struct2 {
    width: _parent;
    height: 50;
}

.struct3 {
    width: 40;
    height: _child;
}

.struct4 {
    width: _parent;
    height: _child;
}

最佳答案

我建议考虑 AST 类型,以免重复自己:

struct Dimension {
    struct Value {
        enum Type { PARENT, CHILD, PIXEL } type;
        int value;

        friend std::ostream& operator<<(std::ostream& os, Value const& v) {
            switch(v.type) {
                case PARENT: return os << "[PARENT:" << v.value << "]";
                case CHILD:  return os << "[CHILD:"  << v.value << "]";
                case PIXEL:  return os << "[PIXEL:"  << v.value << "]";
            }
            return os << "?";
        }
    };

    Value mWidth, mHeight;
};

适应融合:

BOOST_FUSION_ADAPT_STRUCT(Dimension::Value, (Dimension::Value::Type, type)(int, value))
BOOST_FUSION_ADAPT_STRUCT(Dimension, (Dimension::Value, mWidth)(Dimension::Value, mHeight))

现在,我将编写语法来匹配:

    start   = width_ ^ height_;
    width_  = lit("width")  >> ':' >> value_ >> ';';
    height_ = lit("height") >> ':' >> value_ >> ';';

    value_  =
        ( "_child"  >> attr(Dimension::Value::CHILD)  >> attr(0)
        | "_parent" >> attr(Dimension::Value::PARENT) >> attr(0)
        | eps       >> attr(Dimension::Value::PIXEL)  >> int_
        );

注意:

  • 您可以使用置换解析器来实现更多功能
  • 你可以看到attr的使用注入(inject)属性,以便所有分支都合成一个 vector2<Type, int>

添加调试和一些测试用例:

Live On Coliru

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

struct Dimension {
    struct Value {
        enum Type { PARENT, CHILD, PIXEL } type;
        int value;

        friend std::ostream& operator<<(std::ostream& os, Value const& v) {
            switch(v.type) {
                case PARENT: return os << "[PARENT:" << v.value << "]";
                case CHILD:  return os << "[CHILD:"  << v.value << "]";
                case PIXEL:  return os << "[PIXEL:"  << v.value << "]";
            }
            return os << "?";
        }
    };

    Value mWidth, mHeight;
};

BOOST_FUSION_ADAPT_STRUCT(Dimension::Value, (Dimension::Value::Type, type)(int, value))
BOOST_FUSION_ADAPT_STRUCT(Dimension, (Dimension::Value, mWidth)(Dimension::Value, mHeight))

template <typename It, typename Skipper>
struct grammar : qi::grammar<It, Dimension(), Skipper>
{
    grammar() : grammar::base_type(start) {
        using namespace qi;

        start   = width_ ^ height_;
        width_  = lit("width")  >> ':' >> value_ >> ';';
        height_ = lit("height") >> ':' >> value_ >> ';';
        value_  =
            ( "_child"  >> attr(Dimension::Value::CHILD)  >> attr(0)
            | "_parent" >> attr(Dimension::Value::PARENT) >> attr(0)
            | eps       >> attr(Dimension::Value::PIXEL)  >> int_
            );

        BOOST_SPIRIT_DEBUG_NODES((start)(value_)(width_)(height_))
    }
  private:
    qi::rule<It, Dimension(), Skipper> start;
    qi::rule<It, Dimension::Value(), Skipper> value_, width_, height_;
};

int main() {
    using It = std::string::const_iterator;
    grammar<It, qi::space_type> p;

    for (std::string const input : {
            "width: 10;      height: _child;",
            "width: _parent; height: 10;",
            "width: _child;  height: 10;"
            })
    {
        It f = input.begin(), l = input.end();
        std::cout << "\n-----------------------------------\n"
                  << "Parsing '" << input << "'\n";

        Dimension parsed;
        bool ok = qi::phrase_parse(f, l, p, qi::space, parsed);

        if (ok)
            std::cout << "Parsed: (" << parsed.mWidth << "x" << parsed.mHeight << ")\n";
        else
            std::cout << "Parse failed\n";

        if (f!=l)
            std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
    }
}

输出(无调试信息):

-----------------------------------
Parsing 'width: 10;      height: _child;'
Parsed: ([PIXEL:10]x[CHILD:0])

-----------------------------------
Parsing 'width: _parent; height: 10;'
Parsed: ([PARENT:0]x[PIXEL:10])

-----------------------------------
Parsing 'width: _child;  height: 10;'
Parsed: ([CHILD:0]x[PIXEL:10])

关于c++ - 如果我的语法中有 "or"('|' ),如何使用 boost spirit 将数据解析为 C++ Struct,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28976049/

相关文章:

c# - 编码包含字符串的结构

c++ - 使用 Boost.Spirit 编译一个简单的解析器

c++ - 使用 Boost Spirit/Fusion 轻松解析带有枚举字段和 STL 容器的结构

c++ - 如何检查一行中的 3 个节点是否相同 C++?

c++ - 函数不会更改传递的指针 C++

json - 如何在Go中将具有嵌入式struct字段的结构编码(marshal)为平面JSON对象?

c++ - 升压.x3 : attribute accumulates between alternatives

c++ - 在C++ 11中实现COW std::string的可能性

c++ - 指向对象表的指针

c - 从另一个头文件获取静态结构成员的地址