Question

I have the following code:

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>

#include <iostream>
#include <string>
#include <vector>

struct parameter
{
  std::string type;
  std::string name;
};

BOOST_FUSION_ADAPT_STRUCT(
  parameter,
  (std::string, type)
  (std::string, name)
)

inline
std::ostream& operator<<(std::ostream& os, const parameter& param)
{
  os << param.type << ' ' << param.name;

  return os;
}

inline
std::ostream& operator<<(std::ostream& os, const std::vector<parameter>& parameters)
{
  for (const auto& param : parameters)
  {
    os << param;
  }

  return os;
}

struct function
{
  std::vector<parameter> parameters;
};

BOOST_FUSION_ADAPT_STRUCT(
  ::function,
  (std::vector<parameter>, parameters)
)

template <typename Iterator>
struct function_parser : boost::spirit::qi::grammar<Iterator, function(), boost::spirit::qi::ascii::space_type>
{
  function_parser() : function_parser::base_type(start)
  {
    using boost::spirit::qi::alnum;
    using boost::spirit::qi::alpha;

    string %= alpha >> *alnum;
    BOOST_SPIRIT_DEBUG_NODE(string);

    param %= string >> string;
    BOOST_SPIRIT_DEBUG_NODE(param);

    start %= *(param % ',');
    BOOST_SPIRIT_DEBUG_NODE(start);
  }

  boost::spirit::qi::rule<Iterator, std::string()> string;
  boost::spirit::qi::rule<Iterator, parameter, boost::spirit::qi::ascii::space_type> param;
  boost::spirit::qi::rule<Iterator, function(), boost::spirit::qi::ascii::space_type> start;
};

int main()
{
  std::string input_data("int bar, int baz");

  function fn;
  auto itr = input_data.begin();
  auto end = input_data.end();
  function_parser<decltype(itr)> g;
  bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, fn);
  if (res && itr == end)
  {
    std::cout << boost::fusion::tuple_open('[');
    std::cout << boost::fusion::tuple_close(']');
    std::cout << boost::fusion::tuple_delimiter(", ");

    std::cout << "Parsing succeeded \n";
    std::cout << "got: " << boost::fusion::as_vector(fn) << std::endl;
  }
  else
  {
    std::cout << "Parsing failed \n";
  }
}

Output

<start>
  <try>int bar, int baz</try>
  <param>
    <try>int bar, int baz</try>
    <string>
      <try>int bar, int baz</try>
      <success> bar, int baz</success>
      <attributes>[[i, n, t]]</attributes>
    </string>
    <string>
      <try>bar, int baz</try>
      <success>, int baz</success>
      <attributes>[[b, a, r]]</attributes>
    </string>
    <success>, int baz</success>
    <attributes>[]</attributes>
  </param>
  <param>
    <try> int baz</try>
    <string>
      <try>int baz</try>
      <success> baz</success>
      <attributes>[[i, n, t]]</attributes>
    </string>
    <string>
      <try>baz</try>
      <success></success>
      <attributes>[[b, a, z]]</attributes>
    </string>
    <success></success>
    <attributes>[]</attributes>
  </param>
  <param>
    <try></try>
    <string>
      <try></try>
      <fail/>
    </string>
    <fail/>
  </param>
  <success></success>
  <attributes>[[[]]]</attributes>
</start>
Parsing succeeded
got: []

Why there isn't any parameters listed after "got: " message while parsing succeeded? What am I doing wrong?

Was it helpful?

Solution

Three issues:

  1. You're missing parentheses here:

    boost::spirit::qi::rule<Iterator, parameter(), boost::spirit::qi::ascii::space_type> param;
                                               ^^
    

    I agree that it sucks that it isn't apparently easy to improve diagnostics for "stray" template arguments. However, this one is something that you will get used to pretty quickly, as it is idiomatic for any grammar/rule definition.

  2. Your function struct contains only one element, and there are limitations that "break" the abstraction with single-element tuples. This is a /very wellknown/ issue, and should be relieved in Spirit V3. For now, either skip the struct:

    using function = std::vector<parameter>;
    

    or use the 'canonical' workaround:

    start %= eps >> (params % ',');
    
  3. Your start rule has a spurious kleen star. If you want to allow empty param lists, do

    start %= eps >> -(params % ',');
    

    Subtly different (allowing multiple consecutive ,:

    start %= eps >> (-params % ',');
    
  4. Remaining element of style: %= is redundant on rules without semantic actions

See it Live On Coliru

Output:

<start>
  <try>int bar, int baz</try>
  <param>

    <!-- snip -->
    <attributes>[[[i, n, t], [b, a, z]]]</attributes>
  </param>
  <success></success>
  <attributes>[[[[[i, n, t], [b, a, r]], [[i, n, t], [b, a, z]]]]]</attributes>
</start>
Parsing succeeded 
got: [int bar; int baz; ]

Full Code

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>

#include <iostream>
#include <string>
#include <vector>

struct parameter
{
    std::string type;
    std::string name;
};

BOOST_FUSION_ADAPT_STRUCT(
    parameter,
    (std::string, type)
    (std::string, name)
)

inline std::ostream& operator<<(std::ostream& os, const parameter& param) { return os << param.type << ' ' << param.name;  }
inline std::ostream& operator<<(std::ostream& os, const std::vector<parameter>& parameters) {
  for (const auto& param : parameters) { os << param << "; "; }
  return os;
}

struct function
{
    std::vector<parameter> parameters;
};

BOOST_FUSION_ADAPT_STRUCT(
    ::function,
    (std::vector<parameter>, parameters)
)

template <typename Iterator>
struct function_parser : boost::spirit::qi::grammar<Iterator, function(), boost::spirit::qi::ascii::space_type>
{
    function_parser() : function_parser::base_type(start)
    {
        using boost::spirit::qi::alnum;
        using boost::spirit::qi::alpha;

        string %= alpha >> *alnum;
        BOOST_SPIRIT_DEBUG_NODE(string);

        param %= string >> string;
        BOOST_SPIRIT_DEBUG_NODE(param);

        start = boost::spirit::qi::eps >> (param % ',');
        BOOST_SPIRIT_DEBUG_NODE(start);
    }

    boost::spirit::qi::rule<Iterator, std::string()> string;
    boost::spirit::qi::rule<Iterator, parameter(), boost::spirit::qi::ascii::space_type> param;
    boost::spirit::qi::rule<Iterator, function(), boost::spirit::qi::ascii::space_type> start;
};

int main()
{
    std::string input_data("int bar, int baz");

    function fn;
    auto itr = input_data.begin();
    auto end = input_data.end();
    function_parser<decltype(itr)> g;
    bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, fn);
    if (res && itr == end)
    {
        std::cout << boost::fusion::tuple_open('[');
        std::cout << boost::fusion::tuple_close(']');
        std::cout << boost::fusion::tuple_delimiter(", ");

        std::cout << "Parsing succeeded \n";
        std::cout << "got: " << boost::fusion::as_vector(fn) << std::endl;
        //std::cout << "got: " << fn << std::endl;
    }
    else
    {
        std::cout << "Parsing failed \n";
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top