boost spirit ——编译器,语法解析器

使用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类型有点不好处理。

完整的attribute计算规则见 链接链接 and 链接

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值