语法分析器.docx
- 文档编号:5616000
- 上传时间:2022-12-29
- 格式:DOCX
- 页数:14
- 大小:143.55KB
语法分析器.docx
《语法分析器.docx》由会员分享,可在线阅读,更多相关《语法分析器.docx(14页珍藏版)》请在冰豆网上搜索。
语法分析器
编译原理实验报告
实验二:
语法分析器
课程名称:
编译原理
学院:
计算机科学与工程
班级:
姓名:
学号:
教师:
李拥军
2015年01月06日
一、实验目的及内容
设计、编制、调试一个词法分析子程序-识别单词,加深对词法分析的理解掌握以及高级语言语法结构的定义,掌握语法分析的常用方法
二、实验环境
(1)操作系统:
Windows7
(2)编程环境:
VisualStudio2010
三、实验过程及结果
设计该词法分析器的过程中虽然没有实际将所有的状态转移表建立出来,但是所用的思想是根据状态转移表实现对单词的识别。
首先构造一个保留字表,然后,每输入一个字符就检测应该进入什么状态,并将该字符连接到d串后继续输入,如此循环,最后根据所在的接受状态以及保留字表识别单词。
3.1.实现
单词符号串语法分析语法短语(Token串)。
识别规则是语法规则(描述程序结构的规则,通常是由递归规则表示的)。
结果通过判断输入串是否构成一个语法上正确的程序,并生成语法树
赋值语句规则:
<赋值语句>:
:
=<标识符>“:
=”<表达式>
<表达式>:
:
=<表达式>“+”<表达式>
<表达式>:
:
=<表达式>“*”<表达式>
<表达式>:
:
=“(”<表达式>“)”
<表达式>:
:
=<标识符>
<表达式>:
:
=<整数>
<表达式>:
:
=<实数>
编译过程中源程序的各种信息被保留在种种不同的表格里,编译各阶段的工作涉及到构造、查找或更新有关的表格,因此需要有表格管理工作。
出错处理:
编译过程中,发现源程序有错误(词法错误、语法错误、语义错误),编译程序应报告错误的性质和出错的地点,并将错误所造成的影响限制在尽可能小的范围内,使得源程序的其余部分继续被编译下去。
程序的目录结构
TreeNode*stmt_sequence(void)
{TreeNode*t=statement();
……
}
TreeNode*statement(void)
{TreeNode*t=NULL;
switch(token){
caseIF:
t=if_stmt();break;
caseREPEAT:
t=repeat_stmt();break;
caseID:
t=assign_stmt();break;
caseREAD:
t=read_stmt();break;
caseWRITE:
t=write_stmt();break;
//增加
caseWHILE:
t=while_stmt();break;
……
}/*endcase*/
returnt;
}
1.exp->arithmetic-exp|bool-exp|string-exp|comparison-exp
把乘法运算的语法放在更深的一个层次,可以把算术表达式的语法放在布尔表达式的深的层次中,于是可以把关于表达式的语法部分修改成下面的形式,让分析程序能够区分是布尔表达式或是算术表达式:
TreeNode*exp(void)//修改
{TreeNode*t=NULL;
//判断是布尔(算术)表达式还是字符串表达式
if(token==STR)
{
t=newExpNode(StrK);
if(t!
=NULL)
t->attr.name=copyString(tokenString);
match(STR);
}
elseif(token==NUM||token==ID||token==BTRUE||token==BFALSE||token==NOT||token==LPAREN)
t=bool_exp();//算术表达式包含在布尔表达式中
returnt;
}
当分析程序遇到ID,NUM或其他字符的话就可以区别出选择哪条产生式了,首先是exp表达式的话,可以通过区别token为STR还是ID,NUM,(字符来区分,后面的语法也没有冲突。
这样的话就解决了整个表达式的翻译的冲突以及含有算术表达式的复杂布尔表达式。
除了这个问题外,还有就是Tiny+增加了变量的声明语句,这里涉及到变量的类型如何确定的问题,有种解决办法是在语法分析阶段根据声明语句的类型声明符在生成语法树的时候就确定类型,还有一种方法是在语义分析阶段在检查类型的时判断语法树的结点是否是声明语句来确定类型。
要实现上术的语法分析需要增加一些常量:
typedefenum{StmtK,ExpK}NodeKind;
typedefenum{IfK,RepeatK,AssignK,ReadK,WriteK,WhileK}StmtKind;
typedefenum{OpK,ConstK,IdK,StrK,BoolK}ExpKind;
typedefenum{Void,Integer,Boolean,String}ExpType;
对于新增加的类型声明语句,首先创建Declartions结点,然后创建声明结点decl,根据不同的声明类型确定decl的类型,之后创建相应的变量声明结点valist,下面是相应的代码:
TreeNode*parse(void)
{TreeNode*t;
token=getToken();
if(token==INT||token==BOOL||token==STRING)
{
declarations();//程序开头包含声明部分
}
t=stmt_sequence();
if(token!
=ENDFILE)
syntaxError("Codeendsbeforefile\n");
returnt;
}
voiddeclarations(void)
{
ExpTypetype;
while(token==INT||token==BOOL||token==STRING)//decl;
{
switch(token){
caseINT:
type=Integer;
break;
caseBOOL:
type=Boolean;
break;
caseSTRING:
type=String;
break;
default:
type=Void;
}
//type-specifier
match(token);
//varlist
st_insert(tokenString,type,lineno,location++);
match(ID);
while(token==COMMA)
{
match(COMMA);
st_insert(tokenString,type,lineno,location++);
match(ID);
}
match(SEMI);//';'isexpected
}
}
对于语义分析的话,基本上要修改的东西并不多,原来的程序在创建符号表时是遇到AssignK,ReadK:
还有IdK结点时就在符号表中查找,如果没有找到就把变量加入符号表中,语义分析里有类型检查,原来的类型检查只是只考虑整形的类型,这里由于类型扩充到了整型,布尔型,字符串型,因些还要考虑这些新类型,其实要完全实现类型检查,需要考虑到一些类型转换,但为了简单起见对于比较表达式,我只检查其子结点是否为整型,If,while,repeat表达式对布尔表达式检查布尔类型,对于声明语句中的变量,就根据其父结点是什么类型就给它们赋于相应的类型,下面是相应的代码:
staticvoidcheckNode(TreeNode*t)//修改
{
switch(t->nodekind)
{
caseExpK:
switch(t->kind.exp)
{
caseOpK:
if(t->attr.op==NOT&&t->child[0]->type!
=Boolean)
typeError(t,"\'not\'operatorneedsabooleanexpression");
elseif(t->attr.op!
=NOT&&t->child[0]->type!
=t->child[1]->type)
typeError(t,"thetypesofoperandsarenotequal");
if(t->attr.op==EQ||t->attr.op==LT||t->attr.op==LE||t->attr.op==GT||t->attr.op==GE)
t->type=Boolean;
elseif(t->attr.op==AND||t->attr.op==OR||t->attr.op==NOT)
t->type=Boolean;
else
t->type=Integer;
break;
caseConstK:
t->type=Integer;
break;
caseIdK:
t->type=st_gettype(t->attr.name);
break;
caseStrK:
t->type=String;
break;
caseBoolK:
t->type=Boolean;
break;
default:
break;
}
break;
caseStmtK:
switch(t->kind.stmt)
{
caseIfK:
if(t->child[0]->type!
=Boolean)//修改
typeError(t->child[0],"iftestisnotBoolean");
break;
caseAssignK:
t->type=st_gettype(t->attr.name);//增加
if(t->child[0]->type!
=t->type)//修改
typeError(t->child[0],"assignmentofadifferenttypevalue");
break;
caseReadK:
//增加
t->type=st_gettype(t->attr.name);
break;
caseWriteK:
if(t->child[0]->type!
=Integer)
typeError(t->child[0],"writeofnon-integervalue");
break;
caseRepeatK:
if(t->child[1]->type!
=Boolean)
typeError(t->child[1],"repeattestisnotBoolean");
break;
caseWhileK:
if(t->child[0]->type!
=Boolean)
typeError(t->child[0],"whiletestisnotBoolean");
break;
default:
break;
}
break;
default:
break;
}
}
在语义分析中,因此变量的类型都是在声明语句声明后才可以使用,因此在构造符号表的时候就保存了可以使用的变量名,但不能知道它们相应的类型,所以在类型检查中,当检查到变量时,要从符号表中查找有无变量,如果有则获取它的类型,如果没有则说明使用未声明的变量。
在完成语义分析之后是三地址码生成的过程,原来的地址码是发属于接近汇编的代码,三地址码的生成也是用到了语法制导,由于之前已经有生成的语法树,所以只需要对语法树进行深度优先搜索,在搜索的过程中生成三地址码,下面是其相应语句的代码生成语法:
Seq->S;Seq1
S.next=newlabel
Seq1.next=Seq.next
Seq.code=S.code||LabelS.next
||Seq1.code
Seq->S
S.next=Seq.next;Seq.code=S.code
S->repeatSequntilE
S.begin=newlabel
Seq.next=newlabel
E.true=S.next;E.false=S.begin;
S.code=LabelS.begin||Seq.code||LabelSeq.next||E.code
S->ifEthenS1end
E.true=newlabel;
E.fasle=S.next
S1.next=S.next
S.code=E.code||gen(E.true)||S1.code
S->ifEthenS1elseS2end
E.true=newlabel;
E.false=newlabel
S1.next=S.next;S2.next=S.next;
S.code=E.code||gen(E.true||S1.code
gen('goto',S.next)
||gen(E.false||S2.code
S->whileEdoS1end
S.begin=newlabel
E.true=newlabel
E.false=S.next
S.next=S.begin
S.code=gen(S.begin)
||E.code||gen(E.true)
||S.code||gen('goto'S.begin)
E->E1orE2
E1.true=E.true
E.false=newlabel
E2.true=E.true
E2.false=E.false
E.code=E1.code||gen(E.false)||E.code
E->E1andE2
E1.true=newlabel
E1.false=E.false
E2.true=E.true
E2.fasle=E.false
E.code=E1.code||
gen(E1.code)||E2.code
E->notE1
E.true=E.false
E1.false=E.true
E.code=E1.code
E->(E1)
E1.true=E.true
E1.false=E.false
E.code=E1.code
E->id1relopid2
E.code=gen('if'id1.namerelopid2.name
'goto'E.true||gen('goto'E.false)
E-.>fasle
E.code=gen('goto'E.false)
E->true
E.code=gen('goto'E.true)
要生成三地址码,关键是要翻译控制语句,如果能够准确知道控制语句的流程,则可以很容易地生成代码。
对上面的语法可以对它们进行直接翻译。
struct{
char*op;
char*a;
char*b;
char*c;
}mcode[512];
voidemit(char*op,char*a,char*b,char*c)
{
mcode[emitLoc].op=(char*)malloc(sizeof(char)*(strlen(op)+1));
mcode[emitLoc].a=(char*)malloc(sizeof(char)*(strlen(a)+1));
mcode[emitLoc].b=(char*)malloc(sizeof(char)*(strlen(b)+1));
mcode[emitLoc].c=(char*)malloc(sizeof(char)*(strlen(c)+1));
strcpy(mcode[emitLoc].op,op);
strcpy(mcode[emitLoc].a,a);
strcpy(mcode[emitLoc].b,b);
strcpy(mcode[emitLoc].c,c);
emitLoc++;
if(highEmitLoc highEmitLoc=emitLoc; } voidoutput() { inti; for(i=0;i { if(strcmp(mcode[i].op,"+")==0||strcmp(mcode[i].op,"-")==0||strcmp(mcode[i].op,"*")==0 ||strcmp(mcode[i].op,"/")==0||strcmp(mcode[i].op,"and")==0||strcmp(mcode[i].op,"or")==0||strcmp(mcode[i].op,"<")==0||strcmp(mcode[i].op,"<=")==0||strcmp(mcode[i].op,">")==0||strcmp(mcode[i].op,">=")==0||strcmp(mcode[i].op,"=")==0) fprintf(code,"%5d)%s: =%s%s%s\n",i,mcode[i].c,mcode[i].a,mcode[i].op,mcode[i].b); elseif(strcmp(mcode[i].op,"read")==0) fprintf(code,"%5d)%s%s\n",i,mcode[i].op,mcode[i].c); elseif(strcmp(mcode[i].op,"write")==0) fprintf(code,"%5d)%s%s\n",i,mcode[i].op,mcode[i].a); elseif(strcmp(mcode[i].op,": =")==0) fprintf(code,"%5d)%s%s%s\n",i,mcode[i].c,mcode[i].op,mcode[i].a); elseif(strcmp(mcode[i].op,"label")==0|| strcmp(mcode[i].op,"goto")==0) fprintf(code,"%5d)%s%s\n",i,mcode[i].op,mcode[i].c); elseif(strcmp(mcode[i].op,"j=")==0) fprintf(code,"%5d)if%s=%sgoto%s\n",i,mcode[i].a,mcode[i].b,mcode[i].c); } } 3.2.实验结果 四、总结与体会 通过本次实验,对编译有了进一步的深刻理解,进一步的巩固了所学的语义分析的原理和过程,并且对中间代码的生成有了更深的认识。 同时加深了对语法制导翻译原理的理解,掌握了将语法分析所识别的语法成分变换为中间代码的方法。 对编译原理这门非常专业的学科,掌握它的一些最为基本的原理。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语法 分析器