1、 词法分析 一、 实验目的 设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。 二、 实验要求 2.1 待分析的简单的词法 (1)关键字: begin if then while do end 所有的关键字都是小写。 (2)运算符和界符 : = + - * / < <= <> > >= = ; ( ) # (3)其他单词是标识符(ID)和整型常数(SUM),通过以下正规式定义: ID = letter (letter | digit)* NUM = digit digit*
2、4)空格有空白、制表符和换行符组成。空格一般用来分隔ID、SUM、运算符、界符和关键字,词法分析阶段通常被忽略。 2.2 各种单词符号对应的种别码: 表2.1 各种单词符号对应的种别码 单词符号 种别码 单词符号 种别码 bgin 1 : 17 If 2 := 18 Then 3 < 20 wile 4 <> 21 do 5 <= 22 end 6 > 23 lettet(letter|digit)* 10 >= 24 dight dight* 11 = 25 + 13 ; 26 — 14 ( 27
3、 * 15 ) 28 / 16 # 0 2.3 词法分析程序的功能: 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 例如:对源程序begin x:=9: if x>9 then x:=2*x+1/3; end #的源文件,经过词法分析后输出如下序列: (1,begin)(10,x)(18,:=)(11,9)(26,;)(2,if)…… 三、词法分析程序的算法思想: 算法的基本任务是从字符串表示的源程序中识别出具
4、有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。 3.1 主程序示意图: 主程序示意图如图3-1所示。其中初始包括以下两个方面: ⑴ 关键字表的初值。 关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下: Char *rwtab[6] = {“begin”, “if”, “then”, “while”, “do”, “end”,}; 置初值 调用扫描子程序
5、 输出单词二元组 输入串结束 否 是 结束 图3-1 (2)程序中需要用到的主要变量为syn,token和sum 3.2 扫描子程序的算法思想: 首先设置3个变量:①token用来存放构成单词符号的字符串;②sum用来整型单词;③syn用来存放单词符号的种别码。扫描子程序主要部分流程如图3-2所示。 变量初始化 忽略空格 是否文件结束?
6、 返回 是 是 否 字母 拼字符串 数字 其他 运算符、 符号 界符等符号 是否关键字? 返回 拼数 否 对不同符号给出相应的syn值 报错 syn=10 是 syn=
7、1111
syn为对应关键字的单词种别码
图 3-2
四、词法分析程序的C语言程序源代码:
#include
8、 scanf("%c",&ch); prog[p++]=ch; }while(ch!='#'); p=0; do{ scaner(); switch(syn) {case 11:printf("( %-10d%5d )\n",sum,syn); break; case -1:printf("you have input a wrong string\n"); getch(); exit(0); default: printf("(
9、10s%5d )\n",token,syn); break; } }while(syn!=0); getch(); } scaner() { sum=0; for(m=0;m<8;m++)token[m++]=NULL; ch=prog[p++]; m=0; while((ch==' ')||(ch=='\n'))ch=prog[p++]; if(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))) { whil
10、e(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))||((ch>='0')&&(ch<='9'))) {token[m++]=ch; ch=prog[p++]; } p--; syn=10; for(n=0;n<6;n++) if(strcmp(token,rwtab[n])==0) { syn=n+1; break; } } else if((ch>='0')&&(ch<='9
11、')) { while((ch>='0')&&(ch<='9')) { sum=sum*10+ch-'0'; ch=prog[p++]; } p--; syn=11; } else switch(ch) { case '<':token[m++]=ch; ch=prog[p++]; if(ch=='=') { syn=22; token[m++]=ch; }
12、 else { syn=20; p--; } break; case '>':token[m++]=ch; ch=prog[p++]; if(ch=='=') { syn=24; token[m++]=ch; } else { syn=23; p--;
13、 } break; case '+': token[m++]=ch; ch=prog[p++]; if(ch=='+') { syn=17; token[m++]=ch; } else { syn=13; p--; } break; case '-':token[m++]=ch;
14、 ch=prog[p++]; if(ch=='-') { syn=29; token[m++]=ch; } else { syn=14; p--; } break; case '!':ch=prog[p++]; if(ch=='=') { syn=21; t
15、oken[m++]=ch; } else { syn=31; p--; } break; case '=':token[m++]=ch; ch=prog[p++]; if(ch=='=') { syn=25; token[m++]=ch; } else { syn=18
16、 p--; } break; case '*': syn=15; token[m++]=ch; break; case '/': syn=16; token[m++]=ch; break; case '(': syn=27; token[m++]=ch; break; case ')': syn=28;
17、 token[m++]=ch; break; case '{': syn=5; token[m++]=ch; break; case '}': syn=6; token[m++]=ch; break; case ';': syn=26; token[m++]=ch; break; case '\"': syn=30; token[m++]=ch;
18、 break; case '#': syn=0; token[m++]=ch; break; case ':':syn=17; token[m++]=ch; break; default: syn=-1; break; } token[m++]='\0'; } 五、结果分析: 输入begin x:=9: if x>9 then x:=2*x+1/3; end # 后经词法分析输出如下序列:(begi
19、n 1)(x 10)(:17)(= 18)(9 11)(;26)(if 2)…… 如图5-1所示: 图5-1 六、总结: 词法分析的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。通过本试验的完成,更加加深了对词法分析原理的理解。 语法分析 一、 实验目的 编制一个递归下降分析程序,实现对词法分析程序所提供
20、的单词序列的语法检查和结构分析。 二、 实验要求 利用C语言编制递归下降分析程序,并对简单语言进行语法分析。 2.1 待分析的简单语言的语法 用扩充的BNF表示如下: ⑴<程序>::=begin<语句串>end ⑵<语句串>::=<语句>{;<语句>} ⑶<语句>::=<赋值语句> ⑷<赋值语句>::=ID:=<表达式> ⑸<表达式>::=<项>{+<项> | -<项>} ⑹<项>::=<因子>{*<因子> | /<因子> ⑺<因子>::=ID | NUM | (<表达式>) 2.2 实验要求说明 输入单词串,以“#”结束,如果是文法正确的句子,则输出成功信息,打印“
21、success”,否则输出“error”。 例如: 输入 begin a:=9; x:=2*3; b:=a+x end # 输出 success! 输入 x:=a+b*c end # 输出 error 2.3 语法分析程序的酸法思想 (1)主程序示意图如图2-1所示。 置初值 调用scaner读下一个单词符号 调用lrparser 结束 图2-1 语法分析主程序示意图 (2)递归下降分析程序示意图如图2-2所示。 (3)语句串分析过程示意图如图2-3所示。
22、 是否begin? 调用statement函数 否 是 是否 ;? 调用scaner 否 调用语句串分析程序 是 调用scaner 是否end? 否 调用statement函数 是 调用scaner 出错处理 syn=0&&kk=0? 否 图2-3 语句串分析示意图 是 打印分析成功 出错处理
23、 图2-2 递归下降分析程序示意图 (4)statement语句分析程序流程如图2-4、2-5、2-6、2-7所示。 调用term函数 是否标识符? 否 调用expression函数 调用scaner 是否:=? 调用scaner 是否+ , -? 否 否 是 调用scaner 调用term函数 出错处理 出错处理 图2-4 statement语句分析函数示意图 图2-5 expression表达式分析函数示意图 调用scaner 调用factor函数 出错处理
24、 是否* , /? 调用factor函数 是否标识符? 是 否 否 是否整常数? 是 是 否 是否(? 否 是 调用scaner 是否)? 调用expression函数 图 2-6 term分析函数示意图 否 出错处理 调用scaner 调用scaner 是 图2-7 factor分析过程示意图 三、 语法分析程序的C语言程序源代码: #include "stdio.h" #include "string.h" char prog[100],token[8],ch; c
25、har *rwtab[6]={"begin","if","then","while","do","end"}; int syn,p,m,n,sum; int kk; factor(); expression(); yucu(); term(); statement(); lrparser(); scaner(); main() { p=kk=0; printf("\nplease input a string (end with '#'): \n"); do { scanf("%c",&ch); prog[p++]=ch; }whi
26、le(ch!='#'); p=0; scaner(); lrparser(); getch(); } lrparser() { if(syn==1) { scaner(); /*读下一个单词符号*/ yucu(); /*调用yucu()函数;*/ if (syn==6) { scaner(); if ((syn==0)&&(kk==0)) printf("success!\n"); } else { if(kk!=1) printf("the string haven't
27、 got a 'end'!\n"); kk=1; } } else { printf("haven't got a 'begin'!\n"); kk=1; } return; } yucu() { statement(); /*调用函数statement();*/ while(syn==26) { scaner(); /*读下一个单词符号*/ if(syn!=6) statement(); /*调用函数statement();*/ }
28、 return; } statement() { if(syn==10) { scaner(); /*读下一个单词符号*/ if(syn==18) { scaner(); /*读下一个单词符号*/ expression(); /*调用函数statement();*/ } else { printf("the sing ':=' is wrong!\n"); kk=1; } } else { printf("wrong sentence!\n"
29、); kk=1; } return; } expression() { term(); while((syn==13)||(syn==14)) { scaner(); /*读下一个单词符号*/ term(); /*调用函数term();*/ } return; } term() { factor(); while((syn==15)||(syn==16)) { scaner(); /*读下一个单词符号*/
30、 factor(); /*调用函数factor(); */ } return; } factor() { if((syn==10)||(syn==11)) scaner(); else if(syn==27) { scaner(); /*读下一个单词符号*/ expression(); /*调用函数statement();*/ if(syn==28) scaner(); /*读下一个单词符号*/ else { printf("the
31、error on '('\n"); kk=1; } } else { printf("the expression error!\n"); kk=1; } return; } scaner() { sum=0; for(m=0;m<8;m++)token[m++]=NULL; m=0; ch=prog[p++]; while(ch==' ')ch=prog[p++]; if(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A')
32、)) { while(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))||((ch>='0')&&(ch<='9'))) {token[m++]=ch; ch=prog[p++]; } p--; syn=10; token[m++]='\0'; for(n=0;n<6;n++) if(strcmp(token,rwtab[n])==0) { syn=n+1; break; } } else if
33、ch>='0')&&(ch<='9')) { while((ch>='0')&&(ch<='9')) { sum=sum*10+ch-'0'; ch=prog[p++]; } p--; syn=11; } else switch(ch) { case '<':m=0; ch=prog[p++]; if(ch=='>') { syn=21; } else if(ch=='=') { syn=22; } else
34、 { syn=20; p--; } break; case '>':m=0; ch=prog[p++]; if(ch=='=') { syn=24; } else { syn=23; p--; } break; case ':':m=0; ch=prog[p++]; if(ch=='=') { syn=18; } else { syn=17;
35、 p--; } break; case '+': syn=13; break; case '-': syn=14; break; case '*': syn=15;break; case '/': syn=16;break; case '(': syn=27;break; case ')': syn=28;break; case '=': syn=25;break; case ';': syn=26;break; case '#': syn=0;break; default: syn=-1;brea
36、k; } } 四、 结果分析: 输入 begin a:=9; x:=2*3; b:=a+x end # 后输出success! 如图4-1所示: 图4-1 输入 x:=a+b*c end # 后输出 error 如图4-2所示: 图4-2 五、 总结: 通过本次试验,了解了语法分析的运行过程,主程序大致流程为:“置初值”à调用scaner函数读下一个单词符号à调用IrParseà结束。递归下降分析的大致流程为:“先判断是否为begin”à不是则“出错处理”,若是则“调用scaner函数”à调用语句串分析函数à“判断是否为end”à不是则“出
37、错处理”,若是则调用scaner函数à“判断syn=0&&kk=0是否成立”成立则说明分析成功打印出来。不成立则“出错处理”。 语义分析程序 #include "stdio.h" #include "string.h" char prog[100],token[8],ch; char *rwtab[6]={"begin","if","then","while","do","end"}; int syn,p,m,n,sum,q; int kk; struct { char result1[8]
38、 char ag11[8]; char op1[8]; char ag21[8]; } quad[20]; char *factor(); char *expression(); int yucu(); char *term(); int statement(); int lrparser(); char *newtemp(); scaner(); emit(char *result,char *ag1,char *op,char *ag2); main() { int j; q=p=kk=0; printf("\npleas
39、e input a string (end with '#'): ");
do
{ scanf("%c",&ch);
prog[p++]=ch;
}while(ch!='#');
p=0;
scaner();
lrparser();
if(q>19)printf(" to long sentense!\n");
else for (j=0;j 40、
}
int lrparser()
{ int schain=0;
kk=0;
if (syn==1)
{ scaner();
schain=yucu();
if(syn==6)
{ scaner();
if((syn==0)&&(kk==0)) printf("Success!\n");
}
else { if(kk!=1)printf("short of 'end' !\n");
kk=1;
getch();
exit(0);
}
41、}
else { printf("short of 'begin' !\n");
kk=1;
getch();
exit(0);
}
return (schain);
}
int yucu()
{ int schain=0;
schain=statement();
while(syn==26)
{ scaner();
schain=statement();
}
return (schain);
}
int statement()
{ char tt[8],eplace[8];
42、 int schain=0;
if (syn==10)
{ strcpy(tt,token);
scaner();
if(syn==18)
{ scaner();
strcpy(eplace,expression());
emit(tt,eplace,"","");
schain=0;
}
else { printf("short of sign ':=' !\n");
kk=1;
getch();
exit(0);
}
return (schain) 43、
}
}
char *expression()
{ char *tp,*ep2,*eplace,*tt;
tp=(char *)malloc(12);
ep2=(char *)malloc(12);
eplace=(char *)malloc(12);
tt=(char *)malloc(12);
strcpy(eplace,term());
while((syn==13)||(syn==14))
{ if (syn==13)strcpy(tt,"+");
else strcpy(tt,"-");
scaner 44、);
strcpy(ep2,term());
strcpy(tp,newtemp());
emit(tp,eplace,tt,ep2);
strcpy(eplace,tp);
}
return (eplace);
}
char *term()
{ char *tp,*ep2,*eplace,*tt;
tp=(char *)malloc(12);
ep2=(char *)malloc(12);
eplace=(char *)malloc(12);
tt=(char *)malloc(12);
st 45、rcpy(eplace,factor());
while((syn==15)||(syn==16))
{ if (syn==15)strcpy(tt,"*");
else strcpy(tt,"/");
scaner();
strcpy(ep2,factor());
strcpy(tp,newtemp());
emit(tp,eplace,tt,ep2);
strcpy(eplace,tp);
}
return (eplace);
}
char *factor()
{ char *fpla 46、ce;
fplace=(char *)malloc(12);
strcpy(fplace,"");
if(syn==10)
{ strcpy(fplace,token);
scaner();
}
else if(syn==11)
{ itoa(sum,fplace,10);
scaner();
}
else if(syn==27)
{ scaner();
fplace=expression();
if(syn==28) scaner();
else { printf 47、"error on ')' !\n");
kk=1;
getch();
exit(0);
}
}
else { printf("error on '(' !\n");
kk=1;
getch();
exit(0);
}
return (fplace);
}
char *newtemp()
{ char *p;
char m[8];
p=(char *)malloc(8);
kk++;
itoa(kk,m,10);
strcpy(p+1,m);
p[0]= 48、't';
return(p);
}
scaner()
{ sum=0;
for(m=0;m<8;m++)token[m++]=NULL;
m=0;
ch=prog[p++];
while(ch==' ')ch=prog[p++];
if(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A')))
{ while(((ch<='z')&&(ch>='a'))||((ch<='Z')&&(ch>='A'))||((ch>='0')&&(ch<='9')))
{token[ 49、m++]=ch;
ch=prog[p++];
}
p--;
syn=10;
token[m++]='\0';
for(n=0;n<6;n++)
if(strcmp(token,rwtab[n])==0)
{ syn=n+1;
break;
}
}
else if((ch>='0')&&(ch<='9'))
{ while((ch>='0')&&(ch<='9'))
{ sum=sum*10+ch-'0';
ch=prog[p++];
50、
}
p--;
syn=11;
}
else switch(ch)
{ case '<':m=0;
ch=prog[p++];
if(ch=='>')
{ syn=21;
}
else if(ch=='=')
{ syn=22;
}
else
{ syn=20;
p--;
}
break;
case '>':m=0;
ch=prog[p++];
if(ch=='