我正在尝试向ast添加一个大于运算符>
:代码与文档中的代码有95%相同。
下方的两个兴趣点
- 我试图在下面的代码中编写对大于的支持的代码块:注释
term
的解析中有一行无法编译,因为我还不了解语义动作:不知道如何通过phoenix和语义动作绑定lhs > rhs
的lhs
对于Spirit的普通用户来说,这个解决方案应该是微不足道的,但我仍在学习,到目前为止只是通过示例
如有任何帮助,我们将不胜感激。TIA。
代码
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/classic_symbols.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp> // std::regex not fully implemented in stdc++ yet
#include <string>
#include <map>
#include <utility>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct binary_op;
struct unary_op;
struct nil {};
struct expression_ast
{
typedef
boost::variant<
nil // can't happen!
, double
, std::string
, boost::recursive_wrapper<expression_ast>
, boost::recursive_wrapper<binary_op>
, boost::recursive_wrapper<unary_op>
>
type;
expression_ast()
: m_expr(nil()) {}
template <typename Expr>
expression_ast(Expr const& expr)
: m_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 m_expr;
};
struct binary_op
{
binary_op(
char op
, expression_ast const& left
, expression_ast const& right)
: m_op(op), m_left(left), m_right(right) {}
char m_op;
expression_ast m_left;
expression_ast m_right;
};
struct unary_op
{
unary_op(
char op
, expression_ast const& subject)
: m_op(op), m_subject(subject) {}
char m_op;
expression_ast m_subject;
};
expression_ast& expression_ast::operator+=(expression_ast const& rhs)
{
m_expr = binary_op('+', m_expr, rhs);
return *this;
}
expression_ast& expression_ast::operator-=(expression_ast const& rhs)
{
m_expr = binary_op('-', m_expr, rhs);
return *this;
}
expression_ast& expression_ast::operator*=(expression_ast const& rhs)
{
m_expr = binary_op('*', m_expr, rhs);
return *this;
}
expression_ast& expression_ast::operator/=(expression_ast const& rhs)
{
m_expr = binary_op('/', m_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));
}
};
static boost::phoenix::function<negate_expr> neg;
struct ast_print
{
typedef std::string result_type;
std::string operator()(qi::info::nil) const
{
return "";
}
std::string operator()(std::string const& str) const
{
return str;
}
std::string operator()(double d) const
{
std::ostringstream oss;
oss << d;
return oss.str();
}
std::string operator()(expression_ast const& ast) const
{
return boost::apply_visitor(*this, ast.m_expr);
}
std::string operator()(binary_op const& expr) const
{
std::ostringstream oss;
oss << "op:" << expr.m_op << "(";
oss << boost::apply_visitor(*this, expr.m_left.m_expr);
oss << ", ";
oss << boost::apply_visitor(*this, expr.m_right.m_expr);
oss << ')';
return oss.str();
}
std::string operator()(unary_op const& expr) const
{
std::ostringstream oss;
oss << "op:" << expr.m_op << "(";
oss << boost::apply_visitor(*this, expr.m_subject.m_expr);
oss << ')';
return oss.str();
}
};
std::ostream& operator << (std::ostream& stream, const expression_ast& expr)
{
ast_print printer;
stream << printer(expr) << std::endl;
return stream;
}
// CODE ADDED HERE ------------------------------------------------------------
template< char OP >
struct binary_expr
{
template <typename T>
struct result
{
typedef T type;
};
expression_ast operator()(expression_ast const& lhs,expression_ast const& rhs) const
{
return expression_ast(binary_op( OP, lhs, rhs ));
}
};
static boost::phoenix::function<binary_expr<'>'>> gt;
// CODE ADDED END HERE -------------------------------------------------------
template <typename Iterator>
struct ParserGenerator : qi::grammar<Iterator, expression_ast(), ascii::space_type>
{
ParserGenerator() : ParserGenerator::base_type(expression)
{
using qi::_val;
using qi::_1;
using qi::double_;
using qi::iso8859_1::char_;
using qi::iso8859_1::space;
using qi::eol;
using boost::spirit::ascii::string;
comment =
space >> ("//" >> *(char_ - eol) >> eol)
;
expression =
term [_val = _1]
>> *( ('+' >> term [_val += _1])
| ('-' >> term [_val -= _1])
)
;
term =
factor [_val = _1]
>> *( ('*' >> factor [_val *= _1])
| ('/' >> factor [_val /= _1])
// | ('>' >> factor [_val = gt(qi::_val,_1)]) // PROBLEM HERE!
)
;
factor =
symbol [_val = _1]
| double_ [_val = _1]
| '(' >> expression [_val = _1] >> ')'
| ('-' >> factor [_val = neg(_1)])
| ('+' >> factor [_val = _1])
;
symbol %=
(symbol_raw
>> *( string("[") >> +qi::digit >> string("]"))
>> *( string(".") >> symbol ))
;
symbol_raw %=
+(qi::alpha | qi::char_( "_" ))
;
BOOST_SPIRIT_DEBUG_NODE(expression);
BOOST_SPIRIT_DEBUG_NODE(term);
BOOST_SPIRIT_DEBUG_NODE(factor);
BOOST_SPIRIT_DEBUG_NODE(comment);
BOOST_SPIRIT_DEBUG_NODE(symbol);
BOOST_SPIRIT_DEBUG_NODE(symbol_raw);
}
qi::rule<Iterator, expression_ast(), ascii::space_type>
expression, term, factor, comment;
qi::rule<Iterator, std::string(), ascii::space_type>
symbol, symbol_raw;
};
}
int main(int argc, char* argv[])
{
using boost::spirit::ascii::space;
using client::expression_ast;
using client::ast_print;
typedef std::string::const_iterator iterator_type;
typedef client::ParserGenerator<iterator_type> ParserGenerator;
ParserGenerator pg; // our grammar
std::string predicate( "i_.c>x[0]" );
expression_ast ast;
ast_print printer;
iterator_type iter = predicate.begin(), end = predicate.end();
if ( phrase_parse( iter, end, pg, space, ast ))
{
std::cerr << printer( ast ) << std::endl;
}
return 0;
}
TL;DR使用
template <typename, typename> struct result { typedef expression_ast type; };
在binary_expr
结构内部。原因如下:
您声明了一个函子对象,用作Phoenix懒惰演员。
函子在Boost文档中被称为延迟/多态可调用对象(PCE)。这意味着构建Phoenix actors的表达式模板实际上只会在应用程序的实际时间对函数参数进行实际的重载解析/类型推导。
函数返回类型不能被推导出来(就像它不能在正则(非懒惰)C++中一样)。这就是boost库使用boost_RESULT_OF协议的地方。
注意使用C++11时,RESULT_OF协议是过时/冗余的,因为C++11具有
decltype
。为了实现这一点,在大多数编译器上,您需要#define BOOST_RESULT_OF_USE_DECLTPYE
(尽管v1_52_0的发行说明清楚地表明,
decltype
正成为足够支持它的编译器的默认版本)。
此协议所包含的内容是:
- 当您将Polymorphic可调用对象与
n
参数一起使用时,boost将在嵌套类模板result
内查找嵌套的type
typedef,并使用n
实际参数类型进行参数化这将是返回类型 - 实际的函子调用使用这些参数调用
operator()
。在这里,C++将被留下来做过载解析
通常,您应该以完全参数化的风格编写函子:
template<char OP>
struct binary_expr
{
template <typename, typename> struct result { typedef expression_ast type; };
template <typename A, typename B>
typename result<A,B>::type operator()(A const&a,B const&b) const {
return expression_ast(binary_op( OP, a, b ));
}
};
这是有效的。但是,由于参数类型的实际重载解析是由编译器完成的,因此可以将operator()
签名更改为使用固定类型:
template <typename A, typename B>
expression_ast operator()(A const&a,B const&b) const {
return binary_op(OP, a, b);
}
如果你愿意,你可以取消(一些)模板参数:
template <typename E>
expression_ast operator()(E const&a,E const&b) const {
return binary_op(OP, a, b);
}
甚至根本不是一个功能模板:
expression_ast operator()(expression_ast const&,expression_ast const&) const;
只要一个重载与传递的实际参数类型匹配,一切都很好然而,重要的一点是,无论operator()
的签名如何,都会对result<...>::type
进行评估,因此它将需要精确的预期参数数。
还需要注意的是,您可以以这种方式组合不同arity的函子:
template <char OP> struct operator_expr
{
template <typename T, typename=T> struct result
{ typedef expression_ast type; };
expression_ast operator()(expression_ast const& expr) const
{ return expression_ast(unary_op(OP, expr)); }
expression_ast operator()(expression_ast const&a, expression_ast const&b) const
{ return binary_op(OP, a, b); }
};
static boost::phoenix::function<operator_expr<'-'>> neg;
static boost::phoenix::function<operator_expr<'>'>> gt;
这是有效的,因为result<T>::type
和result<T,U>::type
都是具有此定义的有效类型表达式。
奖金备注:
你说的地方有点奇怪
template <typename T> struct result { typedef T type; };
而不是
template <typename> struct result { typedef expression_ast type; };
这是因为返回类型实际上应该是而不是,这取决于实际的参数类型。在您的示例中,参数类型通常是相同的,但在技术上没有意义。
如果使用decltype,则可以在没有BOOST_RESULT_OF协议的情况下执行操作。这意味着您可以直接删除嵌套的
result
结构你也可以调整正常功能作为凤凰城演员:
#define BOOST_SPIRIT_USE_PHOENIX_V3 // ... expression_ast neg_expr(expression_ast const&a) { return unary_op ('-', a); } expression_ast gt_expr (expression_ast const&a, expression_ast const&b) { return binary_op('>', a, b); } BOOST_PHOENIX_ADAPT_FUNCTION(expression_ast, neg, neg_expr, 1) BOOST_PHOENIX_ADAPT_FUNCTION(expression_ast, gt, gt_expr, 2)
这将基本上为您编写函数对象,包括RESULT_OF协议位。
最后,您可以使用标准的Phoenix演员,而不是定义自定义演员。在这种情况下,不需要任何上述内容:
using phx = boost::phoenix; // ... | ('>' >> factor [_val = phx::construct<binary_op>('>', _val, _1)]) // PROBLEM HERE! // ... | ('-' >> factor [_val = phx::construct<unary_op>('-', _1)])
总结
完整的代码如下:http://ideone.com/Xv9IH1并在上进行了测试
- MSVC 2012,boost 1_52_0,Win64
- GCC 4.8,boost 1_52_0,Win64