Boost :: spirit :: qi解析器不消耗整个字符串

时间:2014-08-26 20:55:28

标签: c++ boost boost-spirit-qi

我正在为一个简单的计算器创建一个语法,但是我无法弄清楚为什么一个特定的测试用例不起作用的原因。这是我的解析器的一个功能示例:

#include <iostream>
#include <vector>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_char.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
using namespace boost::spirit;
using namespace boost::phoenix;
using std::endl;
using std::cout;
using std::string;
using std::vector;

void fPushOp(const string& op){
  cout << "PushOp: " << op << endl;
}

void fPushInt(string& my_str){
  cout << "PushInt: " << my_str << endl;
}

template<class Iterator>
struct Calculator : public qi::grammar<Iterator> {

    qi::rule<Iterator>  
      expression, logical_or_expression, logical_and_expression, negate_expression, series_expression,
      single_expression, inclusive_or_expression, exclusive_or_expression, and_expression, equality_expression, 
      relational_expression, shift_expression, additive_expression, multiplicative_expression, 
      term, complement_factor, factor, number, integer, variable, variable_combo, word, result;

    Calculator() : Calculator::base_type(result)
    {
          number = 
              lexeme[
                qi::as_string[
                    ("0x" >> +qi::char_("0-9a-fA-F"))     
                  | ("0b" >> +qi::char_("0-1"))
                  | ("0" >>  +qi::char_("0-7"))
                  | +qi::char_("0-9")
                ] [bind(&fPushInt, qi::_1)]
              ] 
             ;

          complement_factor = number
              | ('~' >> number)[bind(&fPushOp, "OP_COMPLEMENT")]
              | ('!' >> number)[bind(&fPushOp, "OP_NEGATE")];
              ;
          term = complement_factor
            >> *( (".." >> complement_factor)[bind(&fPushOp, "OP_LEGER")]
                | ('\\' >> complement_factor)[bind(&fPushOp, "OP_MASK")]
                ); 
          multiplicative_expression = term
            >> *( ('/' >> term)[bind(&fPushOp, "OP_DIV")]
                | ('%' >> term)[bind(&fPushOp, "OP_MOD")]
                | ('*' >> term)[bind(&fPushOp, "OP_MUL")]
                );
          additive_expression = multiplicative_expression
            >> *( ('+' >> multiplicative_expression)[bind(&fPushOp, "OP_ADD")]
                | ('-' >> multiplicative_expression)[bind(&fPushOp, "OP_SUB")]
                );
          shift_expression = additive_expression
            >> *( (">>" >> additive_expression)[bind(&fPushOp, "OP_SRL")]
                | ("<<" >> additive_expression)[bind(&fPushOp, "OP_SLL")]
                );
          relational_expression = shift_expression
            >> *( ('<' >> shift_expression)[bind(&fPushOp, "OP_LT")]
                | ('>' >> shift_expression)[bind(&fPushOp, "OP_GT")]
                | ("<=" >> shift_expression)[bind(&fPushOp, "OP_LET")]
                | (">=" >> shift_expression)[bind(&fPushOp, "OP_GET")]
                );
          equality_expression = relational_expression 
            >> *( ("==" >> relational_expression)[bind(&fPushOp, "OP_EQ")]
                | ("!=" >> relational_expression)[bind(&fPushOp, "OP_NEQ")] 
                );
          and_expression = equality_expression 
            >> *(('&' >> equality_expression)[bind(&fPushOp, "OP_AND")]); 
          exclusive_or_expression = and_expression 
            >> *(('^' >> and_expression)[bind(&fPushOp, "OP_XOR")]); 
          inclusive_or_expression = exclusive_or_expression 
            >> *(('|' >> exclusive_or_expression)[bind(&fPushOp, "OP_OR")]); 
          single_expression = inclusive_or_expression;
          series_expression = inclusive_or_expression 
            >> *((',' >> inclusive_or_expression)[bind(&fPushOp, "OP_SERIES")]);
          logical_and_expression = series_expression
            >> *(("&&" >> series_expression)[bind(&fPushOp, "OP_LOGICAL_AND")]); 
          logical_or_expression = logical_and_expression 
            >> *(("||" >> logical_and_expression)[bind(&fPushOp, "OP_LOGICAL_OR")]);
          expression = logical_or_expression;

          result = expression;
    }
};

int main(){
  Calculator<string::const_iterator> calc;
  const string expr("!3 && 0,1");
  string::const_iterator it = expr.begin();
  parse(it, expr.end(), calc, qi::space);
  cout << "Remaining: " << (string(it,expr.end())) << endl;

  return 0;
}

预期输出如下:

PushInt: 3
PushOp: OP_NEGATE
PushInt: 0
PushInt: 1
PushOp: OP_SERIES
PushOp: OP_LOGICAL_AND
Remaining: 

expr!3 && 0,1时的当前输出似乎表明不会消耗&& 0,1

PushInt: 3
PushOp: OP_NEGATE
Remaining:  && 0,1

如果expr!3&&0,1,那么它的工作正常。在调用qi::space时使用qi::parse队长,我看不出这两个字符串是如何被区别对待的。有谁能指出我的问题?

1 个答案:

答案 0 :(得分:3)

您的规则未声明船长:

qi::rule<Iterator>  

因此,他们隐式地lexeme s 。有关船长的lexeme[]背景信息,请参阅Boost spirit skipper issues

正确应用船长

  • 您需要在语法和规则定义

    中声明队长
    template<class Iterator, typename Skipper = qi::space_type>
    struct Calculator : public qi::grammar<Iterator, Skipper> {
    
        qi::rule<Iterator, Skipper>  
          expression, logical_or_expression, logical_and_expression, negate_expression, series_expression,
          single_expression, inclusive_or_expression, exclusive_or_expression, and_expression, equality_expression, 
          relational_expression, shift_expression, additive_expression, multiplicative_expression, 
          term, complement_factor, factor, result;
    
        qi::rule<Iterator>  
          number, integer, variable, variable_combo, word;
    
  • 在传递队长类型

    的实例时需要使用phrase_parse
    phrase_parse(it, expr.end(), calc, qi::space);
    

固定代码

补充说明:

  • 清理了包含(更喜欢包含完整的phoenix.hpp,因为如果你缺少微妙的位,你的将会被莫名其妙的错误所困扰。当然,如果你知道哪些位,感觉通过有选择地包括子标题来自由减少编译时间)
  • 除非绝对必要,否则我强烈反对using namespace的建议。在这种情况下,您很容易在bind的众多品牌之一中产生混淆。而且,不,只说using boost::phoenix::ref是不够的,因为

    using boost::phoenix::ref;
    std::string s;
    bind(foo, ref(s))(); 
    
    由于ADL,

    最终使用std::ref而不是boost::phoenix::ref

#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

void fPushOp(const std::string& op){
    std::cout << "PushOp: " << op << std::endl;
}

void fPushInt(std::string& my_str){
    std::cout << "PushInt: " << my_str << std::endl;
}

template<class Iterator, typename Skipper = qi::space_type>
struct Calculator : public qi::grammar<Iterator, Skipper> {

    qi::rule<Iterator, Skipper>  
      expression, logical_or_expression, logical_and_expression,
        negate_expression, series_expression, single_expression,
        inclusive_or_expression, exclusive_or_expression, and_expression,
        equality_expression, relational_expression, shift_expression,
        additive_expression, multiplicative_expression, term,
        complement_factor, factor, result;

    qi::rule<Iterator>  
        number, integer, variable, variable_combo, word;

    Calculator() : Calculator::base_type(result)
    {
        number = 
            qi::lexeme[
              qi::as_string[
                  ("0x" >> +qi::char_("0-9a-fA-F"))     
                | ("0b" >> +qi::char_("0-1"))
                | ("0" >>  +qi::char_("0-7"))
                | +qi::char_("0-9")
              ] [phx::bind(&fPushInt, qi::_1)]
            ] 
           ;

        complement_factor = number
            | ('~' >> number)[phx::bind(&fPushOp, "OP_COMPLEMENT")]
            | ('!' >> number)[phx::bind(&fPushOp, "OP_NEGATE")];
            ;
        term = complement_factor
          >> *( (".." >> complement_factor)[phx::bind(&fPushOp, "OP_LEGER")]
              | ('\\' >> complement_factor)[phx::bind(&fPushOp, "OP_MASK")]
              ); 
        multiplicative_expression = term
          >> *( ('/' >> term)[phx::bind(&fPushOp, "OP_DIV")]
              | ('%' >> term)[phx::bind(&fPushOp, "OP_MOD")]
              | ('*' >> term)[phx::bind(&fPushOp, "OP_MUL")]
              );
        additive_expression = multiplicative_expression
          >> *( ('+' >> multiplicative_expression)[phx::bind(&fPushOp, "OP_ADD")]
              | ('-' >> multiplicative_expression)[phx::bind(&fPushOp, "OP_SUB")]
              );
        shift_expression = additive_expression
          >> *( (">>" >> additive_expression)[phx::bind(&fPushOp, "OP_SRL")]
              | ("<<" >> additive_expression)[phx::bind(&fPushOp, "OP_SLL")]
              );
        relational_expression = shift_expression
          >> *( ('<' >> shift_expression)[phx::bind(&fPushOp, "OP_LT")]
              | ('>' >> shift_expression)[phx::bind(&fPushOp, "OP_GT")]
              | ("<=" >> shift_expression)[phx::bind(&fPushOp, "OP_LET")]
              | (">=" >> shift_expression)[phx::bind(&fPushOp, "OP_GET")]
              );
        equality_expression = relational_expression 
          >> *( ("==" >> relational_expression)[phx::bind(&fPushOp, "OP_EQ")]
              | ("!=" >> relational_expression)[phx::bind(&fPushOp, "OP_NEQ")] 
              );
        and_expression = equality_expression 
          >> *(('&' >> equality_expression)[phx::bind(&fPushOp, "OP_AND")]); 
        exclusive_or_expression = and_expression 
          >> *(('^' >> and_expression)[phx::bind(&fPushOp, "OP_XOR")]); 
        inclusive_or_expression = exclusive_or_expression 
          >> *(('|' >> exclusive_or_expression)[phx::bind(&fPushOp, "OP_OR")]); 
        single_expression = inclusive_or_expression;
        series_expression = inclusive_or_expression 
          >> *((',' >> inclusive_or_expression)[phx::bind(&fPushOp, "OP_SERIES")]);
        logical_and_expression = series_expression
          >> *(("&&" >> series_expression)[phx::bind(&fPushOp, "OP_LOGICAL_AND")]); 
        logical_or_expression = logical_and_expression 
          >> *(("||" >> logical_and_expression)[phx::bind(&fPushOp, "OP_LOGICAL_OR")]);
        expression = logical_or_expression;

        result = expression;
    }
};

int main(){
  Calculator<std::string::const_iterator> calc;

  const std::string expr("!3 && 0,1");
  std::string::const_iterator it = expr.begin();

  phrase_parse(it, expr.end(), calc, qi::space);

  std::cout << "Remaining: " << std::string(it,expr.end()) << std::endl;

  return 0;
}