1

I would like to parse the following text:

group RGB
group RRGB
group GBBB
group RRGGG

The resulting AST would be a struct that represents counts of each character:

struct group
{
    int r;
    int g;
    int b;
};

For the inputs above it would be 1,1,1, 2,1,1, 0,1,3, 2,3,0.

I can't come with any grammar that would conveniently count characters and enforce their order (GBR should fail the parse).

https://www.boost.org/doc/libs/develop/libs/spirit/doc/x3/html/spirit_x3/quick_reference/directive.html

There is x3::repeat parser but in only enfoces certain count of characters, it's attribute is a container.

x3::matches[a] has bool attribute but I don't know how many times a character may appear

There is no parser that would count appearance and return number of matches. I want a grammar like x3::lit("group") >> count['R'] >> count['G'] >> count['B'], but have no idea how count should be defined.

Right now the only working solution I can think of would be x3::lit("group") >> (*x3::char_['R'] >> *x3::char_['G'] >> *x3::char_['B'])[func] which then calls func that would just operate on the string. IMO this is not a clean solution and it requires semantic actions and creates unnecessary strings.

Xeverous
  • 973
  • 1
  • 12
  • 25
  • Now it's an interesting problem, and Im curious how it can be done, since I don't have much experience with spirit. But is there a reason to use a full fledged parser when a regex might (easily enough) to the trick ? – darune Jan 10 '19 at 12:11
  • you wrote: __it's attribute is a container__ - wouldn't the size of container be the number of matches ? – darune Jan 10 '19 at 12:41
  • This RGB thing is only a part of the grammar. Regarding the container: yes, the size of the container would be the number of matches but I want to have 3 integers, not 3 vectors and avoid semantic actions. – Xeverous Jan 10 '19 at 13:25

1 Answers1

2

Slightly modifying "x3/directive/matches.hpp" you can get something like this:

#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>

namespace not_boost { namespace not_spirit { namespace not_x3
{
    template <typename Subject>
    struct count_directive : boost::spirit::x3::unary_parser<Subject, count_directive<Subject>>
    {
        using base_type = boost::spirit::x3::unary_parser<Subject, count_directive<Subject>>;
        static bool const has_attribute = true;
        using attribute_type = int;

        count_directive(Subject const& subject) : base_type(subject) {}

        template <typename Iterator, typename Context
          , typename RContext, typename Attribute>
        bool parse(Iterator& first, Iterator const& last
          , Context const& context, RContext& rcontext, Attribute& attr) const
        {
            int count=0;
            while(this->subject.parse(first, last, context, rcontext, boost::spirit::x3::unused))
            {
                count++;
            }
            boost::spirit::x3::traits::move_to(count, attr);
            return true;
        }
    };

    struct count_gen
    {
        template <typename Subject>
        count_directive<typename boost::spirit::x3::extension::as_parser<Subject>::value_type>
        operator[](Subject const& subject) const
        {
            return { boost::spirit::x3::as_parser(subject) };
        }
    };

    auto const count = count_gen{};
}}}

Running on Wandbox

llonesmiz
  • 155
  • 2
  • 11
  • 20
  • I would prefer not to modifycopy library's source, could it be transfomed to be a less intrusive (not putting new symbols into x3 namespace)? I'm afraid so far Spirit X3 has no API for writing own parsers. – Xeverous Jan 10 '19 at 13:37