boost之spirit学习-mini_c(5)

时间:2021-05-26 18:52:09

继续看看周边的小东西,从易到难。今天是annotation.hpp

annotation.hpp里定义了一个类:annotation。

顾名思义,它是给抽象语法树里的对象做标注的。标注什么呢?标注对象的位置。每个待标注的对象有一个id,这个id是一个iterator数组的下标。每次标注一个对象时,就把该对象的位置追加到iter数组的尾部,同时把该数据在数组中的下标作为id赋给该对象。

具体如何实现的呢?上代码:

    template <typename Iterator>
    struct annotation
    {   
        template <typename, typename>
        struct result { typedef void type; };

        std::vector<Iterator>& iters;
        annotation(std::vector<Iterator>& iters)
          : iters(iters) {}

首先,annotation定义了一个result嵌套结构体类型。这是boost的一个常用手法,用于获取函数对象的类型。例如函数对象F可能声明两个函数:

A operator() (int);
B operator() (double);
但怎么在编译时得知F(double)的返回值类型是A还是B呢?C++11引入了decltype可以解决这个问题。但在还没有C++11的时候,就要自己指定了。然后用boost::result_of<F(T1, T2...)>::type来获取。

annotation这个地方的result定义和boost::result_of还不太一样,不是用函数签名作为模板参数,而是直接用函数参数作为模板函数。这个应该是phoenix::function内部的使用方式吧(我猜测)。


然后定义了一个vector<Iterator>的引用。这个引用来自error_handler,全局只有一份,多个annotation对象共享。

        struct set_id
        {
            typedef void result_type;

            int id;
            set_id(int id) : id(id) {}

            void operator()(ast::function_call& x) const
            {
                x.function_name.id = id;
            }

            void operator()(ast::identifier& x) const
            {
                x.id = id;
            }

            template <typename T>
            void operator()(T& x) const
            {
                // no-op
            }
        };

        void operator()(ast::operand& ast, Iterator pos) const
        {
            int id = iters.size();
            iters.push_back(pos);
            boost::apply_visitor(set_id(id), ast);
        }

接下来这段比较有意思。定义了一个叫set_id的函数对象类型。用来给各种不同的语法对象设置id。看起来很蛋疼的样子,不就是设置一个id字段嘛,似乎有种脱了裤子放屁的味道。

其实不然,这是boost::variant的规范使用模式。boost::variant相当于一个C++版的高级union。你不知道它里面现在存的是什么类型,但你想针对每种不同的类型做一个特定的操作,boost::apply_visitor就派上用场了。它会根据ast里当前存储的实际类型,调用相应的operator()重载函数。

        void operator()(ast::variable_declaration& ast, Iterator pos) const
        {   
            int id = iters.size();
            iters.push_back(pos);
            ast.lhs.id = id; 
        }   

        void operator()(ast::assignment& ast, Iterator pos) const
        {   
            int id = iters.size();
            iters.push_back(pos);
            ast.lhs.id = id; 
        }   

        void operator()(ast::return_statement& ast, Iterator pos) const
        {   
            int id = iters.size();
            iters.push_back(pos);
            ast.id = id; 
        }   

        void operator()(ast::identifier& ast, Iterator pos) const
        {   
            int id = iters.size();
            iters.push_back(pos);
            ast.id = id; 
        }   
    };  

后边就是针对各种不同的语法对象做标注的重载函数了。这些operator()函数分别对应着annotation的各处使用:

        on_success(identifier,
            annotation_function(error_handler.iters)(_val, _1));
        on_success(variable_declaration,
            annotation_function(error_handler.iters)(_val, _1));
        on_success(assignment,
            annotation_function(error_handler.iters)(_val, _1));
        on_success(return_statement,
            annotation_function(error_handler.iters)(_val, _1));
        on_success(primary_expr,
            annotation_function(error_handler.iters)(_val, _1));
on_success是spirit::qi的一个函数:

    template <
        typename Iterator, typename T0, typename T1, typename T2
      , typename F>
    void on_success(rule<Iterator, T0, T1, T2>& r, F f)
接受两个参数,第一个是处理的规则 ,第二个是个函数对象,在成功匹配规则时调用。

函数对象的调用为:

                typedef
                    fusion::vector<
                        Iterator&
                      , Iterator const&
                      , Iterator const&>
                params;
                skip_over(first, last, skipper);
                params args(first, last, i);
                f(args, context);
f只接受两个参数,但第一个参数是boost::fusion::vector,估计spirit的_1、_2会把这种类型的参数打平。

所以可以认为f接受4个参数:first、last、i、context。

_val表示rule解析出的attribute,存储在context里。_1就表示first啦。


ok ,今天的解析到此为止,下班回家。改天正式开始解析function、statement、expression这三个解析代码的核心。