How do I capture the original input into the synthesized output from a spirit grammar?

后端 未结 3 1401
日久生厌
日久生厌 2021-01-06 22:14

I\'m working on a boost::spirit::qi::grammar and would like to copy a portion of the original text into the synthesized output structure of the grammar (more specifically, t

3条回答
  •  一生所求
    2021-01-06 23:01

    Another alternative using a custom directive dont_eat that returns the subject attribute but does not consume any input. This is possibly slower since the rule ints is parsed twice, but I believe that the syntax is nicer (and it's a good excuse to try creating your own directive)(It's a slightly modified version of "boost/spirit/home/qi/directive/lexeme.hpp").

    dont_eat.hpp

    #if !defined(DONT_EAT_HPP)
    #define DONT_EAT_HPP
    
    #if defined(_MSC_VER)
    #pragma once
    #endif
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    namespace custom 
    { 
        BOOST_SPIRIT_TERMINAL(dont_eat); 
    }
    
    namespace boost { namespace spirit
    {
        ///////////////////////////////////////////////////////////////////////////
        // Enablers
        ///////////////////////////////////////////////////////////////////////////
        template <>
        struct use_directive // enables dont_eat
          : mpl::true_ {};
    }}
    
    namespace custom
    {
    
    
        template 
        struct dont_eat_directive : boost::spirit::qi::unary_parser >
        {
            typedef Subject subject_type;
            dont_eat_directive(Subject const& subject)
              : subject(subject) {}
    
            template 
            struct attribute
            {
                typedef typename
                    boost::spirit::traits::attribute_of::type
                type;
            };
    
            template 
            bool parse(Iterator& first, Iterator const& last
              , Context& context, Skipper const& skipper
              , Attribute& attr) const
            {
                Iterator temp = first;
                boost::spirit::qi::skip_over(temp, last, skipper);
                return subject.parse(temp, last, context, skipper, attr);
            }
    
            template 
            boost::spirit::info what(Context& context) const
            {
                return info("dont_eat", subject.what(context));
    
            }
    
            Subject subject;
        };
    }//custom
        ///////////////////////////////////////////////////////////////////////////
        // Parser generators: make_xxx function (objects)
        ///////////////////////////////////////////////////////////////////////////
    namespace boost { namespace spirit { namespace qi
    {
        template 
        struct make_directive
        {
            typedef custom::dont_eat_directive result_type;
            result_type operator()(unused_type, Subject const& subject, unused_type) const
            {
                return result_type(subject);
            }
        };
    }}}
    
    namespace boost { namespace spirit { namespace traits
    {
        ///////////////////////////////////////////////////////////////////////////
        template 
        struct has_semantic_action >
          : unary_has_semantic_action {};
    
        ///////////////////////////////////////////////////////////////////////////
        template 
        struct handles_container, Attribute
            , Context, Iterator>
          : unary_handles_container {};
    }}}
    
    #endif
    

    main.cpp

    #include 
    #include 
    #include 
    #include 
    #include "dont_eat.hpp"
    
    namespace qi = boost::spirit::qi;
    namespace phx = boost::phoenix;
    
    struct ints_type
    {
       std::vector data;
       std::string inttext; 
    };
    
    struct A
    {
        std::string header;
        ints_type ints;
    
    };
    
    BOOST_FUSION_ADAPT_STRUCT(
        ints_type,
        (std::vector, data)
        (std::string, inttext)
    )
    
    BOOST_FUSION_ADAPT_STRUCT(
         A,
         (std::string, header)
         (ints_type, ints)
    )
    
    template 
    struct parser : qi::grammar< Iterator, A() >
    {
        parser() : parser::base_type(start)
        {
            header %= qi::lexeme[ +qi::alpha ];
            ints = qi::lexeme[qi::int_ % qi::char_(",_")]; 
            ints_string = custom::dont_eat[ints] >> qi::as_string[qi::raw[ints]];
            start %= header >> ' ' >> ints_string;
        }
    
        qi::rule header;
        qi::rule() > ints;
        qi::rule ints_string;
        qi::rule start;
    };
    
    int main()
    {
        A output;
        std::string input("out 1,2_3");
        auto iter = input.begin();
        parser p;
        bool r = qi::parse(iter, input.end(), p, output);
        if( !r || iter != input.end() )
        {
            std::cout << "did not parse";
        }
        else
        {
            // would like output.inttext to be "1,2_3"
            std::cout << output.header << ": " << output.ints.inttext << " -> [ ";
            for( auto & i: output.ints.data )
                std::cout << i << ' ';
            std::cout << ']' << std::endl;
        }
    }
    

提交回复
热议问题