2016-08-10 2 views
0

불행히도 (MSVC가 지원되어야 함) X3을 사용할 수 없으므로 qi를 사용하여 일반 파서 요소를 만들려고합니다.qi를 사용하여 일반 구문 분석기를 어떻게 작성합니까?

template<class T> struct parse_type; 

I는 다음과 같이 사용할 수 있습니다 : 아이디어는 템플릿 구조체 것입니다

template<class T> T from_string(std::string const& s) 
{ 
    T res; 
    parse_type<T> t; 
    ... 
    if (phrase_parse(...,parse_type<T>(),...,t)) 
} 

또는이

template<class T,class Alloc> 
struct parse_type<std::vector<T,Alloc>> 
{ 
    // Parse a vector using rule '[' >> parse_type<T> % ',' > ']'; 
} 

주된 목적처럼 전문 쉽지 수 있도록하는 것입니다 예를 들어 파싱 std :: tuple, boost :: optional 그리고 boost :: variant (qi의 탐욕스러운 특성 때문에 마지막 하나는 자동 일 수 없다).

어떻게 접근했는지에 대한 의견을 보내 주시면 감사하겠습니다. 현재 qi :: grammar에 기초를두고 있지만 문법은 X3에서 지원되지 않으며 MSVC가 이것을 컴파일 할 때 X3을 사용하고 싶습니다. 또한 선장에게 제공하는 데 약간 불편합니다. 대안은 parse_type에 적절한 규칙을 반환하는 정적 함수를 갖는 것입니다. 이것이 더 깨끗한 접근법이라면 나는 고려 중이다.

모든 의견을 환영합니다.

Update2 : 런타임시 오류가 발생하는 컴파일 가능한 예제로 대체 된 코드 조각.

#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <string> 
#include <string> 
#include <iostream> 
#include <iostream> 

// Support to simplify 
using iter = std::string::const_iterator; 
void print(std::vector<int> const& v) 
{ 
    std::cout << '['; 
    for (auto i: v) std::cout << i << ','; 
    std::cout << "]"; 
} 

namespace qi = boost::spirit::qi; 

// My rule factory - quite useless if you do not specialise 
template<class T> struct ps_rule; 

// An example of using the factory 
template<class T> 
T from_string(std::string const& s) 
{ 
    T result; 
    iter first { std::begin(s) }; 
    auto rule = ps_rule<T>::get(); 
    phrase_parse(first,std::end(s),rule,qi::space,result); 
    return result; 
} 

// Specialising rule for int 
template<> 
struct ps_rule<int> 
{ 
    static qi::rule<iter,int()> get() { return qi::int_; } 
}; 

// ... and for std::vector (where the elements must have rules) 
template<class T,class Alloc> 
struct ps_rule<std::vector<T,Alloc>> 
{ 
    static qi::rule<iter,std::vector<T,Alloc>()> get() 
    { 
     qi::rule<iter,std::vector<T,Alloc>()> res; 
     res.name("Vector"); 
     res = 
       qi::lit('{') 
      >> ps_rule<T>::get() % ',' 
      >> '}'; 
     return res; 
    } 
}; 

int main() 
{ 
    // This one works like a charm. 
    std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n"); 

    std::vector<int> v {1,2,3,4,5,6}; 

    // This one fails 
    std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n"); 
} 

코드는 766 부스트/function_template.hpp 라인에 실패 : 여기에 코드이 코드는 부스트의 멤버 함수 :: function4 가

result_type operator()(BOOST_FUNCTION_PARMS) const 
{ 
    if (this->empty()) 
    boost::throw_exception(bad_function_call()); 

    return get_vtable()->invoker 
      (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); 
} 

는, 부스트 : 융합 :: vector0> & , boost :: spirit :: unused_type const &> 이며 get_vtable이 잘못된 포인터를 반환한다는 점이 문제입니다.

+0

나는 그다지 신경 쓰지 않지만 여기에 다운 투표의 이유가 무엇인지 궁금하다. – user3721426

답변

2

귀하의 주된 문제점은 qi::rule의 복사 생성자가 원본 규칙 (귀하의 경우 로컬 변수)을 참조한다는 것입니다. 이 문제를 피할 수있는 한 가지 방법은 qi::rulecopy 멤버 함수를 사용하는 것이지만이 경우 전문화의 반환 유형을 ps_rule으로 약간 변경해야합니다. 당신이 그렇게되면

static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get() 
{ 
    //[...] (same as before) 
    return res.copy(); 
} 

이 같은 문제는 고립에서 작동하는 것 같았다 비록 당신의 ps_rule<int>으로 발생한다. 당신은 유사한 뭔가를 할 수 있지만 규칙이 필요하지 않습니다이 경우, 그냥 같은 것을 사용하는 (심지어 성능의 관점에서) 더 나은 것 :

static qi::int_type get() { return qi::int_; } 

Full Sample (Running on WandBox)

#include <boost/spirit/include/qi.hpp> 
#include <string> 
#include <iostream> 

// Support to simplify 
using iter = std::string::const_iterator; 
void print(std::vector<int> const& v) 
{ 
    std::cout << '['; 
    for (auto i: v) std::cout << i << ','; 
    std::cout << "]"; 
} 

namespace qi = boost::spirit::qi; 

// My rule factory - quite useless if you do not specialise 
template<class T> struct ps_rule; 

// An example of using the factory 
template<class T> 
T from_string(std::string const& s) 
{ 
    T result; 
    iter first { std::begin(s) }; 
    auto rule = ps_rule<T>::get(); 
    qi::phrase_parse(first,std::end(s),rule,qi::space,result); 
    return result; 
} 

// Specialising rule for int 
template<> 
struct ps_rule<int> 
{ 
    static qi::int_type get() { return qi::int_; } 
}; 


// ... and for std::vector (where the elements must have rules) 
template<class T,class Alloc> 
struct ps_rule<std::vector<T,Alloc>> 
{ 
    static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get() 
    { 
     qi::rule<iter,std::vector<T,Alloc>()> res; 
     res.name("Vector"); 
     res = 
       qi::lit('{') 
      >> ps_rule<T>::get() % ',' 
      >> '}'; 
     return res.copy(); 
    } 
}; 

int main() 
{ 
    // This one works like a charm. 
    std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n"); 

    std::vector<int> v {1,2,3,4,5,6}; 

    std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n"); 

    std::vector<std::vector<int> > vv {{1,2,3},{4,5,6}}; 

    std::cout << ((from_string<std::vector<std::vector<int>>>("{{1,2,3},{4,5,6}}") == vv) ? "OK\n":"Failed\n"); 

} 

PS : 기본 템플릿에서 Spirit's own machinery to create parsers automatically을 사용하면 많은 전문화를 줄일 수 있습니다. Here is an example.

+0

좋아요! 당신의 도움을 주셔서 대단히 감사합니다. 나는 사본을 조사했지만 올바른 반환 유형을 얻지 못했습니다. – user3721426

+0

필자도 일종의 자동 파서 (auto parser)를 알고 있었지만 문서를 보지 않았을 뿐이므로 내가 직접 파서를 전문적으로 만들 수 있다는 것을 깨닫지 못했습니다. 나는이 접근법을 추구하고 ps_rule을 제거 할 것입니다. 그렇다면 부분적으로 전문화 된 문제가 발생하지 않는 한. – user3721426

+0

BTW : 답을 얻으 려 시도했으나, 여기에 "불충분 한 점수"가 있기 때문에 분명히 대답 할 수 없습니다. – user3721426

관련 문제