2010-03-11 5 views
6

몇 가지 예외 중 하나를 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 
'

대신 같은 결과를 생산 .

필자가 작성하는 가장 간단한 유형의 파서 중 하나인데 분명히 분명한 사실을 빠뜨려야합니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

답변

7

예, 선장이 개행 문자를 사용합니다. lexeme[eol]은 lexeme 지시어가 no-skipper 모드로 전환하기 전에 skipper를 호출하기 때문에 도움이되지 않습니다 (자세한 내용은 here 참조). 개행 스킵 방지하려면 다른 주장 유형을 사용하거나 그것을 선장를 호출하지 않는 것을 제외하고, 의미 lexeme[] 등가 인 no_skip[eol]eol 부품을 포장하기 위해

. 하지만 no_skip[]이 최근에 추가되었으므로 다음 릴리스에서만 사용할 수 있습니다 (Boost V1.43). 하지만 이미 Boost SVN에 있습니다 (예비 문서는 here 참조).