一、语义分析要解决的问题
•确定类型:确定标识符所关联的数据对象的数据类型。
•类型检查:按照语言的类型规则,对运算及运算分量进行类型检查,必要时做出相应类型转换。
•识别含义:根据程序设计语言的语义定义,确定各个构造部分组合后的含义,做出相应处理(生成中间代码或者目标代码)。
•静态语义检查:比如控制流检查,嵌套层数检查。
二、总体思路说明
之前在语义分析阶段生成了抽象语法树,抽象语法树本身就是一种中间代码,因此现在将语义分析和解释执行结合在一起,对抽象语法树进行一次遍历,完成语义分析、生成符号表并输出最后结果。抽象语法树的遍历类似于属性文法树的遍历。
文法规定:
l 所有变量声明是默认为int类型,int类型默认值为0,double为0.0。
l 除了数组变量外,其余变量在声明是都可与赋值
l 在声明数组时,可以生成多维数组,维数没有限制,声明数组的形式为int[m][n] a; m和n都为常数
l 数组在声明后,默认每个值都为0或者0.0
l 赋值时,int类型可以自动转成double,double类型不能转换为int,true转换为int和double是默认为0或0.0,false默认为1或者1.0。
l 整数和实数都支持16进制类型数 如-0x14ae.123;
l If和while语句中,如果类型为true或者值不为0.0或者0都视为满足条件
l Break跳出最近的循环
l Read语句用于从命令读取数值赋值给变量
l Write语句用于输出expr的值,可以是变量、常数或者和表达式
三、实现代码(代码比较长为了不破坏结构将代码部分字体放小,可以放大看)
1、之前代码完善
之前在声明变量时,如果用int a,b,c;声明语句时,生成语法树的顺序会发生改变,对语义分析会产生影响,因此对varDeclare部分进行修改,使顺序正确。
2、主要类型说明
在实现过程中,新建了三个类:
l Record类:记录着声明的变量的信息
l symbolTable类:符号表,里面有两个表,一个是table表,记录所有的声明的变量,一个是arrayTable,记录所有数组的声明。符号表中存储着Record。符号表的结构是arrarList,而每个内容是record,record里面有一个next属性指向同名的前一个record,内层的声明的变量会取代外层声明的,类似于杂凑表结构。
l Value类:返回表达式计算的结果,包括值和类型
类结构如下
public class Record implements Cloneable {
public static final int intType=0;//数据类型
public static final int doubleType=1;
Token name;//变量名,记录为tokem便于以后报错
ArrayList<Integer> arrayNum;//数组类型变量的维度大小
private int type;
private int intValue;
private double doubleValue;
private int level;//变量的层数
private Record next;//链表指针
public class SymbolTable {
private ArrayList<Record> table;//普通变量
private ArrayList<Record> arraytable;//数组变量
public class Value {
public static final int intType=0;
public static final int doubleType=1;
public static final int trueType=2;
public static final int falseType=3;
private int type;//类型
private int intValue;//int型值
private double doubleValue;//double型值
3、program节点
创建executeProgram函数,初始化level,遍历指向每一个子树,对其调用executeStmt,执行完毕后清除level为0的符号表,并删除整个符号表
//执行executeProgram节点
public void executeProgram(TreeNode program)throws UnexpectedException{
int level=0;//运算的层数
for(TreeNode node :program.getChildren()){
executeStmt(node,level);
}
//倒着删除声明的数目的变量
symbolTable.deleteRecord(level);
//删除整个table
symbolTable.clearTable();
}
4、stmt节点
创建executeStmt,根据treeNode的不同,调用不同的执行函数
//根据treeNode的不同,调用不同的执行函数
public void executeStmt(TreeNode stmtNode,int level)throws UnexpectedException{
switch ( stmtNode.getType()) {
case TreeNode.DECLARENODE:
executeDeclare(stmtNode, level);
break;
case TreeNode.ASSIGNNODE:
executeAssign(stmtNode, level);
break;
case TreeNode.IFNODE:
executeIf(stmtNode,level);
break;
case TreeNode.WHILENODE:
executeWhile(stmtNode,level);
break;
case TreeNode.STMTBLOCKNODE:
executeStmtBlock(stmtNode, level);
break;
case TreeNode.WRITENODE:
executeWrite(stmtNode, level);
break;
case TreeNode.READNODE:
executeRead(stmtNode,level);
break;
case TreeNode.BREAKNODE:
//是program的break,什么都不做
break;
}
}
5、stmtBlock节点