【问题标题】:Parsing delimited list of tokens using Boost Spirit Qi使用 Boost Spirit Qi 解析标记的分隔列表
【发布时间】:2014-10-24 13:07:39
【问题描述】:

使用 boost::spirit::qi 我试图解析由标签组成的行,后跟可变数量的分隔标记。我使用 phrase_parse 调用语法,并使用提供的 blank 解析器作为跳过解析器来保留换行符,因为我需要确保标签是每行的第一项。

简单的基本情况:

label token, token, token

可以用语法解析:

line = label >> (token % ',') >> eol;

我面临的问题是语法应该接受零个或多个标记,并且标记可能是空的。语法应该接受以下几行:

label
label ,
label , token
label token, , token,

我还没有设法创建一个接受上述所有示例的语法。 有关如何解决此问题的任何建议?

编辑:

感谢 sehe 就上述问题提供的所有意见。 现在对于我忘记包括的有趣部分...... 语法还应该接受空行和拆分行。 (没有标签的标记) 当我尝试将标签设为可选时,我得到一个与空字符串匹配的无限循环。

label

label token
token

【问题讨论】:

  • 我建议你改用 ANTLR。我意识到这不是您希望的答案,所以我将其发布为评论。从长远来看,ANTLR 更容易使用,具有更好的工具支持,以及更多可供学习的参考。
  • @JohnZwinck 我们知道你现在不喜欢 Spirit。有很多不喜欢它的地方(以及一般的 c++)。没关系。但是建议在这里使用 ANTLR,而在所陈述的问题之外没有任何上下文也有点可笑。听起来有点像拿大炮打苍蝇。
  • 这更像是告诉一个询问俄语语法问题的人应该改用英语。

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


【解决方案1】:

你应该能够接受空列表

line = label >> -(token % ',') >> eol;

请注意,如果您的船长也跳过 eol,eol 将不起作用(因此不要使用 qi::space,但例如 qi::blank 用于此目的)

另外,根据token 的定义,您也许应该将其更改为也接受“空”令牌


回应评论:一个完整​​的工作示例Live On Coliru

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

namespace qi = boost::spirit::qi;

int main()
{
    using namespace qi;

    using It     = std::string::const_iterator;
    using Token  = std::string;
    using Tokens = std::vector<Token>;

    rule<It, blank_type> label 
        = lexeme[+~char_(":")] >> ':'
        ;

    rule<It, Token(), blank_type> token
        = lexeme[*~char_(",\n")];
        ;

    rule<It, Tokens(), blank_type> line
        = label >> -(token % ',') >> eol
        ;

    for (std::string const input : {
        "my first label: 123, 234, 345 with spaces\n",
        "1:\n",
        "2: \n",
        "3: ,,,\n",
        "4: ,  \t ,,\n",
        "5: ,  \t , something something,\n",
    })
    {
        std::cout << std::string(40, '=') << "\nparsing: '" << input << "'\n";

        Tokens parsed;
        auto f = input.begin(), l = input.end();
        bool ok = phrase_parse(f, l, line, blank, parsed);

        if (ok)
        {
            std::cout << "Tokens parsed successfully, number parsed: " << parsed.size() << "\n";
            for (auto token : parsed)
                std::cout << "token value '" << token << "'\n";
        }
        else
            std::cout << "Parse failed\n";

        if (f != l)
            std::cout << "Remaining input: '" << std::string(f, l) << "'\n";
    }
}

输出:

========================================
parsing: 'my first label: 123, 234, 345 with spaces
'
Tokens parsed successfully, number parsed: 3
token value '123'
token value '234'
token value '345 with spaces'
========================================
parsing: '1:
'
Tokens parsed successfully, number parsed: 1
token value ''
========================================
parsing: '2: 
'
Tokens parsed successfully, number parsed: 1
token value ''
========================================
parsing: '3: ,,,
'
Tokens parsed successfully, number parsed: 4
token value ''
token value ''
token value ''
token value ''
========================================
parsing: '4: ,       ,,
'
Tokens parsed successfully, number parsed: 4
token value ''
token value ''
token value ''
token value ''
========================================
parsing: '5: ,       , something something,
'
Tokens parsed successfully, number parsed: 4
token value ''
token value ''
token value 'something something'
token value ''

【讨论】:

  • 当我尝试为“空”令牌添加规则时,事情就会中断。即使是像token = -lit("token") 这样非常简单的规则也会失败。你能举个例子吗?
  • @Fredrik 我添加了一个示例(我刚刚意识到*(char_ - ',' - eol) 对于令牌来说会更准确一些,但您可以决定:))
  • 附注:如何调试 spirit::qi 语法?我见过一些关于使用某些 #defines 来激活调试信息的 cmets,但我从来没有得到这个工作......
  • 啊……这次google-fu在我身边,我立即找到了答案。只需致电debug(rule)
  • @Fredrik 我建议使用更友好的 BOOST_SPIRIT_DEBUG_NODE 和 BOOST_SPIRIT_DEBUG_NODES 宏:BOOST_SPIRIT_DEBUG_NODES((label)(token)(line))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 2012-12-01
  • 1970-01-01
  • 2017-08-10
相关资源
最近更新 更多