boost spirit - unable to get attributes

我是研究僧i 提交于 2019-12-24 11:34:37

问题


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?


回答1:


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";
    }
}


来源:https://stackoverflow.com/questions/21631066/boost-spirit-unable-to-get-attributes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!