我想说这里所有的语义动作都是无用的[1]。
所有这些都可以替换为放置良好的qi::attr,以简单地就地合成所需的属性,您可以再次使用 Spirit 的自动属性传播魔法。
根据我的经验,如果您开始对最简单的事情使用语义操作,那么最好不要使用 Spirit
先简化
所以,这是我对 AST node 类型的看法:
struct node {
std::string element, category;
std::vector<node> children;
};
BOOST_FUSION_ADAPT_STRUCT(node, element, category, children)
它在功能上与您的相同,但更像 c++,因此更友好。现在,你没有给出规则声明,所以“想象”它们如下:
qi::rule<It, node(), qi::space_type> ABC, ABD, test;
qi::rule<It, std::vector<node>(), qi::space_type> B;
// also for demo purposes:
B = '{' >> -(test % ',') >> '}';
现在,我对你的规则的建议,简化了:
ABC = qi::string("a") >> qi::attr("ABC") >> B >> 'c';
ABD = qi::string("a") >> qi::attr("ABD") >> B >> 'd';
test = ABC | ABD;
确实,您需要hold,因为/otherwise/ ABC/ABD 都将绑定到相同的内部合成属性node&:
ABC = qi::hold[ qi::string("a") >> qi::attr("ABC") >> B >> 'c' ];
ABD = qi::hold[ qi::string("a") >> qi::attr("ABD") >> B >> 'd' ];
这就是你想要/需要的。
测试/演示程序
我为语法构建了一些测试用例,因为我“逆向设计”它并添加了调试代码:
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
struct node {
std::string element, category;
std::vector<node> children;
};
BOOST_FUSION_ADAPT_STRUCT(node, element, category, children)
static std::ostream& operator<<(std::ostream& os, node const& n) {
return os << boost::fusion::as_vector(n);
}
static std::ostream& operator<<(std::ostream& os, std::vector<node> const& v) {
os << "{";
for (size_t i = 0; v.size()>1 && i<v.size()-1; ++i)
os << v.at(i) << ", ";
if (!v.empty()) os << v.back();
return os << "}";
}
namespace qi = boost::spirit::qi;
int main() {
using It = std::string::const_iterator;
qi::rule<It, node(), qi::space_type> ABC, ABD, test;
qi::rule<It, std::vector<node>(), qi::space_type> B;
#if 1
ABC = qi::hold[ qi::string("a") >> qi::attr("ABC") >> B >> 'c' ];
ABD = qi::hold[ qi::string("a") >> qi::attr("ABD") >> B >> 'd' ];
#else
ABC = qi::string("a") >> qi::attr("ABC") >> B >> 'c' ;
ABD = qi::string("a") >> qi::attr("ABD") >> B >> 'd' ;
#endif
test = ABC | ABD;
B = '{' >> -(test % ',') >> '}';
for (std::string const s : {
"a{a{}c}c",
"a{a{}d}d",
"a{a{}c}d",
"a{a{}d,a{}c}c",
})
{
std::cout << "\n-- Parsing '" << s << "'\n";
It f = s.begin(), l = s.end();
node parsed;
bool ok = qi::phrase_parse(f, l, test, qi::space, parsed);
if (ok)
std::cout << "Parsed: " << parsed << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
}
输出(保留):
-- Parsing 'a{a{}c}c'
Parsed: (a ABC {(a ABC {})})
-- Parsing 'a{a{}d}d'
Parsed: (a ABD {(a ABD {})})
-- Parsing 'a{a{}c}d'
Parsed: (a ABD {(a ABC {})})
-- Parsing 'a{a{}d,a{}c}c'
Parsed: (a ABC {(a ABD {}), (a ABC {})})
[1]。另见Boost Spirit: "Semantic actions are evil"?