2014-11-27 4 views
1

나는 boost :: spirit을 사용하여 간단한 표현 파서를 작성하려고했다. 계산기 예제 (참조 : http://www.boost.org/doc/libs/1_41_0/libs/spirit/example/qi/calc2_ast.cpp)로 시작하여 변수에 대한 참조를 나타내는 "ref"규칙을 추가하려고했습니다. 나는 예에 만들어진부스트 파서가 컴파일되지 않음

#include <boost/config/warning_disable.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/variant/recursive_variant.hpp> 
#include <boost/variant/apply_visitor.hpp> 
#include <boost/spirit/include/phoenix_operator.hpp> 
#include <boost/spirit/include/phoenix_function.hpp> 

#include <iostream> 
#include <vector> 
#include <string> 
namespace client{ 


    namespace qi = boost::spirit::qi; 
    namespace ascii = boost::spirit::ascii; 

    /////////////////////////////////////////////////////////////////////////// 
    // Our AST 
    /////////////////////////////////////////////////////////////////////////// 
    struct binary_op; 
    struct unary_op; 
    struct ref { 
     std::string name; 
     ref(const std::string& name) : name(name){} 
    }; 
    struct nil {}; 

    struct expression_ast 
    { 
     typedef 
      boost::variant< 
       nil // can't happen! 
       , unsigned int 
       , ref 
       , boost::recursive_wrapper<expression_ast> 
       , boost::recursive_wrapper<binary_op> 
       , boost::recursive_wrapper<unary_op> 
      > 
     type; 

     expression_ast() 
      : expr(nil()) {} 

     template <typename Expr> 
     expression_ast(Expr const& expr) 
      : expr(expr) {} 

     expression_ast& operator+=(expression_ast const& rhs); 
     expression_ast& operator-=(expression_ast const& rhs); 
     expression_ast& operator*=(expression_ast const& rhs); 
     expression_ast& operator/=(expression_ast const& rhs); 

     type expr; 
    }; 



    struct binary_op 
    { 
     binary_op(
      char op 
      , expression_ast const& left 
      , expression_ast const& right) 
     : op(op), left(left), right(right) {} 

     char op; 
     expression_ast left; 
     expression_ast right; 
    }; 

    struct unary_op 
    { 
     unary_op(
      char op 
      , expression_ast const& subject) 
     : op(op), subject(subject) {} 

     char op; 
     expression_ast subject; 
    }; 

    expression_ast& expression_ast::operator+=(expression_ast const& rhs) 
    { 
     expr = binary_op('+', expr, rhs); 
     return *this; 
    } 

    expression_ast& expression_ast::operator-=(expression_ast const& rhs) 
    { 
     expr = binary_op('-', expr, rhs); 
     return *this; 
    } 

    expression_ast& expression_ast::operator*=(expression_ast const& rhs) 
    { 
     expr = binary_op('*', expr, rhs); 
     return *this; 
    } 

    expression_ast& expression_ast::operator/=(expression_ast const& rhs) 
    { 
     expr = binary_op('/', expr, rhs); 
     return *this; 
    } 

    // We should be using expression_ast::operator-. There's a bug 
    // in phoenix type deduction mechanism that prevents us from 
    // doing so. Phoenix will be switching to BOOST_TYPEOF. In the 
    // meantime, we will use a phoenix::function below: 
    struct negate_expr 
    { 
     template <typename T> 
     struct result { typedef T type; }; 

     expression_ast operator()(expression_ast const& expr) const 
     { 
      return expression_ast(unary_op('-', expr)); 
     } 
    }; 

    struct ref_maker 
    { 
     template <typename T> 
     struct result { typedef T type; }; 

     ref operator()(std::string const& expr) const 
     { 
      return expr; 
     } 
    }; 


    boost::phoenix::function<negate_expr> neg; 
    boost::phoenix::function<ref_maker> make_ref; 

/////////////////////////////////////////////////////////////////////////// 
// Walk the tree 
/////////////////////////////////////////////////////////////////////////// 
struct ast_print 
{ 
    typedef void result_type; 

    void operator()(qi::info::nil) const { 
    } 
    void operator()(int n) const { 
     std::cout << n; 
    } 

    void operator()(expression_ast const& ast) const { 
     boost::apply_visitor(*this, ast.expr); 
    } 

    void operator()(binary_op const& expr) const { 
     std::cout << "op:" << expr.op << "("; 
     boost::apply_visitor(*this, expr.left.expr); 
     std::cout << ", "; 
     boost::apply_visitor(*this, expr.right.expr); 
     std::cout << ')'; 
    } 

    void operator()(unary_op const& expr) const { 
     std::cout << "op:" << expr.op << "("; 
     boost::apply_visitor(*this, expr.subject.expr); 
     std::cout << ')'; 
    } 

    void operator()(ref const& expr) const { 
     std::cout << "ref:" << expr.name ; 
    } 
}; 

    /////////////////////////////////////////////////////////////////////////// 
    // Our calculator grammar 
    /////////////////////////////////////////////////////////////////////////// 
    template <typename Iterator> 
    struct calculator : qi::grammar<Iterator, expression_ast(), ascii::space_type> 
    { 
     calculator() : calculator::base_type(expression) 
     { 
      using qi::_val; 
      using qi::_1; 
      using qi::uint_; 
      using qi::lit; 
      using ascii::alnum; 

      identifier = (alnum [_val += _1]); 

      expression = 
       term       [_val = _1] 
       >> *( ('+' >> term   [_val += _1]) 
        | ('-' >> term   [_val -= _1]) 
        ) 
       ; 

      term = 
       factor       [_val = _1] 
       >> *( ('*' >> factor   [_val *= _1]) 
        | ('/' >> factor   [_val /= _1]) 
        ) 
       ; 

      factor = 
       uint_       [_val = _1] 
       | identifier     [_val = make_ref(_1)] 
       | '(' >> expression   [_val = _1] >> ')' 
       | ('-' >> factor    [_val = neg(_1)]) 
       | ('+' >> factor    [_val = _1]) 
       ; 
     } 
     qi::rule<Iterator, std::string(), ascii::space_type> identifier; 
     qi::rule<Iterator, expression_ast(), ascii::space_type> expression, term, factor; 
    }; 
} 

/////////////////////////////////////////////////////////////////////////////// 
// Main program 
/////////////////////////////////////////////////////////////////////////////// 
int 
main() 
{ 
    std::cout << "/////////////////////////////////////////////////////////\n\n"; 
    std::cout << "Expression parser...\n\n"; 
    std::cout << "/////////////////////////////////////////////////////////\n\n"; 
    std::cout << "Type an expression...or [q or Q] to quit\n\n"; 

    using client::ascii::space; 
    using client::expression_ast; 
    using client::ast_print; 

    typedef std::string::const_iterator iterator_type; 
    typedef client::calculator<iterator_type> calculator; 

    calculator calc; // Our grammar 

    std::string str; 
    while (std::getline(std::cin, str)) 
    { 
     if (str.empty() || str[0] == 'q' || str[0] == 'Q') 
      break; 

     std::string::const_iterator iter = str.begin(); 
     std::string::const_iterator end = str.end(); 
     expression_ast ast; 
     ast_print printer; 
     bool r = phrase_parse(iter, end, calc, space, ast); 

     if (r && iter == end) 
     { 
      std::cout << "-------------------------\n"; 
      std::cout << "Parsing succeeded\n"; 
      printer(ast); 
      std::cout << "\n-------------------------\n"; 
     } 
     else 
     { 
      std::string rest(iter, end); 
      std::cout << "-------------------------\n"; 
      std::cout << "Parsing failed\n"; 
      std::cout << "stopped at: \": " << rest << "\"\n"; 
      std::cout << "-------------------------\n"; 
     } 
    } 

    std::cout << "Bye... :-) \n\n"; 
    return 0; 
} 

유일한 변화는 다음과 같다 : :

  1. ref 구조체와 변형에 항목을 추가 여기 내 코드입니다.
  2. string
  3. identifierstring를 변환 make_ref를 호출하는 factor 규칙에 identifier 옵션을 추가 refstring에서
  4. 을 만들기위한 펑터와 피닉스 기능 make_ref 만들기로 하나의 변수 이름을 읽는 identifier 규칙을 추가 ref으로

코드가 작동하지 않습니다. 이 경우 다음과 같은 종류의 오류 항상 :

/usr/include/boost/spirit/home/phoenix/core/detail/function_eval.hpp:135:69: error: could not convert ‘bench::expr::ref_maker::operator()(const string&) const((*(const string*)boost::phoenix::detail::help_rvalue_deduction<std::basic_string<char> >((* &(& _0)->boost::spirit::argument<N>::eval<boost::phoenix::basic_environment<boost::fusion::vector1<std::basic_string<char>&>, boost::spirit::context<boost::fusion::cons<bench::expr::expression_ast&, boost::fusion::nil>, boost::fusion::vector0<> >, bool, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> >((* & env))))))’ from ‘bench::expr::ref’ to ‘std::basic_string<char>’ 

피닉스 대신 refstring을 반환 make_ref를 원하는 것 같다,하지만이 내가 원하는 것이 아니다. 여기서 내가 뭘 잘못하고 있니?

답변

2

ref_maker의 결과는 항상 입력 인수와 같지 않은 ref입니다. 이

struct ref_maker 
{ 
    template <typename T> 
    struct result { typedef ref type; }; // <-- here. 

    ref operator()(std::string const& expr) const 
    { 
     return expr; 
    } 
}; 

또한, client::와 주에서 bench::expr:: 교체 확인하거나 네임 스페이스 bench::expr가 존재하지 않는 오류를 얻을 수 있습니다.

관련 문제