boost之spirit学习-mini_c(3)

来源:互联网 发布:淘宝有些不能发图评价 编辑:程序博客网 时间:2024/06/10 08:48

前一章分析完了main.cpp,了解了mini_c的主流程。现在来看看抽象语法树的定义:ast.hpp


首先,为一些对象打上id,方便编译错误时由对象的id查找到出错的位置(这个是由annotation记录的,后边会讲)

    struct tagged    {           int id; // Used to annotate the AST with the iterator position.                // This id is used as a key to a map<int, Iterator>                // (not really part of the AST.)    };  

带id的对象包括:identifier、function_call、function、return_statement。


    struct nil {};     struct unary;    struct function_call;    struct expression;    struct identifier : tagged    {           identifier(std::string const& name = "") : name(name) {}        std::string name;    };      typedef boost::variant<            nil           , bool          , unsigned int           , identifier          , boost::recursive_wrapper<unary>          , boost::recursive_wrapper<function_call>          , boost::recursive_wrapper<expression>        >       operand;

后边是对nil的定义,unary(一元表达式)、function_call、expression进行提前声明。

identifier的定义没啥好说的。

operand(操作数)的定义有点意思。操作数可以是空,也可以是bool、unsigned int 或 变量 或 一元表达式 或 函数调用 或 更general的表达式。它用variant来表达这种概念。

但unary、function_call、expression为什么要用boost::recursive_wrapper来定义呢?

因为它们有循环包含关系

  • unary(一元表达式)里有两个成员,一个是optoken,另一个就是operand。
  • expression包含了operation的列表,operation又包含了operand,所以是循环的。
  • function_call里有一个expression的list,所以也是循环包含的

对于这种循环包含的对象,我们一般都用指针相互指向,对象动态分配获得。但这样的话我们就要负责对象的动态分配和释放了,费事又容易出错。因此boost有了recursive_wrapper。

recursive_wrapper定义在<boost/variant/recursive_wrapper.hpp>里。看看代码就知道recursive_wrapper只是对类型T的简单封装,内部维护一个T的指针,内存自动从堆上分配释放,但对外的表现装作和类型T的引用一样。


下面是运算符的定义,没什么好说的:

    enum optoken    {           op_plus,        op_minus,        op_times,        op_divide,        op_positive,        op_negative,        op_not,        op_equal,        op_not_equal,        op_less,        op_less_equal,        op_greater,        op_greater_equal,        op_and,        op_or    };  


下面有一系列语法元素的定义:

    struct unary    {           optoken operator_;        operand operand_;    };      struct operation    {           optoken operator_;        operand operand_;    };      struct function_call    {        identifier function_name;        std::list<expression> args;    };    struct expression    {        operand first;        std::list<operation> rest;    };    struct assignment    {        identifier lhs;        expression rhs;    };    struct variable_declaration    {        identifier lhs;        boost::optional<expression> rhs;    };

unary、function_call、assignment、variable_declaration的定义都很直接明了。

expression的定义复杂点:单个操作数是表达式,单个操作数与其它操作数通过二元运算符连接起来也是表达式。

但总感觉这样的expression定义不符合直观。难道不应该类似这样定义吗:

expr: const | variable | expr + expr | expr - expr | expr * expr | expr / expr | ....

此处存疑,以后再研究为什么


后面是语句级的定义:

    struct if_statement;    struct while_statement;    struct statement_list;    struct return_statement;    typedef boost::variant<            variable_declaration          , assignment          , boost::recursive_wrapper<if_statement>          , boost::recursive_wrapper<while_statement>          , boost::recursive_wrapper<return_statement>          , boost::recursive_wrapper<statement_list>        >    statement;    struct statement_list : std::list<statement> {};    struct if_statement    {        expression condition;        statement then;        boost::optional<statement> else_;    };    struct while_statement    {        expression condition;        statement body;    };    struct return_statement : tagged    {        boost::optional<expression> expr;    };

和之前一样的原因,statement被定义为variant,而且if/while/return/list statement用boost::recursive_wrapper定义

return_statement被用tagged标记。这是为了检查void函数返回非void值时确定return语句的位置。

另一个值得一提的是对于可选的语法部分使用了boost::optional来表达。boost::optional<T>在被赋值时可以当T的引用使,当访问成员时,可以当指针使用(重载了->和*运算符),可以隐式转换成bool类型,用于判断是否有值。


最后是函数及函数列表的定义和两个调试用的辅助函数:

    struct function    {        std::string return_type;        identifier function_name;        std::list<identifier> args;        statement_list body;    };    typedef std::list<function> function_list;    // print functions for debugging    inline std::ostream& operator<<(std::ostream& out, nil)    {        out << "nil"; return out;    }    inline std::ostream& operator<<(std::ostream& out, identifier const& id)    {        out << id.name; return out;    }

最后一段是最彰显boost之奇技淫巧的地方,通过BOOST_FUSION_ADAPT_STRUCT把上面定义的AST类转换成满足fusion的sequence concept的类:

BOOST_FUSION_ADAPT_STRUCT(    client::ast::unary,    (client::ast::optoken, operator_)    (client::ast::operand, operand_))BOOST_FUSION_ADAPT_STRUCT(    client::ast::operation,    (client::ast::optoken, operator_)    (client::ast::operand, operand_))BOOST_FUSION_ADAPT_STRUCT(    client::ast::function_call,    (client::ast::identifier, function_name)    (std::list<client::ast::expression>, args))BOOST_FUSION_ADAPT_STRUCT(    client::ast::expression,    (client::ast::operand, first)    (std::list<client::ast::operation>, rest))BOOST_FUSION_ADAPT_STRUCT(    client::ast::variable_declaration,    (client::ast::identifier, lhs)    (boost::optional<client::ast::expression>, rhs))BOOST_FUSION_ADAPT_STRUCT(    client::ast::assignment,    (client::ast::identifier, lhs)    (client::ast::expression, rhs))BOOST_FUSION_ADAPT_STRUCT(    client::ast::if_statement,    (client::ast::expression, condition)    (client::ast::statement, then)    (boost::optional<client::ast::statement>, else_))BOOST_FUSION_ADAPT_STRUCT(    client::ast::while_statement,    (client::ast::expression, condition)    (client::ast::statement, body))BOOST_FUSION_ADAPT_STRUCT(    client::ast::return_statement,    (boost::optional<client::ast::expression>, expr))BOOST_FUSION_ADAPT_STRUCT(    client::ast::function,    (std::string, return_type)    (client::ast::identifier, function_name)    (std::list<client::ast::identifier>, args)    (client::ast::statement_list, body))

boost::fusion可以参考:http://www.boost.org/doc/libs/1_49_0/libs/fusion/doc/html/fusion。又是一个很难理解的东西。

为什么要把语法树的类用BOOST_FUSION_ADAPT_STRUCT处理一下呢?

这是因为spirit::qi里的parser规则都有一个与之相关联的attribute类型,存放该规则解析出的数据。在mini_c里,每个语法树对象就是规则解析出的数据。但规则是像搭积木一样嵌套堆起来的。例如:

rule_x = rule_a >> rule_b >> rule_c >> rule_d;

表示rule_x是由rule_a、rule_b、rule_c、rule_d依次连接组成。rule_a/b/c/d各自有自己的attribute_type,attr_a/b/c/d。(rule_a >> rule_b >> rule_c >> rule_d)也是一个规则,但它的attribute_type就是boost::fusion::tuple<attr_a, attr_b, attr_c, attr_d>。假设rule_x对应的attribute_type是我们自己定义的attr_x类型。那么怎么把该tuple的数据复制到attr_x里呢?首先把attr_x用BOOST_FUSION_ADAPT_STRUCT处理一下,使它也变成一个和tuple兼容的东西。然后再用boost::fusion::copy把tuple里的东西拷贝到attr_x里去。

为了简单演示这个过程,写了一段小代码:

struct employee{    std::string name;    int         age;    int         salary;};BOOST_FUSION_ADAPT_STRUCT(        employee,        (std::string, name)        (int, age)        (int, salary)        )   std::ostream& operator << (std::ostream& os, const employee& e){    os << e.name << " " << e.age << " " << " " << e.salary;    return os; }int main(int argc, char* argv[]){    boost::fusion::vector<std::string, int, int> fv("JJP", 28, 10000);    employee e;    boost::fusion::copy(fv, e);     std::cout << e << std::endl;    return 0;}











原创粉丝点击