Frage

Ich habe einen Rekord-Parser, der eine von mehreren Ausnahmen auslöst, um anzuzeigen, welche Regel fehlgeschlagen.

Titelei:

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

Wir verwenden die position_iterator von Spirit.Classic , so dass der folgende Strom -insertion Operator ist praktisch.

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

Die Vorlage err_t Faktoren, die den Textvorschlag aus, um die Ausnahmen mit verschiedenen Formen von Parse Versagen zu werfen.

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

Die Ausnahmen verwendeten zusammen mit ihrem err_t Wrapper:

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>();

Die Grammatik sucht nach einfachen Sequenzen. Ohne eps , schlägt die start Regel und nicht a auf einem leeren Eingang.

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

In my_parse, Dump wir den Inhalt des Stroms in eine std::string und verwenden position_iterator Standort der Parse zu verfolgen.

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

Schließlich wird das Hauptprogramm

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

Der obige Code führt MissingA:

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

Ich dachte, der Kapitän könnte die Newline verbraucht haben, aber versuchen lexeme[eol] erzeugt stattdessen das gleiche Ergebnis.

muss ich etwas fehlt offensichtlich, weil dies eine der banalsten Art von Parsern scheint zu schreiben. Was mache ich falsch?

War es hilfreich?

Lösung

Ja, isst der Skipper die Zeilenumbrüche. lexeme[eol] hilft nicht, weil entweder die lexeme Richtlinie vor dem Einschalten den Kapitän ruft zu No-Skipper-Modus (siehe hier für weitere Details).

Um Zeilenumbrüche zu vermeiden Skipping, entweder einen anderen Skipper Typen verwenden, oder wickeln Sie die eol Komponenten in no_skip[eol], die lexeme[] semantisch äquivalent ist, außer es nicht den Skipper nicht aufrufe. Beachten Sie aber, dass no_skip[] wurde vor kurzem nur hinzugefügt, so dass es nur mit der nächsten Version verfügbar sein wird (Boost-V1.43). Aber es ist in den Boost bereits SVN (siehe hier für den vorläufigen docs).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top