如果我的语法中有 "or" ('|'),如何使用 boost spirit 将数据解析为 C++ Struct
How to parse data into C++ Struct with boost spirit if I have an "or" ('|') in my grammar
我有以下 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")) >> ";") >>
"}"
我有一个层次结构,其中一些节点可能占用父节点或子节点的宽度 or/and 高度。所以在我的逻辑中,我首先检查每个节点的 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
注入属性所以分支都合成一个vector2<Type, int>
添加调试和一些测试用例:
#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++ 结构:
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")) >> ";") >>
"}"
我有一个层次结构,其中一些节点可能占用父节点或子节点的宽度 or/and 高度。所以在我的逻辑中,我首先检查每个节点的 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
注入属性所以分支都合成一个vector2<Type, int>
添加调试和一些测试用例:
#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])