2013-02-12 18 views
3

我正在尝试使用boost::spirit:qi从其他参数动态构建解析器。目标是解析一个字符串并用键和值填充std::map<std::string, std::string>。但是,映射的关键字段没有被解析(即在解析器生成之前它是已知的)。如何编写用已知密钥填充地图的语义操作?

我猜我需要编写一个语义操作,将映射的键设置为适当的分析值。我可以看到qi::_1提供了解析器的内容,但我如何引用返回结构(在本例中为std::map)?

如果std::map是在范围内,我可以直接给它分配是这样的:

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

但在我的情况,我想实际产生的解析器,不做解析。我猜我需要更换map_[key]


提供一点更多的内容(的要求):

我首先分析了“模板”的字符串,看起来是这样的:

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

:somewhere:是代表任何字符串,稍后可以通过名称somewhere引用。我有解析器运行良好。

接下来,我想从该模板生成另一解析器解析字符串是这样的:

/path/to/anywhere/nifty.json 

并提供我一个std::map<std::string, std::string> m其中m["somewhere"] == "anywhere"

+0

你能分享一些更详细的信息吗?我想帮忙,但我无法弄清楚密钥是如何预先知道的。有某个地方有钥匙清单吗?还是只有一个?另外,为什么地图不在范围内?它可以是语法的(引用)数据成员,在语法本身时被初始化。 – 2013-02-12 22:33:14

+0

当然:我实际上需要两个文法。第一个解析一个“模板”的种类,为我提供一个键向量。然后我需要使用这一系列的键并动态创建一个接受某种格式的解析器,并使用已经提供的键存储这些字段。它*可能是我可以生成解析器并在一步完成解析,但程序将使用生成的解析器无数次,我想缓存它。 – 2013-02-13 14:08:40

+0

我添加了一些上下文。 – 2013-02-13 14:40:14

回答

1

您应该可以使用phoenix绑定来完成您所要求的内容,但似乎如果我们有更多的上下文,可能会提供更清晰的解决方案。

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

根据密钥来自哪里,您可能还需要使用phx :: ref。

+0

谢谢。我添加了一些上下文。 – 2013-02-13 14:16:13

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

你或许可以消除从该的std ::地图参考通过继承qi :: rule并将其设为私有数据成员来进行调用。