编译器一日一练(DIY系列之复杂语法树)
Posted 嵌入式-老费
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器一日一练(DIY系列之复杂语法树)相关的知识,希望对你有一定的参考价值。
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
代码地址:GitHub - feixiaoxing/DIYCompiler
之前一章,我们谈到过简单语法树的构建。说是语法树,其实就是一个tree node。它的左子树是一个整数,右子树也是一个整数。所有的工作都是为了构建一个div_node。
在此基础上,大家可以思考一下,如果是一个连续的除法,这个时候应该怎么处理?一个比较容易想到的办法就是在原来简单语法树的基础之上,构建更为复杂的语法树。
div_node expr() throws NumberFormatException :
Token a ;
Token b ;
div_node div;
a = <INTEGER>
value_node node_a = new value_node();
node_a.set_value(Integer.parseInt( a.image ));
div = new div_node();
div.set_left_node(node_a);
(
"/" b = <INTEGER>
value_node node_b = new value_node();
node_b.set_value(Integer.parseInt( b.image ));
if(div.get_right_node() == null)
div.set_right_node(node_b);
else
div_node prev = div;
div = new div_node();
div.set_left_node(prev);
div.set_right_node(node_b);
)*
<EOF>
return div ;
上面的代码是一个比较典型的实现方式。其中最重要的几句代码,就是div.get_right_node()这个判断。首先,不管怎么说,node_a和div肯定是要创建的。至于node_b是否要创建,这个要看现场的情况。这里假设node_b需要创建,情况也会有两说。一是当前div的右子树还是null,这个时候不用说,直接赋值上就可以。二是当前div已经满了,这个时候如何解决呢?其实就是在当前div的基础之上再增加一个父节点,将node_b挂到这个父节点上就可以了。
二叉树的访问方法一般是左、右、根,因此有了上面的这个构建语法树的办法,才会按照左、右、根的访问方法将语句按照原来的布局继续解析下去。至此,大家会发现,不管有多少的除法运算,最终的返回值都是一个div_node,只不过他的下面有多少层左子树罢了。
经过这些操作,大家还会发现本来不规则的数据,通过规约,就会被简洁的语法树所替代。很多编译课程告诉我们,其实后面还有语义分析、中间代码生成,汇编生成等等。但其实来说,有了语法树,本身就可以做很多事情,甚至不需要后面的步骤都可以。比如有了这个除法的语法树,计算就是一件很容易的事情了,
class div_node extends node
div_node() set_node_type("/");
public int get_value()
int left = 0, right = 0;
// get left node
if(get_left_node().get_node_type() == "/")
left = ((div_node)get_left_node()).get_value();
else
left = ((value_node)get_left_node()).get_value();
// get right node
if(get_right_node() == null)
return left;
else
right = ((value_node)get_right_node()).get_value();
return left/right;
首先它会查看是否有递归,如果有递归左子树,就会继续递归调用。接着,它会看右子树,看是否确实有除法计算,没有的话,直接返回left即可;有的话,需要进行left、right的除法运算。
上面给出的都是代码的片段,这里给出完整的代码。其实这后面的逻辑都不复杂,关键是思考怎么一步一步实现这一点的。此外,一定要动手实践。哪怕是再简单的东西,有没有自己动手去做,效果还是大不一样的。
options
STATIC = false;
PARSER_BEGIN(Parse)
import java.io.*;
class node
public node left;
public node right;
public String node_type;
node() this.left = this.right = null;
public void set_left_node(node left) this.left = left;
public node get_left_node() return this.left;
public void set_right_node(node right) this.right = right;
public node get_right_node() return this.right;
public void set_node_type(String node_type) this.node_type = node_type;
public String get_node_type() return this.node_type;
class value_node extends node
public int value;
value_node() set_node_type("value_node");
public int get_value() return this.value;
public void set_value(int value) this.value = value;
class div_node extends node
div_node() set_node_type("/");
public int get_value()
int left = 0, right = 0;
// get left node
if(get_left_node().get_node_type() == "/")
left = ((div_node)get_left_node()).get_value();
else
left = ((value_node)get_left_node()).get_value();
// get right node
if(get_right_node() == null)
return left;
else
right = ((value_node)get_right_node()).get_value();
return left/right;
public class Parse
public static void main(String[] args)
for (String arg : args)
try
System.out.println(evaluate(arg).get_value());
catch (ParseException ex)
System.err.println(ex.getMessage());
public static div_node evaluate(String src) throws ParseException
Reader reader = new StringReader(src);
return new Parse(reader).expr();
PARSER_END(Parse)
SKIP: <[" ", "\\t", "\\r", "\\n"]>
TOKEN:
<INTEGER: (["0"-"9"])+>
div_node expr() throws NumberFormatException :
Token a ;
Token b ;
div_node div;
a = <INTEGER>
value_node node_a = new value_node();
node_a.set_value(Integer.parseInt( a.image ));
div = new div_node();
div.set_left_node(node_a);
(
"/" b = <INTEGER>
value_node node_b = new value_node();
node_b.set_value(Integer.parseInt( b.image ));
if(div.get_right_node() == null)
div.set_right_node(node_b);
else
div_node prev = div;
div = new div_node();
div.set_left_node(prev);
div.set_right_node(node_b);
)*
<EOF>
return div ;
以上是关于编译器一日一练(DIY系列之复杂语法树)的主要内容,如果未能解决你的问题,请参考以下文章