创建用于检索字符串向量的语法
Create grammar for retrieving vector of strings
我有一个包含表格数据的文件:
fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
我想提取数据容器的名称,所以我想在特定情况下检索包含 mand1
、mand2
、julia1
.
的向量
我已经阅读了有关 parsing a number list into a vector 的示例,但我想在单独的文件中维护语法。
我创建了一个表示语法的结构,然后使用它来解析包含数据的字符串。我希望输出像
mand1
mand2
julia1
相反我得到
mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
我的解析器识别第一个 fractal
项,但随后它将文件的其余部分解析为单个字符串项,而不是按我的需要进行解析。
我做错了什么?
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <iostream>
using boost::spirit::ascii::space;
using boost::spirit::ascii::space_type;
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::lit;
using boost::spirit::qi::lexeme;
using boost::spirit::qi::skip;
using boost::spirit::ascii::char_;
using boost::spirit::ascii::no_case;
using boost::spirit::qi::rule;
typedef std::string::const_iterator sit;
template <typename Iterator>
struct FractalListParser : boost::spirit::qi::grammar<Iterator, std::vector<std::string>(), boost::spirit::ascii::space_type> {
FractalListParser() : FractalListParser::base_type(start) {
no_quoted_string %= *(lexeme[+(char_ - '"')]);
start %= *(no_case[lit("fractal")] >> no_quoted_string >> '{' >> *(skip[*(char_)]) >> '}');
}
rule<Iterator, std::string(), space_type> no_quoted_string;
rule<Iterator, std::vector<std::string>(), space_type> start;
};
int main() {
const std::string fractalListFile(R"(
fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
)");
std::cout << "Read Test:" << std::endl;
FractalListParser<sit> parser;
std::vector<std::string> data;
bool r = phrase_parse(fractalListFile.begin(), fractalListFile.end(), parser, space, data);
for (auto& i : data) std::cout << i << std::endl;
return 0;
}
如果你使用错误处理,你会发现解析失败,没有任何有效的解析:
输出:
Read Test:
Parse success:
----
mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
Remaining unparsed input: 'fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
'
问题是什么?
您可能想忽略 "body"(在 {}
之间)。因此我想你实际上想要 omit
属性:
>> '{' >> *(omit[*(char_)]) >> '}'
而不是skip(*char_)
.
表达式 *char_
是贪婪的,并且总是匹配到输入的末尾...您可能想限制字符集:
在"name" *~char_("\"{")
中避免"eating"所有的body也是如此。为避免匹配空格,请使用 graph
(例如 +graph - '"'
)。如果你想解析 "identifiers" 是明确的,例如
alpha > *(alnum | char_('_'))
在body*~char_('}')
或*(char_ - '}')
(后者效率较低)。
可选量词的嵌套效率不高:
*(omit[*(char_)])
worst-case 运行时间会很慢(因为 *char_
可能为空,*(omit[*(char_)])
也可能为空)。说出你的意思:
omit[*char_]
拥有词位的最简单方法是从规则声明中删除船长(另请参阅 Boost spirit skipper issues)
程序逻辑:
由于您的示例包含嵌套块(例如mand2
),您需要递归地处理这些块以避免调用第一个 }
外部块的末尾块:
block = '{' >> -block % (+~char_("{}")) >> '}';
松散的提示:
用BOOST_SPIRIT_DEBUG
找出解析的地方rejected/matched。例如。稍微重构规则后:
我们得到了输出 (On Coliru):
Read Test:
<start>
<try>fractal mand1 {\n </try>
<no_quoted_string>
<try>mand1 {\n ;lkkj;kj</try>
<success> {\n ;lkkj;kj;\n}\n\n</success>
<attributes>[[m, a, n, d, 1]]</attributes>
</no_quoted_string>
<body>
<try>{\n ;lkkj;kj;\n}\n\nf</try>
<fail/>
</body>
<success>fractal mand1 {\n </success>
<attributes>[[]]</attributes>
</start>
Parse success:
Remaining unparsed input: 'fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
'
该输出帮助我发现我实际上忘记了 body 规则中的 - '}'
部分...:)
当规则定义中不涉及任何语义操作时,不需要 %=
(docs)
你可能想确保 fractal
实际上是一个单独的词,所以你不匹配 fractalset multi { .... }
演示程序
有了这些,我们就可以有一个工作演示:
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct FractalListParser : qi::grammar<Iterator, std::vector<std::string>(), qi::space_type> {
FractalListParser() : FractalListParser::base_type(start) {
using namespace qi;
identifier = alpha > *(alnum | char_('_'));
block = '{' >> -block % +~char_("{}") >> '}';
start = *(
no_case["fractal"] >> identifier >> block
);
BOOST_SPIRIT_DEBUG_NODES((start)(block)(identifier))
}
qi::rule<Iterator, std::vector<std::string>(), qi::space_type> start;
// lexemes (just drop the skipper)
qi::rule<Iterator, std::string()> identifier;
qi::rule<Iterator> block; // leaving out the attribute means implicit `omit[]`
};
int main() {
using It = boost::spirit::istream_iterator;
It f(std::cin >> std::noskipws), l;
std::cout << "Read Test:" << std::endl;
FractalListParser<It> parser;
std::vector<std::string> data;
bool r = qi::phrase_parse(f, l, parser, qi::space, data);
if (r) {
std::cout << "Parse success:\n";
for (auto& i : data)
std::cout << "----\n" << i << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
打印:
Read Test:
Parse success:
----
mand1
----
mand2
----
julia1
我有一个包含表格数据的文件:
fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
我想提取数据容器的名称,所以我想在特定情况下检索包含 mand1
、mand2
、julia1
.
我已经阅读了有关 parsing a number list into a vector 的示例,但我想在单独的文件中维护语法。
我创建了一个表示语法的结构,然后使用它来解析包含数据的字符串。我希望输出像
mand1
mand2
julia1
相反我得到
mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
我的解析器识别第一个 fractal
项,但随后它将文件的其余部分解析为单个字符串项,而不是按我的需要进行解析。
我做错了什么?
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <iostream>
using boost::spirit::ascii::space;
using boost::spirit::ascii::space_type;
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::lit;
using boost::spirit::qi::lexeme;
using boost::spirit::qi::skip;
using boost::spirit::ascii::char_;
using boost::spirit::ascii::no_case;
using boost::spirit::qi::rule;
typedef std::string::const_iterator sit;
template <typename Iterator>
struct FractalListParser : boost::spirit::qi::grammar<Iterator, std::vector<std::string>(), boost::spirit::ascii::space_type> {
FractalListParser() : FractalListParser::base_type(start) {
no_quoted_string %= *(lexeme[+(char_ - '"')]);
start %= *(no_case[lit("fractal")] >> no_quoted_string >> '{' >> *(skip[*(char_)]) >> '}');
}
rule<Iterator, std::string(), space_type> no_quoted_string;
rule<Iterator, std::vector<std::string>(), space_type> start;
};
int main() {
const std::string fractalListFile(R"(
fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
)");
std::cout << "Read Test:" << std::endl;
FractalListParser<sit> parser;
std::vector<std::string> data;
bool r = phrase_parse(fractalListFile.begin(), fractalListFile.end(), parser, space, data);
for (auto& i : data) std::cout << i << std::endl;
return 0;
}
如果你使用错误处理,你会发现解析失败,没有任何有效的解析:
输出:
Read Test:
Parse success:
----
mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
Remaining unparsed input: 'fractal mand1 {
;lkkj;kj;
}
fractal mand2 {
if (...) {
blablah;
}
}
fractal julia1 {
a = ss;
}
'
问题是什么?
您可能想忽略 "body"(在
{}
之间)。因此我想你实际上想要omit
属性:>> '{' >> *(omit[*(char_)]) >> '}'
而不是
skip(*char_)
.表达式
*char_
是贪婪的,并且总是匹配到输入的末尾...您可能想限制字符集:在"name"
*~char_("\"{")
中避免"eating"所有的body也是如此。为避免匹配空格,请使用graph
(例如+graph - '"'
)。如果你想解析 "identifiers" 是明确的,例如alpha > *(alnum | char_('_'))
在body
*~char_('}')
或*(char_ - '}')
(后者效率较低)。
可选量词的嵌套效率不高:
*(omit[*(char_)])
worst-case 运行时间会很慢(因为
*char_
可能为空,*(omit[*(char_)])
也可能为空)。说出你的意思:omit[*char_]
拥有词位的最简单方法是从规则声明中删除船长(另请参阅 Boost spirit skipper issues)
程序逻辑:
由于您的示例包含嵌套块(例如
mand2
),您需要递归地处理这些块以避免调用第一个}
外部块的末尾块:block = '{' >> -block % (+~char_("{}")) >> '}';
松散的提示:
用
BOOST_SPIRIT_DEBUG
找出解析的地方rejected/matched。例如。稍微重构规则后:我们得到了输出 (On Coliru):
Read Test: <start> <try>fractal mand1 {\n </try> <no_quoted_string> <try>mand1 {\n ;lkkj;kj</try> <success> {\n ;lkkj;kj;\n}\n\n</success> <attributes>[[m, a, n, d, 1]]</attributes> </no_quoted_string> <body> <try>{\n ;lkkj;kj;\n}\n\nf</try> <fail/> </body> <success>fractal mand1 {\n </success> <attributes>[[]]</attributes> </start> Parse success: Remaining unparsed input: 'fractal mand1 { ;lkkj;kj; } fractal mand2 { if (...) { blablah; } } fractal julia1 { a = ss; } '
该输出帮助我发现我实际上忘记了 body 规则中的
- '}'
部分...:)当规则定义中不涉及任何语义操作时,不需要
%=
(docs)你可能想确保
fractal
实际上是一个单独的词,所以你不匹配fractalset multi { .... }
演示程序
有了这些,我们就可以有一个工作演示:
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct FractalListParser : qi::grammar<Iterator, std::vector<std::string>(), qi::space_type> {
FractalListParser() : FractalListParser::base_type(start) {
using namespace qi;
identifier = alpha > *(alnum | char_('_'));
block = '{' >> -block % +~char_("{}") >> '}';
start = *(
no_case["fractal"] >> identifier >> block
);
BOOST_SPIRIT_DEBUG_NODES((start)(block)(identifier))
}
qi::rule<Iterator, std::vector<std::string>(), qi::space_type> start;
// lexemes (just drop the skipper)
qi::rule<Iterator, std::string()> identifier;
qi::rule<Iterator> block; // leaving out the attribute means implicit `omit[]`
};
int main() {
using It = boost::spirit::istream_iterator;
It f(std::cin >> std::noskipws), l;
std::cout << "Read Test:" << std::endl;
FractalListParser<It> parser;
std::vector<std::string> data;
bool r = qi::phrase_parse(f, l, parser, qi::space, data);
if (r) {
std::cout << "Parse success:\n";
for (auto& i : data)
std::cout << "----\n" << i << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
打印:
Read Test:
Parse success:
----
mand1
----
mand2
----
julia1