实验使用 Code::Blocks C++编写
【实验目标】
- 编写一个词法分析程序,要求能够根据用户给定的任意正则表达式,测试数据是否符合给定的正则表达式规范。
- 采用Thompson算法将正规式转化为NFA并输出NFA状态转换矩阵;
- 利用子集法构造DFA并输出DFA状态转换矩阵;
- 用最小化算法得到最小DFA并输出该DFA的状态转换矩阵,用DFA的模拟器算法识别任意输入单词;
- 程序测试数据的一个示例如下:
输入正则表达式:(a|b)*abb
采用Thompson算法将正规式转化为NFA并输出NFA状态转换矩阵,利用子集法构造DFA并输出DFA状态转换矩阵,用最小化算法得到最小DFA并输出该DFA的状态转换矩阵,最后用DFA的模拟器算法识别任意输入单词:
输入测试表达式: abb 输出判定结果:符合词法定义
输入测试表达式:abab 输出判定结果:不符合词法定义
【实验算法】
(1)Thompson算法:语法树构造
算法1. 构造语法树
输入:正则表达式
输出:由正则表达式构建的语法分析树
-
建符号栈和语法栈
-
将#压入符号栈
-
扫描正则表达式,直到扫到结束符#为止,对每次扫描的字符进行如下操作
字符为操作数,则包装为叶子节点,节点指针入操作数栈;
字符为操作符,做如下操作:-
当操作符为’)‘时 ,将与第一个’('之间的操作符栈清空,具体方法如下:
(1)读取栈顶,将栈顶操作符取出;
(2)根据操作符对操作数栈实行相应操作;
(循环(1)(2)步骤直到栈顶为’(’)
(3)删除栈顶操作符‘(’; -
当操作符为’'时,’’优先级最高,操作数栈顶,重新包装入栈。
-
当操作符‘|’、‘+’、‘(’时
(1)与栈顶操作符进行优先级比较;
(2)比栈顶优先级低,则栈顶运算符出栈,根据操作符对操作数栈实行相应操作; (循环(1)(2)直到不满足条件(2)条件)
(3)此时操作符比栈顶优先级高,压入栈;
-
-
此时正则表达式全部扫描结束,清空操作符栈,具体方法如下:
(1)读取栈顶,将栈顶操作符取出;
(2)根据操作符对操作数栈实行相应操作;
(循环(1)(2)步骤直到栈顶为’#’)
结束,此时操作数栈内唯一一个节点指针为语法树树根。
(2)Thompson算法:Thompson算法构造NFA
算法1. Thompson算法构造NFA
输入:语法树
输出:NFA状态转移矩阵Skips_graph
(3)由NFA到DFA
算法1. NFA到DFA(子集法)
输入:状态集Set,NFA的空跳转
输出:状态集Set的空闭包
(4)最小化DFA
算法1. 最小化DFA算法
输入:DFA
输出:最小化后的DFA
【实验完整代码】
#include <iostream>
#include <typeinfo>
#include <stdio.h>
#include <iomanip>
using namespace std;
const int Maxnum=100;
//数据结构
//根节点
typedef struct Node{
char type;//根节点类型
char Char;//叶子结点代表的字符
Node *lchild;//左子树
Node *rchild;//右子树
int start_S;//起始状态
int end_S;//终结状态
}*Tree_root;
//初始化根节点
void default_Node(Node* N){
N->lchild=N->rchild=NULL;
}
//包装叶子节点
Node* default_L_Node(char c){
Node *N=new Node;
default_Node(N);
N->Char=c;
N->type='l';
return N;
}
//后根遍历语法树
int vist_Tree(Tree_root Tree){
if(Tree->type=='l') //是根节点
{cout<<Tree->Char<<" ";return 1;}
else
{
vist_Tree(Tree->lchild);
if(Tree->rchild!=NULL) vist_Tree(Tree->rchild);
cout<<Tree->type<<" ";return 1;}
}
//包装根节点
Node* default_R_Node(char type,Node* lchild,Node* rchild){
Node *N=new Node;
default_Node(N);
N->type=type;
N->lchild=lchild;
N->rchild=rchild;
return N;
}
//符号栈
typedef struct operator_Stack{
char* top;//栈顶
char* bottom;//栈底
char St[Maxnum]; //数组作为栈
};
//初始化栈
void default_op_stack(operator_Stack &S){
S.top=S.bottom=S.St;
}
//入栈
void push_op(operator_Stack &S,char a){
*(S.top)=a;
S.top++;
}
//出栈
char pop_op(operator_Stack &S){
S.top--;
return *S.top;
}
//获得栈顶元素
char top_op(operator_Stack &S){
return *(S.top-1);
}
//语法栈
typedef struct syntax_Stack{
int top;//栈顶
int bottom;//栈底
Node* St[Maxnum]; //节点数组作为栈
};
//初始化栈
void default_sy_stack(syntax_Stack &S){
S.top=S.bottom=0;
}
//入栈
void push_char(syntax_Stack &S,Node* a){
S.St[S.top]=a;
S.top++;
}
//出栈
Node* pop_char(syntax_Stack &S){
S.top--;
return S.St[S.top];
}
//运算符转换对应优先级
int o_astype(char o){
switch ( o ){
case '#':
return 1;
case '(' :
return 2;
case '|':
return 3;
case '+':
return 4;
case ')':
return 5;
case '*':
return 6;
default :
return 0; //不是操作符
}
}
//判断是否加入连接运算符
bool if_cat(char l,char r){
if(!o_astype(l)&&r=='(') //char+(
return 1;
else if(!o_astype(l)&&!o_astype(r)) //char+char
return 1;
else if(l==')'&&!o_astype(r)) //)+char
return 1;
else if(l=='*'&&!o_astype(r)) //*+char
return 1;
else if(l==')'&&r=='(') //)+(
return 1;
else if(l=='*'&&r=='(') //*+(
return 1;
else return 0;
}
//输入正则表达式
string cin_Regular_Exp(){
cout<<"请输入正则表达式:";
string R_exp;
cin>>R_exp;
R_exp.insert(0,"#");
R_exp.append("#"); //加入结束符
//将正则表达式加入cat连接符
int l=0,r=1;
while(R_exp[r]!='#'){
if(if_cat(R_exp[l],R_exp[r]))
R_exp.insert(l+1,"+");
l++;r++;}
return R_exp;
}
//比较运算符优先级
bool if_high(char odd_op,char new_op){
int odd_=o_astype(odd_op);
int new_=o_astype(new_op);
if(new_>odd_) return 1;
return 0;
}
//根据运算符连接节点
Node* Connect_Nodes(char op,syntax_Stack& char_st){
switch ( op ){
case '|': //双目运算符
return default_R_Node(op,pop_char(char_st),pop_char(char_st));
case '+': //双目运算符
return default_R_Node(op,pop_char(char_st),pop_char(char_st));;
case '*': //单目运算符
return default_R_Node(op,pop_char(char_st),NULL);
}
}
//构造语法树
Tree_root Syntax_Tree(string exp){
//建符号栈和语法栈
operator_Stack op_st;
syntax_Stack char_st;
default_op_stack(op_st);
default_sy_stack(char_st);
//将#压入符号栈
push_op(op_st,exp[0]);
//扫描(除了开头和结尾的#)
for(int i=1;i<exp.size()-1;i++)
{
char c=exp[i];
if(!o_astype(c)) //语法内容
push_char(char_st,default_L_Node(c)); //包装叶子节点,压入语法栈
else//符号
{
if(c=='(')
{
push_op(op_st,'(');//直接入栈
}
else if(c==')') //将遇到'('之间栈清空
{ while(top_op(op_st)!='(')
{
char op=pop_op(op_st);
push_char(char_st,Connect_Nodes(op,char_st));
}
pop_op(op_st);
}
else if(c=='*') //遇到最高运算符,直接操作
push_char(char_st,Connect_Nodes(c,char_st));
else{
while(!if_high(top_op(op_st),c)) //循环比较,直到比栈顶操作符优先级高
{
char op=pop_op(op_st);//栈顶元素出栈
push_char(char_st,Connect_Nodes(op,char_st));//得到新的根节点
}
push_op(op_st,c);//压入符号栈
}
}
}
//扫描全部结束 将栈内剩余部分连接
while(top_op(op_st)!='#')
{
char op=pop_op(op_st);
push_char(char_st,Connect_Nodes(op,char_st));
}
cout<<"结束"<<endl;
return char_st.St[char_st.bottom];
}
//在字符数组中加入新元素,若已经存在,则返回数组编号
int add_find_char(char *A,char a,int& size_){
if(size_==0) {A[0]=a;size_++;return Maxnum;}
else{
int i;
for(i=0;i<size_;i++)
if(*(A+i)==a) return i; //找到了 不添加
A[i]=a;size_++;
return Maxnum; //没找到,添加
} }
//得到正则表达式的吸收符号
int Get_absorb_char(string exp,char *ab_chars){
int char_size = 0;
for(int i=0;i<exp.size();i++){
if(!o_astype(exp[i])) //不是操作符
add_find_char(ab_chars,exp[i],char_size);}
return char_size;
}
//状态集
typedef struct states_set{
int st_set[Maxnum];
int length=0;};
//图的矩阵表示
typedef struct Skips_graph{
int S_size;//状态数
int ab_char_size; //吸收字符种类数
char absorb_char[Maxnum]; //吸收字符
int Skips[Maxnum][Maxnum]; //状态跳转
int empty_Skips[Maxnum][Maxnum]; //空状态跳转 每行第一列放该状态的空跳转数
int start_S; //起点
int end_S; //终点(对nfa而言,只有一个终点)
states_set end_set; //终点集(对于dfa而言,有多个终点)
};
//初始化状态转移矩阵
void default_graph(Skips_graph &S,char *ab_char,int ab_char_size){
S.S_size=0;
S.ab_char_size=ab_char_size;
for(int i=0;i<ab_char_size;i++)
S.absorb_char[i]=ab_char[i];
for(int i=0;i<Maxnum;i++)
for(int j=0;j<Maxnum;j++)
S.Skips[i][j]=Maxnum; //表示无跳转
for(int i=0;i<Maxnum;i++)
S.empty_Skips[i][0]=0; //空跳转个数为0
}
//添加新的状态跳转 图 起点 终点 吸收字符
void add_Skips(Skips_graph& S,int B,int E,char c){
int ab_char=add_find_char(S.absorb_char,c,S.ab_char_size);
S.Skips[B][ab_char]=E;
}
void add_empty_Skips(Skips_graph& S,int B,int E){
S.empty_Skips[B][0]=S.empty_Skips[B][0]+1; //空跳转数加一
int size_=S.empty_Skips[B][0];
S.empty_Skips[B][size_]=E;}
//构造叶子节点状态转移
void Leaf_Skip(Skips_graph& S,Node& N){
N.start_S=S.S_size;S.S_size++;
N.end_S=S.S_size;S.S_size++;
add_Skips(S,N.start_S,N.end_S,N.Char); //添加始末状态转移
}
//构造根状态转移
void root_Skip(Skips_graph& S,Node& N){
//或根构造
if(N.type=='|'){
N.start_S=S.S_size;S.S_size++;
N.end_S=S.S_size;S.S_size++;
add_empty_Skips(S,N.start_S,N.lchild->start_S);
add_empty_Skips(S,N.start_S,N.rchild->start_S);
add_empty_Skips(S,N.lchild->end_S,N.end_S);
add_empty_Skips(S,N.rchild->end_S,N.end_S);}
//闭包构造
else if(N.type=='*'){
N.start_S=S.S_size;S.S_size++;
N.end_S=S.S_size;S.S_size++;
add_empty_Skips(S,N.start_S,N.lchild->start_S);
add_empty_Skips(S,N.start_S,N.end_S);
add_empty_Skips(S,N.lchild->end_S,N.lchild->start_S);
add_empty_Skips(S,N.lchild->end_S,N.end_S);}
//连接构造
else if(N.type=='+'){
N.start_S=N.lchild->start_S;
N.end_S=N.rchild->end_S;
add_empty_Skips(S,N.lchild->end_S,N.rchild->start_S);}
else {cout<<"fault!";}
}
//递归构造NFA
void NFA(Tree_root Tree, Skips_graph &S, string exp){
//构造状态转移矩阵
if(Tree->type=='l') //叶子状态转移
Leaf_Skip(S,*Tree);
else
{
NFA(Tree->lchild,S,exp); //构造左子树状态转移
if(Tree->rchild!=NULL) NFA(Tree->rchild,S,exp); //构造右子树状态转移
root_Skip(S,*Tree);//构造根状态转移}
}}
void NFA_(Tree_root Tree, Skips_graph &S, string exp){
//得到吸收符号
char ab_chars[Maxnum]; //正则表达式中的吸收符号
int ab_chars_size=Get_absorb_char(exp,ab_chars);
//初始化状态转移(NFA)
default_graph(S,ab_chars,ab_chars_size);
//构造
NFA(Tree,S,exp);
S.start_S=Tree->start_S;S.end_S=Tree->end_S;}
//读NFA
void vist_Graph(Skips_graph &S,string type){
cout<<"状态跳转矩阵:"<<endl;
cout<<"状态";
for(int i=0;i<S.ab_char_size;i++)
cout<<setw(6)<<S.absorb_char[i];
cout<<setw(8)<<"空跳转";
cout<<endl;
for(int i=0;i<S.S_size;i++)
{cout<<setw(3)<<i;
for(int j=0;j<S.ab_char_size;j++)
if(S.Skips[i][j]==Maxnum) cout<<setw(6)<<"-";
else cout<<setw(6)<<S.Skips[i][j];
//输出空跳转
cout<<setw(6)<<" ";
for(int j=0;j<S.empty_Skips[i][0];j++)
cout<<S.empty_Skips[i][j+1]<<" ";
cout<<endl;}
cout<<"起点:"<<S.start_S<<" ";
if(type=="nfa") cout<<"终点:"<<S.end_S<<" ";
else{
cout<<"终点:";
for(int i=0;i<S.end_set.length;i++)
cout<<S.end_set.st_set[i]<<" ";
}
cout<<endl;}
//向状态集中添加元素
bool add_s_to_set(states_set &Set,int s){
for(int i=0;i<Set.length;i++){
if(Set.st_set[i]==s) return 0;
}
Set.st_set[Set.length]=s;
Set.length++;
return 1;
}
//求集合的空闭包 空跳转
void empty_closure(states_set &Set,int empty_Skips[][Maxnum]){
for(int i=0;i<Set.length;i++){
int p=Set.st_set[i];
for(int j=0;j<empty_Skips[p][0];j++){
add_s_to_set(Set,empty_Skips[p][j+1]);}
}
cout<<"闭包:"<<endl;
for(int i=0;i<Set.length;i++) cout<<Set.st_set[i]<<" ";cout<<endl;
}
//计算从当前状态集出发,经过a字符,能到的下一个状态集
void Smove(states_set Set,states_set &next_Set,int absorb_char,Skips_graph &nfa){
//对每个状态都计算经过a得到的新状态
for(int i=0;i<Set.length;i++)
{ int nfa_i=Set.st_set[i]; //在nfa中的状态
if(nfa.Skips[nfa_i][absorb_char]!=Maxnum) //有跳转
add_s_to_set(next_Set,nfa.Skips[nfa_i][absorb_char]);}
}
//判断当前状态集是否存在,若存在则返回新状态,若不存在则返回0
int if_exist(states_set *Sets,states_set Set,int sets_length){
//找到长度相等的进行比对
int T; //判断是否有相同的状态集
for(int i=0;i<sets_length;i++){
if(Sets[i].length==Set.length){
T=1; //先假设该集合是
for(int j=0;j<Set.length;j++){
int t=0; //判断是否找到相同元素
for(int k=0;k<Sets[i].length;k++)
if(Set.st_set[j]==Sets[i].st_set[k]){t=1;break;}
if(t==0) {T=0;break;} //找不到,该集合不是
}
if(T==1) return i;
}}
return 0;
}
//NFA转为DFA
void DFA(Skips_graph &nfa,Skips_graph &dfa,states_set *sets){
for(int i=0;i<dfa.S_size;i++){
//计算当前状态的跳转状态集 c为吸收字符
for(int c=0;c<dfa.ab_char_size;c++){
states_set next_set;
Smove(sets[i],next_set,c,nfa);//计算Smove(j,char)
if(next_set.length==0) continue; //该状态不吸收该字符
empty_closure(next_set,nfa.empty_Skips);//计算Smove的空闭包
//判断该状态集是否已经存在,如果不存在则作为新状态集加入dfa
int S=if_exist(sets,next_set,dfa.S_size);
if(!S){
S=dfa.S_size;
sets[S]=next_set;
dfa.S_size++;
}
//在dfa上添加当前状态吸收字符的跳转
dfa.Skips[i][c]=S;
}
}
}
//判断集合中是否有某个元素
bool find_s(states_set set_,int s){
for(int i=0;i<set_.length;i++)
if(set_.st_set[i]==s) return 1;
return 0;
}
void DFA_(Skips_graph &nfa,Skips_graph &dfa){
//初始化dfa
default_graph(dfa,nfa.absorb_char,nfa.ab_char_size);
states_set sets[Maxnum];//新旧状态表
//先求起始状态的空闭包
states_set begin_set;
add_s_to_set(begin_set,nfa.start_S);
empty_closure(begin_set,nfa.empty_Skips);
//将起始状态集作为DFA的起始状态加入
dfa.start_S=0;
//将起始状态集加入状态集与新状态的对照表中
sets[dfa.S_size]=begin_set;
dfa.S_size++;
DFA(nfa,dfa,sets);
//找到dfa的所有终点状态
int end_s=nfa.end_S;
for(int i=0;i<dfa.S_size;i++){
if(find_s(sets[i],end_s)){
add_s_to_set(dfa.end_set,i);
}}
}
//将终点集根据吸收的字符划分
bool crack_end(Skips_graph &dfa,int *new_states,int &find_num){
states_set new_end_sets;
//将终态分出:构造nfa到dfa,每个状态只吸收一个符号,找到每个符号对应的终态集,将其标为相同的状态
if(dfa.end_set.length==1)
{new_states[(dfa.end_set.st_set[0])]=dfa.end_set.st_set[0];find_num++;
add_s_to_set(new_end_sets,dfa.end_set.st_set[0]);}
else{
int not_find_num=dfa.end_set.length; //未找到的元素
for(int c=0;c<dfa.ab_char_size;c++){
int T=Maxnum; //表示还未找到第一个终态
for(int i=dfa.S_size-1;i>=0;i--){ //从后往前找
if(find_s(dfa.end_set,dfa.Skips[i][c])) //在接受该字符的跳转里找到终态
{
if(T==Maxnum)
{T=dfa.Skips[i][c];
add_s_to_set(new_end_sets,T);}
new_states[(dfa.Skips[i][c])]=T; //用第一个发现的终态 作为该吸收的终态
not_find_num--;
find_num++;
}
if(not_find_num==0) break;}
if(not_find_num==0) break; }
}
dfa.end_set=new_end_sets;
}
//分裂,分裂成功返回1,不可再分返回0
bool crack(Skips_graph &dfa,int *new_states,int &find_num){
for(int i=0;i<dfa.S_size;i++){
if(new_states[i]!=Maxnum) continue;
for(int c=0;c<dfa.ab_char_size;c++){
int T=1;//表示是否可以区分
for(int j=0;j<dfa.S_size;j++){
if(i==j) continue;
if(new_states[(dfa.Skips[i][c])]==new_states[(dfa.Skips[j][c])]) //跳转的结果在一个集合
{T=0;break;}
}
//可再分
if(T==1) {new_states[i]=i;find_num++;return 1;}}}
return 0;//表示不可再分
}
//判断数组前n个是否出现过当前字符
bool find_int(int *array_,int n,int s){
for(int i=0;i<n;i++){
if(array_[i]==s) return 1;
return 0;
}
}
//查找旧状态为s的新状态
int find_s_in_array(int *array,int s,int length){
for(int i=0;i<length;i++){
if(array[i]==s) return i;
}}
//整理合并后的dfa
void combine(Skips_graph dfa,Skips_graph& min_dfa,int *old_states){
default_graph(min_dfa,dfa.absorb_char,dfa.ab_char_size);//初始化
int new_states[Maxnum];
//将没有出现过的状态加入
for(int i=0;i<dfa.S_size;i++){
if(find_int(old_states,i,old_states[i])) continue;//判断之前是否出现过
for(int c=0;c<dfa.ab_char_size;c++)
min_dfa.Skips[min_dfa.S_size][c]=dfa.Skips[i][c]; //复制进去
new_states[min_dfa.S_size]=old_states[i];//保存老状态
min_dfa.S_size++;
}
//将所有状态调整
for(int i=0;i<min_dfa.S_size;i++){
for(int c=0;c<min_dfa.ab_char_size;c++)
min_dfa.Skips[i][c]=find_s_in_array(new_states,min_dfa.Skips[i][c],min_dfa.S_size);}
//记录起点
min_dfa.start_S=find_s_in_array(new_states,dfa.start_S,min_dfa.S_size);
//终结状态作调整
for(int i=0;i<dfa.end_set.length;i++){
add_s_to_set(min_dfa.end_set,find_s_in_array(new_states,dfa.end_set.st_set[i],min_dfa.S_size));
}
// cout<<min_dfa.end_set.length;
// for(int i=0;i<min_dfa.end_set.length;i++)
// cout<<min_dfa.end_set.st_set[i]<<" ";
}
//最简化DFA
void minimize_DFA(Skips_graph dfa,Skips_graph &min_dfa){
//新状态
int find_num=0; //确定新状态的状态数
int new_states[Maxnum];
for(int i=0;i<dfa.S_size;i++) new_states[i]=Maxnum;
//将终态分出:构造nfa到dfa,每个状态只吸收一个符号,找到每个符号对应的终态集,将其标位相同的状态
crack_end(dfa,new_states,find_num);
//将每个可区分的状态区分出来 条件:未找完或者还可以继续区分
while(find_num!=dfa.S_size&&crack(dfa,new_states,find_num)){;}
//将剩余状态划分为一个状态
if(find_num!=dfa.S_size){
int T=Maxnum;
for(int i=0;i<dfa.S_size;i++){
if(new_states[i]!=Maxnum) continue; //已经找到的
if(T==Maxnum) T=i;
new_states[i]=T;}}
//将新旧状态不同的 修改符号
for(int i=0;i<dfa.S_size;i++){
if(i==new_states[i]) continue;
for(int j=0;j<dfa.S_size;j++){
for(int c=0;c<dfa.ab_char_size;c++){
if(dfa.Skips[j][c]==i) dfa.Skips[j][c]=new_states[i];
}}}
dfa.start_S=new_states[dfa.start_S];//将改变的起点记录下来
combine(dfa,min_dfa,new_states);
}
//返回吸收字母的数字
bool find_ab_char(char a,Skips_graph dfa,int &c){
for(int i=0;i<dfa.ab_char_size;i++)
if(dfa.absorb_char[i]==a){c=i;return 1;}
return 0;
}
//根据dfa作词法分析
bool lexical_analysis(Skips_graph dfa){
string exp;
cout<<"输入词:";
cin>>exp;
cout<<"过程:";
int p=dfa.start_S;cout<<p<<"->";
//根据读入跳转
for(int i=0;i<exp.size();i++){
char char_=exp[i];
int c; //字符在吸收字符数组中的位置
//字符不可被接受
if(!find_ab_char(char_,dfa,c))
{cout<<"失败:"<<char_<<"输入字符不被接受"<<endl;return 0;}
//可以被接受
p=dfa.Skips[p][c];cout<<p<<"->";
//无法跳转
if(p==Maxnum)
{cout<<"失败:吸收"<<char_<<"跳转出错"<<endl;return 0;}}
//全部字符读取成功:若能到达终点
if(find_s(dfa.end_set,p)) //移动到终点处
cout<<"成功"<<endl;
else
cout<<"失败:未到达终点"<<endl;
return 1;
}
int main()
{
int start;
cout<<"开始:";
cin>>start;
while(start){
string exp=cin_Regular_Exp();
cout<<exp<<endl;
//构建语法树
Node* Tree=Syntax_Tree(exp);
//遍历语法树
cout<<"后序遍历语法树: ";
vist_Tree(Tree);
cout<<endl;
//构造NFA
cout<<endl<<"********************Thompson算法构造NFA***********************"<<endl;
Skips_graph nfa;
NFA_(Tree,nfa,exp);
vist_Graph(nfa,"nfa");
//由NFA构造DFA
cout<<endl<<"************************由NFA转为DFA**************************"<<endl;
Skips_graph dfa;
DFA_(nfa,dfa);
vist_Graph(dfa,"dfa");
//最小化dfa
cout<<endl<<"**************************最小化DFA**************************"<<endl;
Skips_graph min_dfa;
minimize_DFA(dfa,min_dfa);
vist_Graph(min_dfa,"dfa");
//进行词法分析
cout<<"*********************************词法分析**************************"<<endl;
char a;
cin>>a;
while(a!='#')
{lexical_analysis(dfa);cin>>a;}
cout<<endl<<"开始:";
cin>>start;
}
}
【实验示例】
【注意事项】
实验未实现 ‘+’,开始时要输入‘1’。