编译器一日一练(DIY系列之复杂语法树)

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

        代码地址:https://github.com/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 ; }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式-老费

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值