Search code examples
c++boostboost-spiritboost-spirit-qi

Why doesn't my boost::spirit rule compile for parsing a list of lists?


I'm trying to write a parser for Open Inventor .iv files using boost::spirit.

I have the following struct for VertexProperty nodes:

struct VertexProperty
{
   std::vector<std::vector<float> > vertices;
   std::vector<std::vector<float> > normals;
   std::vector<std::vector<float> > texCoords;
};

BOOST_FUSION_ADAPT_STRUCT(
   VertexProperty,
   (std::vector<std::vector<float> >, vertices)
   (std::vector<std::vector<float> >, normals)
   (std::vector<std::vector<float> >, texCoords)
   )

And the following rule for parsing it (which doesn't compile):

qi::rule<Iterator, VertexProperty(), Skipper> RULE_VertexProperty;
RULE_VertexProperty = lit("VertexProperty")
   >> char_('{')
   >> lit("vertex") >> char_('[')
   >> repeat(3)[qi::float_] >> *(char_(',') >> repeat(3)[qi::float_])
   >> char_(']')
   >> lit("normal") >> char_('[')
   >> repeat(3)[qi::float_] >> *(char_(',') >> repeat(3)[qi::float_])
   >> char_(']')
   >> lit("texCoord") >> char_('[')
   >> repeat(2)[qi::float] >> *(char_(',') >> repeat(2)[qi::float_])
   >> char_(']') >> char_('}');

Based on this rule, the following should result in a valid VertexProperty parse:

VertexProperty {
   vertex [ 0.0 0.0 1.0,
            1.0 1.0 1.0,
            1.0 0.0 1.0]
   normal [1.0 0.0 0.0,
           0.0 1.0 0.0,
           0.0 0.0 1.0]
   texCoord [0.0 0.0,
             1.0 0.0,
             1.0 1.0]
}

My assumption is that the problem is coming from how I'm trying to parse the comma separated tuples into a vector of a vector.

What is the correct way to parse a list of 3-tuples and or 2-tuples of the form:

[float float float, float float float, float float float]

using boost::spirit?


Solution

  • EDIT I misread the question. Here's the rewrite:

    Live On Coliru

    Live On Coliru (with debug output)

    struct V2 { float a, b;    } ;
    struct V3 { float a, b, c; } ;
    struct VertexProperty { 
        std::vector<V3> vertices, normals;
        std::vector<V2> texCoords; 
    };
    
    BOOST_FUSION_ADAPT_STRUCT(V2, a,b)
    BOOST_FUSION_ADAPT_STRUCT(V3, a,b,c)
    BOOST_FUSION_ADAPT_STRUCT(VertexProperty, vertices,normals,texCoords)
    
    template <typename Iterator>
    struct Parser : qi::grammar<Iterator, VertexProperty()> {
        Parser() : Parser::base_type(start) {
            v2 = qi::double_ >> qi::double_;
            v3 = qi::double_ >> qi::double_ >> qi::double_;
    
            vertexproperty = qi::lit("VertexProperty")
                >> '{'
                >> "vertex"   >> '[' >> (v3 % ',') >> ']'
                >> "normal"   >> '[' >> (v3 % ',') >> ']'
                >> "texCoord" >> '[' >> (v2 % ',') >> ']'
                >> '}';
    
            start = qi::skip(qi::space) [vertexproperty];
    
            BOOST_SPIRIT_DEBUG_NODES((v2)(v3)(vertexproperty))
        }
    
      private:
        qi::rule<Iterator, VertexProperty()> start;
        qi::rule<Iterator, VertexProperty(), qi::space_type> vertexproperty;
        qi::rule<Iterator, V2(),             qi::space_type> v2;
        qi::rule<Iterator, V3(),             qi::space_type> v3;
    };
    

    Notes:

    • use a typed element instead of 'just' a vector
    • use a separate rule for the V3
    • use literals (qi::lit) instead of qi::char_ because you do not want to expose the matched interpunction
    • use the list operator (a % b matches a [b a]... already)

    UPDATE: Full Live Demo

    //#define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/adapted/struct.hpp>
    
    namespace qi = boost::spirit::qi;
    
    struct V2 { float a, b;    } ;
    struct V3 { float a, b, c; } ;
    struct VertexProperty { 
        std::vector<V3> vertices, normals;
        std::vector<V2> texCoords; 
    };
    
    BOOST_FUSION_ADAPT_STRUCT(V2, a,b)
    BOOST_FUSION_ADAPT_STRUCT(V3, a,b,c)
    BOOST_FUSION_ADAPT_STRUCT(VertexProperty, vertices,normals,texCoords)
    
    template <typename Iterator>
    struct Parser : qi::grammar<Iterator, VertexProperty()> {
        Parser() : Parser::base_type(start) {
            v2 = qi::double_ >> qi::double_;
            v3 = qi::double_ >> qi::double_ >> qi::double_;
    
            vertexproperty = qi::lit("VertexProperty")
                >> '{'
                >> "vertex"   >> '[' >> (v3 % ',') >> ']'
                >> "normal"   >> '[' >> (v3 % ',') >> ']'
                >> "texCoord" >> '[' >> (v2 % ',') >> ']'
                >> '}';
    
            start = qi::skip(qi::space) [vertexproperty];
    
            BOOST_SPIRIT_DEBUG_NODES((v2)(v3)(vertexproperty))
        }
    
      private:
        qi::rule<Iterator, VertexProperty()> start;
        qi::rule<Iterator, VertexProperty(), qi::space_type> vertexproperty;
        qi::rule<Iterator, V2(),             qi::space_type> v2;
        qi::rule<Iterator, V3(),             qi::space_type> v3;
    };
    
    int main() {
        using Iterator = std::string::const_iterator;
    
        std::string const sample = "VertexProperty {\n"
            " vertex [ 0.0 0.0 1.0,\n"
            "     1.0 1.0 1.0,\n"
            "     1.0 0.0 1.0]\n"
            " normal [1.0 0.0 0.0,\n"
            "     0.0 1.0 0.0,\n"
            "     0.0 0.0 1.0]\n"
            " texCoord [0.0 0.0,\n"
            "     1.0 0.0,\n"
            "     1.0 1.0]\n"
            "}";
    
        auto f = sample.begin(), l = sample.end();
        VertexProperty data;
        bool ok = qi::parse(f, l, Parser<Iterator>(), data);
    
        if (ok) {
            std::cout << "Parsed: " << data.vertices.size() << ", " << data.normals.size() << ", " << data.texCoords.size() << "\n";
        } else {
            std::cout << "Parse failed\n";
        }
    
        if (f!=l)
            std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
    }
    

    Prints

    Parsed: 3, 3, 3
    

    With debug info

    <vertexproperty>
      <try>VertexProperty {\n ve</try>
      <v3>
        <try> 0.0 0.0 1.0,\n     1</try>
        <success>,\n     1.0 1.0 1.0,\n</success>
        <attributes>[[0, 0, 1]]</attributes>
      </v3>
      <v3>
        <try>\n     1.0 1.0 1.0,\n </try>
        <success>,\n     1.0 0.0 1.0]\n</success>
        <attributes>[[1, 1, 1]]</attributes>
      </v3>
      <v3>
        <try>\n     1.0 0.0 1.0]\n </try>
        <success>]\n normal [1.0 0.0 0</success>
        <attributes>[[1, 0, 1]]</attributes>
      </v3>
      <v3>
        <try>1.0 0.0 0.0,\n     0.</try>
        <success>,\n     0.0 1.0 0.0,\n</success>
        <attributes>[[1, 0, 0]]</attributes>
      </v3>
      <v3>
        <try>\n     0.0 1.0 0.0,\n </try>
        <success>,\n     0.0 0.0 1.0]\n</success>
        <attributes>[[0, 1, 0]]</attributes>
      </v3>
      <v3>
        <try>\n     0.0 0.0 1.0]\n </try>
        <success>]\n texCoord [0.0 0.0</success>
        <attributes>[[0, 0, 1]]</attributes>
      </v3>
      <v2>
        <try>0.0 0.0,\n     1.0 0.</try>
        <success>,\n     1.0 0.0,\n    </success>
        <attributes>[[0, 0]]</attributes>
      </v2>
      <v2>
        <try>\n     1.0 0.0,\n     </try>
        <success>,\n     1.0 1.0]\n}</success>
        <attributes>[[1, 0]]</attributes>
      </v2>
      <v2>
        <try>\n     1.0 1.0]\n}</try>
        <success>]\n}</success>
        <attributes>[[1, 1]]</attributes>
      </v2>
      <success></success>
      <attributes>[[[[0, 0, 1], [1, 1, 1], [1, 0, 1]], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], [[0, 0], [1, 0], [1, 1]]]]</attributes>
    </vertexproperty>