岁月流转,往昔空明

C++博客 首页 新随笔 联系 聚合 管理
  118 Posts :: 3 Stories :: 413 Comments :: 0 Trackbacks

4.从语法树到OP CODE

知道咱们的虚拟机能够执行OP CODE之后,下一步就要考虑,怎么从语法树里面生成咱们需要的OP CODE了。简单来讲,语法树就是将程序的逻辑按照树状组织并保存在内存中的一种形式。有关于更详细的信息,搜“Syntax Tree”,到处都是解释。

一时不明白也没关系,我们来看一个直观的例子。考虑a+b这样一个基本形式的表达式。这个表达式既可以按照我们所写的这样,分为a,+,b三个部分串行表示,也可以表示成下图的样子



可能一个表达式你还看不出来树形的优势。要是表达式级联起来,就显示出这种表示的威力了:


 
这样一个语法树,可以不借助任何别的手段,保存了表达式的优先级关系。这里的语法树表示的就是(A+B)*C的表达式。同时,在语法树上求值也很方便,后根遍历语法树就可以了。即先算出左右节点的值,再根据当前节点符号求出当前节点值。

王阳明说,知行合一。知道了语法树是什么东西,我们就要开始考虑怎么用了。“怎么用”这个问题可以分成两个部分,第一,语法树怎么实现。第二,语法树怎么生成op code。啊,先不要把语法树想象的这么复杂。在这里,我们的运算符只有加号,一个加号也只能带两个int的值节点,而不能递归的带上一个符号节点。也就是说,这棵树只可能有一种形式而已。

首先来解决语法树怎么实现的问题。在这个问题上,我们只需要把握一点,语法树是一个天然的composite模式。我们用一个UML来看看这个只有加法算符的语法树定义:
 
唔,很简洁,不是么。Node_type是一个syntax_node_types类型的枚举,这个枚举告诉以后的代码生成器这个抽象的node究竟是个什么类型,然后代码生成器再还原它原本的类型并生成适当的代码。op是一个operators类型的枚举,表示一个二元运算的操作符。对于本例,只有operators::add可用。
在有了基本实现之后,再考虑一下其它需求,例如语法树节点类型之间的可能存在的循环依赖问题,语法树的深浅拷贝问题,等等,最终SASL的语法树节点接口是这样的:

 1 struct node{
 2     syntax_node_types type;
 3     template <typename NodeT> NodeT* clone() const;
 4     template <typename NodeT> NodeT* deepcopy() const;
 5 protected:
 6     virtual node* clone_impl() const = 0;
 7     virtual node* deepcopy_impl() const = 0;
 8 };
 9 
10 struct binary_expression: public node{
11     operators op;
12     boost::shared_ptr<constant> left_expr;
13     boost::shared_ptr<constant> right_expr;
14 };
15 
16 struct constant: public node{
17     int val;
18 };

道理复杂,不过实际上,并没有那么复杂吧?
下面来解决第二个问题:怎么用表达式树产生代码?我不多解释,直接上代码,相信你一定会看明白的:

1 vm_codegen& vm_codegen::emit_expression( const binary_expression& expr ){
2     if ( expr.op != operators::add ){ return *this; }
3     int c0 = expr.left_expr->val;
4     int c1 = expr.right_expr->val;
5     ins_.push_back( instruction( op_loadrc, r0, c0 ) );
6     ins_.push_back( instruction( op_loadrc, r1, c1 ) );
7     ins_.push_back( instruction( op_add, r0, r1 ) );
8     return *this;
9 }


然后我们将生成语法树,生成code,运行code的代码补上,运行,OK~
你一定会说,啊,硬性绑定寄存器!太可怕了!如果表达式复杂了该怎么办呢?呵呵。这些都是以后的问题了。以后的问题,就由以后的我们去解决好了。今日事,今日毕,时间不早,咱们还是洗洗睡了。

posted on 2009-12-11 10:04 空明流转 阅读(1940) 评论(3)  编辑 收藏 引用

评论

# re: 实用编译器构建指南(二) 2009-12-11 11:09 正心
你为什么非要把代码里带上boost  回复  更多评论
  

# re: 实用编译器构建指南(二) 2009-12-11 11:34 空明流转
@正心
没明白你什么意思。。。  回复  更多评论
  

# re: 实用编译器构建指南(二) 2009-12-12 02:01 陈梓瀚(vczh)
@空明流转
就是说为什么不using namespace boost;

话说,syntax_node_type type;是evil  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理