一、语义分析要解决的问题
确定类型:确定标识符所关联的数据对象的数据类型。
类型检查:按照语言的类型规则,对运算及运算分量进行类型检查,必要时做出相应类型转换。
识别含义:根据程序设计语言的语义定义,确定各个构造部分组合后的含义,做出相应处理(生成中间代码或者目标代码)。
态语义检查:比如控制流检查,嵌套层数检查。

二、总体思路说明 

之前在语义分析阶段生成了抽象语法树,抽象语法树本身就是一种中间代码,因此现在将语义分析和解释执行结合在一起,对抽象语法树进行一次遍历,完成语义分析、生成符号表并输出最后结果。抽象语法树的遍历类似于属性文法树的遍历。

文法规定:

所有变量声明是默认为int类型,int类型默认值为0,double为0.0。

除了数组变量外,其余变量在声明是都可与赋值

在声明数组时,可以生成多维数组,维数没有限制,声明数组的形式为int[m][n] a; m和n都为常数

数组在声明后,默认每个值都为0或者0.0

赋值时,int类型可以自动转成double,double类型不能转换为int,true转换为int和double是默认为0或0.0,false默认为1或者1.0。

整数和实数都支持16进制类型数 如-0x14ae.123;

If和while语句中,如果类型为true或者值不为0.0或者0都视为满足条件

Break跳出最近的循环

Read语句用于从命令读取数值赋值给变量

Write语句用于输出expr的值,可以是变量、常数或者和表达式

三、实现代码(代码比较长为了不破坏结构将代码部分字体放小,可以放大看)

1、之前代码完善

之前在声明变量时,如果用int a,b,c;声明语句时,生成语法树的顺序会发生改变,对语义分析会产生影响,因此对varDeclare部分进行修改,使顺序正确。

2、主要类型说明

在实现过程中,新建了三个类:

Record类:记录着声明的变量的信息

symbolTable类:符号表,里面有两个表,一个是table表,记录所有的声明的变量,一个是arrayTable,记录所有数组的声明。符号表中存储着Record。符号表的结构是arrarList,而每个内容是record,record里面有一个next属性指向同名的前一个record,内层的声明的变量会取代外层声明的,类似于杂凑表结构。


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节点

创建executeStmtBlock函数,对于每一个stmtBlock,代码都新进一层,因此level+1。对其所有的孩子节点,先判断是否出现过了break语句,如果出现则跳出stmtBlock,否则调用stmtBlock。最后删除相应level的记录

//执行stmtBlock,层数递增
public void executeStmtBlock(TreeNode stmtBlock ,int level)throws UnexpectedException{
    level++;//层数递增
    
for(TreeNode node : stmtBlock.getChildren() ){
        //判断是否是breakj节点或者前面出现了break节点
        
if(node.getType()==TreeNode.BREAKNODE||hasBreak==true){
            if(whileNum!=0){
                hasBreak=true;
                break;//有break不断跳出
            
}
        }else {
            executeStmt(node,level);//没有break继续执行
        
}
    }
    symbolTable.deleteRecord(level);//删除符号表
}

6、Value节点

创建executeValue函数,实现执行Value节点。从符号表中取得相应的记录。对于一般变量,如果在符号表中存在,则返回相应的Record,如果不存在则报错。对于数组变量,先在数组表中查找中是否有该数组并检查是否越界,如果存在且没有越界,则查看在table表中有没有一模一样的变量,如果有则返回该变量,如果没有则在table表中创建一个并返回。

//执行value节点,返回相应的符号表记录
public Record executeValue(TreeNode tempChild,int level)throws UnexpectedException{
    Record tempRecord;
    if(tempChild.getChildren().size()==1){
        //如果只是普通变量,直接搜索
        
tempRecord=symbolTable.searchRecord(tempChild.getChildren().get(0).getValue());

    }else{
        //如果是数组变量
        
ArrayList<Integer> nums=new ArrayList<>();
        Value iNum;
        //得到下标数组
        
for(int i=1;i<tempChild.getChildren().size();i++){
            iNum=executeExpr(tempChild.getChildren().get(i),level);//计算数组的下标
            //如果类型下标类型不为int
            
if(iNum.getType()!=Value.intType){
                throw new UnexpectedException(tempChild.getChildren().get(0).getValue(),"数组下标不为整数");
            }else{
                nums.add(iNum.getIntValue());
            }
        }
        //搜索数组
        
tempRecord=symbolTable.searchArrayRecord(tempChild.getChildren().get(0).getValue(),nums);
    }
    if(tempRecord==null){
        throw new UnexpectedException(tempChild.getChildren().get(0).getValue(),"未声明标识符!");
    }
    return tempRecord;
}

7、expr节点

计算expr节点的值,如果是constant节点直接返回记录信息的value,如果是option节点或者negative,则进行计算。当节点类型为16进制是要进行转换,尤其是16进制小数

//执行expr
public Value executeExpr(TreeNode tempChild,int level)throws UnexpectedException{
    Value value=new Value();
    //如果是操作符节点,递归变量并返回运算后的值
    
if(tempChild.getType()==TreeNode.OPTNODE||tempChild.getType()==TreeNode.NEGATIVENODE){
        Value op1,op2;
        //如果是双目运算符
        
if(tempChild.getType()==TreeNode.OPTNODE){
            op1=executeExpr(tempChild.getChildren().get(0),level);
            op2=executeExpr(tempChild.getChildren().get(1),level);
            return executeOption(tempChild.getValue(),op1,op2);
        }else {
            //如果是单目运算符
            
op1=executeExpr(tempChild.getChildren().get(0),level);
            value=executeNegative(op1);
        }
    }else {
        //如果是操作数节点,如果是常数类型则直接返回值,如果是value类型则在表中查找返回值,如果没有找的则报错
        
if (tempChild.getType() == TreeNode.CONSTANTNODE) {
            if (tempChild.getValue().getType() == TOK.LITERAL_INT) {
                value.setType(Value.intType);
                String num=tempChild.getValue().getValue();
                //根据是否是16进制进行转换
                
if(num.contains("x")||num.contains("X")){
                    num=num.replace("0x","");
                    num=num.replace("0X","");
                    value.setIntValue(Integer.parseInt(num,16));
                }else{
                    value.setIntValue(Integer.parseInt(num));
                }

            } else if(tempChild.getValue().getType()==TOK.LITERAL_DOUBLE) {
                value.setType(Value.doubleType);
                String num=tempChild.getValue().getValue();
                //根据是否是16进制进行转换
                
if(num.contains("x")||num.contains("X")){
                    num=num.replace("0x","");
                    num=num.replace("0X","");
                    value.setDoubleValue(hexToDouble(num));
                }else{
                    value.setDoubleValue(Double.parseDouble(num));
                }
            }else if(tempChild.getValue().getType()==TOK.TRUE){
                value.setType(Value.trueType);
            }else{
                value.setType(Value.falseType);
            }
        } else {
            //如果是value节点
            
Record tempRecord = executeValue(tempChild, level);
            //对Value进行赋值
            
if(tempRecord.getType()==Record.intType){
                value.setType(Value.intType);
                value.setIntValue(tempRecord.getIntValue());
            }else{
                value.setType(Value.doubleType);
                value.setDoubleValue(tempRecord.getDoubleValue());
            }
        }
    }
    return value;
}

8、option节点

根据option的类型对两个value进行相应的计算,如果两个操作数中,有一个为double,则最后的类型为double类型

//执行option
public Value executeOption( Token optToken,Value op1,Value op2)throws UnexpectedException{
    Value value=new Value();
    int type;
    //确定最后计算结果的类型,有一个为double则最后结果为double
    
if(op1.getType()==Value.doubleType||op2.getType()==Value.doubleType){
        type=Value.doubleType;
    }else {
        type=Value.intType;
    }
    value.setType(type);
    //对两个操作符进行运算
    
switch (optToken.getType()){
        case TOK.PLUS:
            if(type==Value.intType){
                value.setIntValue(op1.getIntValue()+op2.getIntValue());
            }else {
                //如果都不是整数则全部转换为double类型
                
value.setDoubleValue(op1.getCastValue()+op2.getCastValue());
            }
            break;
        case TOK.MINUS:
            if(type==Value.intType){
                value.setIntValue(op1.getIntValue()-op2.getIntValue());
            }else {
                //如果都不是整数则全部转换为double类型
                
value.setDoubleValue(op1.getCastValue()-op2.getCastValue());
            }
            break;
        case TOK.MUL:
            if(type==Value.intType){
                value.setIntValue(op1.getIntValue()*op2.getIntValue());
            }else {
                //如果都不是整数则全部转换为double类型
                
value.setDoubleValue(op1.getCastValue()*op2.getCastValue());
            }
            break;
        case TOK.DIV:
            //检查除零错误
            
if(op2.getCastValue()==0.0){
                throw new UnexpectedException(optToken,"除数不能为0");
            }
            if(type==Value.intType){
                value.setIntValue(op1.getIntValue()/op2.getIntValue());
            }else {
                //如果都不是整数则全部转换为double类型
                
value.setDoubleValue(op1.getCastValue()/op2.getCastValue());
            }
            break;
        case TOK.PERSENT:
            if(op2.getCastValue()==0.0){
                throw new UnexpectedException(optToken,"余数不能为0");
            }
            if(op1.getType()==Value.doubleType||op2.getType()==Value.doubleType){
                value.setDoubleValue(0.0);
            }else{
                value.setIntValue(op1.getIntValue()-op1.getIntValue()/op2.getIntValue()*op2.getIntValue());
            }
        case TOK.GT:
            if(op1.getCastValue()>op2.getCastValue()){
                value.setType(Value.trueType);
            }else{
                value.setType(Value.falseType);
            }
            break;
        case TOK.LT:
            if(op1.getCastValue()<op2.getCastValue()){
                value.setType(Value.trueType);
            }else{
                value.setType(Value.falseType);
            }
            break;
        case TOK.EQ:
            if(op1.getCastValue()==op2.getCastValue()){
                value.setType(Value.trueType);
            }else{
                value.setType(Value.falseType);
            }
            break;
        case TOK.NEQ:
            if(op1.getCastValue()!=op2.getCastValue()){
                value.setType(Value.trueType);
            }else{
                value.setType(Value.falseType);
            }
            break;
        case TOK.GQT:
            if(op1.getCastValue()>=op2.getCastValue()){
                value.setType(Value.trueType);
            }else{
                value.setType(Value.falseType);
            }
            break;
        case TOK.LQT:
            if(op1.getCastValue()<=op2.getCastValue()){
                value.setType(Value.trueType);
            }else{
                value.setType(Value.falseType);
            }
            break;
    }
    return value;
}

9、negative节点

value的值进行取反

//对操作数进行取反
public Value executeNegative(Value value){
    if(value.getType()==Value.intType){
        value.setIntValue(-value.getIntValue());
    }else{
        value.setDoubleValue(-value.getDoubleValue());
    }
    return value;
}

10、declare节点

    向符号表中添加记录,如果变量为一般变量,直接添加到table中,如果是数组变量,则添加到arrayTable中,并将数组大小记录在record的arrayNum中。如果在声明的时候进行赋值就计算expr结果,进行类型转换   

//执行变量声明语句
public void executeDeclare(TreeNode tempChild,int level)throws UnexpectedException{
    //添加记录
    
Record newRecord=new Record();
    //设置层级
    
newRecord.setLevel(level);
    //设置类型
    
if( tempChild.getChildren().get(0).getValue().getType()== TOK.INT){
        newRecord.setType(Record.intType);
    }else {
        newRecord.setType(Record.doubleType);
    }
    //设置变量名
    
newRecord.setName(tempChild.getChildren().get(1).getValue());
    //判断是否是数组,然后添加到不同的表中
    
if( tempChild.getChildren().get(0).getChildren().size()>0){
        for(int j=0;j<tempChild.getChildren().get(0).getChildren().size();j++){
            newRecord.getArrayNum().add(
                    Integer.parseInt(tempChild.getChildren().get(0).getChildren().get(j).getValue().getValue()));
        }
        symbolTable.addArrayRecord(newRecord);
    }else{
        symbolTable.addRecord(newRecord);
    }
   //如果在声明时赋值则设置value
    
if(tempChild.getChildren().size()==3){
        Value result = executeExpr(tempChild.getChildren().get(2), level);
        //如果类型相等,直接更新值
        
if (newRecord.getType() == result.getType()) {
            if (result.getType() == Value.intType) {
                newRecord.setIntValue(result.getIntValue());
            } else {
                newRecord.setDoubleValue(result.getDoubleValue());
            }
        } else if (newRecord.getType() == Record.intType && result.getType() == Value.doubleType) {
            //如果类型不等,进行类型准换或者报错
            
throw new UnexpectedException(tempChild.getChildren().get(1).getValue(),
                    "类型不兼容,double转int会损失精度");
        } else if(newRecord.getType() == Record.doubleType && result.getType() == Value.intType){
            newRecord.setDoubleValue(result.getIntValue());
        }else if(newRecord.getType()==Record.intType&&result.getType()==Value.trueType){
            newRecord.setIntValue(1);
        }else if(newRecord.getType()==Record.doubleType&&result.getType()==Value.trueType){
            newRecord.setDoubleValue(1.0);
        }else{
            //false类型准换为int或者double是默认为0,不发生变化
        
}
    }
}

11、assign节点

创建executeAssign函数,执行赋值语句。先获取符号表中的变量Record,再计算出expr的值,进行类型比较和类型转换

//执行变量赋值语句
public void  executeAssign(TreeNode tempChild,int level)throws UnexpectedException{
    Record tempRecord=executeValue(tempChild.getChildren().get(0),level);
    //如果找到则更新变量值
    
if(tempRecord!=null) {
        Value result = executeExpr(tempChild.getChildren().get(1), level);
        //如果类型相等,直接更新值
        
if (tempRecord.getType() == result.getType()) {
            if (result.getType() == Value.intType) {
                tempRecord.setIntValue(result.getIntValue());
            } else {
                tempRecord.setDoubleValue(result.getDoubleValue());
            }
        } else if (tempRecord.getType() == Record.intType && result.getType() == Value.doubleType) {
            //如果类型不等,进行类型准换或者报错
            
throw new UnexpectedException(tempChild.getChildren().get(0).getChildren().get(0).getValue(),
                    "类型不兼容,double转int会损失精度");
        } else if(tempRecord.getType() == Record.doubleType && result.getType() == Value.intType){
            tempRecord.setDoubleValue(result.getIntValue());
        }else if(tempRecord.getType()==Record.intType&&result.getType()==Value.trueType){
            tempRecord.setIntValue(1);
        }else if(tempRecord.getType()==Record.doubleType&&result.getType()==Value.trueType){
            tempRecord.setDoubleValue(1.0);
        }else{
            //false类型准换为int或者double是默认为0,不发生变化
        
}
    }
}

12、read节点

从符号表中取得Record并更新值

public void executeRead(TreeNode readNode,int level)throws UnexpectedException{
    //从命令行读取数据
    
Scanner scanner=new Scanner(System.in);
    Record record=executeValue(readNode.getChildren().get(0),level);//从符号表中取得记录
    
if(record.getType()==Record.intType){
        record.setIntValue(scanner.nextInt());
    }else{
        record.setDoubleValue(scanner.nextDouble());
    }
}

13、write节点

创建executeWrite,将其中的expr的值打印出来

//执行write语句
public void executeWrite(TreeNode writeNode,int level)throws UnexpectedException{
    //输出表达式的值
    
Value value=executeExpr(writeNode.getChildren().get(0),level);
    if(value.getType()==Value.intType){
        System.out.println(value.getIntValue());
    }else if(value.getType()==Value.doubleType){
        System.out.println(value.getDoubleValue());
    }else if(value.getType()==Value.trueType){
        System.out.println(true);
    }else{
        System.out.println(false);
    }
}

14、if节点

if语句,当条件为真的时候执行真的语句,条件为假的时候执行假的语句

//执行if语句
public void executeIf(TreeNode ifNode,int level)throws UnexpectedException{
    Value condition=executeExpr(ifNode.getChildren().get(0),level);
    //true和非0值使条件为真
    
if(condition.getType()==Value.trueType||condition.getCastValue()!=0.0){
        executeStmtBlock(ifNode.getChildren().get(1),level);
    }else{
        if(ifNode.getChildren().size()>2){
            executeStmtBlock(ifNode.getChildren().get(2),level);
        }
    }
}

15、while节点

添加一个全局变量whileNum栈,遇到一个进入一个while节点就加一,添加全局变量hasBreak,记录当前是否遇到break。

condition为真时执行循环体,每执行一次判断是否遇到了break,如果遇到了就跳出,否则检查condition,继续循环。当执行完循环体的时候,将hasBreak设置为false并将whileNum-1。

public void executeWhile(TreeNode whileNode,int level) throws UnexpectedException{
    Value condition=executeExpr(whileNode.getChildren().get(0),level);
    whileNum++;
    while (condition.getType()==Value.trueType||condition.getCastValue()!=0.0){
      executeStmtBlock(whileNode.getChildren().get(1),level);
        if(hasBreak==true){
            break;
        }
        condition=executeExpr(whileNode.getChildren().get(0),level);
    }
    hasBreak=false;
    whileNum--;
}

16、Break节点

创建hasBreak变量,当在stmtBlock中遇到break或者hasBreak为true时,如果whileNum不为0,则将hasBreak置为true并跳出,这样就能一直返回到while语句中,再最后跳出。

编译原理——CMM语义分析

相关文章:

  • 2021-07-20
  • 2021-06-10
  • 2021-08-26
  • 2021-09-08
  • 2021-10-27
  • 2022-01-01
  • 2021-05-11
  • 2022-02-08
猜你喜欢
  • 2021-04-09
  • 2021-09-02
  • 2022-01-23
  • 2021-07-12
  • 2021-06-22
  • 2021-08-21
  • 2021-04-03
相关资源
相似解决方案