2013-02-12 3 views
3

boost::spirit:qi을 사용하여 다른 매개 변수에서 파서를 동적으로 작성하려고합니다. 목표는 문자열을 구문 분석하고 키와 값으로 std::map<std::string, std::string>을 채우는 것입니다. 그러나지도의 주요 필드는 구문 분석되지 않습니다 (즉, 파서가 생성되기 전에 알려짐).지도를 알려진 키로 채우기위한 의미 론적 행위를 작성하는 방법은 무엇입니까?

필자는지도의 키를 적절한 구문 분석 값으로 설정하는 의미 론적 작업을 작성해야한다고 생각합니다. qi::_1은 파서의 내용을 제공하지만 반환 구조 (이 경우 std::map)를 어떻게 참조 할 수 있습니까?

parser = lit(prefix) >> value_parser[map_[key] = _1]; 

을하지만 내 경우에는, 실제로 구문 분석을하지, 파서를 생성 할 다음 std::map가의 범위 내 인 경우

, 내가 직접처럼 지정할 수 있습니다. 나는 map_[key]을 대체 할 것이 필요하다고 생각합니다. (요청에 따라)


좀 더 컨텍스트를 제공하려면 다음 중 하나를 표현하기위한 것입니다

/path/to/:somewhere:/nifty.json 

:somewhere: :

내가 먼저 이런 걸 보이는 "템플릿"문자열을 구문 분석 문자열은 나중에 somewhere이라는 이름으로 참조 할 수 있습니다. 나는 파서가 잘 작동하도록했다.

/path/to/anywhere/nifty.json 

그리고 나에게 std::map<std::string, std::string> mm["somewhere"] == "anywhere"을 제공

다음 그 템플릿에서이 같은 문자열을 구문 분석하는 또 다른 파서를 생성합니다.

+0

좀 더 자세히 설명해 주시겠습니까? 도움을 드리고 싶지만 키가 미리 어떻게 알려 졌는지 알 수 없습니다. 어딘가에 열쇠 목록이 있습니까? 아니면 단 하나? 또한지도가 범위에없는 이유는 무엇입니까? 문법 자체가 될 때 초기화되는 문법의 (참조) 데이터 멤버가 될 수 있습니다. –

+0

예 : 실제로 두 문법이 필요합니다. 첫 번째 것은 나에게 키의 벡터를 제공하는 "템플릿"을 파싱합니다. 그런 다음이 일련의 키를 가져 와서 특정 형식을 허용하고 제공된 키가있는 필드를 저장하는 파서를 동적으로 만들어야합니다. 파서를 생성하고 구문 분석을 한 번에 할 수는 있지만 * 프로그램은 생성 된 파서를 무수히 사용하고 캐시하려고합니다. –

+0

그리고 나는 약간의 문맥을 추가했다. –

답변

1

phoenix bind를 사용하면 원하는 내용을 처리 할 수 ​​있지만 더 많은 컨텍스트가 있으면 훨씬 더 깨끗한 솔루션을 사용할 수있는 것처럼 보입니다.

parser = lit(prefix) >> value_parser[phx::ref(map)[key] = qi::_1] 

키의 출처에 따라 phx :: ref를 사용해야 할 수도 있습니다.

+0

감사합니다. 나는 약간의 문맥을 추가했다. –

3

마음에 들었지만 계승 된 속성이 답이 될지 확실하지 않습니다.

// an attempt to demonstrate a parser that takes a std::map by reference and a key by value, 
// then stores a parsed value into the map as the value associated with the given key 

#include <string> 
#include <map> 

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

typedef std::string::const_iterator fwd_iter_t; 

namespace qi = boost::spirit::qi; 
namespace phoenix = boost::phoenix; 

typedef int value_t; // or whatever 
typedef std::map<std::string, value_t> result_map_t; 
// key insight - rules can take "inherited" attributes (parameters in 2nd argument): 
typedef qi::rule<fwd_iter_t, 
       void(result_map_t&, std::string),  // inherit map ref and key to use 
       boost::spirit::ascii::space_type> map_insert_rule_t; 

int main() { 

    result_map_t result_map; 
    std::vector<std::string> keys = { "A", "B", "C" }; 
    std::string test_data = "PREFIX 1\nPREFIX 2\nPREFIX 3"; 

    using boost::phoenix::construct; // to create pairs 
    using boost::phoenix::insert;  // to add pairs to the map 
    typedef result_map_t::value_type result_map_pair_t; 
    // use Phoenix actions to construct the key/value pair and insert it 
    map_insert_rule_t maprule = qi::lit("PREFIX") 
          >> qi::int_[insert(qi::_r1, // inherited map ref 
               construct<result_map_pair_t>(qi::_r2, qi::_1))]; 

    fwd_iter_t beg = test_data.begin(); 
    fwd_iter_t end = test_data.end(); 
    for (auto k_it = keys.begin(); k_it != keys.end(); ++k_it) { 
     using boost::spirit::ascii::space; 
     if (!qi::phrase_parse(beg, end, 
          maprule(phoenix::ref(result_map), *k_it), 
          space)) { 
     std::cerr << "parse failed!" << std::endl; 
     return 1; 
     } 
    } 

    std::cout << "parse results:" << std::endl; 
    for (auto r_it = result_map.begin(); r_it != result_map.end(); ++r_it) { 
     std::cout << r_it->first << " " << r_it->second << std::endl; 
    } 

    return 0; 
} 

당신은 아마에서 표준 : :지도 참조를 제거 할 수 있습니다 대신 동적으로 파서를 만드는, 당신은 키와 각 호출에 공급 상속 된 속성과지도에 대한 참조를 복용 한 파서를 만들 qi :: rule을 상속하고이를 개인 데이터 멤버로 만드는 것.