【问题标题】:How do I capture the original input into the synthesized output from a spirit grammar?如何将原始输入捕获到精神语法的合成输出中?
【发布时间】:2013-04-04 14:38:44
【问题描述】:

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

我猜这可以通过语义操作或语法上下文来完成,但我找不到一个例子可以在不访问原始 parse() 的情况下做到这一点。

这是我目前所拥有的:

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

【问题讨论】:

  • @sehe,我同意可以在您标记的问题的答案中找到答案,但我当然不会期望根据标题找到它。下面的答案还显示了如何捕获规则的一部分而不是整个输入,结果证明这很有帮助。
  • 我同意,我 announced my upvote 是有原因的 :) 请注意,链接的答案不会“仅”存储 所有输入,它为解析表达式提供了精确控制选择输入的哪一部分进行包含,尽管没有iter_pos 的便利
  • 感谢您指出这一点。我得回去看看什么最适合我的其余语法。
  • 显然iter_pos 是一个很好的便利,我认为没有理由不理会它。 (另一个答案向您展示了更多关于如何在没有它的情况下获取迭代器信息的“幕后”想法。)事实上,我刚刚更新了该答案以引用iter_pos 的这个新答案。 :)

标签: c++ boost boost-spirit


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    使用自定义指令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
    

    ma​​in.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;
        }
    }
    

    【讨论】:

      【解决方案3】:

      该指令返回一个fusion::vector2&lt;&gt;,其中主题的属性作为其第一个成员,与合成属性对应的字符串作为其第二个成员。我认为这是最简单的重用方法,只要您充分调整结构即可。我不确定fusion::vector2&lt;&gt; 是否是处理属性的最佳方式,但在我所做的有限测试中,它运行良好。使用此指令,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
      

      ma​​in.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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-01
        相关资源
        最近更新 更多