问:我可以创建自己的回调吗?如何?
答:当然。您通常在 C++ 中使用的任何方式(或查看 Boost Signal2 和/或 Boost Log)
parser(std::function<bool(std::string const& s)> callback)
: parser::base_type(start),
callback(callback)
{
using namespace qi;
start %=
as_string[+graph]
[ _pass = phx::bind(callback, _1) ]
% +space
;
BOOST_SPIRIT_DEBUG_NODES((start));
}
如您所见,您甚至可以让处理程序决定是否应该忽略警告或导致匹配失败。
更新#1我扩展了示例以显示您在评论中提到的一些不相关的挑战(位置、重复检查)。希望这可以帮助
这是一个简单的演示:在 Coliru 上查看 Live ( Word
)
更新#2我什至已经做到了(a)存储源信息而不是迭代器,(b)使它与浮点数(或任何其他公开的属性类型,真的)一起“工作”。
注意它是多么惊人的相似s/Word/Number/
,基本上:Live On Coliru ( Number
)
#define BOOST_RESULT_OF_USE_DECLTYPE // needed for gcc 4.7, not clang++
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <functional>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
// okay, so you want position reporting (actually unrelated):
#include <boost/spirit/include/support_line_pos_iterator.hpp>
using It = boost::spirit::line_pos_iterator<std::string::const_iterator>;
// AST type that represents a Number 'token' (with source and location
// information)
struct Number
{
double value;
size_t line_pos;
std::string source;
explicit Number(double value = 0.0, boost::iterator_range<It> const& range = {})
:
value(value),
line_pos(get_line(range.begin())),
source(range.begin(), range.end())
{}
bool operator< (const Number& other) const { return (other.value - value) > 0.0001; }
};
// the exposed attribute for the parser:
using Words = std::set<Number>;
// the callback signature for our warning; you could make it more like
// `on_error` so it takes the iterators directly, but again, I'm doing the
// simple thing for the dmeo
using Callback = std::function<bool(Number const& s)>;
template <typename It>
struct parser : qi::grammar<It, Words()>
{
parser(Callback warning)
: parser::base_type(start),
warning(warning)
{
using namespace qi;
auto check_unique = phx::end(_val) == phx::find(_val, _1);
word =
raw [ double_ [ _a = _1 ] ] [ _val = phx::construct<Number>(_a, _1) ]
;
start %=
- word [ _pass = check_unique || phx::bind(warning, _1) ]
% +space
>> eoi
;
}
private:
Callback warning;
qi::rule<It, Number(), qi::locals<double> > word;
qi::rule<It, Words()> start;
};
int main(int argc, const char *argv[])
{
// parse command line arguments
const auto flags = std::set<std::string> { argv+1, argv+argc };
const bool fatal_warnings = end(flags) != flags.find("-Werror");
// test input
const std::string input("2.4 2.7 \n\n\n-inf \n\nNaN 88 -2.40001 \n3.14 240001e-5\n\ninf");
// warning handler
auto warning_handler = [&](Number const& w) {
std::cerr << (fatal_warnings?"Error":"Warning")
<< ": Near-identical entry '" << w.source << "' at L:" << w.line_pos << "\n";
return !fatal_warnings;
};
// do the parse
It f(begin(input)), l(end(input));
bool ok = qi::parse(f, l, parser<It>(warning_handler));
// report results
if (ok) std::cout << "parse success\n";
else std::cerr << "parse failed\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
// exit code
return ok? 0 : 255;
}
印刷:
Warning: Near-identical entry 'NaN' at L:6
Warning: Near-identical entry '240001e-5' at L:7
parse success