باستخدام دفعة :: الروح، كيف أحتاج إلى جزء من سجل ليكون على خطها الخاص؟

StackOverflow https://stackoverflow.com/questions/2428371

سؤال

لدي محلل سجل يلقي أحد الاستثناءات العديدة للإشارة إلى فشل القاعدة.

الأمر الأمامي:

#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., ، وبالتالي فإن مشغل الإدراج التالي هو مفيد.

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

القالب err_t عوامل خارج المرجعية لرمي الاستثناءات المرتبطة بأشكال مختلفة من فشل التحليل.

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 على إدخال فارغ.

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 لتتبع موقع التحليل.

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;
}

الرمز أعلاه يلقي MissingA:

إنهاء المسمى بعد إلقاء مثيل "Miskinga" ماذا (): المسار: 2،1: توقع بالقرب من "رأس B C: 3"

اعتقدت أن القاعد قد تستهلك الخط الأوسط، ولكن محاولة lexeme[eol] بدلا من ذلك أنتجت نفس النتيجة.

يجب أن أفتقد شيئا واضحا لأن هذا يبدو واحدا من أكثر أنواع المحللين تافهة للكتابة. ما الخطأ الذي افعله؟

هل كانت مفيدة؟

المحلول

نعم، يأكل القائد الأحرف الخطية. lexeme[eol] لا يساعد إما لأن توجيه Lexme يستدعي Skipper قبل التبديل إلى وضع No-Skipper (انظر هنا لمزيد من التفاصيل).

من أجل تجنب تخطي خطوط نيو هناك، إما استخدام نوع Skipper آخر، أو لف eol مكونات في no_skip[eol], ، وهو ما يعادل دلالة lexeme[], ، إلا أنه لا يستدعي القائد. ملاحظة رغم ذلك no_skip[] تمت إضافته مؤخرا فقط، لذلك سيكون متاحا مع الإصدار التالي فقط (BOOST V1.43). لكنه في دفعة SVN بالفعل (انظر هنا للمستندات الأولية).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top