【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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 ; }
}