编译原理上机指导
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第一部分词法分析
实验一、简单的扫描器设计
一、实验目的:熟悉并实现一个简单的扫描器
二、实验内容:
1.设计扫描器的自动机;
2.设计翻译、生成Token的算法;
3.编写代码并上机调试运行通过。
·要求:输入——源程序文件;
输出——(1)相应的Token序列;
(2)关键字、界符表,符号表,常数表。
三、扫描器设计:
扫描器
单词Token
1.自动机:
空l/d 关键字表和界符表l -1
○0①②-单词编码
d program 3
d -1 procedur
e 4
③④-begin 5
+ end 6
⑤-while 7
* do 8
⑥-+ 9
:= * 10
⑦⑧-:11
-1 := 12
⑨-= 13
……,14
,;15
⑩-
-1
○11-
2.关键字表和界符表:
四、程序实现:
1.数据结构:
char ch; //当前字符
char strToken[]; //当前单词
char *keywords[]={“program”, “procedure”, “begin”,……}; //关键字表、界符表
char ID[][]; //符号表
int Cons[]; //常数表
struct TokenType
{ int code,value; }
struct TokenType Token[]; //Token数组
2.算法设计:
1.初始化;
2.滤除空格,读取第一个非空字符到ch;
3.if (ch是一个字母)
4.处理关键字或标识符;
5. else if (ch是一个数字)
6. 处理常数;
else
7. 处理界符或错误处理;
3.算法求精:
·step2 :
ch=GetChar(); //读取当前字符到ch
while (ch==‟ …)
ch=GetChar();
·step3:
int IsLetter(char ch) //判断ch是否为字母
{ if (ch是A~Z或a~z)
return 1;
else
return 0;
}
·step4:
4.1 在strToken中拼成一个单词;//拼关键字或标识符
4.2 code=Reserve(strToken); //查关键字表;
if (!code) //未查到,是一个标识符
{
4.3 value=InsertID(strToken); //将strToken中的单词插入到符号表中
4.4 生成并输出一个标识符Token;
}
else
4.5 生成并输出一个关键字Token;
·step5:
int IsDigit(char ch) //判断ch是否为数字
{ if (ch是0~9)
return 1;
else
return 0;
}
6.1 在strToken中拼成一个单词;//拼常数
6.2 value=InsertConst(strToken); //将strToken中的单词插入到常数表中6.3 生成并输出一个常数Token;
·step7:
7.1 将ch中的字符拼接到strToken中;
if (ch==‟:‟)
7.2 处理双界符“:=”;
7.3 code:=Bound(strToken); //查界符表
if (!code) //未查到
ProcError(); //错误处理
else
7.4 生成并输出一个界符Token;
·step4.1:
while (IsLetter(ch)||IsDigit(ch))
{ Concat(); //将ch中的字符拼接到strToken中ch=GetChar();
}
·step4.2:
int Reserve(char *strToken)
//用strToken中的单词去查关键字表。
查到了,则返回该关键字的编码;
//否则,返回0
{ int i=0;
while (i<N) //设N为关键字表中元素的个数
{ if (!strcmp(keywords[i], strToken))
return (i+3);
i++;
}
return 0;
}
·step4.3:
int InsertID(char *strToken)
//用strToken中的单词去查符号表。
查到了,则返回该单词在表中的位置值;
//否则,将strToken中的单词插入符号表的尾部,并返回位置值
{ int i=0;
while (i<m) //设m为符号表中已有的标识符的个数
{ if (!strcmp(ID[i], strToken))
return i;
i++;
}
strcpy(ID[i],strToken);
m++;
return i;
}
Token[i].code=1;
Token[i].value=value;
Print(Token[i++]); //输出Token
·step4.5:
Token[i].code=code;
Token[i].value=-1;
Print(Token[i++]); //输出Token
·step6.2:
int InsertConst(char *strToken)
//用strToken中的单词去查常数表。
查到了,则返回该单词在表中的位置值;
//否则,将strToken中的单词插入常数表的尾部,并返回位置值
{ int i=0;
int num;
num=trans(strToken); //将常数串转换为数字
while (i<n) //设n为常数表中已有的常数的个数
{ if (Cons[i]==num)
return i;
i++;
}
Cons[i]=num;
n++;
return i;
}
·step6.3:
Token[i].code=2;
Token[i].value=value;
Print(Token[i++]); //输出Token
·step7.1:
Concat();
·step7.2:
ch=GetChar();
if (ch==‟=‟)
Concat();
else
Retract(); //回溯一个字符
·step7.3:
int Bound(char *strToken); //同int Reserve(char *strToken);·step7.4:
Token[i].code=code;
Token[i].value=-1;
Print(Token[i++]); //输出Token
main()
{ char line[]; //一个程序行
打开源程序文件;
while (文件未结束)
{ GetLine(line); //从文件中读入一个程序行
while (一行未结束)
处理一个单词;
}
关闭文件;
输出Token序列;
输出各类表内容;
return 0;
}
五、源程序代码:
#include "stdafx.h"
#include "string.h"
#define N 15
struct TokenType
{ int code,value; };
char *keywords[]={"program","procedure","begin","end","while","do",
"+","*",":",":=","=",",",";","(",")"}; //关键字表、界符表char ID[10][10]; //符号表
int m;
int Cons[10]; //常数表
int n;
void Print(struct TokenType token) //输出Token
{
printf("(%d %d)",token.code,token.value);
}
void ProcError()
{
printf("err!");
}
int IsLetter(char ch) //判断ch是否为字母
{ if (ch>='A' && ch<='Z' || ch>='a' && ch<='z')
return 1;
else
return 0;
}
int IsDigit(char ch) //判断ch是否为数字
{ if (ch>='0' && ch<='9')
return 1;
else
return 0;
}
int Reserve(char *strToken)
//用strToken中的单词去查关键字表。
查到了,则返回该关键字的编码;
//否则,返回0
{ int i=0;
while (i<N) //设N为关键字表中元素的个数
{ if (!strcmp(keywords[i], strToken))
return (i+3);
i++;
}
return 0;
}
int InsertID(char *strToken)
//用strToken中的单词去查符号表。
查到了,则返回该单词在表中的位置值;
//否则,将strToken中的单词插入符号表的尾部,并返回位置值
{ int i=0;
while (i<m) //设m为符号表中已有的标识符的个数
{ if (!strcmp(ID[i], strToken))
return i;
i++;
}
strcpy(ID[i],strToken);
m++;
return i;
}
int trans(char *str)
{
int num,i;
num=i=0;
while (str[i])
num=10*num+str[i++]-48;
return num;
}
int InsertConst(char *strToken)
//用strToken中的单词去查常数表。
查到了,则返回该单词在表中的位置值;
//否则,将strToken中的单词插入常数表的尾部,并返回位置值
{ int i=0;
int num;
num=trans(strToken); //将常数串转换为数字
while (i<n) //设n为常数表中已有的常数的个数{ if (Cons[i]==num)
return i;
i++;
}
Cons[i]=num;
n++;
return i;
}
int main(int argc, char* argv[])
{
char line[]=" while (x1=10) do y:=x1;";
int i_line;
char ch; //当前字符
char strToken[15]; //当前单词
int i_str;
struct TokenType Token[50]; //Token数组
int code,value;
int i;
i=0;
i_line=0;
ch=line[i_line++]; //读取当前字符到ch
while (ch)
{
i_str=0;
while (ch==' ')
ch=line[i_line++];
if (IsLetter(ch))
{
while (IsLetter(ch)||IsDigit(ch)) //拼关键字或标识符
{ strToken[i_str++]=ch; //将ch中的字符拼接到strToken中ch=line[i_line++];
}
i_line--; //Retract()
strToken[i_str]='\0';
code=Reserve(strToken); //查关键字表;
if (!code) //未查到,是一个标识符
{
value=InsertID(strToken); //将strToken中的单词插入到符号表中
Token[i].code=1;
Token[i].value=value;
Print(Token[i++]); //输出Token
}
else
{
Token[i].code=code;
Token[i].value=-1;
Print(Token[i++]); //输出Token
}
}
else if (IsDigit(ch)) // 处理常数;
{
while (IsDigit(ch)) //拼常数
{ strToken[i_str++]=ch; //将ch中的字符拼接到strToken中ch=line[i_line++];
}
i_line--; //Retract()
strToken[i_str]='\0';
value=InsertConst(strToken); //将strToken中的单词插入到常数表中Token[i].code=2;
Token[i].value=value;
Print(Token[i++]); //输出Token
}
else // 处理界符或错误处理;
{
strToken[i_str++]=ch; //将ch中的字符拼接到strToken中;
if (ch==':') // 处理双界符":=";
{
ch=line[i_line++];
if (ch=='=')
strToken[i_str++]=ch;
else
i_line--; //回溯一个字符
}
strToken[i_str]='\0';
code=Reserve(strToken); //查界符表
if (!code) //未查到
ProcError(); //错误处理
else //生成并输出一个界符Token;
{
Token[i].code=code;
Token[i].value=-1;
Print(Token[i++]); //输出Token
}
}
ch=line[i_line++];
}
printf("\n");
for (i=0;i<m;i++)
printf("%s ",ID[i]);
printf("\n");
for (i=0;i<n;i++)
printf("%d ",Cons[i]);
printf("\n");
printf("Hello World!\n");
return 0;
}
实验二、扫描器类的设计
一、二、三、同实验一。
四、程序设计:
·数据结构:
struct TokenType //Token类型
{ int code,value; };
struct SemRecord //符号表记录类型
{ char name[15];
};
SemRecord ID[10]; //符号表
int m=0; //标识符个数
int Cons[10]; //常数表
int n=0; //常数个数
TokenType Token; //Token
class Scan //扫描器类
{
private:
char *keywords[N]; //关键字表、界符表
char line[50];
int i_line;
char ch; //当前字符
char strToken[15]; //当前单词
int i_str;
int code,value;
int i;
SemRecord *p_ID; //符号表指针
int *p_m;
int *p_Cons; //常数表指针
int *p_n;
public:
Scan();
Scan(SemRecord *p1,int *p2,int *p3,int *p4);
void Read(TokenType *token); //read(w)
private:
void ProcError();
int IsLetter(char ch);
int IsDigit(char ch);
int Reserve(char *strToken);
int InsertID(char *strToken);
int trans(char *str);
int InsertConst(char *strToken);
};
Scan sExam(ID,&m,Cons,&n); //扫描器类对象
五、源程序代码
//scaner.h扫描器类设计
#define N 15
char Keys[N][15]={"program","procedure","begin","end","while","do",
"+","*",":",":=","=",",",";","(",")"};
struct TokenType //Token类型
{ int code,value; };
struct SemRecord //符号表记录类型
{ char name[15];
};
class Scan //扫描器类
{
private:
char *keywords[N]; //关键字表、界符表
char line[50];
int i_line;
char ch; //当前字符
char strToken[15]; //当前单词
int i_str;
int code,value;
int i;
SemRecord *p_ID; //符号表指针
int *p_m;
int *p_Cons; //常数表指针
int *p_n;
public:
Scan();
Scan(SemRecord *p1,int *p2,int *p3,int *p4);
void Read(TokenType *token); //read(w)
private:
void ProcError();
int IsLetter(char ch);
int IsDigit(char ch);
int Reserve(char *strToken);
int InsertID(char *strToken);
int trans(char *str);
int InsertConst(char *strToken);
};
Scan::Scan(SemRecord *p1,int *p2,int *p3,int *p4)
{
p_ID=p1;
p_m=p2;
p_Cons=p3;
p_n=p4;
int i;
for (i=0;i<N;i++)
keywords[i]=Keys[i];
i_line=0;
strcpy(line," ab*(c+123*ab)+x#";
};
void Print(struct TokenType token) //输出Token {
printf("(%d %d)",token.code,token.value);
}
void Scan::ProcError()
{
printf("err!");
}
int Scan::IsLetter(char ch) //判断ch是否为字母{ if (ch>='A' && ch<='Z' || ch>='a' && ch<='z')
return 1;
else
return 0;
}
int Scan::IsDigit(char ch) //判断ch是否为数字{ if (ch>='0' && ch<='9')
return 1;
else
return 0;
}
int Scan::Reserve(char *strToken)
//用strToken中的单词去查关键字表。
查到了,则返回该关键字的编码;
//否则,返回0
{ int i=0;
while (i<N) //设N为关键字表中元素的个数
{ if (!strcmp(keywords[i], strToken))
return (i+3);
i++;
}
return 0;
}
int Scan::InsertID(char *strToken)
//用strToken中的单词去查符号表。
查到了,则返回该单词在表中的位置值;
//否则,将strToken中的单词插入符号表的尾部,并返回位置值
{ int i=0;
while (i<*p_m) //设m为符号表中已有的标识符的个数
{ if (!strcmp((p_ID+i)->name, strToken))
return i;
i++;
}
strcpy((p_ID+i)->name,strToken);
(*p_m)++;
return i;
}
int Scan::trans(char *str)
{
int num,i;
num=i=0;
while (str[i])
num=10*num+str[i++]-48;
return num;
}
int Scan::InsertConst(char *strToken)
//用strToken中的单词去查常数表。
查到了,则返回该单词在表中的位置值;
//否则,将strToken中的单词插入常数表的尾部,并返回位置值
{ int i=0;
int num;
num=trans(strToken); //将常数串转换为数字
while (i<*p_n) //设n为常数表中已有的常数的个数
{ if (p_Cons[i]==num)
return i;
}
p_Cons[i]=num;
(*p_n)++;
return i;
}
void Scan::Read(TokenType *token)
{
if (line[i_line]=='\0')
i_line=0;
ch=line[i_line++]; //读取当前字符到ch
if (ch=='#')
{
token->code=0;
token->value=-1;
return;
}
i_str=0;
while (ch==' ')
ch=line[i_line++];
if (IsLetter(ch))
{
while (IsLetter(ch)||IsDigit(ch)) //拼关键字或标识符
{ strToken[i_str++]=ch; //将ch中的字符拼接到strToken中ch=line[i_line++];
}
i_line--;
strToken[i_str]='\0';
code=Reserve(strToken); //查关键字表;
if (!code) //未查到,是一个标识符
{
value=InsertID(strToken); //将strToken中的单词插入到符号表中
token->code=1;
token->value=value;
}
else
{
token->code=code;
token->value=-1;
}
else if (IsDigit(ch)) // 处理常数;
{
while (IsDigit(ch)) //拼常数
{ strToken[i_str++]=ch; //将ch中的字符拼接到strToken中ch=line[i_line++];
}
i_line--;
strToken[i_str]='\0';
value=InsertConst(strToken); //将strToken中的单词插入到常数表中
token->code=2;
token->value=value;
}
else // 处理界符或错误处理;
{
strToken[i_str++]=ch; //将ch中的字符拼接到strToken中;
if (ch==':') // 处理双界符":=";
{
ch=line[i_line++];
if (ch=='=')
strToken[i_str++]=ch;
else
i_line--; //回溯一个字符
}
strToken[i_str]='\0';
code=Reserve(strToken); //查界符表
if (!code) //未查到
ProcError(); //错误处理
else //生成并输出一个界符Token;
{
token->code=code;
token->value=-1;
}
}
};
//主程序
#include "string.h"
#include "scaner.h"
int main(int argc, char* argv[])
{
SemRecord ID[10]; //符号表
int m=0;
int Cons[10]; //常数表int n=0;
int i;
TokenType Token;
Scan sExam(ID,&m,Cons,&n);
do
{
sExam.Read(&Token);
printf("(%d,%d)",Token.code,Token.value);
}while (Token.code);
printf("\n");
for (i=0;i<m;i++)
printf("%s ",ID[i].name);
printf("\n");
for (i=0;i<n;i++)
printf("%d ",Cons[i]);
printf("\n");
printf("Hello World!\n");
return 0;
}
实验三、Pascal 常数处理机类的设计
一、实验目的:熟悉并实现一个Pascal 常数处理机
二、实验内容:
1.设计自动机;
2.设计处理Pascal 常数的算法;
3.编写代码并上机调试运行通过。
·要求:输入——一个Pascal 常数字符串;
输出——相应Pascal 常数;
三、常数处理机设计:
1. 自动机: e
d d d
+|- -1
+① d ② . ③ d ④ e ⑤ ⑥ d ⑦ ⑧ - d
-1
-1
·自动机的转换矩阵:
d ·
e +|- -1 开始
1 2 0 0 0 0 +
2 2
3 5 0 8 s:=1
3 4 0 0 0 0
4 4 0
5 0 8 5 7 0 0
6 0
6 7 0 0 0 0
7 7 0 0 0 8 y
8 0 0 0 0 0 - n
0---err
getchar(ch)
n y err
输出NUM
结束
2. 翻译器:
·主控程序:
q s s=8? 查状态表s:=δ(s,ch)
s=0?
·语义动作q i:
q1:初始化,N:=P:=m:=t:=0;e:=1;NUM:=0;
q2:N:=10*N+(d);
q3:t:=1; 将字符d转换为数字d
q4:N:=10*N+(d);m:=m+1;
q5:t:=1;
q6:if …-… then e:=-1;
q7:P:=10*P+(d);
q8:NUM:=N*10e*P-m;STOP。
四、程序实现:
1.数据结构:
int aut[ ][ ]={ {2, 0, 0, 0, 0}, {2, 3, 5, 0, 8}, {4, 0, 0, 0, 0}, { 4, 0, 5, 0, 8},
{7, 0, 0, 6, 0}, {7, 0, 0, 0, 0}, {7, 0, 0, 0, 8}, {0, 0, 0, 0, 0}};//状态转换矩阵int s; //当前状态
int n,p,m,e,t; //尾数值,指数值,小数位数,指数符号,类型float num; //常数
char strNum[]; //常数字符串
char ch; //当前符号
2.算法设计:
step1 初始化;
scanf(strNum); //读入常数字符串
s=1;
act(1,ch); //执行q1
while (s<>8 && s<>0)
{
step2 GetChar(ch); //读取当前符号到ch中
step3 s=find(s,ch); //查状态表
step4 act(s,ch); //执行q s
}
if (s==8)
printf(num); //输出num
else
ProcError(); //错误处理
※算法求精:
·step3:
int find(int s, char ch) //s---当前状态;ch---当前符号
{ int i,j; //行和列
i=s-1; //将s映射到行标记i
3.1 j=map(ch); //将ch映射到列标记j
return auto(i,j); //返回下一个状态值
}
·step4:
void act(int s, char ch)
{ switch (s)
{
case 1: n=0; m=0; p=0; t=0; e=1; num=0; break;
case 2: n=10*n+ch-48; break;
case 3: t=1; break;
case 4: n=10*n+ch-48; m++; break;
case 5: t=1; break;
case 6: if (ch=='-') e=-1; break;
case 7: p=10*p+ch-48; break;
case 8: num=n*pow(10,e*p-m);
}
}
·step3.1:
int map(char ch)
{ int j;
switch ch
{ case …0‟~‟9‟: j=0; break;
case ….‟: j=1; break;
case …E‟, …e‟: j=2; break;
case …+‟, …-‟: j=3; break;
default: j=4;
}
return j;
}
五、源程序代码:
#include "math.h"
#include <iostream.h>
int Pas_aut[8][5]={2,0,0,0,0,
2,3,5,0,8,
4,0,0,0,0,
4,0,5,0,8,
7,0,0,6,0,
7,0,0,0,0,
7,0,0,0,8,
0,0,0,0,0}; //状态转换矩阵
char line[30]; //常数字符串
int i_line=0; //指针
class PascalCons
{
private:
int aut[8][5]; //状态转换矩阵
int s; //当前状态
int n,p,m,e,t; //尾数值,指数值,小数位数,指数符号,类型
double num; //常数
char ch; //当前符号
public:
PascalCons();
double number(int *i); //
private:
void ProcError();
int map(char ch); //当前符号到自动机矩阵的列标记的映射
int find(int s, char ch); //查矩阵
void act(int s, char ch); //结点处的语义动作
};
PascalCons::PascalCons()
{
int i,j;
for (i=0;i<8;i++) //自动机转换矩阵初始化
for (j=0;j<5;j++)
aut[i][j]=Pas_aut[i][j];
ch=' ';
};
void PascalCons::ProcError()
{
cout<<"err!"<<endl;
};
int PascalCons::map(char ch)
{ int j;
if (ch>='0' && ch<='9')
j=0;
else if (ch=='.')
j=1;
else if (ch=='E'||ch=='e')
j=2;
else if (ch=='+'||ch=='-')
j=3;
else
j=4;
return j;
}
int PascalCons::find(int s, char ch) //s---当前状态;ch---当前符号{ int i,j; //行和列
i=s-1; //将s映射到行标记i
j=map(ch); //将ch映射到列标记j
return aut[i][j]; //返回下一个状态值
}
void PascalCons::act(int s, char ch)
{
switch (s)
{
case 1: n=0; m=0; p=0; t=0; e=1; num=0; break;
case 2: n=10*n+ch-48; break;
case 3: t=1; break;
case 4: n=10*n+ch-48; m++; break;
case 5: t=1; break;
case 6: if (ch=='-') e=-1; break;
case 7: p=10*p+ch-48; break;
case 8: num=n*pow(10,e*p-m);
}
}
double PascalCons::number(int *p)
{
s=1;
act(s,ch); //执行q1
while (s!=8)
{
ch=line[*p]; //读取当前符号到ch中
(*p)++;
s=find(s,ch); //查状态表
if (s==0)
break;
act(s,ch); //执行qs
}
if (s==8)
return num; //输出num
else
{
ProcError();
return 0; //错误处理
}
};
int main(int argc, char* argv[])
{
PascalCons a;
cout<<"please input a number:";
cin>>line; //读入常数字符串
cout<<a.number(&i_line)<<endl;
printf("Hello World!\n");
return 0;
}
实验四、扫描器设计
一、实验目的:
熟悉并实现一个扫描器
二、实验内容:
1.设计扫描器的自动机
2.设计翻译、生成Token 的算法
3.实现代码并调试运行
·要求:输入——源程序文件;
输出—— 相应的Token 序列;
关键字表和界符表;
符号表和常数表;
三、扫描器设计:
单词
Token 1. 自动机 e
d d d
+|- -1
+① d ② . ③ d ④ e ⑤ ⑥ d ⑦ d -
-1
l/d -1
-1
l ⑧ -
-1
b ⑨ b ⑩ -
-1
-
-1
… l ‟ -
-1
-1
“ ” -
-1
{ } -1
-
-1 -
其中:d 为数字,l 为字母,b 为界符。
19 18 20 21 13 扫描器 11 12 14 15 16 17 25 22 23 24
2.关键字表和界符表:
program ;
begin :
end (
var )
while ,
do :=
repeat +
until -
for *
to /
if >
then >=
else ==
<
<=
四、程序实现
1.数据结构设计
2.算法设计与算法求精
五、源程序清单(要求加注释)
第二部分语法分析
实验五、表达式语法分析设计(1)
一、实验目的:熟悉递归下降语法分析器设计
二、实验内容:
1.设计递归下降语法分析器算法;
2.编写代码并上机调试运行通过。
·要求:输入——表达式;
输出——表达式语法是否正确。
三、语法分析器设计:
1.算术表达式文法
G(E): E → E ω0 T | T
T → T ω1 F | F
F → i | (E)
2.文法变换:G‟(E) E →T {ω0 T}
T →F {ω1 F}
F → i | (E)
3. 递归下降子程序框图:
E: 入口T:入口
T F
n ω0? n ω1?
y y 出口出口
read(w) read(w)
T F
F: 入口主程序:Z →E
开始
( ? n i ? n err
read(w) read(w)
E
E
err n # ?
err n ) ? y y
read(w)
结束
出口
四、源程序清单:
void E();
void T();
void F();
char exp[50]; //算术表达式区
int i=0;
char w; //当前单词
int main(int argc, char* argv[])
{
printf("please input your expression:");
scanf("%s",exp); //输入表达式
w=exp[i++]; //read(w)
E();
if (w=='#')
printf("OK!");
else
printf("err");
return 0;
}
void E()
{
T();
while ( w=='+' || w=='-')
{
w=exp[i++]; //read(w)
T();
}
}
void T()
{
F();
while ( w=='*' || w=='/')
{
w=exp[i++]; //read(w)
F();
}
}
void F()
{
if ( w=='(')
{
w=exp[i++]; //read(w)
E();
if ( w!=')')
{
printf("err");
}
else
w=exp[i++]; //read(w) }
else if ((w>='a' && w<='p')||(w>='0' && w<='9'))
{
w=exp[i++]; //read(w) }
else
{
printf("err");
}
}
实验六、表达式语法分析设计(2)
一、实验目的:熟悉LL(1)语法分析器设计
二、实验内容:
1.设计LL(1)语法分析器算法;
2.编写代码并上机调试运行通过。
·要求:输入——表达式;
输出——表达式语法是否正确。
三、语法分析器设计:
1.算术表达式文法
G(E): E → E ω0 T | T
T → T ω1 F | F
F → i | (E)
2.文法变换:G‟(E) :E→Te
e→+Te|ε
T→Ft
t→*Ft|ε
F → i | (E)
3.LL(1)分析表
i + * ( ) #
E eT, P 100eT, P 1 0 0
e 0eT, N 200ε, P 3ε, P 3
T tF, P 400tF, P 400
t 0ε, P 3tF, N 50ε, P 3ε, P 3
F ε, N 600)E, N 7 0 0
) 0000ε, N 6 0
# 00000OK 8
二、程序实现
·数据结构:
char syn[15]; //语法栈
int top; //栈顶指针
char w; //当前单词
char exp[15]; //表达式区
int i =0; //表达式指针
int ll1[7][6] //LL(1)分析表
int i,j; //表行和列
int code; //表项
·算法设计:
初始化 #’入栈,开始符号E 入栈
N
P read(w)
y n y err
三、源程序清单
int main(int argc, char* argv[])
{
char syn[15]; //语法栈
int top; //栈顶指针
char w; //当前单词
char exp[15]; //表达式区
int i =0; //表达式指针
int ll1[7][6]={1,0,0,1,0,0, //LL(1)分析表
0,2,0,0,3,3,
4,0,0,4,0,0,
0,3,5,0,3,3,
6,0,0,7,0,0,
0,0,0,0,6,0,
0,0,0,0,0,8};
int i,j; //表行和列
int code; //表项
struct
{
char stack_act[3];
char input_act;
}action[8]={{"eT",'P'},{"eT",'N'},{"",'P'},{"tF",'P'},{"tF",'N'},{"",'N'},{"}E",'N'},{"OK",' '}};
//输入表达式
printf("please input your expression:");
scanf("%s",exp);
//初始化
开始 结束 (SYN(top),w)查分析表 空? OK? 执行栈操作 N/P?
syn[0]='#';
syn[1]='E';
top=1;
//读第一个单词
w=exp[i ++]; //read(w) while (1)
{
//查分析表code=ll1(i,j)
switch (syn[top])
{
case 'E':i=0;break;
case 'e':i=1;break;
case 'T':i=2;break;
case 't':i=3;break;
case 'F':i=4;break;
case ')':i=5;break;
case '#':i=6;
} //行
if ((w>='a' && w<='p')||(w>='0' && w<='9'))
j=0;
else if ((w=='+')||(w=='-'))
j=1;
else if ((w=='*')||(w=='/'))
j=2;
else if (w=='(')
j=3;
else if (w==')')
j=4;
else if (w='#')
j=5; //列code=ll1[i][j];
//空或OK
if (!code||!strcmp(action[code-1].stack_act,"OK"))
break;
//栈操作
syn[top]='\0';
strcat(syn,action[code-1].stack_act);
top=strlen(syn)-1;
//输入操作
if (action[code-1].input_act=='N')
w=exp[i ++];
}
if (code)
printf("OK!");
else
printf("err!");
return 0;
}
实验七、表达式语法分析设计(3)
一、实验目的:熟悉算符优先语法分析器设计
二、实验内容:
1.设计算符优先语法分析器算法;
2.编写代码并上机调试运行通过。
·要求:输入——表达式;
输出——表达式语法是否正确。
三、语法分析器设计
1.算术表达式文法
G(E): E → E ω0 T | T
T → T ω1 F | F
F → i | (E)
2.算符优先分析表
+ * i ( ) # + > 3< 1< 1< 1 > 3 >3
* > 3> 3< 1< 1> 3> 3
i > 3> 30 0> 3> 3
( < 1< 1 < 1< 1= 20
) > 3> 300 > 3 >3
# < 1< 1< 1< 10 OK 4
四、程序实现
·数据结构:
char syn[15]; //语法栈
int top; //栈顶指针
int top_in; //移进指针
int handle[10]; //<栈,存放<的位置
int top_h; //<栈顶指针
char w; //当前单词
char exp[15]; //表达式区
int i_exp =0; //表达式指针
int prio[6][6]; //优先分析表
int i,j; //表行和列
int code; //表项
char rules[N][10]; //产生式
·算法设计:
PUSH(#)
read(w) ——读单词
——移进
归约—— y err n
n y
n y
例:i*(i+i)#
(1) syn handle # 0
top top_in top_h w=i , #<i
(2) 移进,读
syn handle # i 0 1
top top_in top_h w=*, i>*
(3) 归约(F i),不读
syn handle # 0
top top_in top_h w=*, #<*
开始 结束 查表(SYN(top),w) 空? (#S#)? ·> ? REDUCE(SYN) PUSH(w)
(4) 移进,读
syn handle
# * 0 1
top top_in top_h
w=(,*<(
(5) 移进,读
syn handle
# * ( 0 1 3
top top_in top_h
w=i,(<i
(6) 移进,读
syn handle
# * ( i 0 1 3 4
top top_in top_h
w=+,i>+
(7) 归约(F i),不读
syn handle
# * ( 0 1 3
top top_in top_h
w=+,(<+
(8) 移进,读
syn handle
# * ( + 0 1 3 4
top top_in top_h w=i,+<i
(9) 移进,读
syn handle
# * ( + i 0 1 3 4 6
top top_in top_h w=),i>)
(10) 归约(F→i),不读
syn handle
# * ( + 0 1 3 4
top top_in top_h w=),+>)
(11) 归约(E→E+T),不读
syn handle
# * ( 0 1 3
top top_in top_h
w=),(=)
(12) 移进,读
syn handle
# * ( ) 0 1 3
top top_in top_h
w=#,)>#
(13) 归约(F→(E)),不读
syn handle
# * 0 1
top top_in top_h
w=#,*<#
(14) 归约(T→T*F),不读
syn handle
# 0
top top_in top_h
w=#,OK
·算法求精:
查表(SYN(top),w):
i=map(SYN[top]); //定位行和列
j=map(w);
code=prio[i][j]; //查表
PUSH(w):syn[top_in]=w; //进栈
if (code==1) //记录句柄的左端位置
handle[++top_h]=top+1;
top=top_in++;
REDUCE(SYN):
syn[top_in]=‟\0‟;
i=0;
while (strcmp(rules[i],syn+handle[top_h]) && i<N) //比较产生式
i++;
if (i==N)
printf(“err!”);
else
{
syn[handle[top_h]]=‟ …;//归约
top= handle[top_h]-1;
top_in= handle[top_h]+1;
top_h--;
}
五、源程序清单
#include "string.h"
#define N 4
int map(char ch) //
{
char loca[7]={'+','*','i','(',')','#'};
char *p;
p=strchr(loca,ch);
if (p==NULL)
return -1;
else
return p-loca;
}
int main(int argc, char* argv[])
{
char syn[15]; //语法栈
int top; //栈顶指针
int top_in; //移进指针
int handle[10]; //<栈
int top_h; //<栈顶指针
char w; //当前单词
char exp[15]; //表达式区
int i_exp=0; //表达式指针
int prio[6][6]={3,1,1,1,3,3,
3,3,1,1,3,3,
3,3,0,0,3,3,
1,1,1,1,2,0,
3,3,0,0,3,3,
1,1,1,1,0,4}; //优先分析表
int i,j; //表行和列
int code; //表项
char rules[N][10]={" + "," * ","i","( )"}; //产生式
printf("please input your expression:");
scanf("%s",exp); //输入表达式
syn[0]='#'; //初始化
top=0; top_in=1;
handle[0]=0;
top_h=0;
w=exp[i_exp++]; //read(w)
while (1)
{
//查分析表code=prio(i,j)
i=map(syn[top]); //定位行和列
j=map(w);
code=prio[i][j]; //查表
//空或OK
if (code==0 || code==4)
break;
//栈操作and 输入操作
if (code<3) //< or =,push(w)
{
syn[top_in]=w; //进栈
if (code==1) //记录句柄的左端位置
handle[++top_h]=top+1;
top=top_in++;
w=exp[i_exp++];
}
else //>,REDUCE(SYN)
{
syn[top_in]='\0';
i=0;
while (strcmp(rules[i],syn+handle[top_h]) && i<N) //比较产生式
i++;
if (i==N)
{
code=0;
printf("err!");
break;
}
else
{
syn[handle[top_h]]=' '; //归约
top= handle[top_h]-1;
top_in= handle[top_h]+1;
top_h--;
}
}
}
if (code)
printf("OK!");
else
printf("err!");
return 0;
}
实验八、表达式语法分析设计(4)
一、实验目的:熟悉LR语法分析器设计
二、实验内容:
1.设计SLR(1)语法分析器算法;
2.编写代码并上机调试运行通过。
·要求:输入——表达式;
输出——表达式语法是否正确。
三、语法分析器设计
1.算术表达式文法
G(E):E‟→E [0]
E→E+T [1]
E→T [2]
T→T*F [3]
T→F [4]
F→(E) [5]
F→i [6]
2.SLR(1)分析表:
状ACTION GOTO
态i + * ( ) # E T F
0 s 5 0 0 s 4 0 0 1 2 3
1 s 6 A 0
2 r 2 s 7 r 2 r 2
3 r
4 r 4 r 4 r 4
4 s
5 s 4 8 2 3
5 r
6 r 6 r 6 r 6
6 s 5 s 4 9 3
7 s 5 s 4 10
8 s 6 s 11
9 r 1 s 7 r 1 r 1
10 r 3 r 3 r 3 r 3
11 r 5 r 5 r 5 r 5
四、程序实现
·数据结构:
char syn[15]; //语法栈
int sta[15]; //状态栈
int top; //栈顶指针
char w; //当前单词
char exp[15]; //表达式区
int i_exp=0; //表达式指针
struct ActType //Action 表项类型 { char act; //s or r int rule_num; //i or j }; int Goto[12][3]; //Goto 分析表 ActType Action[12][6]; //Action 分析表 int i,j; //表行和列 ActType code_act; //Action 表项 int code_goto; //Goto 表项
struct { char Vn; int len;
} rules[N]; //产生式左部Vn ,右部长度len
·算法设计:
PUSH(SYN,#)
PUSH(STA,0)
read(w) ——读单词
——移进
归约—— y err n y n
r i s i
·算法求精:
查Action 表(STA(top),w):
i=sta[top]; //定位行和列 j=map(w);
开始 结束 ACTION(STA(top),w) 空? acc? s i /r i REDUCE PUSH(SYN,w) PUSH(STA,i)
code_act.act=Action[i][j].act; //查表
code_act.rule_num=Action[i][j].rule_num;
PUSH(SYN,w):
syn[++top]=w;
PUSH(STA,i):
sta[top]=code_act.rule_num;
REDUCE: A β
for (j=0;j<rules[code_act.rule_num].len;j++) //弹栈|β|次
top--;
//查Goto表(STA[top], A)
i=sta[top]; //
j=map(rules[code_act.rule_num].Vn);
code_goto=Goto[i][j];
syn[++top]=rules[code_act.rule_num].Vn; //PUSH(SYN,A)
sta[top]=code_goto; //PUSH(STA, Goto(STA[top], A))
五、源程序清单
#include "string.h"
#define N 7
struct ActType //表项
{
char act; //s or r
int rule_num; //i or j
};
char item_act[12][6]={'s',' ',' ','s',' ',' ',
' ','s',' ',' ',' ','A',
' ','r','s',' ','r','r',
' ','r','r',' ','r','r',
's',' ',' ','s',' ',' ',
' ','r','r',' ','r','r',
's',' ',' ','s',' ',' ',
's',' ',' ','s',' ',' ',
' ','s',' ',' ','s',' ',
' ','r','s',' ','r','r',
' ','r','r',' ','r','r',
' ','r','r',' ','r','r'};
int item_num[12][6]={5,0,0,4,0,0,0,6,0,0,0,0,
0,2,7,0,2,2,0,4,4,0,4,4,
5,0,0,4,0,0,0,6,6,0,6,6,。