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

Posted 嵌入式-老费

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器一日一练(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 ; 

以上是关于编译器一日一练(DIY系列之简单语法树)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

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