2

I have some issues with parser writing with Spirit::Qi 2.4. I have a series of key-value pairs to parse in following format <key name>=<value>.

Key name can be [a-zA-Z0-9] and is always followed by = sign with no white-space between key name and = sign. Key name is also always preceded by at least one space.

Value can be almost any C expression (spaces are possible as well), with the exception of the expressions containing = char and code blocks { }.

At the end of the sequence of the key value pairs there's a { sign.

I struggle a lot with writing parser for this expression. Since the key name always is preceded by at least one space and followed by = and contains no spaces I defined it as

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

Value can be almost anything, but it can not contain = nor { chars, so I defined it as:

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

I thought about using look-ahead's like this to catch the value:

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

But it won't work, for some reason (seems like the ValueExpression greedily goes up to the = sign and "doesn't know" what to do from there). I have limited knowledge of LL parsers, so I'm not really sure what's cooking here. Is there any other way I could tackle this kind of sequence?

Here's example series:

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

Additional info: since this is a part of a much larger grammar I can't really solve this problem any other way than by a Spirit.Qi parser (like splitting by '=' and doing some custom parsing or something similar).

Edit:

I've created minimum working example here: http://ideone.com/kgYD8
(compiled under VS 2012 with boost 1.50, but should be fine on older setups as well).

4

1 回答 1

3

我建议你看看文章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)];
于 2012-07-05T22:06:31.180 回答