Boost.Spirit解析可选前缀 [英] Boost.Spirit Parsing Optional Prefix
问题描述
我正在尝试解析由空格分隔的,可选标记的字符串.例如
I'm attempting to parse a string of whitespace-delimited, optionally-tagged keywords. For example
descr:expense type:receivable customer 27.3
其中冒号前的表达式是标记,并且是可选的(即假定使用默认标记).
where the expression before the colon is the tag, and it is optional (i.e. a default tag is assumed).
我不能完全让解析器做我想做的事情.我对规范进行了一些小的改编示例,其目的是解析键/值对(非常类似于HTTP查询字符串).
I can't quite get the parser to do what I want. I've made some minor adaptations from a canonical example whose purpose it is to parse key/value pairs (much like an HTTP query string).
typedef std::pair<boost::optional<std::string>, std::string> pair_type;
typedef std::vector<pair_type> pairs_type;
template <typename Iterator>
struct field_value_sequence_default_field
: qi::grammar<Iterator, pairs_type()>
{
field_value_sequence_default_field()
: field_value_sequence_default_field::base_type(query)
{
query = pair >> *(qi::lit(' ') >> pair);
pair = -(field >> ':') >> value;
field = +qi::char_("a-zA-Z0-9");
value = +qi::char_("a-zA-Z0-9+-\\.");
}
qi::rule<Iterator, pairs_type()> query;
qi::rule<Iterator, pair_type()> pair;
qi::rule<Iterator, std::string()> field, value;
};
但是,当我解析它时,当标记被忽略时, optional< string>
并非为空/false.相反,它具有值的副本.该对的第二部分也具有该值.
However, when I parse it, when the tag is left out, the optional<string>
isn't empty/false. Rather, it's got a copy of the value. The second part of the pair has the value as well.
如果untagged关键字不能是标签(语法规则,例如,具有小数点),那么事情就会像我期望的那样工作.
If the untagged keyword can't be a tag (syntax rules, e.g. has a decimal point), then things work like I'd expect.
我做错了什么?这是PEG的概念性错误吗?
What am I doing wrong? Is this a conceptual mistake with the PEG?
推荐答案
相反,它具有值的副本.该对的第二部分也具有该值.
Rather, it's got a copy of the value. The second part of the pair has the value as well.
这是容器属性和回溯的常见陷阱:请使用 qi :: hold
,例如了解Boost.spirit的字符串解析器
This is the common pitfall with container attributes and backtracking: use qi::hold
, e.g. Understanding Boost.spirit's string parser
pair = -qi::hold[field >> ':'] >> value;
完整示例 在Coliru上直播
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/optional/optional_io.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
typedef std::pair<boost::optional<std::string>, std::string> pair_type;
typedef std::vector<pair_type> pairs_type;
template <typename Iterator>
struct Grammar : qi::grammar<Iterator, pairs_type()>
{
Grammar() : Grammar::base_type(query) {
query = pair % ' ';
pair = -qi::hold[field >> ':'] >> value;
field = +qi::char_("a-zA-Z0-9");
value = +qi::char_("a-zA-Z0-9+-\\.");
}
private:
qi::rule<Iterator, pairs_type()> query;
qi::rule<Iterator, pair_type()> pair;
qi::rule<Iterator, std::string()> field, value;
};
int main()
{
using It = std::string::const_iterator;
for (std::string const input : {
"descr:expense type:receivable customer 27.3",
"expense type:receivable customer 27.3",
"descr:expense receivable customer 27.3",
"expense receivable customer 27.3",
}) {
It f = input.begin(), l = input.end();
std::cout << "==== '" << input << "' =============\n";
pairs_type data;
if (qi::parse(f, l, Grammar<It>(), data)) {
std::cout << "Parsed: \n";
for (auto& p : data) {
std::cout << p.first << "\t->'" << p.second << "'\n";
}
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
}
打印
==== 'descr:expense type:receivable customer 27.3' =============
Parsed:
descr ->'expense'
type ->'receivable'
-- ->'customer'
-- ->'27.3'
==== 'expense type:receivable customer 27.3' =============
Parsed:
-- ->'expense'
type ->'receivable'
-- ->'customer'
-- ->'27.3'
==== 'descr:expense receivable customer 27.3' =============
Parsed:
descr ->'expense'
-- ->'receivable'
-- ->'customer'
-- ->'27.3'
==== 'expense receivable customer 27.3' =============
Parsed:
-- ->'expense'
-- ->'receivable'
-- ->'customer'
-- ->'27.3'
这篇关于Boost.Spirit解析可选前缀的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!