编译原理课程设计C编译器词法分析与语法分析的实现.docx
《编译原理课程设计C编译器词法分析与语法分析的实现.docx》由会员分享,可在线阅读,更多相关《编译原理课程设计C编译器词法分析与语法分析的实现.docx(51页珍藏版)》请在冰豆网上搜索。
编译原理课程设计C编译器词法分析与语法分析的实现
编译原理课程设计报告
课题名称:
C-编译器词法分析与语法分析的实现
提交文档学生姓名:
黄臻旸
提交文档学生学号:
1043041227
同组成员名单:
无
指导教师姓名:
金军
指导教师评阅成绩:
指导教师评阅意见:
.
.
提交报告时间:
2013年6月5日
1、课程设计目标
本次实验,本C-编译器主要设计并且实现了C-编译器的词法分析功能与语法分析功能。
2、分析与设计
2.1、说明所用的方法:
各部分的实现方法(scanner:
手工实现、Lex;parser:
递归下降、LL
(1)、LR(0)、SLR
(1)、LR
(1)、LALR
(1)、Yacc),所用编程语言
实现内容
所用的实验方法
所用编程语言
scanner
手工实现
C语言
parse
递归下降
C语言
2.2、系统总图:
2.2.1、scanner部分:
2.2.1.1、实验原理:
扫描程序的任务是从源代码中读取字符并形成由编译器的以后部分(通常是分析程序)处理的逻辑单元。
由扫描程序生成的逻辑单元称作记号(token),将字符组合成记号与在一个英语句子中将字母将字母构成单词并确定单次的含义很相像。
在此程序中,我将记号分成了以下类型:
typedefenum{//按照书上附录B程序布局,放在globals.h中
ENDFILE,ERROR,
IF,ELSE,INT,RETURN,VOID,WHILE,
ID,NUM,
ASSIGN,PLUS,MINUS,TIMES,OVER,LT,LET,BT,BET,EQ,NEQ,
//=+-*/<<=>>===!
=
LPAREN_1,RPAREN_1,SEMI,COM,LPAREN_2,RPAREN_2,LPAREN_3,RPAREN_3,LIN,RIN
//{};,[]()/*
}TokenType;
其中,关键字有:
else、if、int、return、void、while;
专用符号有:
+、-、*、/、<、<=、>、>=、==、~=、=、;、,、(、)、[、]、{、}、/*、*/
其他标记是ID、NUM,通过下列正则表达式定义:
ID=letterletter*
NUM=digitdigit*
letter=a|..|z|A|..|Z
digit=0|..|9
小写大写字母是有区别的。
空格由空白、换行符和制表符组成。
空格通常被忽略,除了他必须分开ID、NUM关键字。
注释常用通常的C语言符号/*...*/围起来。
注释可以放在任何空白出现的位置(即注释不能放在标记内)上,且可以超过一行。
注释不能嵌套。
2.2.1.2、实验方法:
我通过对scanner部分原理的了解,确定了他的NFA,再将NFA转化成DFA,并且将状态数最小化。
最后根据我所得的DFA与课后TINY的示例程序编写scanner.c。
最后所得的DFA:
2.2.1.3、编程方法:
编程采用C语言。
初始状态设置为START,当需要得到下一个token时,取得此token的第一个字符,并且按照DFA与对此字符的类型的分析,转换状态。
重复此步骤,直到DONE为止,输出token类型。
此中难点在于对于注释的分析,因此我将判断注释分成几个步骤。
当字符为“/”时,状态转换为INASSIGN_1(自创的)再判断下一个字符,如果为“*”则是注释,如果是其他的则字符停滞与当前字符(ungetNextChar()),并且输出“/”。
在开始时一直未注意停滞与当前字符,因此总是读不出“/v*”中的“v”,在调试多次后才得以解决。
2.2.2、parse部分:
2.2.2.1、实验原理:
C-语言的各个语法规则:
1.program→declaration-list
2.declaration-list→declaration-listdeclaration|declaration
3.declaration→var-declaration|fun-declaration
4.var-declaration→type-specifierID;|type-specifierID[NUM];
5.type-specifier→int|void
6.fun-declaration→type-specifierID(params)compound-stmt(在课后解释中compound-stmt前面没有“|”符号)
7.params→params-list|void
8.param-list→param-list,param|param
9.param→type-specifierID|type-specifierID[]
10.compound-stmt→{local-declarationsstatement-list}
11.local-declarations→local-declarationsvar-declaration|empty
12.statement-list→statement-liststatement|empty
13.statement→expression-stmt|compound-stmt|selection-stmt|
iteration-stmt|return-stmt
14.expression-stmt→expression;|;
15.selection-stmt→if(expression)statement|if(expression)
statementelsestatement
16.iteration-stmt→while(expression)statement
17.return-stmt→return;|returnexpression;
18.expression→var=expression|simple-expression
19.var→ID|ID[expression]
20.simple-expression→additive-expressionrelopadditive-expression|
additive-expression
21.relop→<=|<|>|>=|==|!
=
22.additive-expression→additive-expressionaddopterm|term
23.addop→+|-
24.term→termmulopfactor|factor
25.mulop→*|/
26.factor→(expression)|var|call|NUM
27.call→ID(args)
28.args→arg-list|empty
29.arg-list→arg-list,expression|expression
2.2.2.2、实验方法:
本次试验完成parse方法我采用的是递归下降的方法。
根据C-语言的规则,我们可以得出C-语言语法的EBNF。
下面是我在代码中所使用的自己整理出来的语法规则:
注:
{}为重复[]为选择,{}为本来的意思
1.program→declaration-list
2.declaration-list→declaration{declaration}
3.declaration→int|void|emptyID[factor];|(params)compound-stmt
4.params→params-list|void
5.param-list→param{param}
6.param→int|voidIDempty|[]
7.compound-stmt→{local-declarationsstatement-list}
8.local-declarations→{declaration}|empty
9.statement-list→{statement}|empty
10.statement→expression-stmt|compound-stmt|selection-stmt|
iteration-stmt|return-stmt
11.expression-stmt→expression;|;
12.selection-stmt→if(expression)statement{elsestatement}
13.iteration-stmt→while(expression)statement
14.return-stmt→return;|expression;
15.expression→simple-expression{=expression}
16.var→ID[[expression]]
17.simple-expression→additive-expression{relopadditive-expression}
18.relop→<=|<|>|>=|==|!
=
19.additive-expression→additive-expression{addopterm}
20.addop→+|-
21.term→termmulop{factor}
22.mulop→*|/
23.factor→(expression)|NUM|ID(args)|[expression]|empty
24.args→arg-list|empty
25.arg-list→expression{expression}
其中,3条declaration15条expression和23条factor是重点修改内容。
2.2.2.3、编程方法:
编程采用C语言。
分析从program开始,逐层向下扩展。
依靠下一步所得到的token,根据整理过后的语法规则来判断语法树的结点生成与走向。
并且根据语法规则来确定语法树的末结点的内容。
此处的难点在于语法规则的整理。
若是按照书上原来的29条语法规则来写,就会发现在树的生成方法与逻辑上会很难实现。
最开始时我便参照着原29条语法规则来写的,虽然全程序都没有报错,但是分析程序时一直分析不了。
最后停下编程来从笔头整理了一阵子语法规则后,语法分析程序可以分析了,只有个别错误了。
再不断地在每一个结点生成时fprintf(listing,”\n-------itsXXXXtime\n”)一下,逐步排查错误,最终才确定了剩下的这25个语法规则。
2.2.3、代码设计说明
程序globalsfiles包里面分为SourceFiles文件夹和HeaderFiles文件夹
SourceFiles文件夹中包含:
main.c;parse.c;scan.c;util.c
HeaderFiles文件夹中包含:
globals.h;parse.h;scan.h;util.h
其中:
main.c负责程序的执行方式(cmd命令行下执行globals.exe+测试代码文件名)
globals.h负责treeNode的声明,TokenType类型的声明,与NodeKind等的声明
util.c负责新结点的构造,词法的输出,语法的输出,以及输出显示的布局
voidprintToken(TokenTypetoken,constchar*tokenString){}
//根据书上原型做的,但根据语法规则所需要的,做过大量修改
TreeNode*newStmtNode(StmtKindkind){}
//根据书上原型做的,未经修改
TreeNode*newExpNode(ExpKindkind){}
//根据书上原型做的,未经修改
TreeNode*newParamNode(void){}
//自己根据所需建立
TreeNode*newDecNode(void){}
//自己根据所需建立
char*copyString(char*s){}
//根据书上原型做的,未经修改
staticvoidprintSpaces(void){}
//根据书上原型做的,未经修改
voidprintTree(TreeNode*tree){}
//根据书上原型做的,但根据语法规则所需要的,做过大量修改
util.h负责util.c中所用函数的声明
scan.c负责词法状态的声明,词法的分析
typedefenum{START,INASSIGN_1,L_ASSIGN,R_ASSIGN,
E_ASSIGN,N_ASSIGN,INASSIGN_2,INCOMMENT,
INNUM,INID,DONE}StateType;
//根据书上原型做的,但根据语法规则所需要的,做过大量修改
staticintgetNextChar(void){}
//根据书上原型做的,未经修改
staticvoidungetNextChar(void){}
//根据书上原型做的,未经修改
staticstruct{char*str;TokenTypetok;}reservedWords[MAXRESERVED]=
{"if",IF},{"else",ELSE},{"int",INT},{"return",RETURN},{"void",VOID},{"while",WHILE}};
//根据书上原型做的,但根据语法规则所需要的,做过大量修改
staticTokenTypereservedLookup(char*s){}
//根据书上原型做的,未经修改
TokenTypegetToken(void){}
//根据书上原型做的,但根据语法规则所需要的,做过大量修改
scan.h负责对scan.c中所用函数的声明
parse.c负责语法树的分析
staticvoidsyntaxError(char*message){}
//根据书上原型做的,未经修改
staticvoidmatch(TokenTypeexpected){}
//根据书上原型做的,未经修改
//以下的全部为自己所写
staticTreeNode*program(void);
staticTreeNode*declaration_list(void);
staticTreeNode*declaration(void);
staticTreeNode*params(void);
staticTreeNode*param_list(void);
staticTreeNode*param(void);
staticTreeNode*compound_stmt(void);
staticTreeNode*local_declaration(void);
staticTreeNode*statement_list(void);
staticTreeNode*statement(void);
staticTreeNode*expression_stmt(void);
staticTreeNode*selection_stmt(void);
staticTreeNode*iteration_stmt(void);
staticTreeNode*return_stmt(void);
staticTreeNode*expression(void);
staticTreeNode*var(void);
staticTreeNode*simple_expression(void);
staticTreeNode*additive_expression(void);
staticTreeNode*term(void);
staticTreeNode*factor(void);
staticTreeNode*args(void);
staticTreeNode*arg_list(void);
parse.h负责parse.c中所用函数的声明
此次所做程序输入格式为:
cmd命令行下执行globals.exe+测试代码文件名
输出地方:
cmd命令行
输出内容:
词法分析与语法树
3、程序代码实现
3.1、获取输入部分(在main.c中):
此处因为并未有所修改,均参照附录B所写,所以略去。
3.2、词法分析部分(在scan.c中):
其中部分代码因为参照附录B中内容而未修改,所以略去。
typedefenum{//此处便如2.2.1.2图所示,定义11个中间状态,其中INASSIGN分成了L_ASSIGN,R_ASSIGN,而在分析注释结束时添加了INASSIGN_2状态
E_ASSIGN,N_ASSIGN,
START,INASSIGN_1,L_ASSIGN,R_ASSIGN,
E_ASSIGN,N_ASSIGN,INASSIGN_2,INCOMMENT,
INNUM,INID,DONE
}StateType;
staticstruct{char*str;TokenTypetok;}reservedWords[MAXRESERVED]=
{{"if",IF},{"else",ELSE},{"int",INT},{"return",RETURN},{"void",VOID},{"while",WHILE}};//此处根据C-的保留字做了一定的修改
TokenTypegetToken(void){//独立完成
inttokenStringIndex=0;
TokenTypecurrentToken;
StateTypestate=START;
intsave;
while(state!
=DONE){
intc=getNextChar();
save=TRUE;
//fprintf(listing,"\nScanner:
state=%d\n",state);//测试每一步状态
switch(state){
caseSTART:
if(isdigit(c))
state=INNUM;//是数字
elseif(isalpha(c))
state=INID;//是ID
//fprintf(listing,"\n--isalpha\n");}//测试是否进行到此处
elseif(c=='/'){
save=FALSE;
state=INASSIGN_1;//判断注释
//fprintf(listing,"\n--is/\n");
}
elseif((c=='')||(c=='\t')||(c=='\n'))
save=FALSE;
elseif(c=='<'){
save=FALSE;
state=L_ASSIGN;
}
elseif(c=='>'){
save=FALSE;
state=R_ASSIGN;
}
elseif(c=='='){
save=FALSE;
state=E_ASSIGN;
}
elseif(c=='!
'){
save=FALSE;
state=N_ASSIGN;
}
else{
state=DONE;
switch(c){
caseEOF:
save=FALSE;
currentToken=ENDFILE;
break;
case',':
currentToken=COM;
break;
case'=':
currentToken=EQ;
break;
case'+':
currentToken=PLUS;
break;
case'-':
currentToken=MINUS;
break;
case'*':
currentToken=TIMES;
break;
case'(':
currentToken=LPAREN_3;
break;
case')':
currentToken=RPAREN_3;
break;
case'[':
currentToken=LPAREN_2;
break;
case']':
currentToken=RPAREN_2;
break;
case'{':
currentToken=LPAREN_1;
break;
case'}':
currentToken=RPAREN_1;
break;
case';':
currentToken=SEMI;
break;
default:
currentToken=ERROR;
break;
}
}
break;
caseINCOMMENT:
save=FALSE;
if(c==EOF){
state=DONE;
currentToken=ENDFILE;
}
elseif(c=='*')state=INASSIGN_2;//判断出注释
elsestate=INCOM