📄 analyze.cpp
字号:
/******************************************************************
** 文件名: analyze.cpp
** 描 述: 这是一个完整的语义分析器,实现C-语言的语义分析功能。基本的
** 流程是:先建立一个空符号表,再建一个语法分析器,语法分析器会
** 产生一棵对于源程序的语法树,再把语法树中的源程序中的函数名,
** 变量名及它们的作用域,所在行数等等信息存储在符号表中,建立
** 完毕后,再对语法树进行后序遍历执行语言语义检查,主要是表达式
** 类型匹配检查,变量有效性检查,函数及变量作用域检查。函数返
** 类型正确性检查,函数调用时的参数表检查。
** C-的所有符号都是有作用域的, 但goto语句突破了这个限制,
** 所以,必须为goto语句单独执行第二遍语义分析。
******************************************************************/
#include "globals.h"
#include "ExternGla"
#include "scan.h"
#include "prase.h"
#include "symtab.h"
#include "analyze.h"
/*********************************************************
**静态成员变量的声明。
*******************************************************/
int Canalyzer::m_ilocation=1; //记录变量或函数名出现的顺序(占的内存位置)
Csymboltab* Canalyzer::m_Csymtab; //符号表。
CTreeNode *Canalyzer::m_syntaxTree; //语法树。
CPraser *Canalyzer::m_pPraser; //语法分析器。
int Canalyzer::m_ilineno;
int Canalyzer::m_ierror=0;
/********************************************
**构造函数,完成语义分析器的所有功能。
********************************************/
Canalyzer::Canalyzer(){
try{
m_Csymtab = new Csymboltab;
CPraser *m_pPraser=new CPraser;
m_syntaxTree=m_pPraser->m_program;
cout<<"Building Symbol Table..."<<endl;
buildSymtab(m_syntaxTree); //根据语法树建立完整的符号表。
if(AllFlags.m_iTraceAnalyze){
cout<<"\nSmybol table:\n\n";
m_Csymtab->printSymtab();}
cout<<"Checkint Types..."<<endl;
typeCheck(m_syntaxTree); //执行语义检查。
if(m_ierror==1) exit(0); //有错误存在,代码生成被阻止。
}
catch (bad_alloc){ //处理堆分配异常。
cout<<"Can't be allocated in heap, the programme must be terminated."<<endl;
exit(1);}
catch (...){
cout<<"Something error,the program was terminated.";
exit(1);}
}
Canalyzer::~Canalyzer(){
delete m_pPraser;
delete m_Csymtab;
}
/*******************************************************
**先序遍历语法树完成符号表的创建(当preProc=insertNode,postProc=nullProc时)
**后序遍符号表完成语义分析。(当preProc=nullProc,postProc=checdNode时)
**********************************************************/
void Canalyzer::traverse(CTreeNode* t, void(* preProc)(CTreeNode *),
void(* postProc)(CTreeNode *)){
if(t!= NULL){
preProc(t);
for(int i=0;i<3;i++)
traverse(t->m_pchild[i],preProc,postProc);
postProc(t);
traverse(t->m_pbrother,preProc,postProc);
}
}
/**********************************************
**空函数,不执行任何功能,主要是为辅助traverse
************************************************/
void Canalyzer::nullProc(CTreeNode *t){
return;
}
/***********************************************
**向符号表中插入一个变量或函数名。
***********************************************/
void Canalyzer::insertNode(CTreeNode *t){
switch(t->m_Ennodekind){
case FuncK:
if(m_Csymtab->st_lookup(t->m_strIDname,t->m_strScope)==-1)
m_Csymtab->st_insert(t->m_strIDname, t->m_strScope,
t->m_EnTypevalue,t->m_ilineno, m_ilocation++);
else{ //函数定义不可能两次出现,否则为重定义。
cout<<"Error in line "<<t->m_ilineno<<": function: '"
<<t->m_strIDname<<"'"<<" : redefinition"<<endl;
m_ierror=1;}
break;
case DeclK:
case ParaK:
if(m_Csymtab->st_lookup(t->m_strIDname,t->m_strScope)==-1)
m_Csymtab->st_insert(t->m_strIDname, t->m_strScope, t->m_EnTypevalue,
t->m_ilineno, m_ilocation++);
else{ //同一变量的定义在同一作用域内也不能两次出现,否则为重定义。
cout<<"Error in line "<<t->m_ilineno<<": '"<<t->m_strIDname
<<"'"<<" : redefinition"<<endl;
m_ierror=1;}
break;
case ExpK:
switch(t->kind.m_EnExpKind){
case IdK:
if(t->m_pfather->m_Ennodekind==StmtK && t->m_pfather
->kind.m_EnStmtKind==GotoK)
break; //对goto的地址符号将在第二遍进行处理。
if(m_Csymtab->st_lookup(t->m_strIDname,t->m_strScope)==-1
&& m_Csymtab->st_lookup(t->m_strIDname,"Global")==-1){
//表达式中的符号不可能在表中找不到,因为事前必须声明。
//但一个函数中的变量可能是局部的,也可能是全局的。
cout<<"Error in line "<<t->m_ilineno<<": '"
<<t->m_strIDname<<"' : undeclared identifier"<<endl;
m_ierror=1;}
else{
//如果是局部的,插入一个局部符号。
if(m_Csymtab->st_lookup(t->m_strIDname,t->m_strScope)!=-1)
m_Csymtab->st_insert(t->m_strIDname, t->m_strScope
, t->m_EnTypevalue, t->m_ilineno, 0);
else//否则为全局的。
m_Csymtab->st_insert(t->m_strIDname, "Global"
, t->m_EnTypevalue, t->m_ilineno, 0);}
break;
default:
break;}
break;
case StmtK:
switch(t->kind.m_EnStmtKind){
case AddressK: //符号地址。
if(m_Csymtab->st_lookup(t->m_pchild[0]->m_strIDname,"Global")!=-1){
cout<<"Error in line "<<t->m_ilineno<<": '"
<<t->m_pchild[0]->m_strIDname<<"': label redefined"<<endl;
m_ierror=1;}
else
m_Csymtab->st_insert(t->m_pchild[0]->m_strIDname, "Global",
t->m_EnTypevalue,t->m_ilineno, m_ilocation++);
break;
default:
break;}
break;
default:
break;}
}
/************************************************
** 对符号表执行语义检查。
*************************************************/
void Canalyzer::checkNode(CTreeNode *t){
CTreeNode *p=t; //为break,continue的检查所专用。
switch(t->m_Ennodekind){
case ExpK:
switch(t->kind.m_EnExpKind){
case OpK://表达式中,表达式的类型由精度高的子表达式类型决定。
if(t->m_pchild[1]==NULL){
t->m_EnTypevalue=t->m_pchild[0]->m_EnTypevalue;
break;}
if(t->m_pchild[0]->m_EnTypevalue == VOID ||
t->m_pchild[1]->m_EnTypevalue == VOID){
cout<<"Error in line "<<t->m_ilineno<<
": illegal use of type 'void'"<<endl;
m_ierror=1;}
else if(t->m_pchild[0]->m_EnTypevalue == FLOAT ||
t->m_pchild[1]->m_EnTypevalue == FLOAT)
t->m_EnTypevalue=FLOAT;
else if(t->m_pchild[0]->m_EnTypevalue == INT ||
t->m_pchild[1]->m_EnTypevalue == INT)
t->m_EnTypevalue=INT;
else t->m_EnTypevalue=CHAR;
break;
case IdK: //变量的类型在符号表中可以找到。
if((t->m_EnTypevalue=m_Csymtab->
st_lookuptype(t->m_strIDname,t->m_strScope))==ERROR)
t->m_EnTypevalue=m_Csymtab->st_lookuptype(t->m_strIDname,"Global");
break;
default:
break;}
break;
case StmtK:
switch(t->kind.m_EnStmtKind){
case ReturnK: //函数返回类型必须精确匹配。
if(t->m_pchild[0]==NULL){ //return不带参数,则函数返回类型必为void.
if(m_Csymtab->st_lookuptype(t->m_strScope, "Global")!=VOID){
cout<<"Error in line "<<t->m_ilineno<<": Function '"
<<t->m_strScope<<"' must return a value."<<endl;
m_ierror=1;}
}
break;
case BreakK:
//break语句的基本处理思想是沿着树往双亲结点搜索,如果找不到for,或while
//语句,则说明该break句子位置不对,continue相同处理。
while(p->m_pfather!=NULL && (p->m_pfather->m_Ennodekind!=StmtK ||
(p->m_pfather->kind.m_EnStmtKind!=ForK
&& p->m_pfather->kind.m_EnStmtKind!=WhileK)))
p=p->m_pfather;
if(p->m_pfather==NULL){
cout<<"Error in line "<<t->m_ilineno<<": illegal break"<<endl;
m_ierror=1;}
break;
case ContinueK: //同continue语句。
while(p->m_pfather!=NULL && (p->m_pfather->m_Ennodekind!=StmtK ||
(p->m_pfather->kind.m_EnStmtKind!=ForK
&& p->m_pfather->kind.m_EnStmtKind!=WhileK)))
p=p->m_pfather;
if(p->m_pfather==NULL){
cout<<"Error in line "<<t->m_ilineno<<": illegal continue."<<endl;
m_ierror=1;}
break;
default:
break;}
break;
default:
break;}
}
/*************************************************
** 因为goto语句是突破作用域范围的限制的,所以必须
** 为它单独执行一遍语义检查。
*************************************************/
void Canalyzer::checkgoto(CTreeNode *t){
switch(t->m_Ennodekind){
case StmtK:
switch(t->kind.m_EnStmtKind){
case GotoK:
if(m_Csymtab->st_lookup(t->m_pchild[0]->m_strIDname)==-1){
cout<<"Error in line "<<t->m_ilineno<<": '"<<t->m_pchild[0]->
m_strIDname<<"'"<<" was undefined."<<endl;
m_ierror=1;}
else
m_Csymtab->st_insert(t->m_pchild[0]->m_strIDname,
t->m_EnTypevalue,t->m_ilineno, 0);
break;
default:
break;}
break;
default:
break;}
}
/********************************************************
**先序遍历语法树构造符号表,并打印出符号表。
********************************************************/
void Canalyzer::buildSymtab(CTreeNode* syntaxTree){
traverse(syntaxTree, insertNode,nullProc);
}
/******************************************************
**后序遍历语法树,结合符号表执行语义分析。
** 在第二遍语义检查结束时,确定main函数的存在。
******************************************************/
void Canalyzer::typeCheck(CTreeNode* syntaxTree){
traverse(syntaxTree,nullProc,checkNode);
traverse(syntaxTree,nullProc,checkgoto); //执行第二遍语义分析,为goto匹配符号地址。
if(m_Csymtab->st_lookup("main","Global")==-1){ //语义分析结束,确定一下main函数是否存在。
cout<<"unresolved external symbol main"<<endl;
m_ierror=1;}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -