使用Boost.Spirit解析混合值和键值对

时间:2012-02-04 06:50:10

标签: c++ boost-spirit boost-spirit-qi

我有一个由混合变量($(name))和变量值对($(name:value))组成的简单语法。我有一个手工编码的递归解析器,但我有兴趣将它用作学习Spirit的练习,最终我需要更复杂的语法(/很快)。

无论如何,我正在使用的一组可能的表单(从完整语法中简化)是:

$(variable)     // Uses simple look-up, recursion and inline replace
$(name:value)   // Inserts a new variable into the local lookup table

我目前的规则如下:

typedef std::map<std::string, std::string> dictionary;

template <typename Iterator>
bool parse_vars(Iterator first, Iterator last, dictionary & vars, std::string & output)
{
    using qi::phrase_parse;
    using qi::_1;
    using ascii::char_;
    using ascii::string;
    using ascii::space;
    using phoenix::insert;

    dictionary statevars;

    typedef qi::rule<Iterator, std::string()> string_rule;
    typedef qi::rule<Iterator, std::pair<std::string, std::string>()> pair_rule;

    string_rule state = string >> ':' >> string; // Error 3
    pair_rule variable = 
    (
        char_('$') >> '(' >> 
        (
            state[insert(phoenix::ref(statevars), _1)] |
            string[output += vars[_1]] // Error 1, will eventually need to recurse
        ) >> ')'
    ); // Error 2

    bool result = phrase_parse
    (
        first, last, 
        (
            variable % ','
        ), 
        space
    );

    return r;
}

如果不是很明显,我不知道精神是如何工作的,而且文档除了实际解释之外还有其他所有内容,所以这是一个小时的例子。

我特别质疑的部分是变量规则中的前导char_('$'),但删除它会导致移位运算符错误(编译器会将'$' >> '('解释为右移)。

编译时,我收到与 state 规则相关的错误,特别是创建该对和查找:

  1. 错误C2679:binary'[':找不到运算符,它采用'const boost :: spirit :: _ 1_type'类型的右手操作数(或者没有可接受的转换)
  2. 错误C2512:'boost :: spirit :: qi :: rule :: rule':没有合适的默认构造函数
  3. 将查找(vars[_1])更改为简单的+=会产生:

    3 。错误C2665:'boost :: spirit :: char_class :: classify :: is':15个重载中没有一个可以转换所有参数类型

    错误1似乎与_1占位符的类型(属性?)有关,但它应该是一个字符串,用于打印或连接到输出字符串。 2似乎是由1引起的噪音。

    错误3,挖掘模板错误堆栈,似乎与无法将 state 规则转换为一对相关,这似乎很奇怪,因为它几乎完全符合其中一条规则this example

    如何修改变量规则以正确处理两种输入表单?

2 个答案:

答案 0 :(得分:2)

有几点需要注意:

  1. 要调整std::pair(以便您可以将它与地图一起使用),您应该包括(至少)

    #include <boost/fusion/adapted/std_pair.hpp>
    
  2. 看起来您正在尝试创建符号表。您可以使用qi::symbols作为

  3. 避免将输出生成与解析混合在一起,这会使事情过度复杂化

  4. 我没有“修复”上述所有内容(由于缺乏上下文),但我很乐意帮助解决其中的任何其他问题。

    这是一个与OP非常接近的固定代码版本。 编辑现在也测试了它,输出如下:

    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/fusion/adapted/std_pair.hpp>
    #include <map>
    
    namespace qi    = boost::spirit::qi;
    namespace phx   = boost::phoenix;
    
    typedef std::map<std::string, std::string> dictionary;
    
    template <typename Iterator, typename Skipper = qi::space_type>
        struct parser : qi::grammar<Iterator, Skipper>
    {
        parser(dictionary& statevars, std::string& output) : parser::base_type(start)
        {
            using namespace qi;
            using phx::insert; 
    
            with_initializer = +~char_(":)") >> ':' >> *~char_(")");
    
            simple           = +~char_(")");
    
            variable         = 
                "$(" >> (
                       with_initializer  [ insert(phx::ref(statevars), qi::_1) ] 
                     | simple            [ phx::ref(output) += phx::ref(statevars)[_1] ]
                 ) >> ')';
    
            start = variable % ',';
    
            BOOST_SPIRIT_DEBUG_NODE(start);
            BOOST_SPIRIT_DEBUG_NODE(variable);
            BOOST_SPIRIT_DEBUG_NODE(simple);
            BOOST_SPIRIT_DEBUG_NODE(with_initializer);
        }
    
      private:
        qi::rule<Iterator, std::pair<std::string, std::string>(), Skipper> with_initializer;
        qi::rule<Iterator, std::string(), Skipper> simple;
        qi::rule<Iterator, Skipper> variable;
        qi::rule<Iterator, Skipper> start;
    };
    
    template <typename Iterator>
    bool parse_vars(Iterator &first, Iterator last, dictionary & vars, std::string & output)
    {
        parser<Iterator> p(vars, output);
        return qi::phrase_parse(first, last, p, qi::space);
    }
    
    int main()
    {
        const std::string input = "$(name:default),$(var),$(name)";
        std::string::const_iterator f(input.begin());
        std::string::const_iterator l(input.end());
    
        std::string output;
        dictionary table;
    
        if (!parse_vars(f,l,table,output))
            std::cerr << "oops\n";
        if (f!=l)
            std::cerr << "Unparsed: '" << std::string(f,l) << "'\n";
        std::cout << "Output:   '" << output << "'\n";
    }
    

    输出:

    Output:   'default'
    

答案 1 :(得分:-2)

你必须有char _(&#39; $&#39;)否则&gt;&gt;是&#39; char&#39;在两边 - 你需要至少有一种精神类型来获得重载的运算符&gt;&gt ;.

您可能还需要使用凤凰城的_1。

另请看一下: http://boost-spirit.com/home/articles/qi-example/parsing-a-list-of-key-value-pairs-using-spirit-qi/