몇 가지 예외 중 하나를 throw하는 레코드 구문 분석기가있어 어떤 규칙이 실패했는지 나타냅니다.boost :: spirit을 사용하면 레코드의 일부가 자체 행에 있어야 할 필요가 있습니까?
서문 :
#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;
우리는 Spirit.Classic에서 position_iterator
을 사용하므로 다음과 같은 스트림 삽입 연산자는 편리합니다. 구문 분석 실패의 다른 형태와 관련된 예외를 던지는에 대한 상용구 밖으로
std::ostream&
operator<<(std::ostream& o, const file_position &fp)
{
o << fp.file << ": " << fp.line << ',' << fp.column;
return o;
}
템플릿 err_t
요인. 자신의 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());
}
};
예외 :
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
이 없으면 빈 입력에 a
이 아닌 start
규칙이 실패합니다.
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
던졌습니다 : 나는 선장이 줄 바꿈을 소비하지만, lexeme[eol]
을 시도했을 수 있습니다 생각
terminate called after throwing an instance of 'MissingA' what(): path: 2,1: expecting near 'Header B C: 3 '대신 같은 결과를 생산 .
필자가 작성하는 가장 간단한 유형의 파서 중 하나인데 분명히 분명한 사실을 빠뜨려야합니다. 내가 도대체 뭘 잘못하고있는 겁니까?