2015-04-18 3 views
2

Я попытался изменить логическое выражение парсер здесь: Boolean expression (grammar) parser in c++C++ Boost spirit, несколько оценок одного и того же _val?

При создании переменных, я пытаюсь вызвать новую функцию, «processval». Это просто функция идентификации с некоторым выходом, чтобы увидеть, когда вызывается эта функция. Но дело в том, что эта функция называется более 10 раз, когда у меня есть только 2 переменные. Кто-нибудь понимает, почему?

Код here

#define BOOST_SPIRIT_USE_PHOENIX_V3 
#include <iostream> 
#include <string> 
#include <boost/phoenix/function/adapt_function.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <boost/spirit/include/phoenix_operator.hpp> 
#include <boost/variant/recursive_wrapper.hpp> 

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

struct op_or {}; 
struct op_and {}; 
struct op_xor {}; 
struct op_not {}; 

typedef int var; 
template <typename tag> struct binop; 
template <typename tag> struct unop; 

typedef boost::variant<var, 
     boost::recursive_wrapper<unop <op_not> >, 
     boost::recursive_wrapper<binop<op_and> >, 
     boost::recursive_wrapper<binop<op_xor> >, 
     boost::recursive_wrapper<binop<op_or> > 
     > expr; 

template <typename tag> struct binop 
{ 
    explicit binop(const expr& l, const expr& r) : oper1(l), oper2(r) { } 
    expr oper1, oper2; 
}; 

template <typename tag> struct unop 
{ 
    explicit unop(const expr& o) : oper1(o) { } 
    expr oper1; 
}; 

struct printer : boost::static_visitor<void> 
{ 
    printer(std::ostream& os) : _os(os) {} 
    std::ostream& _os; 

    // 
    void operator()(const var& v) const { _os << v; } 

    void operator()(const binop<op_and>& b) const { print(" & ", b.oper1, b.oper2); } 
    void operator()(const binop<op_or >& b) const { print(" | ", b.oper1, b.oper2); } 
    void operator()(const binop<op_xor>& b) const { print("^", b.oper1, b.oper2); } 

    void print(const std::string& op, const expr& l, const expr& r) const 
    { 
     _os << "("; 
      boost::apply_visitor(*this, l); 
      _os << op; 
      boost::apply_visitor(*this, r); 
     _os << ")"; 
    } 

    void operator()(const unop<op_not>& u) const 
    { 
     _os << "("; 
      _os << "!"; 
      boost::apply_visitor(*this, u.oper1); 
     _os << ")"; 
    } 
}; 

std::ostream& operator<<(std::ostream& os, const expr& e) 
{ boost::apply_visitor(printer(os), e); return os; } 

int processval(int s) 
{ 
    std::cout << "processing val : " << s << std::endl; 
    return s; 
} 

BOOST_PHOENIX_ADAPT_FUNCTION(int, process_, processval, 1) 

template <typename It, typename Skipper = qi::space_type> 
    struct parser : qi::grammar<It, expr(), Skipper> 
{ 
    parser() : parser::base_type(expr_) 
    { 
     using namespace qi; 

     expr_ = or_.alias(); 

     or_ = (xor_ >> "or" >> or_) [ _val = phx::construct<binop<op_or >>(_1, _2) ] | xor_ [ _val = _1 ]; 
     xor_ = (and_ >> "xor" >> xor_) [ _val = phx::construct<binop<op_xor>>(_1, _2) ] | and_ [ _val = _1 ]; 
     and_ = (not_ >> "and" >> and_) [ _val = phx::construct<binop<op_and>>(_1, _2) ] | not_ [ _val = _1 ]; 
     not_ = ("not" > simple  ) [ _val = phx::construct<unop <op_not>>(_1)  ] | simple [ _val = _1 ]; 

     simple = (('(' > expr_ > ')') | var_); 
     var_ = int_ [ _val = process_(_1) ]; 

    } 

    private: 
    qi::rule<It, var() , Skipper> var_; 
    qi::rule<It, expr(), Skipper> not_, and_, xor_, or_, simple, expr_; 
}; 

int main() 
{ 
    for (auto& input : std::list<std::string> { 
     "1 and 2" 
      }) 
    { 
     auto f(std::begin(input)), l(std::end(input)); 
     parser<decltype(f)> p; 

     try 
     { 
      expr result; 
      bool ok = qi::phrase_parse(f,l,p,qi::space,result); 

      if (!ok) 
       std::cerr << "invalid input\n"; 
      else 
       std::cout << "result: " << result << "\n"; 

     } catch (const qi::expectation_failure<decltype(f)>& e) 
     { 
      std::cerr << "expectation_failure at '" << std::string(e.first, e.last) << "'\n"; 
     } 

     if (f!=l) std::cerr << "unparsed: '" << std::string(f,l) << "'\n"; 
    } 

    return 0; 
} 

EDIT:

Лучшее решение изменить грамматику. См. Bill's comment

ответ

2

Это связано с тем, как работает парсер, подобный духу. Давайте посмотрим на то, где эти призывы к processval() происходят:

Давайте использовать более простую грамматику, чтобы говорить о том, что происходит:

A = (B >> C) | (B >> B >> C) 
B = "B" [ process_(_1) ] 
C = "C" 

Учитывая вход "BBC", мы на самом деле разобрать B три раза! Это означает, что любые семантические действия, такие как _process, которые мы приложили, также будут выполняться три раза.

Давайте ходить синтаксический, чтобы показать, почему это происходит:

  1. Мы начинаем A.
  2. Первое выражение в A, которое мы должны попробовать, это B >> C.
  3. Первый B в этом выражении успешно разбирается. Итак, мы выполняем семантическое действие.
  4. Затем мы должны следовать этому B с C. Этот синтаксический анализ завершился неудачно, потому что следующий элемент в нашей строке: B.
  5. Затем мы подходим к нашему дереву разбора, пока не найдем эту альтернативу в правиле A.
  6. Теперь у нас есть опция A = B >> B >> C. Давайте попробуем это.
  7. Первый B успешно разбирается. Поэтому мы называем наше семантическое действие во второй раз.
  8. Второй B успешно разбирается. Поэтому мы называем наше семантическое действие в третий раз.
  9. C успешно разбирается.
  10. Это значит, что A успешно проанализирован.
+0

Благодарим вас за ответ. Однако я не понимаю, почему, когда я добавляю выходы в процесс, они вызываются только один раз: [пример здесь] (http://coliru.stacked-crooked.com/a/cb2db980131b6414). И другой вопрос: в этом случае, есть ли уловка, чтобы избежать этого? Или нужно обработать анализируемый результат? – waffle

+1

@ waffle: Давайте сделаем так, чтобы наши звонки cout были evaulated ленивыми! http://coliru.stacked-crooked.com/a/78df94f0c49f4da4 –

+0

@ waffle: И чтобы исправить это, нам нужно переписать нашу грамматику.__Rewrite version 1: __ 'or_ = xor_ >> - (" или ">> или_);' __Rewrite version 2: __ 'or_ = xor_%" или ";' –