关闭

boost之spirit学习-mini_c(3)

854人阅读 评论(1) 收藏 举报

前一章分析完了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;
}











0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:6415次
    • 积分:169
    • 等级:
    • 排名:千里之外
    • 原创:10篇
    • 转载:0篇
    • 译文:0篇
    • 评论:2条
    文章分类
    文章存档
    最新评论