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

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

        代码地址:https://github.com/feixiaoxing/DIYCompiler

        之前我们做四则运算的时候,用的都是数值进行计算。大家有没有思考过,其实可以用二叉树的形式,把这些语法都串起来、构建起来。举一个例子,现在有一个除法运算,如果没有语法树,那么jj格式的内容基本是这样的,

options {
    STATIC = false;
}
 
PARSER_BEGIN(Parse)
import java.io.*;
public class Parse {
    public static void main(String[] args) {
        for (String arg : args) {
            try {
                System.out.println(evaluate(arg));
            } catch (ParseException ex) {
                System.err.println(ex.getMessage());
            }
        }
    }
 
    public static long 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"])+>
}
 
long expr() throws NumberFormatException :
{
    Token a ;
    Token b ;
    int value = 0 ;
}
{
    a = <INTEGER> "/" b = <INTEGER>
    { 
		value = Integer.parseInt( a.image ) / Integer.parseInt(b.image); 
	}
    <EOF>
    { return value ; }
}

        上面的语法比较简单。左边是INTEGER,右边也是一个INTEGER,这样直接把这两个整数进行相除就可以了。现在,我们决定不直接处理,而是用二叉树把它们构建起来。

        所谓的二叉树,就是每一个tree_node,都有一个left_node,也有一个right_node。其中left_node和right_node可能是leaf_node,也可以是另外一个tree_node,这就是二叉树。

        这从某种意义上说,它就和编程语法很类似。比如说四则运算中,一般总要有一个src1,一个src2,一个dst吧,src1和src2怎么组合,应该有一个opr吧。所以,src1对应left_node,src2对应right_node,不同的tree_node本身对应不同的opr,而tree_node的value对应dst。所以,感觉从某种程度上说,tree_node就是为编译方法量身打造的。

        做好上面的除法运算,不失一般性,首先可以定义一个基础node。这个基础node只包含了left、right、node_type这些基础信息。

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

        有了基础node,就可以进一步扩展为value_node、div_node。其中value_node的定义是这样的,

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

        而div_node的定义是这样的,

class div_node extends node
{
	div_node() { set_node_type("/");}
	
	public int get_value() {
		assert(get_left_node().get_node_type() == "value_node");
		assert(get_right_node().get_node_type() == "value_node");
		return ((value_node)get_left_node()).get_value() / ((value_node)get_right_node()).get_value();
	}
}

        有了value_node和div_node,下面即可以生成最简单的语法树了,

div_node expr() throws NumberFormatException :
{
    Token a ;
    Token b ;
    div_node div;
}
{
    a = <INTEGER> "/" b = <INTEGER>
    { 
		value_node node_a = new value_node();
		node_a.set_value(Integer.parseInt( a.image ));

		value_node node_b = new value_node();
		node_b.set_value(Integer.parseInt( b.image ));
		
		div = new div_node();
		div.set_left_node(node_a);
		div.set_right_node(node_b);
		
	}
    <EOF>
    { return div ; }
}

        这个函数的内容不复杂。最重大的意义,在于这个返回值。做过实验的同学都知道,这个函数的返回值之前是long,现在变成了div_node,所以整个函数的意义就是它能够返回语法树。

        为了便于大家理解语法树的概念,这里给出全部的Parse.jj代码,

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() {
		assert(get_left_node().get_node_type() == "value_node");
		assert(get_right_node().get_node_type() == "value_node");
		return ((value_node)get_left_node()).get_value() / ((value_node)get_right_node()).get_value();
	}
}

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> "/" b = <INTEGER>
    { 
		value_node node_a = new value_node();
		node_a.set_value(Integer.parseInt( a.image ));

		value_node node_b = new value_node();
		node_b.set_value(Integer.parseInt( b.image ));
		
		div = new div_node();
		div.set_left_node(node_a);
		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、付费专栏及课程。

余额充值