我有一个语法规则,如下所示:
rule<Iterator, utree()> expr, factor;
expr =
(
+(
(
toks.symbol // toks.symbol attribute type is an std::string
[
// I'm trying to force the attribute type to be utree
_val = construct<utree::list_type>()
// some code here
]
| (
factor >>
+(
// the 3 tokens here have unsigned long attribute
toks.arrow >> factor
| toks.squiggleArrow >> factor
| toks.dot >> factor
)
)
[
// I'm trying to force the attribute type to be utree
_val = construct<utree::list_type>()
// some code here
]
) >> toks.assign // toks.assign is token_def<omit>
) >> factor
) [ _val = ternary(_1, _2) ]
;
据我所知:
http://boost-spirit.com/home/articles/attribute_handling/attribute-propagation-and-attribute-compatibility/
在这种情况下,应该禁用属性兼容性,因为语义行动就在那里。尽管如此,我在里面看到了一个编译错误ternary(),表明_1的类型不是像I那样的向量会期待,但事实确实如此:
vector<
variant<std::string,
fusion::vector2<utree,
fusion::vector2<long unsigned int, utree>
>
>
>
这意味着,出于某种原因,语义动作没有发挥作用!
有什么线索表明为什么会发生这种情况吗?
注意:我在这里粘贴了一个最小化的例子来显示问题:
http://pastebin.com/rgiy2QBW
谢谢!
编译器抱怨的赋值在ternaryImpl::operator()
主体内部,这意味着,显然,语义操作确实启动了!
现在,尽管SA阻止自动属性传播是正确的(除非运算符%=用于规则分配),但这并不意味着基本语法分析器暴露的类型会神奇地改变。
您在问题中列出的类型准确地反映了解析器表达式/运算符将返回的内容:
- 或者(
|
)解析为变体 - 序列(
>>
)解析为fusion::vector2<...>
等
现在,这里是我的简单、最小的更改,它将使这个编译。确切地说,诀窍是通过将具有显式属性类型的子规则拆分为,使属性分配对您有效。这允许Spirit为您进行属性转换。
struct Parser: public qi::grammar<Iterator, utree()>
{
template <typename Tokens>
Parser(const Tokens &toks):
Parser::base_type(expression)
{
chain = +(
(
toks.symbol
[
_val = construct<utree::list_type>()
// some code here
]
| (
factor >>
+(
toks.arrow >> factor
| toks.dot >> factor
)
)
[
_val = construct<utree::list_type>()
// some code here
]
) >> toks.assign
);
expression = factor
| (chain >> factor) [ _val = ternary(_1, _2) ]
;
}
rule<Iterator, utree::list_type()> chain;
rule<Iterator, utree()> expression, factor, test;
};
注意:如果您愿意,您应该能够在没有额外规则定义的情况下执行同样的操作(例如使用
qi::attr_cast<>
、qi::as<>
),但我怀疑它是否可读/可维护
PS与使用SA的calc_utree_ast.cpp
版本相比,calc_utree_naive.cpp
必然意味着更明确的规则属性类型。
以下是完整的编译版本,注释中有一些内联注释:
// #define BOOST_SPIRIT_USE_PHOENIX_V3
// #define BOOST_RESULT_OF_USE_DECLTYPE
#include <algorithm>
#include <string>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;
namespace spirit = boost::spirit;
namespace phx = boost::phoenix;
using lex::token_def;
using qi::rule;
using qi::_1;
using qi::_2;
using qi::_val;
using spirit::utree;
using phx::construct;
// base iterator type
typedef std::string::iterator BaseIteratorT;
// token type
typedef lex::lexertl::token<BaseIteratorT, boost::mpl::vector</*double, int, */std::string> > TokenT;
// lexer type
typedef lex::lexertl::actor_lexer<TokenT> LexerT;
template <typename LexerT>
struct Tokens: public lex::lexer<LexerT>
{
Tokens()
{
using lex::_pass;
using lex::pass_flags;
// literals
symbol = "[a-zA-Z_?](\w|\?)*|@(\w|\?)+";
arrow = "->";
dot = '.';
assign = "=";
// literal rules
this->self += symbol;
this->self += arrow;
this->self += dot;
this->self += assign;
}
~Tokens() {}
// literal tokens
token_def<std::string> symbol;
token_def<> arrow, dot; // HINT: lex::omit here?
/*
* ^ Otherwise, expect these to be all exposed as Qi attributes as well, so
* _1, _2, _3, _4 a bit more than you'd expect
*/
token_def<lex::omit> assign;
};
struct ternaryImpl
{
template <typename Expr1Type, typename Expr2Type>
struct result { typedef utree type; };
template <typename Expr1Type, typename Expr2Type>
utree operator()(Expr1Type &vec, Expr2Type &operand) const {
utree ret;
for (typename Expr1Type::iterator it = vec.begin(); it != vec.end(); ++it) {
// some code
ret = *it;
// more code
}
// some code here
return ret;
}
};
phx::function<ternaryImpl> ternary = ternaryImpl();
template <typename Iterator>
struct Parser: public qi::grammar<Iterator, utree()>
{
template <typename Tokens>
Parser(const Tokens &toks):
Parser::base_type(expression)
{
chain = +(
(
toks.symbol
[
_val = construct<utree::list_type>()
// some code here
]
| (
factor >>
+(
toks.arrow >> factor
| toks.dot >> factor
)
)
[
_val = construct<utree::list_type>()
// some code here
]
) >> toks.assign
);
expression = factor
| (chain >> factor) [ _val = ternary(_1, _2) ]
;
}
rule<Iterator, utree::list_type()> chain;
rule<Iterator, utree()> expression, factor, test;
};
int main()
{
typedef Tokens<LexerT>::iterator_type IteratorT;
Tokens<LexerT> l;
Parser<IteratorT> p(l);
}