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

标签 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::parse 时使用 qi::space skipper ,我看不出这两个字符串有何不同。谁能指出我的问题吗?

最佳答案

你的规则没有声明 skipper :

qi::rule<Iterator>  

因此,它们隐式地是lexemes。有关与 skipper 相关的 lexeme[] 的背景,请参阅 Boost spirit skipper issues

正确应用 skipper

  • 您需要在语法和规则定义中声明 skipper

    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;
    
  • 在传递 skipper 类型的实例时需要使用phrase_parse

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

固定代码

进一步说明:

  • 清理了包含内容(最好包含完整的 phoenix.hpp,因为如果您缺少细微的部分,您将会被莫名其妙的错误所困扰。当然,如果您知道哪些位,可以通过有选择地包含子 header 来随意减少编译时间)
  • 除非绝对必要,否则我强烈建议不要使用命名空间。在这种情况下,您很容易在 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;
}

关于c++ - Boost::spirit::qi 解析器不消耗整个字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25514984/

相关文章:

c++ - 该程序应该创建一个包含结果的输出文件,但文件中没有任何内容

c++ - 这些构造函数有什么区别?

c++ - 为什么我的 Linux 编译的二进制文件在 Windows 上运行时不起作用?

c++ - 为什么我的 boost::spirit 规则不能编译用于解析列表列表?

c++ - 语法分解问题

c++ - std::shared_ptr、继承和模板参数推导问题

c++ - 使用boost将给定目录中每个最低级别文件夹的文件递归复制到另一个目录 - C++

继承场景下的C++交换问题

python - 如何获取或查看xgboost的梯度统计值?

c++ - boost::spirit 和语法