避免在增强精神语义动作中构建模板
avoid construct template in boost spirit semantic action
此代码有效。这个问题是关于让它(看起来)更好。
我看过关于 utree
s 的文章,但我不确定这是最好的方法。
让我向您展示代码的 "ugly" 版本,它使用 construct<>
newcall =(nocaselit(L"new") > tyname)
[_val = construct<common_node>(type_cmd_new,key_typename, construct<std::wstring>(_1))];
规则声明为:
qi::rule<Iterator, common_node(), Skipper> newcall
qi::rule<Iterator, std::wstring()> tyname;
目标公共 AST 节点是:
struct common_node {
template <typename X>
common_node(node_type t, node_key k1, const X & m1)
第一个参数是节点类型,第二个是某种成员键,最后一个是作为模板参数给出的有效负载(稍后存储在变体中)。
我们可以避免使用 construct
模板吗?
这是我 always link to Boost Spirit: "Semantic actions are evil"? 的经典案例:避免语义操作。
在这种情况下,我不知道你的 AST 到底是什么样子([=30= 是什么],key_typename
从哪里来等等)所以我不能给你看太多。
通常我会调整节点类型并为具体节点类型声明规则。如果这不起作用,我更喜欢 phoenix::function<>
包装器:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
struct SomeComplicatedType {
enum Type { None, NewCall };
struct Key{};
SomeComplicatedType(Type = {}, Key = {}, std::string n = "") : name(std::move(n)) { }
std::string name;
};
static SomeComplicatedType::Key const s_default_key;
template <typename It>
struct Grammar : qi::grammar<It, SomeComplicatedType()>
{
Grammar() : Grammar::base_type(start) {
using namespace qi;
start = skip(space) [new_];
tyname = raw[(alpha|'_') >> +(alnum|'_')];
new_ = no_case["new"] > tyname [_val = make_new(_1)];
BOOST_SPIRIT_DEBUG_NODES((start)(new_)(tyname))
}
private:
qi::rule<It, SomeComplicatedType()> start;
qi::rule<It, SomeComplicatedType(), qi::space_type> new_;
qi::rule<It, std::string()> tyname;
struct make_complicated_t {
SomeComplicatedType::Type _type;
SomeComplicatedType operator()(std::string const& s) const {
return SomeComplicatedType{_type, s_default_key, s};
}
};
boost::phoenix::function<make_complicated_t> make_new { make_complicated_t{SomeComplicatedType::NewCall } };
};
int main() {
std::string const input = "new Sandwich";
SomeComplicatedType result;
if (parse(input.begin(), input.end(), Grammar<std::string::const_iterator>{}, result))
std::cout << "Parsed: " << result.name << "\n";
}
版画
Parsed: Sandwich
此代码有效。这个问题是关于让它(看起来)更好。
我看过关于 utree
s 的文章,但我不确定这是最好的方法。
让我向您展示代码的 "ugly" 版本,它使用 construct<>
newcall =(nocaselit(L"new") > tyname)
[_val = construct<common_node>(type_cmd_new,key_typename, construct<std::wstring>(_1))];
规则声明为:
qi::rule<Iterator, common_node(), Skipper> newcall
qi::rule<Iterator, std::wstring()> tyname;
目标公共 AST 节点是:
struct common_node {
template <typename X>
common_node(node_type t, node_key k1, const X & m1)
第一个参数是节点类型,第二个是某种成员键,最后一个是作为模板参数给出的有效负载(稍后存储在变体中)。
我们可以避免使用 construct
模板吗?
这是我 always link to Boost Spirit: "Semantic actions are evil"? 的经典案例:避免语义操作。
在这种情况下,我不知道你的 AST 到底是什么样子([=30= 是什么],key_typename
从哪里来等等)所以我不能给你看太多。
通常我会调整节点类型并为具体节点类型声明规则。如果这不起作用,我更喜欢 phoenix::function<>
包装器:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
struct SomeComplicatedType {
enum Type { None, NewCall };
struct Key{};
SomeComplicatedType(Type = {}, Key = {}, std::string n = "") : name(std::move(n)) { }
std::string name;
};
static SomeComplicatedType::Key const s_default_key;
template <typename It>
struct Grammar : qi::grammar<It, SomeComplicatedType()>
{
Grammar() : Grammar::base_type(start) {
using namespace qi;
start = skip(space) [new_];
tyname = raw[(alpha|'_') >> +(alnum|'_')];
new_ = no_case["new"] > tyname [_val = make_new(_1)];
BOOST_SPIRIT_DEBUG_NODES((start)(new_)(tyname))
}
private:
qi::rule<It, SomeComplicatedType()> start;
qi::rule<It, SomeComplicatedType(), qi::space_type> new_;
qi::rule<It, std::string()> tyname;
struct make_complicated_t {
SomeComplicatedType::Type _type;
SomeComplicatedType operator()(std::string const& s) const {
return SomeComplicatedType{_type, s_default_key, s};
}
};
boost::phoenix::function<make_complicated_t> make_new { make_complicated_t{SomeComplicatedType::NewCall } };
};
int main() {
std::string const input = "new Sandwich";
SomeComplicatedType result;
if (parse(input.begin(), input.end(), Grammar<std::string::const_iterator>{}, result))
std::cout << "Parsed: " << result.name << "\n";
}
版画
Parsed: Sandwich