建立预测分析表
递归便于书写,但代价比较高,表驱动的效率更好。根据上篇的SELECT集去建立预测分析表:
程序
Main.cpp
#include"Rely.h"
#define MAXLEN 100
using namespace std;
//****************************************************
char c;//通用
bool psr=true;//记录语法分析阶段是否出错
int s_len=0;//读入的字符串长度
char* s_pre=new char[MAXLEN];//原始串
int numNow=0;//表驱动的步骤数
list<int> ls_term;//模拟模式队列:顺序地存终结符种别的list
//list<int>::iterator it;//list的游动迭代器
list<int>::iterator it_fst;//list的头部迭代器,用来计算下标
list<string> ls_str;//原串的截断串列表:模拟队列
//queue<string> qu_str;//原串的截断串队列
list<string> ls_td;//推导符组成的列表:模拟栈
//list<string>::iterator it;//一个通用的list游标
//****************************************************
//语法分析:传入分析栈栈顶元素,模式队列头枚举值
void ParseFunction(const char* str,int n){
if(0==strcmp("E",str)){
if(identifier==n || unsignint==n || lparen==n){
cout<<"E->IK1";
ls_td.pop_back();//弹出E
ls_td.push_back("K1");//压入K1
ls_td.push_back("I");//压入I
}else if(add_sub==n){
cout<<"E->AIK1";
ls_td.pop_back();//弹出E
ls_td.push_back("K1");//压入K1
ls_td.push_back("I");//压入I
ls_td.push_back("A");//压入A
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}else if(0==strcmp("K1",str)){
if(add_sub==n){
cout<<"K1->AIK1";
ls_td.pop_back();//弹出K1
ls_td.push_back("K1");//压入K1
ls_td.push_back("I");//压入I
ls_td.push_back("A");//压入A
}else if(rparen==n || end==n){
cout<<"K1->ε";
ls_td.pop_back();//弹出K1
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}else if(0==strcmp("I",str)){
if(identifier==n || unsignint==n || lparen==n){
cout<<"I->FK2";
ls_td.pop_back();//弹出I
ls_td.push_back("K2");//压入K2
ls_td.push_back("F");//压入F
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}else if(0==strcmp("K2",str)){
if(mul_div==n){
cout<<"K2->MFK2";
ls_td.pop_back();//弹出K2
ls_td.push_back("K2");//压入K2
ls_td.push_back("F");//压入F
ls_td.push_back("M");//压入M
}else if(add_sub==n || rparen==n || end==n){
cout<<"K2->ε";
ls_td.pop_back();//弹出K2
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}else if(0==strcmp("F",str)){
if(identifier==n){
cout<<"F->i";
ls_td.pop_back();//弹出F
ls_td.push_back("i");//压入i
}else if(unsignint==n){
cout<<"F->u";
ls_td.pop_back();//弹出F
ls_td.push_back("u");//压入u
}else if(lparen==n){
cout<<"F->(E)";
ls_td.pop_back();//弹出F
ls_td.push_back(")");//压入)
ls_td.push_back("E");//压入E
ls_td.push_back("(");//压入(
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}else if(0==strcmp("A",str)){
if(add_sub==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"+")){
cout<<"A->'+'";
ls_td.pop_back();//弹出A
ls_td.push_back("+");//压入+
}else if(add_sub==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"-")){
cout<<"A->'-'";
ls_td.pop_back();//弹出A
ls_td.push_back("-");//压入-
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}else if(0==strcmp("M",str)){
if(mul_div==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"*")){
cout<<"M->'*'";
ls_td.pop_back();//弹出M
ls_td.push_back("*");//压入*
}else if(mul_div==n && 0==strcmp(string(*(ls_str.begin())).c_str(),"/")){
cout<<"M->'/'";
ls_td.pop_back();//弹出M
ls_td.push_back("/");//压入/
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}else{
cout<<"无路可走"<<endl;
psr=false;
}
}
//传入一个字符串,返回和它意义等价的枚举值
Status analyze(const char* str){
if(0==strcmp("+",str) || 0==strcmp("-",str))
return add_sub;
else if(0==strcmp("*",str) || 0==strcmp("/",str))
return mul_div;
else if(0==strcmp("i",str))
return identifier;
else if(0==strcmp("u",str))
return unsignint;
else if(0==strcmp("(",str))
return lparen;
else if(0==strcmp(")",str))
return rparen;
else if(0==strcmp("#",str))
return end;
//全不匹配,返回的是不会被加入这个模式队列的none
return none;
}
//****************************************************
int main(int argc, char** argv) {
//读入输入串并记录长度,大写字母全部变为小写
while('\n'!=(c=getchar()))
{
if('A'<=c && c<='Z')
c=c+'a'-'A';
s_pre[s_len++]=c;
}
s_pre[s_len]='\0';
cout<<"分词情况:\n\t";
//解析输入串并存入list,把原串的截断串也存到一个queue里
cutWord(ls_term,s_pre,s_len,ls_str);
//在解析后的两个容器尾部添加一个#[结尾标识]
ls_term.push_back(end);
//qu_str.push("#");
ls_str.push_back("#");
cout<<"输入串情况(串截断队列):\n\t";
for(list<string>::iterator it=ls_str.begin();it!=ls_str.end();it++){
cout<<*it<<" ";
//如果后面改进了,可以在这里顺便做第一次的长度计算
//quLenSum+=string(*it).length();
}
cout<<endl;
/*
while(!qu_str.empty()){
//摧毁性验证!为了能输出看,还是用list模拟队列和栈吧
cout<<qu_str.front()<<" ";
qu_str.pop();
}
cout<<endl;
*/
cout<<"词法分析情况(模式队列):\n\t";
for(list<int>::iterator it=ls_term.begin();it!=ls_term.end();it++)
cout<<*it<<" ";
cout<<"\n"<<endl;
//输出表头
cout<<" 步骤 | 分析栈 | 剩余输入串 | 推导所用产生式或匹配"<<endl;
ls_td.push_back("#");
ls_td.push_back("E");
while(true==psr && !ls_td.empty() && !ls_str.empty() && !ls_term.empty()){
//格式输出前三列
printf("%7d | ",++numNow);
//[这里可以改进]重新计算分析栈中字符长度和
int stLenSum=0;//记录分析栈中字符长度和
for(list<string>::iterator it=ls_td.begin();it!=ls_td.end();it++){
cout<<*it;
stLenSum+=string(*it).length();
}
for(int i=stLenSum+2;i<=14;i++)
cout<<" ";//输出第二列(分析栈)剩余的空格
cout<<"|";
//[这里可以改进]重新计算分析栈中字符长度和
int quLenSum=0;//记录剩余输入串队列中字符长度和
for(list<string>::iterator it=ls_str.begin();it!=ls_str.end();it++){
quLenSum+=string(*it).length();
}
for(int i=quLenSum+2;i<=24;i++)
cout<<" ";//输出第三列(输入串)前置的空格
for(list<string>::iterator it=ls_str.begin();it!=ls_str.end();it++)
cout<<*it;
cout<<" | ";
//对第四列,先尝试匹配,失败则从表(分支段)中查找产生式
//因为输入串已经全部小写化,不必担心和非终结符重名
list<string>::iterator it1=ls_td.end();
it1--;//指向模拟栈的栈顶
list<int>::iterator it2=ls_term.begin();//指向模式队列的头
list<string>::iterator it3=ls_str.begin();//指向串截断队列的头
//终结符尝试:对这个字符串进行向枚举值的转换
int k=analyze(string(*it1).c_str());
//如果和模式队列的枚举值匹配
if(k==int(*it2)){
//输出的是外显的串截断队列的头
cout<<"\""<<*it3<<"\"匹配";
if(k!=end){
//把两个队列同步出队(删除list头元素)
ls_str.erase(ls_str.begin());
ls_term.erase(ls_term.begin());
//模式栈栈顶弹出
ls_td.pop_back();
}else{
//一方面erase不能去掉结尾元素
//另一方面end需要特别判断
ls_str.pop_back();
ls_term.pop_back();
ls_td.pop_back();
//如果还有没空的list
if(!ls_td.empty() || !ls_str.empty() || !ls_term.empty()){
psr=false;
}
}
}else{
//语法分析:传入栈顶元素,模式队列头元素
ParseFunction(string(*it1).c_str(),*it2);
}
cout<<endl;
}
if(true==psr)
cout<<"语法正确"<<endl;
else
cout<<"语法错误"<<endl;
delete[] s_pre;
return 0;
}
Rely.h
#ifndef __RELY_H__
#define __RELY_H__
#include<bits/stdc++.h>
using namespace std;
//待分析对象类型
enum Status{
end=-2,//结尾标识,即#符号
none=0,//啥也不是|空格|制表符,不会被添加到list中
expression=1,//表达式
item=2,//项
add_sub=3,//加减
mul_div=4,//乘除
factor=5,//因子
identifier=6,//标识符
unsignint=7,//无符号整数
lparen=8,//左括号
rparen=9//右括号
};
//当前字符类型
enum Sign{
nop=0,//没有符号|空格|制表符
num=7,//数字0-9
word=6,//字母[a-z]|[A-Z]
error=-1,//非法字符
//下面的符号可以直接被解析为待分析对象类型
//所以使用和其对应的相同数值,而且名字不能相同
addSub=3,//加减号
mulDiv=4,//乘除号
lParen=8,//左括号
rParen=9//右括号
};
//****************************************************
//对传入的串分词,获取模式并存入list,另:存下原串的截断串
void cutWord(list<int>& ls,char* cArry,const int& len,list<string>& ls2);
//传入一字符,获取字符类型
Sign getSign(char c);
//****************************************************
//对传入的串分词,获取模式并存入list,另:存下原串的截断串
void cutWord(list<int>& ls,char* cArry,const int& len,list<string>& ls2)
{
bool isHead=true;//指示是否在扫描非终结符的头
//不直接使用枚举类型,因为枚举类型不能转型为另一枚举类型或者int类型
int nowPat=none;//正在处理的模式,取决于模式中头一字符
int nowSign=nop;//正在处理的符号类型
int pre=0;//记录上一模式的开始位置
for(int i=0;i<len;i++)//对于数组中的每个字符
{
nowSign=getSign(cArry[i]);//获取当前符号的类型
if(error==nowSign)//处理非法字符
{
cout<<"[命名错误]存在非法字符\n"<<endl;
exit(0);
}
//cout<<now<<endl;
/*连续向下走的情况*/
//当前在继续正常处理标识符
if(identifier==nowPat && (num==nowSign || word==nowSign))
continue;
//当前在继续正常处理无符号整数
if(unsignint==nowPat && num==nowSign)
continue;
/*命名异常立即报错*/
//常数模式下的数字后跟字母不允许
if(unsignint==nowPat && word==nowSign){
cout<<"[命名错误]纯常数数字后跟字母非法\n"<<endl;
exit(0);
}
/*改变类型向下走,不作检查,词法分析阶段再处理*/
nowPat=nowSign;//在设计枚举时设计成数字相同的
if(none!=nowPat)//空白符不被存入list中
{
//cout<<nowPat<<" ";
ls.push_back(nowPat);//其它字符上转int被存入list中
//新增:存入上一模式的实际串
if(i!=0){
char* ext=new char[i-pre+1];//小的临时字符数组
for(int j=pre;j<i;j++){
cout<<cArry[j];
ext[j-pre]=cArry[j];//暂时存进去
}
ext[i-pre]='\0';
string s(ext);
ls2.push_back(s);//存入截断串列表
//qu.push(s);//存入截断串队列
delete[] ext;
cout<<" ";
}
//存好之后再把pre位置改变为当前位置
pre=i;
}
}
//因为串总是落后一次,所以模式结束,串还有一个
char* ext=new char[len-pre+1];//小的临时字符数组
for(int j=pre;j<len;j++){
cout<<cArry[j];
ext[j-pre]=cArry[j];//暂时存进去
}
ext[len-pre]='\0';
string s(ext);
ls2.push_back(s);//存入截断串列表
//qu.push(s);//存入截断串队列
delete[] ext;
cout<<" ";
cout<<endl;
}
//传入一字符,获取字符类型
Sign getSign(char c)
{
if(' '==c || '\t'==c)
return nop;//0
else if('0'<=c && c<='9')
return num;//1
else if(('a'<=c && 'z'<=c)||('A'<=c && 'Z'<=c))
return word;//2
else if('+'==c || '-'==c)
return addSub;//3
else if('*'==c || '/'==c)
return mulDiv;//4
else if('('==c)
return lParen;//8
else if(')'==c)
return rParen;//9
return error;//5
}
#endif
运行结果
错误语法:
正确语法: