编译器一日一练(DIY系列之语义分析)
Posted 嵌入式-老费
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器一日一练(DIY系列之语义分析)相关的知识,希望对你有一定的参考价值。
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
代码地址:https://github.com/feixiaoxing/DIYCompiler
语义分析一般是跟在语法分析后面进行的。语义分析的内容比较广。它的输入是语法树,并且在这过程当中会生成符号表,利用符号表的意义去检测语义是否正确。记录的符号表一般有全局变量、函数名、局部变量、参数名这些。此外,除了检测符号表之外,还会检测一些关键字的合理性,比如break外面是否有switch或者循环、continue是否外面有循环、goto是否真的有label与之匹配等等。总之,在中间代码生成前,所有的错误都要在这个阶段来被发现。
如果大家读到这里,还是不能明白语义分析的话,接下来可以举几个例子来说明,分析下哪些语句是符合语法,但是不符合语义的,比如
1)全局标识符重名;
2)局部标识符重名;
3)调用函数不存在;
4)调用函数参数不匹配等等。
当然,因为我们做的是四则运算,并且挑了一个除法来做运算。而除法里面最不能做的,就是除数不能等于0。基于这一点,我们就可以做一个简单的除法分析检测。
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;
因为检测的是一个除法运算,或者是连续除法运算,所以除数只能出现在右子树。检测的方法是自顶而下进行的。如check_value函数所示,首先检测右子树是否为空,如果不为空,并且发现除数为0,那么打印出来并且报警。接着继续检测左子树,判断左子树是否是递归语法树,如果是则继续检测递归语法树,反之则返回为1。整个流程还是比较清晰的。
语义分析和语法分析基本是一一对应的,比如有一个if-statement的语法树,就会一个check_if_statement的语义分析。其他语法以此类推。
说了这么多,大家还没有看到添加check_value后的效果是什么,我们可以编译测试下,
C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day08>java Parse 1/0
Div by Zero
-1
C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day08>java Parse 2/1/0
Div by Zero
-1
C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day08>java Parse 2/0/1
Div by Zero
-1
C:\\Users\\feixiaoxing\\Desktop\\DIYCompiler\\day08>java Parse 6/2/0/3/1
Div by Zero
-1
从运行结果看,基本还是符合我们要求的。为了便于大家理解整个过程,这里给出完整的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()
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;
public class Parse
public static void main(String[] args)
for (String arg : args)
try
System.out.println(evaluate(arg).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();
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系列之语义分析)的主要内容,如果未能解决你的问题,请参考以下文章