使用boost :: spirit,我如何要求一部分记录在自己的行上? [英] Using boost::spirit, how do I require part of a record to be on its own line?

查看:93
本文介绍了使用boost :: spirit,我如何要求一部分记录在自己的行上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个记录解析器,它抛出几个异常中的一个,以指示哪个规则失败。

I have a record parser that throws one of several exceptions to indicate which rule failed.

前端:

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>

using namespace boost::spirit;
using namespace boost::spirit::ascii;
using namespace boost::spirit::qi;
using namespace boost::spirit::qi::labels;

using boost::phoenix::function;
using boost::phoenix::ref;
using boost::spirit::qi::eol;
using boost::spirit::qi::fail;
using boost::spirit::qi::lit;
using boost::spirit::qi::on_error;

using BOOST_SPIRIT_CLASSIC_NS::file_position;
using BOOST_SPIRIT_CLASSIC_NS::position_iterator;

我们使用 position_iterator Spirit.Classic ,因此下面的流插入运算符很方便。

We use the position_iterator from Spirit.Classic, so the following stream-insertion operator is handy.

std::ostream&
operator<<(std::ostream& o, const file_position &fp)
{
  o << fp.file << ": " << fp.line << ',' << fp.column;
  return o;
}

模板 err_t 因此抛出了与不同形式的解析失败相关联的异常的样板。

The template err_t factors out the boilerplate for throwing the exceptions associated with different forms of parse failure.

template <typename Exception>
struct err_t {
  template <typename, typename, typename>
  struct result { typedef void type; };

  template <typename Iterator>
  void operator() (info const &what, Iterator errPos, Iterator last) const
  {
    std::stringstream ss;
    ss << errPos.get_position()
       << ": expecting " << what
       << " near '" << std::string(errPos, last) << "'\n";
    throw Exception(ss.str());
  }
};

err_t

class MissingA : public std::runtime_error {
  public: MissingA(const std::string &s) : std::runtime_error(s) {}
};

class MissingB : public std::runtime_error {
  public: MissingB(const std::string &s) : std::runtime_error(s) {}
};

class MissingC : public std::runtime_error {
  public: MissingC(const std::string &s) : std::runtime_error(s) {}
};

function<err_t<MissingA> > const missingA = err_t<MissingA>();
function<err_t<MissingB> > const missingB = err_t<MissingB>();
function<err_t<MissingC> > const missingC = err_t<MissingC>();
function<err_t<std::runtime_error> > const other_error =
  err_t<std::runtime_error>();

语法寻找简单的序列。没有 eps ,则在空输入上 start 规则将失败,而不是 a

The grammar looks for simple sequences. Without eps, the start rule fails rather than a on an empty input.

template <typename Iterator, typename Skipper>
struct my_grammar
  : grammar<Iterator, Skipper>
{
  my_grammar(int &result)
    : my_grammar::base_type(start)
    , result(result)
  {
    a = eps > lit("Header A") > eol;
    b = eps > lit("Header B") > eol;
    c = eps > lit("C:") > int_[ref(result) = _1] > eol;
    start = a > b > c;

    a.name("A");
    b.name("B");
    c.name("C");

    on_error<fail>(start, other_error(_4, _3, _2));
    on_error<fail>(a, missingA(_4, _3, _2));
    on_error<fail>(b, missingB(_4, _3, _2));
    on_error<fail>(c, missingC(_4, _3, _2));
  }

  rule<Iterator, Skipper> start;
  rule<Iterator, Skipper> a;
  rule<Iterator, Skipper> b;
  rule<Iterator, Skipper> c;
  int &result;
};

my_parse 将流转换为 std :: string 并使用 position_iterator 跟踪解析的位置。

In my_parse, we dump the contents of the stream into a std::string and use position_iterator to track the parse's location.

int
my_parse(const std::string &path, std::istream &is)
{
  std::string buf;
  is.unsetf(std::ios::skipws);
  std::copy(std::istream_iterator<char>(is),
            std::istream_iterator<char>(),
            std::back_inserter(buf));

  typedef position_iterator<std::string::const_iterator> itertype;
  typedef my_grammar<itertype, boost::spirit::ascii::space_type> grammar;
  itertype it(buf.begin(), buf.end(), path);
  itertype end;

  int result;
  grammar g(result);

  bool r = phrase_parse(it, end, g, boost::spirit::ascii::space);
  if (r && it == end) {
    std::cerr << "success!\n";
    return result;
  }
  else {
    file_position fpos = it.get_position();
    std::cerr << "parse failed at " << fpos << '\n';
    return -9999;
  }
}

最后,主程序

int main()
{
  std::stringstream ss;
  ss << "Header A\n"
     << "Header B\n"
     << "C: 3\n";

  int val = my_parse("path", ss);
  std::cout << "val = " << val << '\n';

  return 0;
}

上面的代码throws MissingA

The code above throws MissingA:

terminate called after throwing an instance of 'MissingA'
  what():  path: 2,1: expecting  near 'Header B
C: 3
'

我认为队长可能已经使用换行符,但尝试 lexeme [ eol] 而不是产生相同的结果。

I thought the skipper might have consumed the newline, but attempting lexeme[eol] instead produced the same result.

我必须缺少一些明显的东西,因为这似乎是一个最琐碎的解析器。我做错了什么?

I must be missing something obvious because this seems one of the most trivial sort of parsers to write. What am I doing wrong?

推荐答案

是的,船长吃换行符。 lexeme [eol] 没有帮助,因为lexeme指令在切换到无跳跃模式之前调用船长(参见此处了解详情)。

Yes, the skipper eats the newline characters. lexeme[eol] doesn't help either because the lexeme directive invokes the skipper before switching to no-skipper mode (see here for more details).

为避免跳过新行,请使用不同的队长类型,或将 eol c $ c> no_skip [eol] ,它在语义上等价于 lexeme [] ,除了它不调用船长。请注意, no_skip [] 最近才添加,因此它只能在下一个版本中使用(Boost V1.43)。但它已经在Boost SVN中了(参见此处为初步文档)。

In order to avoid skipping newlines, either use a different skipper type, or wrap the eol components into no_skip[eol], which is semantically equivalent to lexeme[], except it does not invoke the skipper. Note though, that no_skip[] has been added recently only, so it will be available with the next release only (Boost V1.43). But it is in the Boost SVN already (see here for the preliminary docs).

这篇关于使用boost :: spirit,我如何要求一部分记录在自己的行上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆