【问题标题】:Spirit Qi sequence parsing issues灵气序列解析问题
【发布时间】:2012-07-05 08:17:10
【问题描述】:

我在使用 Spirit::Qi 2.4 编写解析器时遇到了一些问题。 我有一系列键值对以以下格式解析<key name>=<value>

键名可以是[a-zA-Z0-9],后面总是跟=符号,键名=符号之间没有空格。 键名也总是前面至少有一个空格。

几乎可以是任何 C 表达式(也可以是空格),但包含 = 字符和代码块 { } 的表达式除外。

在键值对序列的末尾有一个{ 符号。

我在为这个表达式编写解析器方面遇到了很多困难。由于键名前面总是至少有一个空格,后面是= 并且不包含空格,因此我将其定义为

  KeyName %= [+char_("a-zA-Z0-9_") >> lit("=")] ;

值几乎可以是任何东西,但它不能包含={ 字符,所以我将其定义为:

  Value %=  +(char_ - char_("{=")) ;

我想过用这样的前瞻来捕捉值:

ValueExpression 
    %= ( 
      Value  
      >> *space 
      >> &(KeyName | lit("{"))
    )
    ;

但由于某种原因它不起作用(似乎ValueExpression 贪婪地上升到= 标志并且“不知道”从那里做什么)。我对 LL 解析器的了解有限,所以我不太确定这里在做什么。有没有其他方法可以解决这种序列?

这是示例系列:

EXP1=FunctionCall(A, B, C) TEST="Example String" \
AnotherArg=__FILENAME__ - 'BlahBlah' EXP2= a+ b+* {

附加信息:因为这是一个更大的语法的一部分,除了 Spirit.Qi 解析器之外,我无法真正解决这个问题(比如用 '=' 分割并做一些自定义解析或类似的东西)。

编辑:

我在这里创建了最小的工作示例:http://ideone.com/kgYD8
(在 VS 2012 下使用 boost 1.50 编译,但在较旧的设置上也应该没问题)。

【问题讨论】:

  • 如果将 +(char_-char_("{=")) 改为 +~char_("{=") 会怎样?
  • 我可以为您提供一些示例代码中令我感到奇怪的事情,但我担心它可能与您的 真实代码 无关(因为这是最小化)。如果您关心,我可以回答您可能有的更多问题,beyond the answer I posted,如果您发布新问题/您的真实代码。
  • 好的,请分享您的见解!

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


【解决方案1】:

我建议你看看文章Parsing a List of Key-Value Pairs Using Spirit.Qi

大大简化了你的代码,而

  • 添加属性处理
  • 移除凤凰语义动作
  • 规则调试

就是这样,不用多说:

#define BOOST_SPIRIT_DEBUG

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <map>

namespace qi = boost::spirit::qi;
namespace fusion = boost::fusion;

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

template <typename It, typename Skipper>
struct grammar : qi::grammar<It, data_t(), Skipper>
{
    grammar() : grammar::base_type(Sequence)
    {
        using namespace qi;

        KeyName  = +char_("a-zA-Z0-9_") >> '=';
        Value    = qi::no_skip [+(~char_("={") - KeyName)];
        Sequence = +(KeyName > Value);

        BOOST_SPIRIT_DEBUG_NODE(KeyName);
        BOOST_SPIRIT_DEBUG_NODE(Value);
        BOOST_SPIRIT_DEBUG_NODE(Sequence);
    }
  private:
    qi::rule<It, data_t(), Skipper>      Sequence;
    qi::rule<It, std::string()>          KeyName; // no skipper, removes need for qi::lexeme
    qi::rule<It, std::string(), Skipper> Value;
};

template <typename Iterator>
data_t parse (Iterator begin, Iterator end)
{
    grammar<Iterator, qi::space_type> p;

    data_t data;

    if (qi::phrase_parse(begin, end, p, qi::space, data)) {
        std::cout << "parse ok\n";
        if (begin!=end) {
            std::cout << "remaining: " << std::string(begin,end) << '\n';
        }
    } else {
        std::cout << "failed: " << std::string(begin,end) << '\n';
    }

    return data;
}

int main ()
{
    std::string test(" ARG=Test still in first ARG ARG2=Zombie cat EXP2=FunctionCall(A, B C) {" );
    auto data = parse(test.begin(), test.end());

    for (auto& e : data)
        std::cout << e.first << "=" << e.second << '\n';
}

输出将是:

parse ok
remaining: {
ARG=Test still in first ARG 
ARG2=Zombie cat 
EXP2=FunctionCall(A, B C) 

如果您真的希望 '{' 成为最后一个值的一部分,请更改此行:

Value    = qi::no_skip [+(char_ - KeyName)];

【讨论】:

  • 很棒的答案,谢谢。我不了解规则调试,但仍然对船长感到有些困惑。尽管我很喜欢 Spirit 的想法,但我发现缺乏适当的一站式文档(所有内容似乎都散布在数百篇这样的文章中)、最佳实践和更详细的示例使得它真的很难做对了。我不知道你可以调试这样的规则,我也喜欢你的示例的编译时间是我的 1/5。
  • @kurczak Spirit 不是一个 simple 库:它偏爱功能和灵活性,牺牲了简单性。对我来说,文档很棒。你似乎在寻找的是经验。像这样一个不简单的框架,唯一的办法就是:重复应用。如果您缺少合适的玩具项目,文章/样本/测试是一个很好的灵感来源。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-20
  • 1970-01-01
  • 2013-11-18
  • 2020-01-10
  • 1970-01-01
相关资源
最近更新 更多