将 Boost Spirit Lex 语义操作转换为 Phoenix - 如何访问 _val?
Converting a Boost Spirit Lex semantic action to Phoenix - How to access _val?
我为我的 Boost Spirit Lexer 编写了一个语义操作,用于将字符串中的转义序列转换为它们所代表的含义。它运行完美,我想将它转换为 Boost Phoenix 表达式,但无法编译它。
这是有效的方法:
// the semantic action
struct ConvertEscapes
{
template <typename ItT, typename IdT, typename CtxT>
void operator () (ItT& start, ItT& end, lex::pass_flags& matched, IdT& id, CtxT& ctx)
{
static boost::wregex escapeRgx(L"(\\r)|(\\n)|(\\t)|(\\\\)|(\\\")");
static std::wstring escapeRepl = L"(?1\r)(?2\n)(?3\t)(?4\\)(?5\")";
static std::wstring wval; // static b/c set_value doesn't seem to copy
auto const& val = ctx.get_value();
wval.assign(val.begin(), val.end());
wval = boost::regex_replace(wval,
escapeRgx,
escapeRepl,
boost::match_default | boost::format_all);
ctx.set_value(wval);
}
};
// the token declaration
lex::token_def<std::wstring, wchar_t> literal_str;
// the token definition
literal_str = L"\\"([^\\\"]|(\\.))*\\""; // string with escapes
// adding it to the lexer
this->self += literal_str [ ConvertEscapes() ];
这是我尝试转换的:
this->self += literal_str
[
lex::_val = boost::regex_replace(lex::_val /* this is the place I can't figure out */,
boost::wregex(L"(\\r)|(\\n)|
(\\t)|(\\\\)|(\\\")"),
L"(?1\r)(?2\n)(?3\t)(?4\\)(?5\")",
boost::match_default | boost::format_all)
];
无法从 _val
构造 wstring
。 _val
也没有begin()
和end()
,那怎么用呢?
这个 std::wstring(lex::_start, lex::_end)
也失败了,因为这些参数不被识别为迭代器。
在 this question 中,我找到了 phoenix::construct<std::wstring>(lex::_start, lex::_end)
,但这也不会真正导致 wstring
。
如何获取当前标记的字符串或一对 wchar_t
迭代器?
我要唱那句经常听到的"Why"?
这一次,有充分的理由。
一般来说,避免语义操作:Boost Spirit: "Semantic actions are evil"?。
Phoenix Actors 不必要地比专用仿函数复杂。它们有一个甜蜜点(主要是简单的赋值或内置操作)。但是,如果演员是任何一种不平凡的人,你会看到复杂性迅速增加,不仅对人类而且对编译器也是如此。这导致
- 编译速度慢
- 次优发出的代码
- 更难维护源代码
- 新的 类 错误(例如,当表达式模板包含对 locals/temporaries 的引用时,Boost Proto(以及 Phoenix)所做的事情 而不是 阻止或信号。事实上,它通过假设所有模板表达式都是短暂的来鼓励它,但我离题了)。
Interestingly: Spirit X3 dropped Phoenix altogether, even though Phoenix was once the brain child of Spirit³.
The new style uses c++14 polymorphic lambdas, that look 90% like the helper function object in the original code, but inline as a lambda.
这个具体案例
无法工作。完全没有。
问题是您将 lazy/deferred 演员与直接调用混合在一起。那永远行不通。 phoenix::construct<std::wstring>(lex::_start, lex::_end)
的类型 不应该 为 std::wstring
。当然。它应该是一个懒惰的演员¹,可以在以后的某个时间用来创建一个std::wstring
。
既然我们知道(以及为什么)phoenix::construct<std::wstring>(lex::_start, lex::_end)
是一个 actor 类型,那么应该清楚为什么在其上调用 boost::regex_replace
是完全虚假的。你不妨说
struct implementation_defined {} bogus;
boost::regex_replace(bogus, re, fmt, boost::match_default | boost::format_all);
想知道为什么它不能编译。
总结:
您应该只拥有专用仿函数。您 可以 当然 Phoenix 可以调整您需要的正则表达式函数,但它所做的只是转移一些语法糖的复杂性。
我总是选择更天真的方法,这种方法对于经验丰富的 c++ 程序员来说更容易理解,并且可以避免走钢丝操作带来的陷阱²。
尽管如此,如果您好奇的话,这里有一个指针:
http://www.boost.org/doc/libs/1_63_0/libs/phoenix/doc/html/phoenix/modules/function.html
#include <iostream>
#include <boost/regex.hpp>
#include <boost/phoenix.hpp>
#include <boost/spirit/include/lex_lexer.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/lex.hpp>
namespace lex = boost::spirit::lex;
BOOST_PHOENIX_ADAPT_FUNCTION(std::wstring, regex_replace_, boost::regex_replace, 4)
template <typename... T>
struct Lexer : lex::lexer<T...> {
Lexer() {
// the token definition
literal_str = L"\\"([^\\\"]|(\\.))*\\""; // string with escapes
// adding it to the lexer
this->self += literal_str [
lex::_val = regex_replace_(lex::_val,
boost::wregex(L"(\\r)|(\\n)|(\\t)|(\\\\)|(\\\")"),
L"(?1\r)(?2\n)(?3\t)(?4\\)(?5\")",
boost::match_default | boost::format_all)
];
}
// the token declaration
lex::token_def<std::wstring, wchar_t> literal_str;
};
int main() {
typedef lex::lexertl::token<std::wstring::const_iterator, boost::mpl::vector<std::wstring, wchar_t>> token_type;
typedef Lexer<lex::lexertl::actor_lexer<token_type>> lexer_type;
typedef lexer_type::iterator_type lexer_iterator_type;
}
¹ 认为可以在以后调用的组合函数对象
² 如果您将其设计为 EDSL 以供非专家进一步配置,则天平可能会倾斜,但是您将承担额外的责任来记录您的 EDSL 以及可以使用它的限制条件
³我们应该说,大脑的精灵?
我为我的 Boost Spirit Lexer 编写了一个语义操作,用于将字符串中的转义序列转换为它们所代表的含义。它运行完美,我想将它转换为 Boost Phoenix 表达式,但无法编译它。
这是有效的方法:
// the semantic action
struct ConvertEscapes
{
template <typename ItT, typename IdT, typename CtxT>
void operator () (ItT& start, ItT& end, lex::pass_flags& matched, IdT& id, CtxT& ctx)
{
static boost::wregex escapeRgx(L"(\\r)|(\\n)|(\\t)|(\\\\)|(\\\")");
static std::wstring escapeRepl = L"(?1\r)(?2\n)(?3\t)(?4\\)(?5\")";
static std::wstring wval; // static b/c set_value doesn't seem to copy
auto const& val = ctx.get_value();
wval.assign(val.begin(), val.end());
wval = boost::regex_replace(wval,
escapeRgx,
escapeRepl,
boost::match_default | boost::format_all);
ctx.set_value(wval);
}
};
// the token declaration
lex::token_def<std::wstring, wchar_t> literal_str;
// the token definition
literal_str = L"\\"([^\\\"]|(\\.))*\\""; // string with escapes
// adding it to the lexer
this->self += literal_str [ ConvertEscapes() ];
这是我尝试转换的:
this->self += literal_str
[
lex::_val = boost::regex_replace(lex::_val /* this is the place I can't figure out */,
boost::wregex(L"(\\r)|(\\n)|
(\\t)|(\\\\)|(\\\")"),
L"(?1\r)(?2\n)(?3\t)(?4\\)(?5\")",
boost::match_default | boost::format_all)
];
无法从 _val
构造 wstring
。 _val
也没有begin()
和end()
,那怎么用呢?
这个 std::wstring(lex::_start, lex::_end)
也失败了,因为这些参数不被识别为迭代器。
在 this question 中,我找到了 phoenix::construct<std::wstring>(lex::_start, lex::_end)
,但这也不会真正导致 wstring
。
如何获取当前标记的字符串或一对 wchar_t
迭代器?
我要唱那句经常听到的"Why"?
这一次,有充分的理由。
一般来说,避免语义操作:Boost Spirit: "Semantic actions are evil"?。
Phoenix Actors 不必要地比专用仿函数复杂。它们有一个甜蜜点(主要是简单的赋值或内置操作)。但是,如果演员是任何一种不平凡的人,你会看到复杂性迅速增加,不仅对人类而且对编译器也是如此。这导致
- 编译速度慢
- 次优发出的代码
- 更难维护源代码
- 新的 类 错误(例如,当表达式模板包含对 locals/temporaries 的引用时,Boost Proto(以及 Phoenix)所做的事情 而不是 阻止或信号。事实上,它通过假设所有模板表达式都是短暂的来鼓励它,但我离题了)。
Interestingly: Spirit X3 dropped Phoenix altogether, even though Phoenix was once the brain child of Spirit³.
The new style uses c++14 polymorphic lambdas, that look 90% like the helper function object in the original code, but inline as a lambda.
这个具体案例
无法工作。完全没有。
问题是您将 lazy/deferred 演员与直接调用混合在一起。那永远行不通。 phoenix::construct<std::wstring>(lex::_start, lex::_end)
的类型 不应该 为 std::wstring
。当然。它应该是一个懒惰的演员¹,可以在以后的某个时间用来创建一个std::wstring
。
既然我们知道(以及为什么)phoenix::construct<std::wstring>(lex::_start, lex::_end)
是一个 actor 类型,那么应该清楚为什么在其上调用 boost::regex_replace
是完全虚假的。你不妨说
struct implementation_defined {} bogus;
boost::regex_replace(bogus, re, fmt, boost::match_default | boost::format_all);
想知道为什么它不能编译。
总结:
您应该只拥有专用仿函数。您 可以 当然 Phoenix 可以调整您需要的正则表达式函数,但它所做的只是转移一些语法糖的复杂性。
我总是选择更天真的方法,这种方法对于经验丰富的 c++ 程序员来说更容易理解,并且可以避免走钢丝操作带来的陷阱²。
尽管如此,如果您好奇的话,这里有一个指针:
http://www.boost.org/doc/libs/1_63_0/libs/phoenix/doc/html/phoenix/modules/function.html
#include <iostream>
#include <boost/regex.hpp>
#include <boost/phoenix.hpp>
#include <boost/spirit/include/lex_lexer.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/lex.hpp>
namespace lex = boost::spirit::lex;
BOOST_PHOENIX_ADAPT_FUNCTION(std::wstring, regex_replace_, boost::regex_replace, 4)
template <typename... T>
struct Lexer : lex::lexer<T...> {
Lexer() {
// the token definition
literal_str = L"\\"([^\\\"]|(\\.))*\\""; // string with escapes
// adding it to the lexer
this->self += literal_str [
lex::_val = regex_replace_(lex::_val,
boost::wregex(L"(\\r)|(\\n)|(\\t)|(\\\\)|(\\\")"),
L"(?1\r)(?2\n)(?3\t)(?4\\)(?5\")",
boost::match_default | boost::format_all)
];
}
// the token declaration
lex::token_def<std::wstring, wchar_t> literal_str;
};
int main() {
typedef lex::lexertl::token<std::wstring::const_iterator, boost::mpl::vector<std::wstring, wchar_t>> token_type;
typedef Lexer<lex::lexertl::actor_lexer<token_type>> lexer_type;
typedef lexer_type::iterator_type lexer_iterator_type;
}
¹ 认为可以在以后调用的组合函数对象
² 如果您将其设计为 EDSL 以供非专家进一步配置,则天平可能会倾斜,但是您将承担额外的责任来记录您的 EDSL 以及可以使用它的限制条件
³我们应该说,大脑的精灵?