编译器一日一练(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系列之复杂语法树)的主要内容,如果未能解决你的问题,请参考以下文章

编译器一日一练(DIY系列之语法树打印)

编译器一日一练(DIY系列之语法树打印)

编译器一日一练(DIY系列之简单语法树)

编译器一日一练(DIY系列之简单语法树)

编译器一日一练(DIY系列之语义分析)

编译器一日一练(DIY系列之语义分析)