【问题标题】:trigger warning from boost spirit parser从 boost Spirit 解析器触发警告
【发布时间】:2013-10-07 02:07:56
【问题描述】:

如何在 boost Spirit 解析器中添加警告。

编辑: ...可以报告位置问题

例如,如果我有一个整数解析器:

('0' >> oct)
| int_

我希望能够做这样的事情:

('0' >> oct)
| "-0" --> trigger warning("negative octal values are not supported, it will be interpreted as negative decimal value and the leading 0 will be ignored")
| int_

【问题讨论】:

  • 是常规语义动作不是一个选项吗?你被什么困住了?
  • 似乎语义操作仅限于它提供的用于生成合理反馈的选项......我需要使用迭代器和原始规则来收集必要的信息。 on_error 为“免费”提供所有这些。我希望有人能以更通用的方式解决这个问题。可能是基于 on_error 和 action accept 的东西。
  • on_error 仅“免费”提供它,因为它将“免费”匹配失败。如果您想继续运行,只需使用某种回调或将警告存储在容器中。
  • 我可以创建自己的回调吗?怎么样?

标签: c++ boost boost-spirit boost-spirit-qi


【解决方案1】:

问。我可以创建自己的回调吗?怎么样?

答。当然。您通常在 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 我已扩展示例以显示您在 cmets 中提到的一些不相关的挑战(位置、重复检查)。希望这会有所帮助

这是一个简单的演示:看它Live on Coliru (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

【讨论】:

  • 从语义操作中回调是一种选择,但它似乎不足以提供良好的警告报告消息。当你说我可以有一个回调时,我认为有一种方法可以创建 on_error 类型的回调,该回调可以在特定情况下触发。这样的回调将能够提供警告源的位置。
  • @gsf 有什么区别?如果您想“分离”发出警告的位置和“处理”的位置,请查看 phx::throw_phx::catch_。但是,它不会改变任何事情,因为警告是一个决定。您必须决定使用给定的规则和给定的诊断消息在给定时间发出警告。没有拐弯抹角。除了,也许,您可以制作一个自定义解析器指令来封装一些重复机制。祝你好运。 boost-spirit.com/home/articles/qi-example/…
  • 您在提供警告源位置的警告消息和没有提供警告源位置的警告消息中看不到区别?
  • @gsf (a) 你为什么要问 (b) on_error 有什么特别之处?你可以只传递迭代器。您已经在对原始帖子的评论中提到了“原始”。
  • @gsf 我看不出这有什么相关性或改变本质上很重要。我已经更新了示例(主要是 cmets ...)以显示您的要求。我已经停止比较浮点数了,因为 a. 比较浮点数是否相等是一种代码味道,而 b. 我没有你的 AST 和位置信息,所以这不值得我花时间。我希望这可以帮助你。 (请注意,该示例现在接受命令行选项-Werror 以将警告视为解析错误!)
猜你喜欢
  • 1970-01-01
  • 2012-01-17
  • 2012-01-17
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多