如何从精神语法中将原始输入捕获到合成输出中?

时间:2013-04-04 14:38:44

标签: c++ boost boost-spirit

我正在研究boost :: spirit :: qi :: grammar,并希望将原始文本的一部分复制到语法的合成输出结构中(更具体地说,是匹配其中一个组件的部分)规则)。语法最终将被用作更复杂语法的子语法,因此我实际上无法访问原始输入。

我猜这可以通过语义动作或语法上下文来完成,但是我无法找到一个没有访问原始解析()的例子。

这是我到目前为止所拥有的:

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace qi = boost::spirit::qi;

struct A
{
    std::string header;
    std::vector<int> ints;
    std::string inttext;
};

BOOST_FUSION_ADAPT_STRUCT(
    A,
    (std::string, header)
    (std::vector<int>, ints)
    //(std::string, inttext)
)

template <typename Iterator>
struct parser : qi::grammar< Iterator, A() >
{
    parser() : parser::base_type(start)
    {
        header %= qi::lexeme[ +qi::alpha ];
        ints %= qi::lexeme[ qi::int_ % qi::char_(",_") ]; // <---- capture the original text that matches this into inttext
        start %= header >> ' ' >> ints;
    }

    qi::rule<Iterator, std::string()> header;
    qi::rule<Iterator, std::vector<int>() > ints;
    qi::rule<Iterator, A()> start;
};

int main()
{
    A output;
    std::string input("out 1,2_3");
    auto iter = input.begin();
    parser<decltype(iter)> 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.inttext << " -> [ ";
        for( auto & i: output.ints )
            std::cout << i << ' ';
        std::cout << ']' << std::endl;
    }
}

3 个答案:

答案 0 :(得分:3)

类似于您在不使用语义操作的情况下提出的问题:

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>

namespace qi = boost::spirit::qi;
using boost::spirit::repository::qi::iter_pos;

struct ints_type
{
   std::vector<int> data;
   std::string::const_iterator begin;
   std::string::const_iterator end;   
};

struct A
{
    std::string header;
    ints_type ints;
};

BOOST_FUSION_ADAPT_STRUCT(
    ints_type,
    (std::string::const_iterator, begin)
    (std::vector<int>, data)
    (std::string::const_iterator, end)
)

BOOST_FUSION_ADAPT_STRUCT(
     A,
     (std::string, header)
     (ints_type, ints)
)

template <typename Iterator>
struct parser : qi::grammar< Iterator, A() >
{
    parser() : parser::base_type(start)
    {
        header %= qi::lexeme[ +qi::alpha ];
        ints %= qi::lexeme[ iter_pos >> qi::int_ % qi::char_(",_") >> iter_pos ]; // <---- capture the original text that matches this into inttext
        start %= header >> ' ' >> ints;
    }

    qi::rule<Iterator, std::string()> header;
    qi::rule<Iterator, ints_type() > ints;
    qi::rule<Iterator, A()> start;
};

int main()
{
    A output;
    std::string input("out 1,2_3");
    auto iter = input.begin();
    parser<decltype(iter)> 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 << ": " << std::string(output.ints.begin,output.ints.end) << " -> [ ";
        for( auto & i: output.ints.data )
            std::cout << i << ' ';
        std::cout << ']' << std::endl;
    }
}

使用语义动作:

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
using boost::spirit::repository::qi::iter_pos;

struct ints_type
{
   std::vector<int> data;
   std::string inttext; 
};

struct A
{
    std::string header;
    ints_type ints;

};

BOOST_FUSION_ADAPT_STRUCT(
    ints_type,
    (std::vector<int>, data)
    (std::string, inttext)
)

BOOST_FUSION_ADAPT_STRUCT(
     A,
     (std::string, header)
     (ints_type, ints)
)

template <typename Iterator>
struct parser : qi::grammar< Iterator, A() >
{
    parser() : parser::base_type(start)
    {
        header %= qi::lexeme[ +qi::alpha ];
        ints = qi::lexeme[
                  (iter_pos >> qi::int_ % qi::char_(",_") >> iter_pos)
                     [phx::at_c<0>(qi::_val)=qi::_2,
                      phx::at_c<1>(qi::_val)=phx::construct<std::string>(qi::_1,qi::_3)] 
               ]; 
        start %= header >> ' ' >> ints;
    }

    qi::rule<Iterator, std::string()> header;
    qi::rule<Iterator, ints_type() > ints;
    qi::rule<Iterator, A()> start;
};

int main()
{
    A output;
    std::string input("out 1,2_3");
    auto iter = input.begin();
    parser<decltype(iter)> 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;
    }
}

答案 1 :(得分:1)

使用自定义指令dont_eat的另一种替代方法,它返回主题属性但不消耗任何输入。这可能会慢一些,因为规则ints被解析了两次,但我相信语法更好(这是尝试创建自己的指令的一个很好的借口)(它是“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 <boost/spirit/home/qi/meta_compiler.hpp>
#include <boost/spirit/home/qi/skip_over.hpp>
#include <boost/spirit/home/qi/parser.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/spirit/home/support/common_terminals.hpp>
#include <boost/spirit/home/qi/detail/attributes.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/spirit/home/support/handles_container.hpp>

namespace custom 
{ 
    BOOST_SPIRIT_TERMINAL(dont_eat); 
}

namespace boost { namespace spirit
{
    ///////////////////////////////////////////////////////////////////////////
    // Enablers
    ///////////////////////////////////////////////////////////////////////////
    template <>
    struct use_directive<qi::domain, custom::tag::dont_eat> // enables dont_eat
      : mpl::true_ {};
}}

namespace custom
{


    template <typename Subject>
    struct dont_eat_directive : boost::spirit::qi::unary_parser<dont_eat_directive<Subject> >
    {
        typedef Subject subject_type;
        dont_eat_directive(Subject const& subject)
          : subject(subject) {}

        template <typename Context, typename Iterator>
        struct attribute
        {
            typedef typename
                boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type
            type;
        };

        template <typename Iterator, typename Context
          , typename Skipper, typename Attribute>
        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 <typename Context>
        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 <typename Subject, typename Modifiers>
    struct make_directive<custom::tag::dont_eat, Subject, Modifiers>
    {
        typedef custom::dont_eat_directive<Subject> result_type;
        result_type operator()(unused_type, Subject const& subject, unused_type) const
        {
            return result_type(subject);
        }
    };
}}}

namespace boost { namespace spirit { namespace traits
{
    ///////////////////////////////////////////////////////////////////////////
    template <typename Subject>
    struct has_semantic_action<custom::dont_eat_directive<Subject> >
      : unary_has_semantic_action<Subject> {};

    ///////////////////////////////////////////////////////////////////////////
    template <typename Subject, typename Attribute, typename Context
        , typename Iterator>
    struct handles_container<custom::dont_eat_directive<Subject>, Attribute
        , Context, Iterator>
      : unary_handles_container<Subject, Attribute, Context, Iterator> {};
}}}

#endif

<强>的main.cpp

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include "dont_eat.hpp"

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

struct ints_type
{
   std::vector<int> data;
   std::string inttext; 
};

struct A
{
    std::string header;
    ints_type ints;

};

BOOST_FUSION_ADAPT_STRUCT(
    ints_type,
    (std::vector<int>, data)
    (std::string, inttext)
)

BOOST_FUSION_ADAPT_STRUCT(
     A,
     (std::string, header)
     (ints_type, ints)
)

template <typename Iterator>
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<Iterator, std::string()> header;
    qi::rule<Iterator, std::vector<int>() > ints;
    qi::rule<Iterator, ints_type() > ints_string;
    qi::rule<Iterator, A()> start;
};

int main()
{
    A output;
    std::string input("out 1,2_3");
    auto iter = input.begin();
    parser<decltype(iter)> 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;
    }
}

答案 2 :(得分:1)

该指令返回fusion::vector2<>,其主题的属性作为其第一个成员,而对应于合成属性的字符串作为其第二个成员。我认为只要您充分调整结构,这是最简单的重用方法。我不确定这个fusion::vector2<>是处理属性的最佳方法,但在有限的测试中,我已经完成了它。使用此指令,ints_string规则将只是:

ints_string=custom::annotate[ints];
//or ints_string=custom::annotate[qi::lexeme[qi::int_ % qi::char_(",_")]];

Example on LWS.

<强> annotate.hpp

#if !defined(ANNOTATE_HPP)
#define ANNOTATE_HPP

#if defined(_MSC_VER)
#pragma once
#endif

#include <boost/spirit/home/qi/meta_compiler.hpp>
#include <boost/spirit/home/qi/skip_over.hpp>
#include <boost/spirit/home/qi/parser.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/spirit/home/support/common_terminals.hpp>
#include <boost/spirit/home/qi/detail/attributes.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/spirit/home/support/handles_container.hpp>

namespace custom 
{ 
    BOOST_SPIRIT_TERMINAL(annotate); 
}

namespace boost { namespace spirit
{
    ///////////////////////////////////////////////////////////////////////////
    // Enablers
    ///////////////////////////////////////////////////////////////////////////
    template <>
    struct use_directive<qi::domain, custom::tag::annotate> // enables annotate
      : mpl::true_ {};
}}

namespace custom
{


    template <typename Subject>
    struct annotate_directive : boost::spirit::qi::unary_parser<annotate_directive<Subject> >
    {
        typedef Subject subject_type;
        annotate_directive(Subject const& subject)
          : subject(subject) {}

        template <typename Context, typename Iterator>
        struct attribute
        {
            typedef 
                boost::fusion::vector2<
                    typename boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type
                    ,std::string
                >
            type;
        };

        template <typename Iterator, typename Context
          , typename Skipper, typename Attribute>
        bool parse(Iterator& first, Iterator const& last
          , Context& context, Skipper const& skipper
          , Attribute& attr) const
        {
            boost::spirit::qi::skip_over(first, last, skipper);
            Iterator save = first;
            typename boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type attr_;
            if(subject.parse(first, last, context, skipper, attr_))
            {
                boost::spirit::traits::assign_to(attr_,boost::fusion::at_c<0>(attr));
                boost::spirit::traits::assign_to(std::string(save,first),boost::fusion::at_c<1>(attr));
                return true;
            }
            first = save;
            return false;
        }

        template <typename Context>
        boost::spirit::info what(Context& context) const
        {
            return info("annotate", subject.what(context));

        }

        Subject subject;
    };
}//custom
    ///////////////////////////////////////////////////////////////////////////
    // Parser generators: make_xxx function (objects)
    ///////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace qi
{
    template <typename Subject, typename Modifiers>
    struct make_directive<custom::tag::annotate, Subject, Modifiers>
    {
        typedef custom::annotate_directive<Subject> result_type;
        result_type operator()(unused_type, Subject const& subject, unused_type) const
        {
            return result_type(subject);
        }
    };
}}}

namespace boost { namespace spirit { namespace traits
{
    ///////////////////////////////////////////////////////////////////////////
    template <typename Subject>
    struct has_semantic_action<custom::annotate_directive<Subject> >
      : unary_has_semantic_action<Subject> {};

    ///////////////////////////////////////////////////////////////////////////
    template <typename Subject, typename Attribute, typename Context
        , typename Iterator>
    struct handles_container<custom::annotate_directive<Subject>, Attribute
        , Context, Iterator>
      : unary_handles_container<Subject, Attribute, Context, Iterator> {};
}}}

#endif

<强>的main.cpp

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include "annotate.hpp"

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

struct ints_type
{
   std::vector<int> data;
   std::string inttext; 
};

struct A
{
    std::string header;
    ints_type ints;

};

BOOST_FUSION_ADAPT_STRUCT(
    ints_type,
    (std::vector<int>, data)
    (std::string, inttext)
)

BOOST_FUSION_ADAPT_STRUCT(
     A,
     (std::string, header)
     (ints_type, ints)
)

template <typename Iterator>
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::annotate[ints];
        start %= header >> ' ' >> ints_string;
    }

    qi::rule<Iterator, std::string()> header;
    qi::rule<Iterator, std::vector<int>() > ints;
    qi::rule<Iterator, ints_type() > ints_string;
    qi::rule<Iterator, A()> start;
};

int main()
{
    A output;
    std::string input("out 1,2_3");
    auto iter = input.begin();
    parser<decltype(iter)> p;
    std::string annotation;
    bool r = qi::parse(iter, input.end(), custom::annotate[p], output, annotation);
    if( !r || iter != input.end() )
    {
        std::cout << "did not parse";
    }
    else
    {
        // would like output.inttext to be "1,2_3"
        std::cout << "annotation: " << annotation << std::endl;
        std::cout << output.header << ": " << output.ints.inttext << " -> [ ";
        for( auto & i: output.ints.data )
            std::cout << i << ' ';
        std::cout << ']' << std::endl;
    }
}