Search code examples
c++boost-spirit-qi

Create and write to vector on the fly


I want to create vector and append values to it (if any) in one spirit rule. Is it possible? I tried something like below but with no success. Read code comments for details please. Thanks.

typedef std::vector<double> number_array;
typedef std::vector<std::string> string_array;

typedef boost::variant<number_array, string_array> node 

template<typename Iterator>
struct parser 
      : qi::grammar<Iterator, node(), ascii::space_type> {

    parser(parser_impl* p) 
            : parser::base_type(expr_, ""), 
              error_handler(ErrorHandler(p)) {

        // Here I want to create vector on the fly 
        // and append values to newly created vector.
        // but this doesn't compile, specifically phoenix::push_back(...)
        number_array_ = qi::lit('n[')[qi::_val = construct<number_array>()] >> 
                       -(qi::double_ % ',')[phoenix::push_back(phoenix::ref(qi::_val), qi::_1)] >> ']';

        // this doesn't compile too
        string_array_ = qi::lit('s[')[qi::_val = construct<string_array>()] >> 
                       -(quoted_string % ',')[phoenix::push_back(phoenix::ref(qi::_val), qi::_1)] >> ']';                       

        quoted_string %= "'" > qi::lexeme[*(qi::char_ - "'")] > "'";

        expr_ = number_array_[qi::_val =  qi::_1] | string_array_[[qi::_val =  qi::_1]];
    }
    qi::rule<Iterator, number_array(), ascii::space_type> number_array_;
    qi::rule<Iterator, string_array(), ascii::space_type> string_array_;
    qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
    qi::rule<Iterator, node(), ascii::space_type> expr_;    
};

Solution

  • Most important note here is that I think you can just do without all the semantic actions.

    They only do what the default attribute rules already do (_val = _1 for scalar attributes, insert(_val, end(_val), _1) for conainer attributes, basically).

    This means you can just write the whole shebang as

        number_array_ = "n[" >> -(qi::double_ % ',')   >> ']';
        string_array_ = "s[" >> -(quoted_string % ',') >> ']';
    
        quoted_string = "'" > qi::lexeme[*(qi::char_ - "'")] > "'";
    
        expr_         = number_array_ | string_array_;
    

    And this will work. Note that I fixed the multibyte literals 'n[' and 's[n'.

    See also Boost Spirit: "Semantic actions are evil"?