3

我有一个由混合变量 ( $(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;
}

如果不是很明显,我不知道 Spirit 是如何工作的,文档除了实际的解释之外什么都有,所以这大约是一个小时的例子。

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

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

  1. 错误 C2679:二进制“[”:未找到采用“const boost::spirit::_1_type”类型的右侧操作数的运算符(或没有可接受的转换)
  2. 错误 C2512:'boost::spirit::qi::rule::rule':没有合适的默认构造函数可用

将查找 ( vars[_1]) 更改为简单的+=给出:

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

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

错误 3,挖掘模板错误堆栈,似乎与无法将状态规则变成一对有关,这似乎很奇怪,因为它几乎完全匹配此示例中的一条规则。

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

4

2 回答 2

2

需要注意的几点:

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

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

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

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

这是一个与 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'
于 2012-02-05T13:26:18.450 回答
-2

你必须有 char_('$') 否则>>两边都是'char' - 你需要至少有一种精神类型才能获得重载的运算符>>。

您可能还需要使用 _1 from phoenix。

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

于 2012-02-04T11:36:32.960 回答