So I'm trying hard to get boost::spirit::qi under my skin. My toy example so far is a parser that parses Wavefront OBJ material libraries that have the following format:
newmtl ShortBox
Ka 0.6 0.6 0.6
Kd 0.5 0.5 0.5
Ks 0 0 0
d 1
Ns 0
illum 2
However the ordering of the arguments to the material ShortBox can vary. I've created the following grammar that successfully parses it:
template <typename Iterator>
struct mtllib_grammar : qi::grammar<Iterator, qi::blank_type> {
mtllib_grammar() : mtllib_grammar::base_type(mtl)
{
using qi::char_;
using qi::double_;
using qi::int_;
mtl =
(
("newmtl" >> +(char_ - qi::eol) >> qi::eol >> *(mtl_details) >> *(mtl))
| ("#" >> *(qi::char_ - qi::eol) >> qi::eol >> *(mtl))
);
mtl_details =
(
("Ka" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
| ("Kd" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
| ("Ks" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
| ("d" >> int_ >> qi::eol >> *(mtl_details))
| ("Ns" >> int_ >> qi::eol >> *(mtl_details))
| ("illum" >> int_ >> qi::eol >> *(mtl_details))
);
}
qi::rule<Iterator, qi::blank_type> mtl;
qi::rule<Iterator, qi::blank_type> mtl_details;
};
Now I would like to build a std::map<std::string,Material>
where Material
is defined as:
struct Material {
Material()
{
Ns = 0.0f;
Ke = glm::vec3(0.0f);
Kd = glm::vec3(0.0f);
Ks = glm::vec3(0.0f);
Ka = glm::vec3(0.0f);
}
~Material() {}
glm::vec3 Ka;
glm::vec3 Kd;
glm::vec3 Ks;
glm::vec3 Ke;
float Ns;
};
with the following fusion adaptations:
BOOST_FUSION_ADAPT_STRUCT(
glm::vec3,
(float, x)
(float, y)
(float, z)
)
BOOST_FUSION_ADAPT_STRUCT(
Material,
(glm::vec3, Ka)
(glm::vec3, Kd)
(glm::vec3, Ks)
(glm::vec3, Ke)
(float, Ns)
)
So my current idea is to change rule mtl_details
such that it returns a complete Material
and rule mtl
into returning a map of said Material with key being the string after newmtl
. However, I'm lost at how to use attributes to build the Material object from the parse tree, mapping all hits of Ka, Kd, Ks
ect. onto the same struct. Reading examples they all seem to either implicitly get mapped onto whatever variable is associated to it, or they map only to simple values, not objects.
Thanks to cv_and_he I found the solution. First ONLY the parameters that can occur in the file should be mapped using
BOOST_FUSION_ADAPT_STRUCT
, so don't map Material::Ke since it cannot occur naturally in the file format.Next, my grammer ended up like the following:
With
omit
around unused parameters PLUS comments. Otherwise the compiler borks about being unable to map onto the types given in the rules.