实验一(一) 基于C语言的TINY编译器与TM虚拟机(1学时)
实验目的
初步认识编译器系统和目标机,体会系统编程,了解条件编译和增量编程。
实验任务
- 获取TINY语言编译器相关源代码。
- 了解TINY语言定义,可参见文档《TINY语言及其编译器实现》。第一次实验时对TINY语言有大致了解即可,后续实验中会陆续阅读更多TINY语言及其编译器的文档,详细了解其词法、语法、语义、运行时环境、目标机器、目标语言等内容,最终能编写出该语言的编译器。
- 生成和使用TINY语言的编译器。
- 大致了解TINY编译器源代码。
实验内容
- 生成TINY编译器
- 生成TM虚拟机
- 通过条件编译生成部分编译器
- 使用flex生成TINY编译器
- 使用bison生成TINY编译器
具体步骤详见tiny编译器的使用.ppt。
备注
1.实验资料
- TINY编译器及TM虚拟机源代码
下载loucom.ZIP并解压。
- FLEX和Bison程序
下载flexbison.zip并解压。
- 开发环境
兼容C的开发环境,如:VS或者DEV C++。
此部分实验学生自行完成,要求人人过关。
(一)不写实验报告,把心得体会写在实验一(二)的实验报告的心得体会部分。
实验建议
这个实验环境配置比较麻烦,当时做实验的时候见过很多人环境配置出错,甚至装了XP虚拟机的都有。。。建议使用VC++6.0版本建立工程,可以省去很多麻烦。
实验一(二)DFA的编程实现(3学时)
实验目的
通过本次实验,加深对DFA及其识别的语言的理解,学习对一般的DFA的表达方法与编程实现方法。
实验任务
编写一个C语言程序,模拟实现DFA识别字符串的过程。
实验内容
- DFA的输入;
- DFA的存储与读写;
- DFA的正确性检查;
- DFA的语言集列表显示;
- 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的语言集)。
代码实现
#include<iostream>
#include<string.h>
#include<fstream>
#include<vector>
#include<assert.h>
using namespace std;
struct TransitionDiagram //状态转换图类
{
char cur,next,input; //当前状态,下一状态,转换条件
TransitionDiagram(char c,char i,char n) //构造函数
{
cur=c;
input=i;
next=n;
}
};
struct DFA_class //DFA结构体
{
string state; //状态集
string letter; //字符集
string start_state; //开始状态(实际使用时只取一个,也就是字符串的[0]位)
string end_state; //接收状态集,可以多个
int count; //符合条件的字符串个数
vector<TransitionDiagram>Trans_Rule; //转换规则数组,数组的类型是上面定义的class
}DFA; //一个对象
int find_letter(char a,string s) //从一个字符串s中查找是否有字符a
{
int len=s.length();
for(int i=0;i<len;i++)
{
if(a==s[i])
return 1;
}
return 0;
}
int Check_letter(string a) //查找当前字符串a是否有元素重复
{
int len=a.length();
for(int i=0;i<len;i++)
{
for(int j=i+1;j<len;j++)
{
if(a[i]==a[j])
return 0;
}
}
return 1;
}
int find_start_state(string a) //查找输入的开始状态是否属于状态集
{
for(int i=0;i<DFA.state.length();i++)
{
if(a[0]==DFA.state[i])
return 1;
}
return 0;
}
int find_end_state(string a) //查找输入的接收状态是否均属于状态集
{
int count=0;
int len1=a.length();
int len2=DFA.state.length();
for(int i=0;i<len1;i++)
{
for(int j=0;j<len2;j++)
{
if(a[i]==DFA.state[j])
{
count++;
break;
}
}
}
if(count==len1)
return 1;
else
return 0;
}
int Check_rule(string a) //查找输入的转换规则合法性,转换条件是否属于字符集、转换状态之间是否均属于状态集
{
if(find_letter(a[0],DFA.state) && find_letter(a[1],DFA.letter) && find_letter(a[2],DFA.state))
return 1;
return 0;
}
char move(char c,char ii) //如果能够和现有的转换条件匹配,就移动当前状态到下一个,否则返回特定字符
{
for(int i=0;i<DFA.Trans_Rule.size();i++)
{
if(DFA.Trans_Rule[i].cur==c && DFA.Trans_Rule[i].input==ii) //如果能够匹配转换条件,那么跳转到下一个状态
{
return DFA.Trans_Rule[i].next;
}
}
return '?'; //如果不能匹配,那么返回一个问号代表错误状态
}
void init_and_input() //输入数据的函数,包括了数据合法性检查
{
DFA.count=0;
char Rule[3];
bool flag=false;
do{
if(Check_letter(DFA.letter)==0)
cout<<"字符重复,请重新";
cout<<"输入字符集:";
cin>>DFA.letter;
}while(Check_letter(DFA.letter)==0);
DFA.letter+='O'; //输入字符集之后,在字符集的最后加上一个O代表others
do{
if(Check_letter(DFA.state)==0)
cout<<"状态重复,请重新";
cout<<"输入状态集:";
cin>>DFA.state;
}while(Check_letter(DFA.state)==0);
do{
if(find_start_state(DFA.start_state)==0 && flag==true || DFA.start_state.length()>1)
cout<<"开始状态输入错误,请重新";
cout<<"请输入开始状态:";
cin>>DFA.start_state;
flag=true;
}while(find_start_state(DFA.start_state)==0 || DFA.start_state.length()>1);
do{
if(find_end_state(DFA.end_state)==0)
cout<<"接受状态集输入错误,请重新";
cout<<"请输入接受状态集:";
cin>>DFA.end_state;
}while(find_end_state(DFA.end_state)==0);
cout<<"请输入状态转换表,格式(1a2),O代表其他字符,输入星号‘*’停止:"<<endl;
while(cin>>Rule)
{
if(Rule[0]=='*')
break;
else if(Check_rule(Rule))
DFA.Trans_Rule.push_back(TransitionDiagram(Rule[0],Rule[1],Rule[2]));
else
cout<<"转换表输入错误,请检查状态和字符是否在范围内。"<<endl;
}
}
void write() //将DFA的属性写入lab1.dfa文件
{
fstream ff("lab1.txt",ios::out);
ff<<"字符集:"<<DFA.letter.substr(0,DFA.letter.length()-1)<<endl; //输入字符集的时候需要去掉最后一个元素,因为最后一个默认O在字符集内
ff<<"状态集:"<<DFA.state<<endl;
ff<<"开始状态:"<<DFA.start_state<<endl;
ff<<"结束状态集:"<<DFA.end_state<<endl;
ff<<"状态转换表:"<<endl;
for(int i=0;i<DFA.Trans_Rule.size();i++)
ff<<DFA.Trans_Rule[i].cur<<" "<<DFA.Trans_Rule[i].input<<" "<<DFA.Trans_Rule[i].next<<endl;
ff.close();
}
void read()
{
fstream ff("lab1.dfa",ios::in);
assert(ff.is_open());
string tmp;
ff>>DFA.letter>>DFA.state>>DFA.start_state>>DFA.end_state;
DFA.letter+='O';
while(!ff.eof())
{
ff>>tmp;
DFA.Trans_Rule.push_back(TransitionDiagram(tmp[0],tmp[1],tmp[2]));
}
cout<<"文件读取完毕!"<<endl;
ff.close();
}
void identify(string s) //检测一个字符串s是否能够被当前的DFA接收
{
char cur_state=DFA.start_state[0];
int len=0;
while(len<s.length())
{
string tmp="";
for(int i=0;i<DFA.Trans_Rule.size();i++) //如果下一个输入匹配不到任何确定的转换条件,那么匹配一个others
{
if(cur_state==DFA.Trans_Rule[i].cur && DFA.Trans_Rule[i].input!='O')
tmp+=DFA.Trans_Rule[i].input;
}
if(find_letter(s[len],tmp)==0)
s[len]='O'; //无论当前输入是什么,只要匹配不到确定的规则,都将当前位改为others
cur_state=move(cur_state,s[len]); //对于字符串的每一个元素,做基于DFA状态转换规则的移动
if(len==s.length()-1 && find_letter(cur_state,DFA.end_state)==0)
{
cout<<"该字符串不能被DFA接收。"<<endl;
break;
}
len++;
}
if(find_letter(cur_state,DFA.end_state))
cout<<"该字符串能被DFA接收。"<<endl;
}
void generate(char cur_state,int num,string tmp="") //生成所有长度不大于num且符合DFA的字符串
{
if(cur_state=='?' || num<0) //如果当前状态是错误的,或者长度超出限制,应当停止递归,回到上一层
return;
num--; //进入下一层递归,可用字符减少一个
if(DFA.end_state.find(cur_state)!=DFA.end_state.npos) //如果当前状态在接收状态集中被找到了,代表DFA能够接收字符串
{
cout<<tmp<<endl; //将这个字符串输出,符合要求的字符串数量加一
DFA.count++;
}
for(int i=0;i<DFA.letter.length();i++) //列举所有可能的字符串情况,一一判断
{
tmp+=DFA.letter[i];
generate(move(cur_state,DFA.letter[i]),num,tmp); //将当前的字符串循环加上字符集中的每一个元素,一一进行递归判断
tmp.erase(tmp.length()-1,1); //如果加上字符之后,该字符串跳转到错误状态或长度超限,那么需要返回到这里,必须要去掉刚刚加上的那个不符合要求的字符
}
}
int main()
{
int choice,n;
cout<<"请选择1-手动输入,2-从lab1.dfa读取数据:";
cin>>choice;
if(choice==1)
init_and_input();
else
read();
write();
cout<<"请输入一个整数,求满足DFA长度不大于该整数的所有字符串:";
cin>>n;
generate(DFA.start_state[0],n);
cout<<"一共有上述"<<DFA.count<<"个符合条件的字符串。"<<endl;
cout<<"输入一个字符串,判断是否满足该DFA:";
string s;
cin>>s;
identify(s);
return 0;
}