使用boost :: spirit的简单表达 [英] Simple expression with boost::spirit

查看:119
本文介绍了使用boost :: spirit的简单表达的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要解析simple_expression ::= limit int_number (days | hours | minutes).我写了语法代码

I need to parse simple_expression ::= limit int_number (days | hours | minutes). I wrote code for grammar

struct Parser: grammar<std::string::const_iterator, boost::spirit::ascii::space_type>
{
public:
  Parser(ConditionTree& a_lTree):
    Parser::base_type(limit_expression),
    m_lTree(a_lTree)
  {
    using boost::spirit::qi::uint_;

    using boost::spirit::qi::_1;
    using boost::spirit::qi::_2;

    limit_expression = limit_days_operator | limit_hours_operator | limit_minutes_operator ;

    limit_days_operator = ( string("limit") > uint_ > string("days") )[ phoenix::bind( &ConditionTree::AddDaysLimitOperator, m_lTree, _2) ]  ;
    limit_hours_operator = ( string("limit") > uint_ > string("hours") )[ phoenix::bind( &ConditionTree::AddHoursLimitOperator, m_lTree, _2) ]  ;
    limit_minutes_operator = ( string("limit") > uint_ > string("minutes") )[ phoenix::bind( &ConditionTree::AddMinutesLimitOperator, m_lTree, _2) ] ;

    BOOST_SPIRIT_DEBUG_NODE(limit_expression);

    BOOST_SPIRIT_DEBUG_NODE(limit_days_operator);
    BOOST_SPIRIT_DEBUG_NODE(limit_hours_operator);
    BOOST_SPIRIT_DEBUG_NODE(limit_minutes_operator);
  }

  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_expression;
  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_days_operator;
  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_hours_operator;
  rule<std::string::const_iterator, boost::spirit::ascii::space_type> limit_minutes_operator;

  ConditionTree& m_lTree;
}


void main()
{

  ConditionTree oTree;

  Parser parser(oTree);

  std::string strTest("limit5minutes");

  std::string::const_iterator it_begin(strTest.begin());
  std::string::const_iterator it_end(strTest.end());

  bool result = phrase_parse(it_begin, it_end, parser, space);
}

但是它不能编译并出现以下两个错误: /usr/include/boost/spirit/home/support/argument.hpp:103: ошибка: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::result_of::get_arg<boost::fusion::vector1<unsigned int&>, 1>::index_is_out_of_bounds::************)())'

But it can't compile with following 2 errors: /usr/include/boost/spirit/home/support/argument.hpp:103: ошибка: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::result_of::get_arg<boost::fusion::vector1<unsigned int&>, 1>::index_is_out_of_bounds::************)())'

/usr/include/boost/spirit/home/phoenix/bind/detail/member_function_ptr.hpp:103: ошибка: invalid initialization of reference of type 'const unsigned int&' from expression of type 'mpl_::void_'

在线 limit_days_operator = ( string("limit") > uint_ > string("days") )[ phoenix::bind( &ConditionTree::AddDaysLimitOperator, m_lTree, _2) ] ;

我试图将语义动作移至uint_:

I tryed to move semantic action to uint_ :

limit_days_operator = string("limit") > uint_ [ phoenix::bind( &ConditionTree::AddDaysLimitOperator, m_lTree, _1) ] > string("days")  ;
limit_hours_operator = string("limit") > uint_ [ phoenix::bind( &ConditionTree::AddHoursLimitOperator, m_lTree, _1) ] > string("hours")  ;

然后解析器正确读取limit5days,但不能正确读取limit5minutes,因为,正如我所看到的,limit5dayslimit5hours没什么区别.

Then the parser correctly read limit5days, but not correctly limit5minutes, because, as I see, it can't differ limit5days from limit5hours.

推荐答案

这里发生了很多事情.但是,到我整理完一些小东西以尝试阅读并使其完整(SSCCE)时,它也进行了编译:

There's a lot going on here. However, by the time I was done fixing up little things trying to read it and make it complete (SSCCE), it compiled too: Live On Coliru

我在这里简要说明了一些要点:

I briefly illuminate some individual points here:

  • 语义操作中的绑定表达式按值复制m_lTree,这样您就不能使成员发生变异.在这里需要phx::ref
  • 期望点使得无法成功解析除天" 限制之外的任何内容
  • 使用lit,除非您实际上想将字符串的值用作属性.更好的是,只需编写"limit" >> uint_ >> "days",因为其余的都由重载超载
  • the bind expressions in the semantic action copy m_lTree by value, this way you can't mutate the member. phx::ref was required here
  • the expectation points made it impossible to parse anything other than "days" limits successfully
  • use lit unless you actually want to consume the value of the string as an attribute. Better yet, just write "limit" >> uint_ >> "days" because overload overloading does the rest

您已经注意到该语法中有许多反模式,使其变得复杂:

As you've noticed there are number of anti-patterns in this grammar, making it complex:

  • Boost Spirit: "Semantic actions are evil"?
  • the reference-type member m_lTree leads to Action At A Distance and tight coupling.

总而言之,我会通过 避免语义动作 ,并将解析和评估分开. 只需解析为Limit结构:

That all said, I'd simplify things by avoiding semantic actions, and separating parsing and evaluation. Simply parsing into a Limit struct:

qi::rule<Iterator, ConditionTree::Limit(), Skipper> limit_expression;

limit_expression = "limit" >> qi::uint_ >> unit_;

处理没有单独的表达式分支的单元:

Handles the units without separate expression branches:

unit_.add("days",    ConditionTree::Limit::days)
         ("hours",   ConditionTree::Limit::hours)
         ("minutes", ConditionTree::Limit::minutes);

因为,现在,语法只是解析一个Limit对象,您可以在解析后以原子方式进行求值:

Because, now, the grammar just parses a Limit object you can do the evaluation atomically after the parse:

ConditionTree::Limit limit;
if (phrase_parse(iter, end, parser, ascii::space, limit))
{
    AddLimit(oTree, limit);
}

将解析与评估分开可以使您添加

Separating parsing from evaluation enables you to add things like

  • 重复评估相同的表达式(无需重复分析)
  • 在评估之前简化表达式树
  • 评估前验证
  • 调试
  • 稍微有趣一点,它允许您以功能样式编写ApplyLimit,而无需更改对象:

  • repeated evaluation of the same expression (without repeated parsing)
  • simplifying the expression tree before evaluation
  • validating before evaluation
  • debugging
  • slightly more interestingly, allows you to write ApplyLimit in a functional style, not mutating an object:

ConditionTree ApplyLimit(ConditionTree const& ct, Limit limit) { 
    return ct + limit; // do something here
}

  • 但最重要的是:它极大地简化了语法,并为您节省了生命的时间,而这本来可以更好地用在这里

  • but most importantly: it hugely simplifies the grammar and saves you hours of your life that were better spent otherwise

    查看此 在Coliru上直播 ,输出:

    See this Live On Coliru, outputting:

    void AddLimit(ConditionTree&, ConditionTree::Limit): 5 minutes
    

    列出

    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    
    namespace qi    = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    
    struct ConditionTree {
        struct Limit {
            unsigned value;
            enum unit_t { days, hours, minutes } unit;
        };
    
        friend void AddLimit(ConditionTree& ct, Limit limit) {
            std::cout << "AddLimit: " << limit.value;
            switch (limit.unit) {
                case Limit::days:    std::cout << " days\n"; break;
                case Limit::hours:   std::cout << " hours\n"; break;
                case Limit::minutes: std::cout << " minutes\n"; break;
            }
        }
    };
    
    BOOST_FUSION_ADAPT_STRUCT(ConditionTree::Limit, (unsigned,value)(ConditionTree::Limit::unit_t,unit))
    
    template <typename Iterator = std::string::const_iterator, typename Skipper = ascii::space_type>
    struct Parser: qi::grammar<Iterator, ConditionTree::Limit(), ascii::space_type>
    {
        public:
            Parser() : Parser::base_type(limit_expression)
        {
            unit_.add("days", ConditionTree::Limit::days)
                     ("hours", ConditionTree::Limit::hours)
                     ("minutes", ConditionTree::Limit::minutes);
    
            limit_expression = "limit" >> qi::uint_ >> unit_;
        }
    
        qi::symbols<char, ConditionTree::Limit::unit_t> unit_;
        qi::rule<Iterator, ConditionTree::Limit(), Skipper> limit_expression;
    };
    
    int main()
    {
        ConditionTree oTree;
    
        Parser<> parser;
    
        std::string strTest("limit5minutes");
    
        std::string::const_iterator iter(strTest.begin()), end(strTest.end());
    
        ConditionTree::Limit limit;
        if (phrase_parse(iter, end, parser, ascii::space, limit))
        {
            AddLimit(oTree, limit);
        }
    }
    

    这篇关于使用boost :: spirit的简单表达的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆