目录

  • 1 语义分析
    • 1.1 功能
    • 1.2 错误类型
    • 1.3 中间代码
    • 1.4 符号表
    • 1.5 设计思路
  • 2 完整代码
  • 3 总结

词法分析: 词法分析——TEST编译器(1)
语法分析: 语法分析——TEST编译器(2)

1 语义分析

1.1 功能

  • 进行语义检查。对表达式中的操作数进行类型的一致性检查,例如误把函数名误当作变量名使用
  • 进行语义处理。对声明语句和可执行语句的处理,其中声明语句(int a ;)将变量名及其有关属性填入符号表
  • 把可执行语句生成相应的中间代码。

1.2 错误类型

  • 函数名重复定义
  • 变量名重复定义
  • 标识符没有定义
  • call之后的标识符不是函数名
  • 赋值语句的标识符不是变量名
  • 运算语句的标识符不是变量名

1.3 中间代码

用一个栈来保存中间代码,其中包含每一个执行指令和操作数。类似汇编指令集合,使用时按顺序一条条读取,然后根据指令和操作数做出对应的动作。

1.4 符号表

在进行语义分析的过程中要使用符号表记录每一个标识符的信息,符号表记录了出现的每一个函数、变量的名字、地址、类型,根据这些信息从而可以判断出一个标识符是否定义过、使用的类型对不对等等。而且记录了标识符的地址,方便在虚拟机部分存储变量位置。

1.5 设计思路

  • 思路就是在语法分析代码的基础上进行语义分析,在语法分析的同时,对每一个句子进行语义分析,所以当语法分析结束时语义分析也就结束了。如果当前句子没问题就生产对应的中间代码
  • 中间代码中有部分指令需要操作数,而在按顺序进行语义分析的时候有时候是无法直接确定操作数的内容的,这是就需要用到反填的操作。反填简单说就是确定了当前指令在中间代码集合中的位置,但是不知道其具体的操作数是多少,就先找个临时变量记下这个指令的位置,等到可以确定的时候再根据这个临时变量找到这个指令然后填入它的操作数。例如,BRF代表判断条件为假时就跳转到操作数对应的指令位置,这个一般用在if…else里,BRF就用在if的判断之后,如果为假就进入else语句,因为中间代码是按顺序执行的,那么在判断条件之后就应该加上BRF,但此时还不知道要跳转到哪个位置,所以要等if为真的语句全部写完之后,之后就轮到else语句了,只有到了这个时候才知道BRF的操作数应该填什么,所以else语句开始的位置是BRF的操作数的值。
  • 在进行语义分析的过程中要时刻通过符号表对出现的标识符进行判断,判断类型是否正确、是否定义过等等。所以在考虑提升语义分析的效率时就要考虑如何对符号表进行搜索可以提高效率
  • 需要使用常数,但是常数是字符串类型:由于从文件中读取二元组时,得到的都是字符串类型的数据,当需要用到常数时,就必须先将字符串数字转换成整型数字。如果手动转换的话会很麻烦,那么可以使用atoi()函数,就会自动返回一个整型数字。但是需要注意的是,该函数是将字符串类型的数字转换成整型的数字,而不能把其他非数字的字符串转换成数字。

2 完整代码

#include<bits/stdc++.h>
using namespace std ;
enum symbolType {function, variable};   //用枚举类型方便比较类型
struct tree {       //语法树string data ;vector<tree*> son ;tree* addSon(string str) {tree* newSon = new tree() ;newSon->data = str ;son.push_back(newSon) ;return newSon ;}
} *root;
struct symbol {              //符号表char name[20] ;         //标识符名字int address ;           //地址enum symbolType kind ;  //标识符的类型 函数名或变量名
};
struct code {                   //中间代码char operationCode[10] ;    //操作码int operand ;               //操作数
};FILE *inputFile ;   //输入文件
FILE *outputFile ;  //输出文件
char inputAddress[20] ;     //输入文件地址
char outputAddress[20] ;    //输出文件地址
char type[20] ;     //单词类型
char value[40] ;    //单词值
int line = 0 ;      //当前读到第几行
int offset = 0 ;    //变量地址
int codeLength = 0 ;    //中间代码长度
int symbolLength = 0 ;  //符号表数据个数
vector<symbol> symbolTable ;    //符号表
code codes[200] ;            //中间代码//存在函数之间相互调用,要先声明函数
int parseAlanalysis() ;
int program() ;
int fun_declaration(tree* father) ;
int main_declaration(tree* father) ;
int function_body(tree* father) ;
int declaration_list(tree* father) ;
int declaration_stat(tree* father) ;
int statement_list(tree* father) ;
int statement(tree* father) ;
int if_stat(tree* father) ;
int while_stat(tree* father) ;
int for_stat(tree* father) ;
int read_stat(tree* father) ;
int write_stat(tree* father) ;
int compound_stat(tree* father) ;
int call_stat(tree* father) ;
int expression_stat(tree* father) ;
int expression(tree* father) ;
int bool_expr(tree* father) ;
int additive_expr(tree* father) ;
int term(tree* father) ;
int factor(tree* father) ;
int do_while_stat(tree* father) ;
void getNext() ;//将当前数据存入语法树中
void add(tree* p) {p->addSon(value) ;
}
//输出语法树
void printTree(tree* p, int x) {for(int i=0;i<x;i++)    //每次输出之前都要先输出一定的空格实现深度printf("|  ") ;printf("%s\n", p->data.c_str()) ;int num = p->son.size() ;if(num == 0)return ;for(int i=0; i<num; i++)    //输出子内容printTree(p->son[i],x+1);   //深度加一return ;
}
//读取下一行内容
void getNext() {fscanf(inputFile, "%s%s\n", type, value) ;printf("第%d行  %s   %s\n", line, type, value) ;  //输出读取到的当前行 有错误时便于定位错误line++ ;
}
//将标识符添加到符号表中
int insertSymbolTable(enum symbolType kind, char name[]) {symbol temp ;       //临时变量,存要记录的新符号//检查是否存在重复定义switch (kind) {case function : {   //函数for(int i=0; i<symbolTable.size(); i++) {if((strcmp(name, symbolTable[i].name))==0 && (symbolTable[i].kind==function))return 12 ;         //函数名重复定义}temp.kind = function ;      //没有重复的函数就正常赋值break ;}case variable : {   //变量for(int i=0; i<symbolTable.size(); i++) {if((strcmp(name, symbolTable[i].name))==0 && (symbolTable[i].kind==variable))return 13 ;         //变量名重复定义}temp.kind = variable ;temp.address = offset ;     //变量要有地址offset++ ;break ;}}strcpy(temp.name, name) ;symbolTable.push_back(temp) ;       //将新的符号添加到符号表中symbolLength++ ;return 0 ;
}
//查找标识符再符号表中的位置
int lookup(char name[], int *place) {for(int i=0; i<symbolTable.size(); i++) {if((strcmp(name, symbolTable[i].name)) == 0) {  //名字相同就找到*place = i ;     //找到就把位置赋值然后返回return 0 ;}}return 14 ;         //没有找到说明没有定义,报错
}
//判断条件
int judge(tree* father) {int flag = 0 ;//检查 (getNext() ;if(strcmp("(", type) != 0)return 4 ;add(father) ;//检查 exprgetNext() ;flag = expression(father) ;     //判断条件if(flag > 0)return flag ;//可能存在多重判断语句while(strcmp("&&", type)==0 || strcmp("||", type)==0) {add(father) ;//检查 exprgetNext() ;flag = expression(father) ;     //判断条件}//检查 )if(strcmp(")", type) != 0)return 5 ;add(father) ;return flag ;
}
//运算因子
int factor(tree* father) {     // < factor >::=‘(’ < additive_expr >‘)’|ID|NUMtree* current = father->addSon("<factor>") ;int flag = 0 ;//括号有优先级,需要先处理其中的算术表达式if(strcmp("(", type) == 0) {add(current) ;getNext() ;flag = additive_expr(current) ;if(flag > 0)return flag ;if(strcmp(")", type) != 0)return 5 ;add(current) ;getNext() ;}else {  //因子还可能是标识符或者常数,都是最小单位了,没问题就直接返回if(strcmp("ID", type)==0) {int place ;flag = lookup(value, &place) ;if(flag > 0)return flag ;if(symbolTable[place].kind != variable) //运算语句中的标识符要是return 17 ;strcpy(codes[codeLength].operationCode, "LOAD") ;       //将变量存入codes[codeLength].operand = symbolTable[place].address ;    //将要使用的变量地址存入codeLength++ ;add(current) ;getNext() ;return flag ;}else if(strcmp("NUM", type)==0) {strcpy(codes[codeLength].operationCode, "LOADI") ;       //将变量存入codes[codeLength].operand = atoi(value) ;//由于从文件读取出来的是字符串,需要把字符串类型的数字转换成整型codeLength++ ;add(current) ;getNext() ;return flag ;}elsereturn 7 ;}return flag ;
}
//运算项
int term(tree* father) {    //< term >::=<factor>{(*| /)< factor >}tree* current = father->addSon("<term>") ;int flag = 0 ;//检查 termflag = factor(current) ;if(flag > 0)return flag ;//之后可能有多个运算过程所以要用循环不能用ifwhile(strcmp("*", type)==0 || strcmp("/", type)==0) {char temp[20] ;     //暂时存储是什么类型的运算符,之后没有问题就根据对应的类型填写中间代码strcpy(temp, type) ;add(current) ;//检查 termgetNext() ;flag = factor(current) ;if(flag > 0)return flag ;//将对应的运算符加入到中间代码中if(strcmp("*", temp) == 0)strcpy(codes[codeLength++].operationCode, "MULT") ;       //乘else if(strcmp("/", temp) == 0)strcpy(codes[codeLength++].operationCode, "DIV") ;       //除}return flag ;
}
//算数表达式
int additive_expr(tree* father) {   // < additive_expr>::=<term>{(+|-)< term >}tree* current = father->addSon("<additive_expr>") ;int flag = 0 ;//检查 termflag = term(current) ;if(flag > 0)return flag ;//之后可能有多个运算过程所以要用循环不能用ifwhile(strcmp("+", type)==0 || strcmp("-", type)==0) {char temp[20] ;     //暂时存储是什么类型的运算符,之后没有问题就根据对应的类型填写中间代码strcpy(temp, type) ;add(current) ;//检查 termgetNext() ;flag = term(current) ;if(flag > 0)return flag ;//将对应的运算符加入到中间代码中if(strcmp("+", temp) == 0)strcpy(codes[codeLength++].operationCode, "ADD") ;       //加else if(strcmp("-", temp) == 0)strcpy(codes[codeLength++].operationCode, "SUB") ;       //减}return flag ;
}
//布尔表达式
int bool_expr(tree* father) {   //<bool_expr>::=<additive_expr>|<additive_expr>(>|<|>=|<=|==|!=)<additive_expr>//  <bool_expr>::=<additive_expr>{(>|<|>=|<=|==|!=)<additive_expr>}tree* current = father->addSon("<bool_expr>") ;int flag = 0 ;//检查 additive_exprflag = additive_expr(current) ;if(flag > 0)return flag ;//之后可能有各种关系运算符,有就继续判断,没有就结束if(strcmp(">", type)==0 || strcmp("<", type)==0 || strcmp(">=", type)==0 ||strcmp("<=", type)==0 || strcmp("==", type)==0 || strcmp("!=", type)==0) {char temp[20] ;     //暂时存储是什么类型的运算符,之后没有问题就根据对应的类型填写中间代码strcpy(temp, type) ;add(current) ;//检查 additive_exprgetNext() ;flag = additive_expr(current) ;if(flag > 0)return flag ;//将对应的运算符加入到中间代码中if(strcmp(">", temp) == 0)strcpy(codes[codeLength++].operationCode, "GT") ;       //大于else if(strcmp(">=", temp) == 0)strcpy(codes[codeLength++].operationCode, "GE") ;       //大于等于else if(strcmp("<", temp) == 0)strcpy(codes[codeLength++].operationCode, "LES") ;      //小于else if(strcmp("<=", temp) == 0)strcpy(codes[codeLength++].operationCode, "LE") ;       //小于等于else if(strcmp("==", temp) == 0)strcpy(codes[codeLength++].operationCode, "EQ") ;       //等于else if(strcmp("!=", temp) == 0)strcpy(codes[codeLength++].operationCode, "NOTEQ") ;    //不等于}return flag ;
}
//表达式
int expression(tree* father) {  // < expression >::= ID=<bool_expr>|<bool_expr>tree* current = father->addSon("<expression>") ;int flag = 0 ;int filePlace ;     //记录当前文件位置int place ;char t1[40], t2[40] ;   //临时存取数据//此处进来的数据是IDif(strcmp("ID", type) == 0) {   //< expression >::= ID=<bool_expr>//add(current) ;filePlace = ftell(inputFile) ;  //进来数据在文件中的数据//检查 =fscanf(inputFile, "%s%s\n", t1, t2) ;   //首先把数据存到临时的数组中line++ ;if(strcmp("=", t1) == 0) {    //该表达式例如 a=xxxx赋值flag = lookup(value, &place) ;if(flag > 0)return flag ;if(symbolTable[place].kind != variable) //赋值语句只能对变量进行return 16 ;printf("第%d行  %s   %s\n", line-1, t1, t2) ;add(current) ;current->addSon(t1) ;//检查 bool_exprgetNext() ;   //如果进入到此处就继续往下读取数据就可以了,之后数据还是存在原来的数组中flag = bool_expr(current) ;if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "STO") ;     //将赋值内容存入变量中codes[codeLength].operand = symbolTable[place].address ;codeLength++ ;}else {                      //该表达式例如 a>=xxxxline-- ;            //多读取的行数要减掉fseek(inputFile, filePlace, 0) ;  //读取数据返回到 = 前的标识符,即刚进来的ID的位置flag = bool_expr(current) ;if(flag > 0)return flag ;}}else        //< expression >::= <bool_expr>flag = bool_expr(current) ;return flag ;
}
//表达式序列
int expression_stat(tree* father) {     //<expression_stat>::=< expression >;|;tree* current = father->addSon("<expression_stat>") ;//add(current) ;int flag = 0 ;//检查第一个是 ;if(strcmp(";", type) == 0) {add(current) ;getNext() ;return flag ;}//检查expressionflag = expression(current) ;if(flag > 0)return flag ;//检查末尾的 ;if(strcmp(";", type) == 0) {add(current) ;getNext() ;return 0 ;}else            //缺少 ;return 3 ;
}
//调用函数语句
int call_stat(tree* father) {   // < call _stat>::= call ID‘(’ ‘)’ ;int flag = 0 ;int place ;tree* current = father->addSon("<call _stat>") ;add(current) ;//检查 IDgetNext() ;if(strcmp("ID", type) != 0)return 6 ;add(current) ;flag = lookup(value, &place) ;if(flag > 0)return flag ;if(symbolTable[place].kind != function) { //call之后不是函数名属于错误flag = 15 ;return flag ;}//检查 (getNext() ;if(strcmp("(", type) != 0)return 4 ;add(current) ;getNext() ;//可能带有参数while(strcmp("NUM", type)==0 || strcmp("ID", type)==0) {   //call xxx(yyy,....)add(current) ;//检查是否存在多个参数getNext() ;if(strcmp(",", type) == 0) {       //有逗号说明有多个参数add(current) ;getNext() ;if(strcmp("NUM", type)!=0 && strcmp("ID", type)!=0)  //如果逗号之后不是新的参数就出错return 10 ;}else            //没有参数了就结束break ;}//检查 )if(strcmp(")", type) != 0)return 5 ;add(current) ;getNext() ;//检查 ;if(strcmp(";", type) != 0)return 3 ;add(current) ;strcpy(codes[codeLength].operationCode, "CAL") ;     //调用函数codes[codeLength].operand = symbolTable[place].address ;     //进入函数的开始位置codeLength++ ;getNext() ;return 0 ;  //没有问题就返回0
}
//复合语句
int compound_stat(tree* father) {   // <compound_stat>::=’{‘<statement_list>’}‘tree* current = father->addSon("<compound_stat>") ;add(current) ;int flag = 0 ;getNext() ;flag = statement_list(current) ;if(flag > 0)return flag ;add(current) ;getNext() ;return flag ;   //不管是否正确直接返回, } 在statement_list已经判断了
}
//write语句
int write_stat(tree* father) {  // <write_stat>::=write <expression>;tree* current = father->addSon("<write_stat>") ;add(current) ;int flag = 0 ;//检查 expressiongetNext() ;flag = expression(current) ;if(flag > 0)return flag ;//检查 ;if(strcmp(";", type) != 0)return 3 ;add(current) ;strcpy(codes[codeLength].operationCode, "OUT") ;     //输出codeLength++ ;getNext() ;return 0 ;
}
//read语句
int read_stat(tree* father) {   //<read_stat>::=read ID;int place ;int flag ;tree* current = father->addSon("<read_stat>") ;add(current) ;//检查 IDgetNext() ;if(strcmp("ID", type) != 0)return 6 ;flag = lookup(value, &place) ;       //在读取之前要先检查一下该变量是否被定义if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "IN") ;     //输入codeLength++ ;strcpy(codes[codeLength].operationCode, "STO") ;    //将输入的值存入对应变量codes[codeLength].operand = symbolTable[place].address ;    //存入指定地址的变量中codeLength++ ;add(current) ;//检查 ;getNext() ;if(strcmp(";", type) != 0)return 3 ;add(current) ;getNext() ;     //读取下一个语句的内容return 0 ;
}
//for语句
int for_stat(tree* father) {    // <for_stat>::= for’(‘<expr>;<expr>;<expr>’)’<statement>tree* current = father->addSon("<for_stat>") ;add(current) ;int flag = 0 ;int cx1, cx2, entance1, entance2 ;//检查 (getNext() ;if(strcmp("(", type) != 0)return 4 ;add(current) ;//检查 expr1getNext() ;flag = expression(current) ;     //判断条件if(flag > 0)return flag ;//检查 ;if(strcmp(";", type) != 0)return 3 ;add(current) ;entance1 = codeLength ;         //记录下判断语句的入口,每次循环完都要回到此处//检查 expr2getNext() ;flag = expression(current) ;     //判断条件if(flag > 0)return flag ;//检查 ;if(strcmp(";", type) != 0)return 3 ;add(current) ;strcpy(codes[codeLength].operationCode, "BFR") ;    //判断为假就结束forcx1 = codeLength ;          //暂时不知道结束位置,等待反填codeLength++ ;strcpy(codes[codeLength].operationCode, "BR") ;    //判断为真就进入循环语句cx2 = codeLength ;          //暂时不知道语句开始位置,等待反填codeLength++ ;//检查 expr3getNext() ;entance2 = codeLength ;         //记录条件3的入口,用于语句的反填flag = expression(current) ;     //判断条件if(flag > 0)return flag ;//检查 )if(strcmp(")", type) != 0)return 5 ;add(current) ;strcpy(codes[codeLength].operationCode, "BR") ;    //执行完就进入判断codes[codeLength].operand = entance1 ;      //跳转的位置是判断语句的入口codeLength++ ;//检查 statementgetNext() ;codes[cx2].operand = codeLength ;       //反填判断为真的跳转入口flag = statement(current) ;             //for中的语句strcpy(codes[codeLength].operationCode, "BR") ;    //执行完就进入条件3codes[codeLength].operand = entance2 ;             //跳转的位置是条件3的入口codeLength++ ;codes[cx1].operand = codeLength ;       //反填结束的跳转入口return flag ;           //最后一个语句,不管是否正确都直接输出即可
}
//do_while语句
int do_while_stat(tree* father) {    // do ‘{’ < statement > ‘}’ while ‘(‘<expr >’)’ ;tree* current = father->addSon("<do_while_stat>") ;add(current) ;int flag = 0 ;int cx, entance ;//检查 {getNext() ;if(strcmp("{", type) != 0)return 1 ;entance = codeLength ;      //执行语句的入口//检查执行语句flag = statement(current) ; //检查完执行语句后 } 已经检查完了if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "BF") ;    //执行完语句就进入判断语句codes[codeLength].operand = codeLength + 1 ;        //判断语句就是下一句codeLength++ ;//检查 whileif(strcmp("while", type) != 0)return 11 ;add(current) ;//检查判断语句flag = judge(current) ;if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "BRF") ;    //为假就退出循环cx = codeLength ;                      //暂时不知道循环退出位置,等待反填codeLength++ ;strcpy(codes[codeLength].operationCode, "BR") ;     //为真就返回执行语句codes[codeLength].operand = entance ;           //返回执行语句入口codeLength++ ;codes[cx].operand = codeLength ;           //此处是循环出口,反填getNext() ;return flag ;
}
//while语句
int while_stat(tree* father) {      //<while_stat>::= while ‘(‘<expr >’)’ < statement >int flag = 0 ;int cx, entance ;tree* current = father->addSon("<while_stat>") ;add(current) ;entance = codeLength ;      //每次循环都要重新判断条件,所以要记录判断条件的入口flag = judge(current) ;if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "BRF") ;    //为假就退出whiliecx = codeLength ;       //反填结束时的位置codeLength++ ;//检查 statementgetNext() ;flag = statement(current) ;    //  while中的语句if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "BR") ;    //语句结束后要重新回到判断codes[codeLength].operand = entance ;       //回到判断起点codeLength++ ;codes[cx].operand = codeLength ;        //反填结束跳转的位置return flag ;           //最后一个语句,不管是否正确都直接输出即可
}
//if语句
int if_stat(tree* father) {     // <if_stat>::= if ‘(‘<expr>’)’ <statement > [else < statement >]int flag = 0 ;int cx1, cx2 ;tree* current = father->addSon("<if_stat>") ;add(current) ;flag = judge(current) ;     //判断条件if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "BRF") ;    //为假就进入elsecx1 = codeLength ;      //记录if为假时结束跳转的地方,之后反填codeLength++ ;//检查 statementgetNext() ;flag = statement(current) ;    //  if中的语句if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "BR") ;     //为真结束之后退出ifcx2 = codeLength ;      //记录if结束时跳转的地方,之后反填codeLength++ ;codes[cx1].operand = codeLength ;   //if为假要跳转到else的开始语句//检查 elseif(strcmp("else", type) == 0) {   //当前的数据由上一步中获得了add(current) ;//检查 statementgetNext() ;flag = statement(current) ;    //else中的语句if(flag > 0)return flag ;}codes[cx2].operand = codeLength ;   //if结束时跳转的地方return flag ;
}
//语句内容
int statement(tree* father) {/*<statement>::=<if_stat>|<while_stat>|<for_stat>|<read_stat>|<write_stat>|
<compound_stat> |<expression_stat> | < call _stat> */tree* current = father->addSon("<statement>") ;int flag = 0 ;//从声明中结束后得到第一个语句单词if(flag==0 && strcmp("if", type)==0)flag = if_stat(current) ;if(flag==0 && strcmp("while", type)==0)flag = while_stat(current) ;if(flag==0 && strcmp("for", type)==0)flag = for_stat(current) ;if(flag==0 && strcmp("read", type)==0)flag = read_stat(current) ;if(flag==0 && strcmp("write", type)==0)flag = write_stat(current) ;if(flag==0 && strcmp("{", type)==0)    //复合语句要从 { 开始flag = compound_stat(current) ;if(flag==0 && strcmp("call", type)==0)flag = call_stat(current) ;if(flag==0 && (strcmp("(", type)==0 || strcmp(";", type)==0 ||strcmp("NUM", type)==0 || strcmp("ID", type)==0) )flag = expression_stat(current) ;if(flag==0 && strcmp("do", type)==0)    //do_while语句flag = do_while_stat(current) ;return flag ;
}
//语句序列
int statement_list(tree* father) {  //<statement_list>::=<statement_list><statement>| ε//<statement_list>::={<statement>}tree* current = father->addSon("<statement_list>") ;int flag = 0 ;while(strcmp("}", type) != 0) { //到 } 结束if(feof(inputFile))     //一直到末尾都没有 } 则出错return 2 ;flag = statement(current) ;    //查看具体的语句if(flag > 0)return flag ;}return flag ;
}
//声明内容
int declaration_stat(tree* father) {        //<declaration_stat>::=int ID;int flag = 0 ;tree* current = father->addSon("<declaration_stat>") ;add(current) ;//检查 IDgetNext() ;if(strcmp("ID", type) != 0)   //缺少标识符return 6 ;add(current) ;flag = insertSymbolTable(variable, value) ;    //将变量名添加到符号表中if(flag > 0)return flag ;//检查 赋值getNext() ;if(strcmp("=", type) == 0) {    //变量名之后是 = 则需要赋值add(current) ;//检查 赋值getNext() ;if(strcmp("NUM", type) != 0)    //缺少赋值return 7 ;add(current) ;//检查 ;getNext() ;if(strcmp(";", type) != 0)   //缺少 ;return 3 ;add(current) ;}else {  //检查 ;if(strcmp(";", type) != 0)   //缺少 ;return 3 ;add(current) ;}return 0 ;              //正常就返回0
}
//声明序列
int declaration_list(tree* father) {    //<declaration_list>::=<declaration_list><declaration_stat> |ε//<declaration_list>::={<declaration_stat>}tree* current = father->addSon("<declaration_list>") ;int flag = 0 ;while(1) {getNext() ;if(strcmp("int", type) != 0)   //不是int说明已经没有变量声明了进入语句内容break ;flag = declaration_stat(current) ;     //具体的声明内容if(flag > 0)return flag ;}return flag ;
}
//函数体
int function_body(tree* father) {       //<function_body>::= ‘{’<declaration_list><statement_list> ‘}’ (tree* current = father->addSon("<function_body>") ;int flag = 0 ;//检查 {getNext() ;if(strcmp("{", type) != 0)   //缺少 {return 1 ;add(current) ;//检查 declaration_listoffset = 2 ;        //每次进入一个函数体的时候,相对地址都要重置为2symbolTable[symbolLength-1].address = codeLength ;  //函数体的入口地址,上一个存在符号表中的是函数名flag = declaration_list(current) ; //如果成功了当前的单词是语句的内容if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "ENTER") ;  //为函数调用开辟空间codes[codeLength].operand = offset ;codeLength++ ;//检查 statement_listflag = statement_list(current) ;   //一开始不用读取新的内容,成功了当前的单词应该是 }if(flag > 0)return flag ;strcpy(codes[codeLength].operationCode, "RETURN") ;  //函数返回codeLength++ ;add(current) ;getNext() ;return flag ;
}
//主函数
int main_declaration(tree* father) {    //<main_declaration>::=main’(‘ ‘ )’ < function_body>int flag = 0 ;tree* current = father->addSon("<main_declaration>") ;add(current) ;getNext() ;//检查 mainif(strcmp("main", value) != 0)   //程序中最后的声明必须是名字为 main 的主函数return 6 ;add(current) ;flag = insertSymbolTable(function, value) ;    //将函数名添加到符号表中if(flag > 0)return flag ;//检查 (getNext() ;if(strcmp("(", type) != 0)   //缺少 (return 4 ;add(current) ;//检查 )getNext() ;if(strcmp(")", type) != 0)   //缺少 )return 5 ;add(current) ;codes[0].operand = codeLength ;     //main函数为程序运行的起点//检查 function_bodyreturn function_body(current) ;     //检查函数体
}
//函数
int fun_declaration(tree* father) {     // <fun_declaration>::= function ID’(‘ ‘ )’< function_body>int flag = 0 ;tree* current = father->addSon("<fun_declaration>") ;   //当前类型add(current) ;              //当前数据没问题就将其加入到树中//检查 IDgetNext() ;if(strcmp("ID", type) != 0)   //缺少标识符代表函数名return 6 ;add(current) ;flag = insertSymbolTable(function, value) ;    //将函数名添加到符号表中if(flag > 0)return flag ;//检查 (getNext() ;if(strcmp("(", type) != 0)   //缺少 (return 4 ;add(current) ;//检查参数getNext() ;while(strcmp("int", type) == 0) {   //int xxx (,....)add(current) ;//检查 标识符getNext() ;if(strcmp("ID", type) != 0)return 6 ;add(current) ;//检查是否存在多个参数getNext() ;if(strcmp(",", type) == 0) {       //有逗号说明有多个参数add(current) ;getNext() ;if(strcmp("int", type) != 0)    //如果逗号之后不是新的参数就出错return 10 ;}else            //没有参数了就结束break ;}//检查 )if(strcmp(")", type) != 0)   //缺少 )return 5 ;add(current) ;//检查 function_bodyreturn function_body(current) ;     //检查函数体
}
//程序
int program() {     //<program> ::={fun_declaration }<main_declaration>int flag = 0 ;strcpy(codes[codeLength].operationCode, "BR") ;    //程序开始先跳转到main函数codeLength++ ;root = new tree() ;root->data = "<program>" ;//检查 全局变量,可能有也可能没有flag = declaration_list(root) ; //如果成功了当前的单词是语句的内容if(flag > 0)return flag ;//检查 fun_declarationwhile(strcmp("function", type) == 0) {   //函数声明,可能有也可能没有flag = fun_declaration(root) ;     //检查函数声明if(flag > 0)return flag ;               //若函数有问题就返回错误类型}codes[0].operand = codeLength ;if(strcmp("int", type) == 0) {  //主函数//检查 main_declarationflag = main_declaration(root) ; //检查主函数内容if(flag > 0)return flag ;}elsereturn 6 ;return flag ;
}
//语法分析
int parseAlanalysis() {//初始化,得到输入输出文件printf("请输入 输入文件地址:") ;scanf("%s", inputAddress) ;if((inputFile=fopen(inputAddress, "r")) == NULL)     //读取输入文件return 8 ;      //读取文件出错就返回对应错误类型printf("请输入 输出文件地址:") ;scanf("%s", outputAddress) ;if((outputFile=fopen(outputAddress, "w")) == NULL)   //读取输出文件return 9 ;      //读取文件出错就返回对应错误类型printf("\n") ;return program() ;      //文件读取成功就看看里面的内容
}int main() {int flag = 0 ;flag = parseAlanalysis() ;      //递归下降语法分析//处理结果printf("\n\n==================================\n\n") ;if(flag == 0) {printf("语义分析成功\n") ;//printTree(root, 0) ;        //输出语法树for(int i=0; i<codeLength; i++)     //将中间代码写入文件fprintf(outputFile, "%s    %d\n", codes[i].operationCode, codes[i].operand) ;printf("\n符号表\n") ;for(int i=0; i<symbolTable.size(); i++)printf("name: %s   address:%d   kind:%d \n",symbolTable[i].name, symbolTable[i].address, symbolTable[i].kind) ;printf("\n") ;printf("中间代码\n") ;for(int i=0; i<codeLength; i++)printf("%d 操作码:%s   操作数:%d\n", i, codes[i].operationCode, codes[i].operand) ;}else {printf("语义分析失败\n") ;switch (flag) {                 //选则具体的错误类型case 1 : printf("第%d行缺少 { \n", line-1) ; break ;case 2 : printf("第%d行缺少 } \n", line-1) ; break ;case 3 : printf("第%d行缺少 ; \n", line-1) ; break ;case 4 : printf("第%d行缺少 ( \n", line-1) ; break ;case 5 : printf("第%d行缺少 ) \n", line-1) ; break ;case 6 : printf("第%d行缺少标识符\n", line-1) ; break ;case 7 : printf("第%d行缺少操作数\n", line-1) ; break ;case 8 : printf("无法打开输入文件%s\n", inputAddress);  break ;case 9 : printf("无法打开输出文件%s\n", outputAddress); break ;case 10 : printf("第%d行缺少参数\n", line-1);  break ;case 11 : printf("第%d行缺少保留字\n", line-1);  break ;case 12 : printf("第%d行函数名 %s 重复定义\n", line-1, value);  break ;case 13 : printf("第%d行变量名 %s 重复定义\n", line-1, value);  break ;case 14 : printf("第%d行标识符 %s 没有定义\n", line-1, value);  break ;case 15 : printf("第%d行call之后的标识符 %s 不是函数名\n", line-1, value);  break ;case 16 : printf("第%d行赋值语句的标识符 %s 不是变量名\n", line-1, value);  break ;case 17 : printf("第%d行运算语句的标识符 %s 不是变量名\n", line-1, value);  break ;}}return 0 ;
}

3 总结

  • 在写语义分析的代码时,需要填写符号表,有些部分在课上老师讲过,所以写起来很轻松,但是有一部分内容需要自己考虑,最开始什么都没考虑就直接写,结果逻辑上一直出问题,反复改了很多次才完成。这时才想起来在课上是进行了模拟,知道了每一步是什么,什么时候进行反填等等。于是在之后的打代码过程中,我都是先在纸上画出流程图然后手写了一遍过程,对整个填写符号表的过程有了深刻理解之后再来打代码就变得很轻松了。所以,先理解再执行是很高效的方法。

语义分析——TEST编译器(3)相关推荐

  1. 自己动手构造编译系统:编译、汇编与链接2.1.4 语义分析

    2.1.4  语义分析 编译原理教材中,将语言的文法分为4种:0型.1型.2型.3型,并且这几类文法对语言的描述能力依次减弱.其中,3型文法也称为正规文法,词法分析器中有限自动机能处理的语言文法正是3 ...

  2. 尝试再造python编译器:龙书重制版

    一段时间前,我们用go编写了python的词法解析器.由于近一段时间事情繁多,同时囊中羞涩,因此更多的精力投入到了和"变现"相关的工作,对编译原理,数据库这些极为基础且底层的技术有 ...

  3. 深入gcc编译器:C/C++代码如何变为可执行程序

    揭秘gcc编译器:C/C++代码如何变为可执行程序(Demystifying gcc Compiler: How C/C++ Code Becomes Executable) 一.引言(Introdu ...

  4. C 语言编程 — 程序的编译流程

    目录 文章目录 目录 文章目录 C 程序的编译流程 预处理 编译 汇编 链接 编译多个源文件 文章目录 <C 语言编程 - GCC 工具链> <C 语言编程 - 程序的编译流程> ...

  5. hive 开窗函数_Hive的架构剖析

    本文主要介绍Hive的架构和以及HQL的查询阶段,主要内容包括: Hive的架构 架构中的相关组件介绍 HQL的查询阶段 Hive的架构 hive的基本架构图如下图所示: 相关组件介绍 数据存储 Hi ...

  6. 像 IDE 一样使用 vim

    本文转载自:https://github.com/yangyangwithgnu/use_vim_as_ide ##[目录] 0 vim 必知会  ........0.1 .vimrc 文件  ... ...

  7. 所需即所获:IDE = _plugins_ + vim

    新版迁移:https://github.com/yangyangwithgnu/use_vim_as_ide http://yangyangwithgnu.github.io/ 0 vim 必知会   ...

  8. 了解splinternet:世界能否真正做到全球化?

    You may already be familiar with the term splinternet, a neologism made up of "split" and ...

  9. 将VIM打造成强大的IDE

    转载自:所需即所获:像 IDE 一样使用 vim 如侵犯您的版权,请联系:2378264731@qq.com --------------------------------------------- ...

最新文章

  1. dataframe按行按列选择方法
  2. 《Java并发编程实践》学习笔记之一:基础知识
  3. VTK:PolyData之PointSampler
  4. android 恢复出厂设置 时间,Android 恢复出厂设置后,时间不能恢复为:2013年1月1日...
  5. hadoop mysql 存储过程_hadoop 存储过程
  6. 垃圾收集:提高吞吐量
  7. 对流扩散方程matlab向前向后差分,解纯对流方程几种向后特征差分格式的比较
  8. MySQL : MySQL如何查看操作记录
  9. JAVA受检异常和非受检异常举例
  10. python怎么播放音乐_Python实现在线音乐播放器
  11. 计算机考苏州公务员考试,苏州公务员考试难度
  12. c语言万年历查询程序代码,C语言实现万年历程序
  13. 设置360浏览器默认以极速模式打开
  14. 血浆分拣机上位机人机交互界面设计
  15. 如何提高BT的下载速度?
  16. unity打印生成之后的条形码(二维码也行)
  17. BIOS实战之Super IO-Smart Fan
  18. word两个不同表格合并,防止自动调整
  19. 计算机四级初级程序员考试试卷
  20. 使用 jodd:form tag

热门文章

  1. ARM立即数的构成和有效立即数的判断
  2. 中华英才网爬虫程序解析(4)-分布式爬虫redis
  3. 由一份诊断报告引发的思考
  4. 解密!高德地图九大绝密卷宗带你畅游上海迪士尼
  5. 无人机实验平台(七) 实验平台的坐标转换(上)
  6. Java+Swing+mysql5实现超市商品管理系统
  7. linux 文件中查找内容
  8. JavaScript 触发浏览器页面全屏,某div区域全屏
  9. docker pull提示x509错误的对应方法
  10. 《蓝桥杯CT107D单片机竞赛板》:蜂鸣器模块