编译器一日一练(DIY系列之中间代码生成)

Posted 嵌入式-老费

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器一日一练(DIY系列之中间代码生成)相关的知识,希望对你有一定的参考价值。

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        代码地址:https://github.com/feixiaoxing/DIYCompiler/blob/master/day09/Parse1.jj

        说到中间代码生成,目前来说用的比较多的一个方案就是llvm。也就是说,如果把ast语法树翻译成llvm支持的中间代码,那么后端的事情就不需要自己操心了。当然,如果开发的是商用编译器,确实可以这么做。

        但是今天我们翻译的是四则运算,所以可以做的简单一点。中间代码生成,说白了就是翻译ast。遍历到哪个节点,就翻译哪个节点。这个时候一般会出现两个问题,一个是中间变量的生成;一个是ast的递归。

        说到中间变量,主要是因为在计算的过程当中需要对中间变量进行命名。而这个中间变量原来在实际代码中是完全不存在的,是编译器本身为了自身的需要临时添加的。比如,可以这么处理,

	// important index
	public static int allocate_index()
	
		index += 1;
		return index;
	

        如上面代码所示,完全可以用tmp+index的形式来完成,这样生成的临时变量肯定不会发生重名的情况。

        解决了重命名,接下来就是处理递归代码的情况。这个时候一般是按照左、右、根的形式来进行的。即先翻译左子树的代码,再翻译右子树的代码,最后翻译根的代码,这也是符合翻译的基本逻辑。

	// generate inter-mediate code
	public void generate_intermediate_code()
	
		int value;
		
		if(get_left_node().get_node_type() == "/")
		
			((div_node)get_left_node()).generate_intermediate_code();
		
		
		if(null == get_right_node())
		
			return;
		

		set_node_name("tmp" + Integer.toString(Parse.allocate_index()));
		if(get_left_node().get_node_type() == "/")
		
			System.out.println("div " + get_node_name() + " " + get_left_node().get_node_name() + " / " + Integer.toString(((value_node)get_right_node()).get_value()));
		
		else
		
			System.out.println("div " +get_node_name() + " " + Integer.toString(((value_node)get_left_node()).get_value()) + " / " + Integer.toString(((value_node)get_right_node()).get_value()));
		
	

        上面这段代码的逻辑就是,如果有左子树,就先翻译左子树。如果右节点为空,则退出,这主要是防止只有一个数字出现的情况。接下里就是调用allocate_index,创建一个临时tmp变量,同时将这个名字赋值给节点。最后判断当前节点的类型,如果是单纯数字,则输出div 数字,数字即可;如果不是,则翻译成div 临时变量名,数字的格式。

        为了验证我们的翻译是否成功,可以测试一下,

C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day09>java Parse 2/1
div tmp1 2 / 1

tmp1
     2
     1

C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day09>java Parse 6/2/3/1
div tmp1 6 / 2
div tmp2 tmp1 / 3
div tmp3 tmp2 / 1

tmp3
     tmp2
          tmp1
               6
               2
          3
     1

C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day09>java Parse 8/1/2/3/1
div tmp1 8 / 1
div tmp2 tmp1 / 2
div tmp3 tmp2 / 3
div tmp4 tmp3 / 1

tmp4
     tmp3
          tmp2
               tmp1
                    8
                    1
               2
          3
     1

        如上述代码所示,分成两个部分,第一部分打印成中间代码,第二部分打印成语法树。语法树的部分上一节已经提过,这里只是将/换成了临时变量名。回过头来看中间代码生成。我们不妨看最后一个案例8/1/2/3/这个。首先,tmp1=8/1,接着就是tmp2=tmp1/2、tmp3=tmp2/3、tmp4=tmp3/1,这样就完成了所有的翻译动作。

        实际效果如何,大家最好自己跑一跑。这里贴出完成jj文件,供大家参考。

options 
    STATIC = false;

 
PARSER_BEGIN(Parse)
import java.io.*;

class node

	public node left;
	public node right;
	public String node_type;
	public String node_name;
	public int depth;
	
	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;
	
	public void set_node_name(String node_name) this.node_name = node_name;
	public String get_node_name() return this.node_name;

	public void set_depth(int depth) this.depth = depth;
	public int get_depth() return this.depth;
	public int calculate_depth()  // node depth should be calculated before print it, so just calculate root node

		int left_depth = 0;
		int right_depth = 0;
		int final_depth = 0;
		if(get_left_node() == null && get_right_node() == null) 
			set_depth(1);
			return 1;
		
		
		if(null != get_left_node()) 
			left_depth = get_left_node().calculate_depth();
		

		if(null != get_right_node())
			right_depth = get_right_node().calculate_depth();
		
		
		final_depth = (left_depth > right_depth) ? (left_depth+1) :(right_depth +1);
		set_depth(final_depth);
		return final_depth;
	
	
	public void print_node(int start_depth, int start_point) // add print node function
	
		int i = 0;
		
		for(i =0; i < (start_point - start_depth*5); i++)
			System.out.print(" ");
		
		
		if(get_node_type() != "value_node")
		
			System.out.println(get_node_name());
		
		else
		
			System.out.println(((value_node)this).get_value());
		

		if(get_left_node() != null) get_left_node().print_node(start_depth -1, start_point);
		if(get_right_node() != null) get_right_node().print_node(start_depth -1, start_point);
	


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;
		
	

	// add semantic check
	public int check_value() 
	
		if(get_right_node() != null)
		
			if( 0 == ((value_node)get_right_node()).get_value())
			
				System.out.println("Div by Zero");
				return -1;
			
		
		
		if(get_left_node().get_node_type() == "/")
		
			return ((div_node)(get_left_node())).check_value();
		

		return 1;
	
	
	// generate inter-mediate code
	public void generate_intermediate_code()
	
		int value;
		
		if(get_left_node().get_node_type() == "/")
		
			((div_node)get_left_node()).generate_intermediate_code();
		
		
		if(null == get_right_node())
		
			return;
		

		set_node_name("tmp" + Integer.toString(Parse.allocate_index()));
		if(get_left_node().get_node_type() == "/")
		
			System.out.println("div " + get_node_name() + " " + get_left_node().get_node_name() + " / " + Integer.toString(((value_node)get_right_node()).get_value()));
		
		else
		
			System.out.println("div " +get_node_name() + " " + Integer.toString(((value_node)get_left_node()).get_value()) + " / " + Integer.toString(((value_node)get_right_node()).get_value()));
		
	


public class Parse 

    public static int index = 0;

    public static void main(String[] args) 
        for (String arg : args) 
            try 
				div_node div = evaluate(arg);
				div.generate_intermediate_code();

				System.out.println("");
				div.calculate_depth();
				div.print_node(div.get_depth(), div.get_depth()*5);

                //System.out.println(div.check_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();
    
	
	// important index
	public static int allocate_index()
	
		index += 1;
		return index;
	
	

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 ));

		// important code about node adding
		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系列之开篇)