I have the following C++ Struct:
struct Dimension {
enum Type { PARENT, CHILD, PIXEL };
Type mWidth_type = Type::PIXEL;
int mWidth = 0;
Type mHeight_type = Type::PIXEL;
int mHeight = 0;
};
My grammar looks like this:
+(char_ - "{") >> "{" >>
-(lit("width") >> ":" >> (int_ | lit("_parent") | lit("_child")) >> ";") >>
-(lit("height") >> ":" >> (int_ | lit("_parent") | lit("_child")) >> ";") >>
"}"
I have a hierarchical structure, where some nodes might take the width or/and height of the parent or child node. So in my logic I check each node's Dimension type first. If it is PIXEL I get the value, otherwise I ask the value form the parent or child node. Because of this in my file I can have the following possibilities (same for height):
width: 10;
In this case, I want to leave Type with the default enum PIXEL and set the value for mWidth.
or
widht: _parent;
In this case, I want to set Type to PARENT and leave mWidth on default 0.
or
width: _child;
In this case, I want to set Type to CHILD and leave mWidth on default 0.
How can I parse this into a Struct? If my dimensions could take only numbers, then I would be able to proceed, but I am stuck because this is a different case. Any hints, ideas, help is much appreciated!
EDIT1:
Here is an example of the text file which needs to be parsed into the above Struct:
.struct1 {
width: 12;
height: 50;
}
.struct2 {
width: _parent;
height: 50;
}
.struct3 {
width: 40;
height: _child;
}
.struct4 {
width: _parent;
height: _child;
}
I'd suggest to factor the AST type so as to not repeat yourself:
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;
};
Adapt it for fusion:
BOOST_FUSION_ADAPT_STRUCT(Dimension::Value, (Dimension::Value::Type, type)(int, value))
BOOST_FUSION_ADAPT_STRUCT(Dimension, (Dimension::Value, mWidth)(Dimension::Value, mHeight))
Now, I'd write the grammar to match:
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_
);
Note:
attr
to inject attributes so the branches all synthesize a vector2<Type, int>
Adding debugging and a few test cases:
#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";
}
}
Output (without debug information):
-----------------------------------
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])