实验一(一)程序设计语言及其编译器实现概览(2小时)
实验目的:学习一门简单的程序设计语言的定义及其编译器实现
实验任务:针对一门简单的程序设计语言,阅读其定义文档,初步了解其编译器的源代码。实验内容:
(1)选择一门语言:TINY或其它语言也可(需自备其编译器的源代码)。
(2)阅读其定义文档,了解语言定义的方法,包括:词法、语法、语义、运行时环境、目标机器、目标语言等内容。
(3)了解其编译器源代码。
对TINY语言编译器,其源代码由多个文件组成,请弄清楚每个文件的作用是什么。
详情请见《编译原理及实践》第 1.7节。请做一个C++工程文件(Win32 Console Application, tiny.dsp),把它们组织起来,然后编译成可执行文件(tiny.exe),即为TINY 语言编译器。然后用它编译TINY语言示例源代码(sample.tny)。看看编译生成的目标文件(sample.tm)是怎样的。要运行目标程序,还需要一个虚拟机,名为TM机。TM 机是以软件形式存在的,其源代码为tm.c,需要编译生成可执行文件tm.exe。然后将目标程序作为TM机的输入运行TM机即可得到所期待的结果。要求读懂main.c、globals.h、util.h、scan.h和util.c、scan.c等文件,三人一组进行讨论,给每一行加上注释,总结你们各自对程序的理解和阅读程序的收获,每组提交1份加了注释的文件和心得。有能力的同学可加上tm.c。
实验一(二)DFA的编程实现(2小时)
实验目的:通过本次实验,加深对DFA及其识别的语言的理解,学习对一般的DFA的表达方法与编程实现方法。
实验任务:编写一个C语言程序,模拟实现DFA识别字符串的过程。
实验内容:(1)DFA的输入;(2)DFA的存储与读写;(3)DFA的正确性检查;(4)DFA 的语言集列表显示;(5)DFA的规则字符串判定;
内容说明:
(1)DFA的输入:
分别输入DFA的“字符集”、“状态集”、“开始状态”、“接受状态集”、“状态转换表”等内容,并保存在设定的变量中。
(2)DFA的存储与读写:
将上述DFA的五元组保存在一个文本文件中,扩展名指定为.dfa。请自行设计DFA文件的存储格式,并说明其含义。能将保存在内存变量中的DFA写入DFA文件,也能将DFA 文件读入内存中。(思考:对稀疏DFA(转换表中为空的地方较多)或用“或”表达转换的DFA(如下的测试用例三),如何改进DFA转换表的表达。)
(3)DFA的正确性检查:
检查所有集合的元素的唯一性。
检查“开始状态”是否唯一,并是否包含在“状态集”中。
检查“接受状态集”是否为空,并是否包含在“状态集”中。
检查“状态转换表”是否满足DFA的要求。
检查“状态转换表”并非填满时的处理是否得当…
(4)DFA的语言集列表显示:
输入待显示的字符串的最大长度N,输出以上定义的DFA的语言集中长度≤N的所有规则字符串。(提示:注意算法中while循环的次数)
(5)DFA的规则字符串判定:
输入(或用字符集随机生成)一个字符串,模拟DFA识别字符串的过程判定该字符串是否是规则字符串(属于DFA的语言集)。
测试用例:
DFA(一)
DFA(二)
DFA(三)
实验要求:
按照《编译原理及实践》参考书第二章中描述的“while循环+双层case选择”的算法
编程实现一般的DFA。
要求能通过以上三个测试用例的测试。
完成内容中(1)(5)部分,可得C。
完成内容中(1)(2)(5)部分,可得B。
完成内容中(1)(2)(4)(5)部分,可得A。
完成全部内容,可得奖励。
判定算法概要:
准备:开始状态s0, 接受状态集F, 状态转换表T(s, c), s∈S, c∈∑
c = getchar();
s = 开始状态s0;
while ( c != EOF ) // 输入未结束则循环…
{
s = T(s, c);
if (s == NULL)
error();
c = getchar();
}
if (s ∈接受状态集F)
accept ();
else
error ();
通用DFA存储格式的建议:(用以上的第三个测试DFA为例)
3 // 字符集中的字符个数(以下两行也可合并成一行)
/ * o // 以空格分隔的字符集。O代表任意非/和*的字符
5 // 状态个数(以下两行也可合并成一行)
1 2 3 4 5 // 状态编号。若约定总是用从1开始的连续数字表示,则此行可省略
1 // 开始状态的编号。若约定为1,则此行可省略
1 // 结束状态个数。若约定为1,则此行可省略
5 // 结束状态的编号
2 -1 -1 // 状态1的所有出去的转换。按字符集中的字符顺序给出。-1表示出错状态
-1 3 -1
3 4 3
5 4 3
-1 -1 -1
实验报告:
同时上交纸质报告与电子版报告。纸质报告以实验分析、实验中遇到的问题及解决方法、实验测试(含屏幕截图)、实验心得等为主,不得大量引用源程序(引用源程序总行数不得超过100行)。电子版报告以源程序、测试用例、输出文件等为主,包括纸质报告的电子版,须确保提并的源程序能编译运行,否则不要上交。电子版请发送到248133074@https://www.doczj.com/doc/c16447792.html,,邮件标题请写明学号、姓名与实验编号,形如:<实验一> <学号> <姓名>。不得抄袭实验报告与源程序,否则后果自负!
提高内容:
(1)图形化的用户界面;
(2)任意DFA的状态转换图、状态转换表的图形绘制;
附实验报告源码
实验一DFA的编程实现
实验目的:
通过本次实验,加深对DFA及其识别的语言的理解,学习对一般的DFA的表达方法与编程实现方法。
实验任务:
编写一个C语言程序,模拟实现DFA识别字符串的过程。
实验内容:
(1)DFA的输入;(2)DFA的存储与读写;(3)DFA的正确性检查;(4)DFA的语言集列表显示;(5)DFA的规则字符串判定;
实验分析:
DFA的初始化
一个DFA的基本信息状态集、字符集、开始状态、结束状态集、状态转换表;
在程序中状态集、字符集、开始状态、结束状态集都用string类型来表示,即可不用考虑用户输入内容的长度。但缺点是字符集只能是字符而不可以是字符串。
状态转换表:为三个字符的字符串,第一个为当前状态,第二个为输入字符,第三个为下一状态。用vector容器存放转换函数的内容
字符串判断
初始化一个DFA,后可以通过一下算法判断一个字符串是否符合该DFA。
判定算法概要:
准备:开始状态s0, 接受状态集F, 状态转换表T(s, c), s∈S, c∈∑
c = getchar();
s = 开始状态s0;
while ( c != EOF ) // 输入未结束则循环…
{
s = T(s, c);
if (s == NULL)
error();
c = getchar();
}
if (s ∈接受状态集F)
accept ();
else
error ();
在实际编写代码中的体现为
void identify() //判断字符串
{
cout<<"请输入字符串"< string str; cin>>str; int i=0; char present=StartStates; while(i { present=move(present,str[i]);//move函数即去遍历转换表,返回下个状态 if(present=='N') // N 即为move返回错误的状态, { break; } i++; } if(FinalStates.find(present)!=FinalStates.npos)//如果返回的状态用find函数属于最终状态集则表示识别 cout<<"该自动机识别此字符串"< else cout<<"该自动机不识别此字符串"< } 对DFA的存储与读写 将DNF的信息写入文件中, 第一行:字符集; 第二行:状态集; 第三行:开始状态; 第四行:结束状态集; 以下行写入状态转换表 按照既定的规定读取文件 中的数据将其赋值,然后 初始化DFA即可; DFA的语言集列表显示: 这一个模块应该是这个实验中比较难的一部分了。我并没有使用while循环的这个做法。而是用函数递归,通过所有字符集的路径去遍历整个DFA。最后将符合条件的字符串输出; void Traversal(char present, int N,string strass="")//遍历DFA的语言集列表 { if(present=='N'||N<0)//若路径已经大于N或者当前状态错误,停止递归return; N--; if(FinalStates.find(present)!=FinalStates.npos) { cout<<"该自动机识别字符串:"< } for(int i=0;i { string temp; temp=strass; strass+=Alphabet[i]; Traversal(move(present,Alphabet[i]),N,strass); strass=temp; } } 递归终止条件的判断。 当N-- 时, N小于0,或者遍历到无路可走是便终止 遇到的问题: 对于递归的终止条件写得不够明确。递归前对字符串赋值strass赋值,递归后应该还原,这一点没有想到。调试了很久。总结,对递归的具体编写还是不熟悉。 目前的代码,字符集和状态只能是一个字符,若要优化可以用vector 实验用例: 实验结果:DFA的初始化 判断字符串: 显示小于N的语言集: 读取DFA文件: 实验总结: 在这次实验中,学习对一般的DFA的表达方法与编程实现方法。对课本提供的算法有了更深刻的认识。在编写和调试代码的过程中对自身的编程能力也有了很大的提高。对自动机和其识别语言有了更透彻的理解。在动手编程之前一定要好好明确实验的理论准备和思路的理清,能帮助我们在实验过程中少走更多的弯路。总之在这次实验中收获很多,提高了动手能力和分析问题的能力。 源代码: //构造一个DFA #include #include #include #include using namespace std; class TransitionTable { public: char present; //当前状态 char next; //下一状态 char input; //输入字符 TransitionTable(char P,char I,char D) { present=P; next=D; input=I; } }; class DFA { public: DFA() { cout<<"1、手动输入,2、读取txt文件"< int select; cin>>select; if(select==1) inti(); if(select==2) read(); } string States; //状态集 char StartStates; //开始状态 string FinalStates; //结束状态集 string Alphabet; //字符集 vector void inti() //初始化自动机 { cout<<"请输入有限状态集S:"< cin>>States; cout<<"请输入字符集A:"< cin>>Alphabet; cout<<"请输入转换函数MOVE --格式为:当前状态-输入字符-下一状态:(输入#结束输入)"< int j=0; while(true) { char input[4]; cin>>input; if(strcmp(input,"#")==0) break; TransitionTable Temp(input[0],input[1],input[2]); Trans.push_back(Temp); } cout<<"请输入开始状态集S0:"< cin>>StartStates; cout<<"请输入结束状态集F:"< cin>>FinalStates; } void identify() //识别字符串 { cout<<"请输入字符串:"< string str; cin>>str; int i=0; char present=StartStates; while(i { present=move(present,str[i]);//move函数即去遍历转换表,返回下个状态 if(present=='N')// N 即为move返回错误的状态, { break; } i++; } if(FinalStates.find(present)!=FinalStates.npos)//如果返回的状态用find函数属于最终状态集则表示识别 cout<<"该自动机识别此字符串"< else cout<<"该自动机不识别此字符串"< } char move(char P,char I) //move 转换函数 { for(int i=0;i { if(Trans[i].present==P&&Trans[i].input==I) return Trans[i].next; } return 'N'; } void read() //从txt文件中读取DNF的信息 { char temp[10]; fstream ff("DNF.txt",ios::in); ff.getline(temp,10); Alphabet=temp; ff.getline(temp,10); States=temp; ff.getline(temp,10); StartStates=temp[0]; ff.getline(temp,10); FinalStates=temp[0]; while(!ff.eof()) { ff.getline(temp,10); TransitionTable Temp(temp[0],temp[1],temp[2]); Trans.push_back(Temp); } } /*将DNF的信息写入文件中, 第一行:字符集; 第二行:状态集; 第三行:开始状态; 第四行:结束状态集; 以下行写入状态转换表*/ void write() { fstream ff("DNF.txt",ios::out); ff< ff< ff< ff< for(int i=0;i ff< ff.close(); } void Traversal(char present, int N,string strass="") //遍历DFA的语言集列表显示{ if(present=='N'||N<0) return; N--; if(FinalStates.find(present)!=FinalStates.npos) { cout<<"该自动机识别字符串:"< } else if(FinalStates.find(present)==FinalStates.npos&&N<0) return ; for(int i=0;i { string temp; temp=strass; strass+=Alphabet[i]; Traversal(move(present,Alphabet[i]),N,strass); strass=temp; } } }; int main() { DFA example; example.identify(); int N; cout<<"请输入N"< cin>>N; example.Traversal(example.StartStates,N); example.write(); system("pause"); return 0; } /*测试用例 输入状态转换表 0a1 0b2 1b2 2a1 1a3 2b3 3a3 3b3 # 0a1 0b2 2a1 1b2 1a3 2b5 3a3 5b5 3b4 5a6 6b4 4a6 6a3 4b5 */