编译原理课程设计算符优先分析法研究附源程序
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1 课程设计的目的和要求 (1)
1.1 课程设计的目的 (1)
1.2 课程设计的要求 (1)
2 系统描述 (1)
2.1 自底向上分析方法的描述: (1)
22算符优先文法的描述: (1)
3)输入符号串,进行移进 -规约分析。
(2)
3 概要设计 (2)
3.1 设计思路 (2)
3.2 系统功能结构 (3)
3.3 技术路线或实现方法 (4)
3.4 开发环境 (4)
4详细设计 (4)
4.1 模块划分 (4)
4.2 主要算法的流程图 (6)
4.3 数据分析与定义 (7)
4.4 系统界面设计 (7)
5测试方法和测试结果 (8)
5.1 测试用例1 (8)
5.2 测试用例2 (9)
5.3 测试用例3 (10)
5.4 测试用例4 (11)
6 结论和展望 (12)
结论 (12)
展望 (12)
学习编译技术课程的体会和对本门课程的评价 (12)
7 参考文献 (12)
8 源代码 (13)
1课程设计的目的和要求
1.1 课程设计的目的
本次设计的时间为1周,目的是通过使用高级语言实现部分算法加强对编译技术和理论的理解。
设计的题目要求具有一定的规模,应涵盖本课程内容和实际应用相关的主要技术。
1.2 课程设计的要求
1、文法使用产生式来定义;
2、用大写字母和小写字母分别表示非终结符和终结符;产生式使用->;
3、文法中的空字符串统一使用@表示;
4、分别给出每一个非终结符的FIRSTVT集和LASTVT集;
5、画出算符优先关系表
6判定给定的文法是否是算符优先文法;
7、给定符号串判定是否是文法中的句子,分析过程用分析表格的方式打印出来。
2系统描述
本次实验使用windows vista操作系统下visual C++6.0平台,使用C语言,利用读文件方式将待分析的文法读入到程序中,通过定义数组和结构体作为具有一定意义或关系的表或栈,存放FIRSTVT、LASTVT、算符优先关系表的元素。
系统能够对由文件读入的文法进行分析,构造出FIRSTVT表和LASTVT 表以及算符优先关系表。
可以根据构造的优先关系表对输入的任意符号串进行分析,判断是否为本文法的句子,若是则打印规约过程。
结果显示到DOS 界面上。
2.1自底向上分析方法的描述:
对输入的符号串自左向右进行扫描,并将输入符逐个移入栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄时(该句柄对应某个产生式的右部),就用该产生式的左部非终结符代替相应右部的文法符号串,这一过程称为规约。
重复这一过程,直到栈中只剩下文法的开始符则分析成功。
2.2 算符优先文法的描述:
只规定算符之间的优先关系,也就是说只考虑终结符之间的优先关系。
由于算富有先分析不考虑非终结符之间的优先关系,在规约过程中只要找到
最左素短语就可以规约
如给定一个文法G[S]:
S->#E#
E->E+T
E->T
T->T*F
T->F
F->P/F
F->P
P->(E)
P->i
利用算符优先文法分析过程处理如下:
1) 计算给定文法中任意两个终结符对(a,b )之间的优先关系,首先计算两个集合
FIRSTVT(B)={b|B- >b-或B->Cb…}
LASTVT(B)={a|B- >-a 或B->…aC}
2)计算三种优先关系,求出算符优先关系表:
3)输入符号串,进行移进-规约分析
3概要设计
3.1 设计思路
1)首先在源程序相同的目录下创建一个txt文档,并在文档中输入待分
析的文法。
然后定义一个输入流文件,调用这个流文件中的open函数打开该
txt文件,再定义一个二维数组通过循环接收读入的产生式。
2)接着开始构造FIRSTVT、LASTVT、算符优先关系表。
在构造表的时候首先定义了两个重要的结构体。
一个结构体作为存放具有一定关系的一对非终结符和终结符,
另一个结构体作为存放上述元素的栈,包括栈顶指针、
栈底指针、栈的长度。
既然定义了栈,就存在对栈的初始化、栈是否为空的判断、入栈、出栈操作,利用循环和指针的操作来定义这些函数,以完成元素的进栈和弹出。
定义了这两个结构体,就可以用来构造FIRSTVT、LASTVT、算符优先关系表。
在构造FIRSTVT表时,通过循环找出每条产生式中的非终结符的FIRSTVT集,并把该非
终结符和终结符压栈,设置标志位,标志一对非终结符和终结符具有对应关系。
LASTVT 表的构造则是将求FIRSTVT的过程翻转过来,可以仅仅将函数中的参数稍作修改就能
够完成。
3)构造算符优先关系表。
算符优先关系表是一个二维数组,用来存放任意两个终结符之间的优先关系。
首先构造表头,通过扫描所有产生式将终结符不重复的存放在一个一维数组中并置为优先关系表的行和列,并将优先关系表其他内容全部初始化为空。
接着遍历所有产生式,找出任意两个终结符
之间存在的关系(可以没有关系),并判断任意两终结符是否至多存在一种优先关系,如发现优先关系表不为空,则证明该文法不是算符优先文法,否则,将相应的关系填入到相应的行列对应的单元中。
4)输入串分析过程的设计。
首先将大于、小于、等于和无关系分别定义成一种类型的数据表示,通过查询符号栈栈顶以及当前符号之间的优先关系来判断移进和规约的操作。
3.2 系统功能结构
图3-1系统功能结构图
函数功能:
Ma in()函数:调用主函数,运行程序;
FirstVt()函数:构造FIRSTVT 表;
LastVt()函数:构造LASTVT 表;
OpPrioTable()函数:构造算符优先关系表;
In putA nalyse()函数:分析输入串是否为文法中的句子,并给出规约过程。
3.3 技术路线或实现方法
算符优先分析法的具体过程如下:
1、首先先输入文件的路径,在read sen[][col])函数中,将需要分析的文法通过输入流文件打开函数open()复制到sen[row][col]中。
2、然后利用FirstVt()构造产生式sen[row][col]的FirstVt表。
先找出形如A->…&••( a为第一个终结符)的产生式,把(A,a)压入Operator栈中。
从Operator
栈顶抛出项(A,a),填入first表相应位置。
在找出形如B->A…的产生式,把(B,a)压入Operator栈中。
循环直到Operator栈为空,此时FirstVt 表构造完毕。
3、然后把产生式右部翻转,调用FirstVt函数求出LastVt表。
4、接着调用OpPriotable ()构造算符优先关系表opTable。
先把产生式中所有终结符填入opTable表第一行和第一列,然后利用产生式和FirstVt表LastVt表分别找出满足=关系、<关系、>关系的算符填表。
若相应位已有关系,说明两个终结符之间至少有两种优先关系,该文法不是算符优先文法。
5、最后调用InputAnalyse ()对输入串进行分析。
初始化分析栈S,依次判断当前输入符a和分析栈中离栈顶最近的终结符S[ j ]的关系,若S[ j ]< a,则a移近,若S[ j ]> a,则往前找到第一个S[ j ]>a,将S[ j -1]到栈顶S[ k ] 规约,若S[ j ]= a,如果S[ j ]=#,贝U接受,如果S[ j ]! =#,贝U移进。
直到接受或者出错,算符优先分析结束。
3.4 开发环境
实验使用windows vista操作系统下的Microsoft Visual C++ 6.0平台,用C 语言完成。
4详细设计
4.1 模块划分
实验分为五个模块,分别是:
1、文件的导入:
read[][]);
2、FirstVt、LastVt 集的构造:
FirstVt (sen[][],first[][],sen」en,frist_len );
LastVt (sen[][],last叩,sen」en,frist_len);
3、算符优先关系表OpPriotable的构造:
OpPriotable (sen,first,last,opTable,sen_len,first_len,&opTable_len;
4、算符优先分析过程的实现:
InputAnalyse( opTable[][col],string[col],opTable_len);
5、主函数:main()。
4.2 主要算法的流程图
图4-1算符优先分析法程序流程图
4.3 数据分析与定义
1、文法(产生式):文法使用产生式来定义
char sen[row][col]:用二维数组表示产生式;
int sen_le n:产生式的个数;
2、FIRSTVT 集:
char first[row][col]:用二维数组构造FIRSTVT 表
int first_len: Firstvt 表的行数;
3、LASTVT 集:
char last[row][col]:用二维数组构造LASTVT 表;
int frist_len : LASTVT 表的行数;
4、算符优先关系表:
char opTable[row][col]:用二维数组表示算符优先关系表;
in t opTable_le n:算符优先关系表的行数和列数;
5、算符优先分析表
char string[col]:用一维数组记录输入串;
char S[SIZE]:用一维数组表示分析栈;
char a当前输入字符;
6、其他数据结构:
typedef struct
{
char non term; // 非终结符
char term; //终结符
}StackEleme nt;
FIRSTVT表或LASTVT表中一个表项(A,a);
7、typedef struct
{
StackEleme nt *top;
StackEleme nt *bottom;
int stacksize;
}stack;
以形如表项(A,a)为元素的栈,在构造FirstVt集的过程中用到;
4.4 系统界面设计
本实验没有考虑界面设计,将结果直接打印输出在DOS界面下
5测试方法和测试结果
5.1 测试用例1
测试目的:使用算符优先分析法对一个算符文法中的句子进行分析。
读入一个算符优先文法进行分析,给出文件路径
成品算符优先文法1.txt。
结果如下:
图5-1测试用例1运行截图1
输入一个字符串,使得该字符串是该文法的一个句子。
则输入字符串i+i*i/(i+i)#时运行结果为:
图5-2测试用例1运行截图2
5.2 测试用例2
测试目的:使用算符优先分析法,分析一个字符串不是该文法的句子, 并 输出出错信息。
输入一个字符串,使得该字符串不是文法的句子
弁析用昙如下:
当前宇符 I
卜H
nn+i 4IN+N »H+N* 4IH+N*1 □W+hl»H 丿 »N+N*Nz(i Z1N+N^(N »N+hl*Nz (N+ HN+NM^ztN+i tiN+NkNzcN+H
«N+N M N/^N
«N+N*N/(N) ^N+N*N/N
9N+NH1
UN+M kfN
杲否缱努? W"
雨||條持号生
*l*i/<1*1>#
i*iz<l+l># *iZ<l+l>U iz<l+l>#
iz<l+l>U zci+i>#
<1+1># i+i>tt +i>#
i>n
1>4 >«
A
优先¥系
约送的1#菁事药翅送送纯进逵拥援逶釣約釣菊受 规構归
稔移JauuJauu 毬 EJ 糕u+I-(I^JTL!TUTUII 按 或味II 乜?*!;弟T 钊彳彳< .
It * * / < > i »-<<<< <
图5-3测试用例2运行截图
5.3 测试用例3
测试目的:读入一个文法,判断该文法不是算符优先文法。
读入一个非算符优先文法进行分析,给出文件路径: 成品 非算符优先文法1.txt 。
运行结果如下:
临先关系義,
分杭堆如下・
栈
当前字特
i
锁徐符号串
^i+i»<l+i*Z4i
鼻i
/ i+i«<d+i*zii 沖丿
i +i»<i+i*ztt
TNzi 4 UNzH + 4th + 4th* i
4lh*i Ci+i*ztt
4lh*H
4lh-«N- <
41N->N*<
1 *L»z«
* L*ztt 41N*N*<H
* L-/U 1 *zlt
w
ZU
4tN*N*<N^N
z# 當
It
fl 4tf0N”<H 咖呻 tl #N*M**<N 屮 tt
itN*N»*<N
#
出错
优先关系
C
约进约遥邊约约进进约进进11
约进诜約鞘约 应糅归糅糅归归禅紡归耀緯移归移器归移稱町7
归 或 4
/ 4 * < * <
淪人要分析的串”仪**结真=
i/i +"i*< i.-*i.*>tt
□
5.4 测试用例4
测试目的:输入一个非算符文法,判断该文法不是算符文法
读入一个非算符文法,给出路径: 文法.txt。
运行结果如下:
成品非算符国| 'D\cou CU r:e_fik\^cn\D篥答氐先卫xg" 谙输人變
裱文件的地址4片小表元「
Il T X\CClLl*CL' C©HUM" F 左 1. W成启身符交注-* Mt
E-^El
E-M
I->T*F
1->F
F-JP IF
F->P
F-JCH
F->1
输人的不旱算荷立注? 是否雄绩?an
图5-5测试用例4运行截图
6结论和展望
结论
本次实验我们基本完成了实验题目的要求。
求出了一个文法中每一个非终结符的FIRSTVT集和LASTVT集,画出算符优先关系表,并判定出给定的文法是否是算符优先文法。
当给定一个符号串时,能够判定是否是文法中的句子,并能够将分析过程打印出来。
通过算符优先分析方法,可以对任意一个文法进行自底向上的分析。
同时,算符优先分析法也存在不足之处,由于忽略文法中的非终结符,会将本不属于文法的句子正确规约,从而引起错误。
本次实验完成了题目的要求,准确把握了将文法通过读文件形式、手动输入分析字符串的题目要求,并在满足要求的基础上进行了创新,添加了对算符文法的判断功能。
实验中,小组成员的分工与合作充分体现了软件工程的思想。
需求分析理解准确,小组成员任务明确,统一函数头、参数,各自完成分内工作,整合工作快速、高效。
同时,实验中还存在很多不足。
调试程序中的错误占用了大部分时间,以至于没有考虑使用界面展示结果。
虽然使用C++开发工具,但实质上还是
在使用C编代码。
在今后的实践中还需注意。
学习编译技术课程的体会和对本门课程的评价
在上编译课的时候,小组成员都觉得自己对算符优先文法已经掌握了,但等真正用程序去实现时,才发现有很多细节我当时没有注意到。
也正以为没有注意到这些细节,导致成员们编程时会出现各种错误,大部分都是在循环时下标或者循环次数的控制上出错。
在进行到一定阶段后发现犯的低级错误越来越少了,对算符优先文法也愈发的吃透了,编程水平也有了进一步的
编译原理如果要深入研究,那会是一门非常深奥的学问,对于现阶段只有60多学时的本科生来说,只能涉及到它一些最基本的原理,和最宏观的方法,要想微观的去研究,还需要在以后的时间里去钻研。
在这次实验中,小组成员也只是用高级语言模拟了编译的一个小方法,在完成实验的过程中,小组成员对编译原理有了进一步的了解,更提高了动手编程能力,是一次经验和知识的积累。
7参考文献
[1]张素琴.编译原理(第2版)[M].北京:清华大学出版社,2005.6, 102-121.
[2]陈维兴.C++面向对象程序设计教程[M].北京:清华大学出版社,2004.8, 258-272.
8源代码
#in clude<iostream.h> #in clude<stdlib.h> #in clude <fstream.h> #defi ne row 50 #defi ne col 50 #defi ne SIZE 50 〃两个重要结构体的定义
//FIRSTVT 表或LASTVT 表中一个表项(A,a )结构体的初始化 typedef struct {
char non term; // 非终结符 char term; // 终结符
}StackEleme nt;
〃存放(A,a)的栈的初始化 typedef struct {
StackEleme nt *top; StackEleme nt *bottom; int stacksize; }stack;
〃初始化(A,a )栈 void In itStack(stack &S) {
S.bottom = new StackEleme nt[SIZE]; if(!S.bottom)
cout<<"存储空间分配失败! "<<e ndl; S.top = S.bottom; S.stacksize = SIZE; }
〃判断(A,a)栈是否为空 bool ifEmpty(stack S)
〃插入栈顶(A,a)元素
void In sert(stack &S,StackEleme nt e) {
{
if(S.top==S.bottom) return true; else return false; //如果栈为空,则返回true //否则不为空,返回false
if(S.top-S.bottom>=S.stacksize)
cout<<"栈已满,无法插入!"<<e ndl;
else
{
S.top->non term=e .non term;
S.top->term=e.term;
S.top++;
}
}
〃弹出栈顶(A,a)元素
StackEleme nt Pop(stack &S)
{
StackEleme nt e;
e.non term = '\0:
e.term = '\0';
if(S.top==S.bottom)
{
cout<<"栈为空,无法进行删除操作!"<<e ndl;
return e;
}
else
{
S.top--;
e.non term=S.top->non term;
e.term=S.top->term;
return e;
}
}
〃终结符与非终结符的判断函数(布尔类型)
bool Termin alJud(char c)
{
if(c>='A'&&c<='Z') return false; // 非终结符返回
false else return true; //终结符返回 true
}
〃判断非终结符在first表中是否已存在
bool ItemJud(char first[][col],i nt frist_le n, char C) for(i nt i=O;i<frist_le n;i++)
{
if(firs t[i][O]==C)
return true; 〃如果first表中已存在此非终结符,则返回 true
}
return false;
}
〃读文件函数
int read sen [][col])
{
char addr[50];
cout<<"请输入要读文件的地址( 用表示):"<<endl;
cin> >addr;
ifstream fin;
fin. ope n( addr,ios::i n);
if(!fi n)
{
cout<<"Ca nnot ope n file!\n"<<en dl;
}
for(i nt i=0;!fi n. eof();i++)
{
fin> >se n[i];
cout<<se n[i]<<e ndl;
}
return i;
}
//FIRSTVT表和LASTVT表中表项(非终结符)的初始化
void Item In it(char sen [][col],char first[][col],char last[][col],i nt sen_len ,i nt &frist_le n)
{
int i;
frist_le n=1;
first[O][O]=se n[0][0];
last[0][0]=se n[0][0];
for(i=1;i<se n_len ;i++)
{
//k if(Termi nalJud(se n[i][0])==false && ItemJud(first,frist_le n,se
n[i][0])==false)
是当前first和last表的长度
first[frist_le n][0]=se n[i][0];
Iast[frist_le n][O]=se n[i][O]; frist_le n++;
void FirstVt(char sen[][col],char first[][col],int sen_len,int frist_len) //
frist_len 是 first
表的行数sen_len是产生式的个数
{
StackEleme nt DFS,record[SIZE];
stack Operator; //创建存放(A, a)的栈
In itStack(Operator);
int i,j,r=0;
for(i=0;i<sen_len;i++) 〃第一次扫描,将能直接得出的first (A , a)放进栈中
{
for(j=3;se n[i][j]!='\O';j++)
{
if(Termi nalJud(se n[ i][j])==true) // 遇到的第一个终结符压
入
{
int exist=0;
DFS. non term=se n[i][0];
DFS.term=se n[i][j];
for(i nt i1=0;i<r;i++)
{
if(record[i1]. non term==se n[i][0 ]&&record[i1].term==se n[i][j])
{
exist=1;
break;
}
}
record[r] .non term=se n[i][0];
record[r].term=se n[i][j];
if(exist==0)
{
Insert(Operator,DFS);//A-aB A-aC (A,a)压栈两次?
record[r]. non term=se n[i][0];
record[r].term=se n[i][j];
r++;
}
break;
}
int location[col]; 〃辅助数组,用来记录first表中放入终结符的位置
for(i=0;i<frist_le n;i++)
locati on [i]=1;
while(!ifEmpty(Operator))
{
int exist=0; //标志位,记录即将入栈的元素是否已经存在
StackEleme nt IDEleme nt,DEIeme nt;
DEleme nt=Pop(Operator); // 弹出栈顶元素
for(i=0;i<frist_le n;i++)
{
if(first[i][O]==DEIeme nt. non term)
{
int n=locati on [i];
first[i][n]=DEIement.term; //将终结符填入相应的first表中
locati on [i]++;
break;
}
}
for(j=0;j<se n_len ;j++)
{
if(se n[j][3]==DEIeme nt.non term) 〃找出能推出当前非终结符的产生式的左部
{
IDEleme nt. non term=se n[j][O];
IDEleme nt.term=DEIeme nt.term;
〃判断将要放进栈里的元素曾经是否出现过,若没有,才压入栈
for(int rO=O;rO<r;rO++) 〃r记录record数组中的元素个数
{
if(record[rO]. non term==IDEIeme nt. non term &&
record[rO].term==IDEIeme nt.term)
{
exist=1; break;
}
}
if(exist==O)
{
In sert(Operator,IDEIeme nt);
record[r]. non term=IDEleme nt. non term; record[r].term=IDEIeme
nt.term;
r++;
}
}//if
}//for
}//while
}
void LastVt(char sen[][col],char last[][col],int sen_len,int frist_len) //firstvt 表与lastvt
表行数一样first_len表示last表的行数
{
int i,j,i1,j1;
char c,record[row][col]={'\0'};
for(i=0;i<se n」en ;i++)
{
for(j=0;se n[i][j]!='\O';j++)
{
record[i][j]=se n[i][j];
}
j=j-1;
for(i1=3,j1=j;i1<j1;i1++,j1--) 〃做翻转,就可以用求 first 的方法求last
{
c=record[i][i1];
record[i][i1]=record[i][j1];
record[i][j1]=c;
} }//for
FirstVt(record,last,se n」en ,frist_le n);
〃判断非终结符在term表中是否已存在
bool TermTableJud(char term[col],i nt term_le n,char C)
{
for(i nt i=O;i<ter m」en ;i++)
{
if(term[i]==C)
return true; 〃如果first表中已存在此非终结符,则返回 1 }
return false;
}
〃构造算符优先关系表
bool OpPriotable(char sen [][col],char first[][col],char last[][col],char
opTable[][col],i nt
sen_len ,i nt first_le n,i nt &opTable_le n)
{
int i,j,term_le n=0;
int i2,i3,opr,opc;
char c1,c2,c3;
for(i=0;i<se n_len ;i++) 〃一维数组term记录关系表中存在的所有终结符
char term[SIZE]={'\0'};
{
for(j=3;se n[i][j]!='\O';j++)
{
if(Termi nalJud(se n[i][j])==true)
if(TermTableJud(term,term」en,sen[i][j])==false) 〃term」en 记录
term
表的长度
{
term[term_le n]=se n[i][j]; term_le n++;
}
}
} 〃得到终结符表
//给优先关系表赋初值,都等于空
for(i=0;i<term_le n+1;i++)
for(j=0;j<term_le n+1;j++)
opTable[i][j]='';
opTable[i][0]=term[i-1]; opTable[0][i]=term[i-1]; }
for(i=0;i<sen_le n; i++) // 找等于关系
{
for(j=5;se n[i][j]!='\O';j++) {
if(Termi nalJud(se n[ i][j-2])==true&& Termi nalJud(se n[ i][j-1])==false&&Termi nalJud(se n[ i][j])==true)
{
c 仁 se n[ i][j-2]; c2=se n[ i][j];
for(opr=1;opr<term_len+1;opr++)
〃在 opTable 表中找至U 该存
入的行标 opr
{
if(opTable[opr][0]==c1)
break;
}
for(opc=1;opc<term_len+1;opc++) //在 opTable 表中找至U 该
存入的列标opc
{
if(opTable[0][opc]==c2)
break;
}
if(opTable[opr][opc]!='') {
cout<<"不是算符优先文法! "<<e ndl; return false; } else {
opTable[opr][opc]='='; }
for(i=1;i<term_le n+1;i++)
{
II 设置优先关系表的表头
}//if
}//for(j)
for(j=4;se n[i][j]!='\O';j++)
{
if(Term in alJud(se n[i][j-1])==true&&Term in alJud(se n[i][j])==true)
{
c仁 se n[ i][j-1];
c2=se n[ i][j];
for(opr=1;opr<term_len+1;opr++) 〃在 opTable 表中找至U该存入的行标 opr
{
if(opTable[opr][0]==c1)
break;
}
for(opc=1;opc<term_len+1;opc++) //在 opTable 表中找至U
该
存入的列标j2
{
if(opTable[0][opc]==c2)
break;
}
if(opTable[opr][opc]!='')
{
cout<<"不是算符优先文法!"<<e ndl;
return false;
}
else
{
opTable[opr][opc]='=:
}
}
}
}//for(i)
for(i=0;i<sen_le n; i++) // 找小于关系
{
for(j=3;se n[i][j]!='\O';j++)
{
if(Term in alJud(se n[i][j])==true&& Term in alJud(se n[i][j+1])==false)
{
c仁sen[ i][j]; 〃c1记录终结符
c2=sen[i][j+1]; //c2 记录非终结符
for(opr=1;opr<term_le n+1;opr++)
〃在opTable表中找到该存入的行标 opr if(opTable[opr][0]==c1)
break;
}
for(opc=0;opc<first_len;opc++) 〃找出非终结符在 first 表中的列标opc
{
if(first[opc][0]==c2)
break;
}
for(i2=1;first[opc][i2]!='\0';i2++)
{
c3=first[opc][i2];
for(i3=1;i3<term」en+1;i3++)
if(opTable[0][i3]==c3)
{
if(opTable[opr][i3]!='')
{
cout<<"不是算符优先文法!"<<e ndl;
return false;
}
else
{
opTable[opr][i3]='<:
}
break;
}
}
}//if
}//for(j)
}//for(i)
for(i=0;i<sen_le n; i++) // 找大于关系
{
for(j=3;se n[i][j]!='\O';j++)
{
if(Termi nalJud(se n[i][j])==false&&se n[i][j+1]!='\O'&&Termi nalJud(se n[ i][j+1])==true)
{
c仁sen [i][j]; 〃c1记录非终结符
c2=sen[i][j+1]; //c2 记录终结符
入的列标j1
{
if(opTable[0][opr]==c2)
break;
}
last表中for(opc=0;opc<first_le n;opc++) // 找出非终结符在
的行标j2
{
if(last[opc][0]==c1)
break;
}
for(i2=1;last[opc][i2]!='\0';i2++)
{
c3=last[opc][i2];
for(i3=1;i3<term」en+1;i3++)
if(opTable[i3][0]==c3)
{
if(opTable[i3][opr]!='')
{
cout<<"不是算符优先文法!"<<e ndl;
return false;
}
else
{ opTable[i3][opr]='>';
}
break;
}
}
}//if
}//for(j)
}//for(i)
opTable_le n=term_le n+1;
return true;
}
〃判断两算符优先关系并给出类型供构造分析表
int Relati on shipJud(char c1,char c2,char opTable[][col],i nt opTable_le n)
{
int i,j;
for(i=1;i<opTable_le n;i++)
{
if(opTable[i][0]==c1)
break;
}
for(j=1;j<opTable_le n;j++)
{
if(opTable[0][j]==c2)
break;
}
if(opTable[i][j]=='<')
return 1;
else if(opTable[i][j]=='=')
return 2;
else if(opTable[i][j]=='>')
return 3;
else return 4;
}
〃判断输入串是不是优先文法
bool PrioGramJud(char sen [][col],i nt sen_len)
{
int i,j;
for(i=0;i<se n」en ;i++)
{
for(j=4;se n[i][j]!='\O';j++)
{
if(Term in alJud(se n[i][j-1])==false&&Term in alJud(se n[i][j])==false)
{
cout<<"输入的不是算符文法!"<<e ndl;
return false;
}
}
}
return true;
}
〃开始分析输入串
void InputAnalyse(char opTable[][col],char string[col],int opTable_len) 〃opTable_len 是
opTable表的长度
{
char a,Q,S[SIZE]={'\0'}; 〃S 是分析栈
char cho1,cho2;
int i,j,relatio n;
int k=0; S[k]='#'; cho2='y:
cout<<"\t\t\t\t 分析过程如下:"<<endl;
cout<<" ------------------------------------------------- "<<e ndl;
〃cout<<" 栈\t当前字符\t优先关系\t移进或规约"<<endl;
〃cout<<"分析过程如下:"<<endl;
cout.width(IO);
cout<<"栈";
cout.width(15);
cout<<"当前字符";
cout.width(20);
cout<<"剩余符号串";
cout.width(15); cout<<"优先关系";
cout.width(15);
cout<<"移进或规约"<<endl;
char* p; p=stri ng;
P++;
for(i=0;stri ng[i]!='\0'&&cho2=='y';i++,p++)
{
a=stri ng[i]; 〃读入当前字符
cho仁'n'; //可否接受
while(cho1==' n')
{
if(Termi nalJud(S[k])==true) j=k; 〃规约使栈内有非终结符
else j=k-1;
relati on=Relati on shipJud(S[j],a,opTable,opTable_le n);
if(relatio n==3) 〃S[j]>a
{
〃cout<<" "<<S<<"\t"<<" "<<a<<"\t\t >\t\t";
cout.setf(ios::left);
cout.width(10);
cout<<S;
cout. un setf(ios::left);
cout.width(15);
cout<<a;
cout.width(20);
cout<<p;
cout.width(15);
cout<<">";
do
{
Q=S[j];
if(Termi nalJud(S[j-1])==true) j=j-1; else j=j-2;
}
while(Relatio nshipJud(S[j],Q,opTable,opTable_le n)!=1); 〃cout<<"
"<<S[j]<<"归约"<<endl;
cout.width(11); cout<<S[j]; cout.width(4); cout<<"归约"<<endl; k=j+1;
S[k]='N';
for(i nt l=k+1;l<SIZE;l++) {
S[l]='\0';
}
}
else if(relati on==1)
{
cho1='y';
〃cout<<" "<<S<<"\t"<<"
cout.setf(ios::left); cout.width(10); cout<<S;
cout. un setf(ios::left); cout.width(15); cout<<a;
cout.width(20); cout<<p;
cout.width(15); cout<<"<";
cout.width(15); cout<<"移进"<<e ndl; k=k+1;
S[k]=a;
}
else if(relati on==2)
//S[j]
=a
//S[j]<a
"<<a<<"\t\t <\t\t 移进"<<e ndl;
}
else
{
} }//whi le }//for
}// // void mai n() { cho1='y';
〃cout<<" "<<S<<"\t"<<" "<<a<<"\t\t
=\t\t";
cout.setf(ios::left); cout.width(10); cout<<S;
cout. un setf(ios::left); cout.width(15); cout<<a; cout.width(20); cout<<p; cout.width(15); cout<<"="; cout.width(15); if(Relatio
nshipJud(S[j],'#',opTable,opTable_le n)==2) {
cout<<" 接受"<<endl; break; } else {
cout<<" 移进"<<endl;
k=k+1; S[k]=a;
} cho1='y';
//cout<<" "<<S<<"\t"<<a<<"\t\t 出错"<<endl; cout<<S<<" "<<a<<" 出错"<<e
ndl;
cout<<"对不起!字符串有错! "<<e ndl; cho2=' n'; break;
char choice='y'; while(choice=='y ')
system("cls");
char
sen
[row][col]={'\0'},first[row][col]={'\0'},last[row][col]={'\0'},opTable[row][col]= {'\0'};
char stri ng[col];
int i,k,p,q;
p=read);
if(PrioGramJud(se n, p)==true)
{
ItemInit(sen,first,last,p,q); //j 记录 FIRSTVT 和 LASTVT 表的长度
FirstVt(se n,first,p,q);
cout<<"\t\t 各非终结符 FIRSTVT 集"<<endl;
cout<<" ------------------------------ "<<e ndl;
for(i=0;i<p;i++)
{for(i nt h=0;h<col;h++)
cout<<first[i][h]<<" ";
cout<<e ndl;
}
// cout<<e ndl;
LastVt(se n, last,p,q);
cout<<"\t\t 各非终结符 LASTVT 集"<<endl;
cout<<" ------------------------------ "<<e ndl;
for(i=0;i<p;i++)
{for(i nt h=0;h<col;h++)
cout<<las t[i][h]<<" ";
cout<<e ndl;
}
// cout<<e ndl;
if(OpPriotable(sen,first,last,opTable,p,q,k)==true) //k 记录 opTable 表的
长度 {
cout<<"\t\t 优先关系表:"<<endl;
cout<<" ---------------------------- "<<e ndl;
for(i=0;i<k;i++)
{for(i nt h=0;h<col;h++)
cout<<opTable[i][h]<<"";
cout<<e ndl;
}
// cout<<e ndl;
cout<<"请输入要分析的串,以#结束:"<<endl; cin> >stri ng;
InputAnalyse(opTable,string,k); Ilk是 opTable 表的长度}
}
cout<<"是否继续?yln"<<e ndl;
cin> >choice;
}
}。