使用spirit能很方便的解析自定义的语法规则,在他的文档中也说明了spirit与regex还有其他库的不同点。灵活,伸缩性好,可以用来搭建小的语法解析器也可以用来开发大型编译器等等。
boost::spirit 目前主要有三部分:
Boost.Qi (for writing parsers), Boost.Karma (for generators) and Boost.Lex (for lexers)
定义语法规则之前首先要了解一下Extended Backus-Normal Form (EBNF)
EBNF可以定义一下生成合法字符串的公式,例如:
例1:
rule1 = "0" | "1" | "2" | "3".
rule2 = "4" | "5" | "6" | "7" | "8" | "9" | rule1.
字符 | 表示 或,则 rule2 可以表示0-9所有的数字;一个复杂点的例子:
例2:
expression = term { ("+" | "-") term} .
term = factor { ("*"|"/") factor} .
factor = constant | variable | "(" expression ")" .
variable = "x" | "y" | "z" .
constant = digit {digit} .
digit = "0" | "1" | "..." | "9" .
表示一个合法的数学计算公式。
使用EBNF表示的语法可以很方便地用boost spirit表示:
例3:
EBNF:
group ::= '(' expression ')'
factor ::= integer | group
term ::= factor (('*' factor) | ('/' factor))*
expression ::= term (('+' term) | ('-' term))*
Boost Spirit:
group = '(' >> expression >> ')';
factor = integer | group;
term = factor >> *(('*' >> factor) | ('/' >> factor));
expression = term >> *(('+' >> term) | ('-' >> term));
其中 ::=
表示 被定义为
的意思,与=
号表示的意思相同,integer
表示所有整数,在例3中如下这些表达式都是合法的:
12345
-12345
+12345
1 + 2
1 * 2
1/2 + 3/4
1 + 2 + 3 + 4
1 * 2 * 3 * 4
(1 + 2) * (3 + 4)
(-1 + 2) * (3 + -4)
1 + ((6 * 200) - 20) / 6
(1 + (2 + (3 + (4 + 5))))
但是这些表达式则不合法:
1/2 +-+ 3/4
1 +/ 2 += 3 + 4
1 + 2 * 3.14 * 4
(1 + 2) .* (3 + 4)
直接上代码:
#include <iostream>
#include <sstream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
using namespace boost::spirit;
int main()
{
std::string cmd_str = "$aaa[bbb]==ccc LINE 123"
std::vector<char> name1;
std::vector<char> name2;
std::vector<char> value;
int var=0;
std::string::iterator iter_begin = cmd_str.begin();
bool parse_ok = qi::phrase_parse(iter_begin, cmd_str.end(),
// Begin grammar
(
('$'
>>(+(qi::char_ - '['))[boost::phoenix::ref( component_name ) = qi::_1] // 获得aaa
>> '['
>> (+(qi::char_ - ']'))[boost::phoenix::ref( variable_name ) = qi::_1] // 获得bbb
>> ']'
>> lit("==")
>> (+(qi::char_-lit("LINE")))[boost::phoenix::ref( target_value ) = qi::_1] // 获得ccc
^
(-(lit("LINE") >> qi::int_[boost::phoenix::ref(var) = qi::_1])) // 获得123
),
// End grammar
ascii::space // skip the space
);
// 判断是否符合语法规则
if (!parse_ok || iter_begin != cmd_str.end())
{
return -1
}
std::cout << std::string(name1.begin(), name1.end()) << std::endl;
std::cout << std::string(name2.begin(), name2.end()) << std::endl;
std::cout << std::string(value.begin(), value.end()) << std::endl;
return 0;
}
// 如果是字符串,也可以使用 qi::as_string [ +(qi::char_ - '[') ] [ boost::phoenix::ref(component_name ) = qi::_1 ]
// 注意,如果使用了qi::phrase_parse并且skipper是ascii::space,则规则会忽略所有的空格,
// 即使是规则中加入 "- ascii::blank" 或者 "- ascii::space"等条件,
// 如果某条语句需要考虑空格影响,则使用qi::parse或者qi::lexeme[rule],例如:
代码:
std::string::const_iterator iter_begin = cmd_str.begin();
bool parse_ok = qi::phrase_parse(iter_begin, cmd_str.end(),
// 解析字符串: $asd[xyz][i] == ijk LINE 1.23
// Begin grammar
(
('$'
>> qi::lexeme[(+(qi::char_ - '[' - ascii::blank)[boost::phoenix::ref( component_name ) += qi::_1])] // 这里将获asd部分
>> '['
>> qi::lexeme[(+(qi::char_ - ']' - ascii::blank)[boost::phoenix::ref( variable_name ) += qi::_1])] // xyz部分
>> ']'
>> -('[' >> qi::int_[boost::phoenix::ref(vector_index) = qi::_1] >> ']')) // i(如果存在)
>> lit("==")
// 必须使用lexeme(不允许跳过空格),否则即使遇到"- ascii::blank" 规则也不会停止解析,因为忽略空格信息
>> qi::lexeme[(+(qi::char_-(lit("LINE")|lit("line")) - ascii::blank)[boost::phoenix::ref( target_value ) += qi::_1])]
),
// End grammar
ascii::space // skip the space
);
正确获得attribute的关键是理解attitute的计算规则,例如:
如果a的属性为A, b的属性为B, 则 (a >> b) 的结果为一个 tuple<A, B>
如果a的属性为A, b的属性也为A, 则 (a >> b) 的结果为 vector<A>
例如: double_ >> int_ ,则 a 为 double_, b 为 int_,然后(double_ >> int_) 输出的属性为 tuple<double, int>
获得属性:
tuple<double, int> ti;
(double_ >> int_)[ref(ti) = _1] // _1 表示parse的结果为一个tuple
vector<int> vi;
(*int_ >> int)[ref(vi)=_1] // _1 表示parse的结果为一个vector
或者
*(int_[phoenix::push_back(vi, _1)]) >> int_[phoenix::push_back(vi, _1)] // 恩,注意括号的位置
int i;
(lit("INT") >> int_)[ref(i) = _1] // 此处lit("INT")的属性为Unused ,则相当于没有属性,因此组合后的属性还是int类型
或者
lit("INT") >> int_[ref(i) = _1] // _1 表示parse的结果为一个int
...
使用 phoenix::push_back 能很方便得到一个vecor,
配合使用lit,否则得到一个tuple类型有点不好处理。