#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<cstring>
using namespace std;
map<char,int>getnum;
char getchar[100]; //获得对应字符
vector<string>proce;
int table[100][100]; //预测分析表
int num=0;int numvt=0; //numvt是终结符集合,0是‘#’,numvt表空字
string first[100];
string follow[200];
void readin()
{
memset(table,-1,sizeof(table));
getnum['#']=0;
getchar[0]='#';
cout<<"请输入终结符集:"<<endl;
char x;
do
{
cin>>x;
getnum[x]=++num;
getchar[num]=x;
}while(cin.peek()!='\n');
numvt=++num;
getnum['@']=numvt; //kong zi
getchar[num]=('@');
cout<<"请输入非终结符集:"<<endl;
do
{
cin>>x;
getnum[x]=++num;
getchar[num]=x;
}while(cin.peek()!='\n');
cout<<"输入所有产生式(空字用‘@’表示),以‘end’结束:"<<endl;
string pro;
while(cin>>pro&&pro!="end")
{
string ss;
ss+=pro[0];
for(int i=3;i<pro.size();i++)
{
if(pro[i]=='|')
{
proce.push_back(ss);
ss.clear();ss+=pro[0];
}
else
{
ss+=pro[i];
}
}
proce.push_back(ss);
}
}
void jiaoji(string &a,string b) //a=a or b 取a,b交集赋值给a
{
set<char>se;
for(int i=0;i<a.size();i++)
se.insert(a[i]);
for(int i=0;i<b.size();i++)
se.insert(b[i]);
string ans;
set<char>::iterator it;
for(it=se.begin();it!=se.end();it++)
ans+=*it;
a=ans;
}
string get_f(int vn,int & has_0) //dfs:vn能推出的不含空字的vt集合,并且判断vn能否推出空字
{
if(vn==numvt)has_0=1;
if(vn<numvt)return first[vn];
string ans;
for(int i=0;i<proce.size();i++)
{
if(getnum[proce[i][0]]==vn)
ans+=get_f(getnum[proce[i][1]],has_0);
}
return ans;
}
void getfirst()
{
for(int i=1;i<=numvt;i++) //终结符,first集是其本身。
{
first[i]+=('0'+i);
}
for(int j=0;j<proce.size();j++) //扫描所有产生式
{
int k=0;int has_0=0; //k扫瞄该产生式
do{
has_0=0;
k++;
if(k==proce[j].size()) //推到最后一个了,则附加空字
{
first[getnum[proce[j][0]]]+=('0'+numvt);
break;
} //合并之
jiaoji(first[getnum[proce[j][0]]],get_f(getnum[proce[j][k]],has_0));
}
while(has_0); //到无法推出空字为止
}
}
void print_first()
{
cout<<"first集如下:"<<endl;
for(int i=1;i<=num;i++)
{
cout<<"first ["<<getchar[i]<<"]: ";
for(int j=0;j<first[i].size();j++)
cout<<getchar[first[i][j]-'0']<<" ";
cout<<endl;
}
cout<<endl;
}
void getfollow()
{
jiaoji(follow[getnum[proce[0][0]]],"0"); //先添加‘#’;
for(int j=0;j<proce.size();j++) //扫所有产生式
{
for(int jj=1;jj<proce[j].size();jj++) //每个非终结符的follow集
{
if(getnum[proce[j][jj]]<=numvt)continue; //vt无follow集
int k=jj; int has_0;
do
{
has_0=0;
k++;
if(k==proce[j].size()) //都能推出空字,follow集=产生式左边的vn,
{
jiaoji(follow[getnum[proce[j][jj]]],follow[getnum[proce[j][0]]]);
break;
}
jiaoji(follow[getnum[proce[j][jj]]],get_f(getnum[proce[j][k]],has_0));
}while(has_0);
}
}
}
void gettable() //得预测分析表
{
for(int i=0;i<proce.size();i++) //扫所有产生式
{
if(proce[i][1]=='@') //直接推出空字的,特判下(follow集=产生式左边的vn中元素填)
{
string flw=follow[getnum[proce[i][0]]];
for(int k=0;k<flw.size();k++)
{
table[getnum[proce[i][0]]][flw[k]-'0']=i;
}
}
string temps=first[getnum[proce[i][1]]];
for(int j=0;j<temps.size();j++) //考察first集
{ if(temps[j]!=('0'+numvt))
{
table[getnum[proce[i][0]]][temps[j]-'0']=i;
}
else //有空字的,考察follw集
{
string flw=follow[getnum[proce[i][1]]];
for(int k=0;k<flw.size();k++)
{
table[getnum[proce[i][0]]][flw[k]-'0']=i;
}
}
}
}
}
string get_proce(int i) //由对应下标获得对应产生式。
{
if(i<0)return " "; //无该产生式
string ans;
ans+=proce[i][0];
ans+="->";
//ans+=(proce[i][0]+"->"); 注意这样不行!思之即可。
for(int j=1;j<proce[i].size();j++)
ans+=proce[i][j];
return ans;
}
void print_table()
{
cout<<"预测分析表如下:"<<endl;
for(int i=0;i<numvt;i++)
cout<<'\t'<<getchar[i];
cout<<endl;
for(int i=numvt+1;i<=num;i++)
{
cout<<getchar[i];
for(int j=0;j<numvt;j++)
{
cout<<'\t'<<get_proce(table[i][j]);
}
cout<<endl;
}
cout<<endl;
}
void print_follow()
{
cout<<"follow集如下:"<<endl;
for(int i=numvt+1;i<=num;i++)
{
cout<<"follow ["<<getchar[i]<<"]: ";
for(int j=0;j<follow[i].size();j++)
cout<<getchar[follow[i][j]-'0']<<" ";
cout<<endl;
}
cout<<endl;
}
string word;
bool analyze() //总控,分析字word的合法性,若合法,输出所有产生式。
{
stack<char>sta;
sta.push('#');sta.push(proce[0][0]);
int i=0;
while(!sta.empty())
{
int cur=sta.top();
sta.pop();
if(cur==word[i]) //是终结符,推进
{
i++;
}
else if(cur=='#') //成功,结束
{
return 1;
}
else if(table[getnum[cur]][getnum[word[i]]]!=-1) //查表
{ int k=table[getnum[cur]][getnum[word[i]]];
cout<<proce[k][0]<<"->";
for(int j=1;j<proce[k].size();j++)
cout<<proce[k][j];
cout<<endl;
for(int j=proce[k].size()-1;j>0;j--) //逆序入栈
{ if(proce[k][j]!='@')
sta.push(proce[k][j]);
}
}
else //失败!
{
return 0;
}
}
return 1;
}
int main()
{
readin();
getfirst();
getfollow();
getfollow();
gettable();
print_first();
print_follow();
print_table();
cout<<"请输入字:"<<endl;
cin>>word;
if(analyze())
cout<<"succeed!该字有效,所用产生式如上。"<<endl;
else cout<<"error!"<<endl;
return 0;
}


/*测试:
( ) i + *
E A T B F
E->TA
A->+TA|@
T->FB
B->*FB|@
F->(E)|i
end
i*i+i a b c d
S A B
S->BA
A->BS|d
B->aA|bS|c
end
adccd + - * / ( ) i
E G T F S 
E->TG
G->+TG|-TG
G->@
T->FS
S->*FS|/FS
S->@
F->(E)
F->i
end
i+i*i
*/

LL(1)语法分析器 //c++实现的更多相关文章

  1. 编译原理简单语法分析器(first,follow,分析表)源码下载

    编译原理(简单语法分析器下载) http://files.cnblogs.com/files/hujunzheng/%E5%8A%A0%E5%85%A5%E5%90%8C%E6%AD%A5%E7%AC ...

  2. Tiny语法分析器(递归下降分析法实现)

    递归规约规则是这样的 program→stmt-sequence stmt-sequence→stmt-sequence;statement|statement statement→if-stmt|r ...

  3. 有没有好用的开源sql语法分析器? - 匿名用户的回答 - 知乎

    有没有好用的开源sql语法分析器? - 匿名用户的回答 - 知乎 presto,hive,drill,calcite,sparksq

  4. 开源语法分析器--ANTLR

      序言 有的时候,我还真是怀疑过上本科时候学的那些原理课究竟是不是在浪费时间.比方学完操作系统原理之后我们并不能自己动手实现一个操作系统:学完数据库原理我们也不能弄出个像样的DBMS出来:相同,学完 ...

  5. SQLite Lemon 语法分析器学习与使用

    本文是浙江大学出版社的<LEMON语法分析生成器(LALR 1类型)源代码情景分析>学习笔记. 用到的Windows下的编译器介绍MinGW(http://www.mingw.org/): ...

  6. [Swift]LeetCode385. 迷你语法分析器 | Mini Parser

    Given a nested list of integers represented as a string, implement a parser to deserialize it. Each ...

  7. 语法分析器初步学习——LISP语法分析

    语法分析器初步学习——LISP语法分析 本文参考自vczh的<如何手写语法分析器>. LISP的表达式是按照前缀的形式写的,比如(1+2)*(3+4)在LISP中会写成(*(+ 1 2)( ...

  8. org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 语法分析器在此文档中遇到多个 "64,000" 实体扩展; 这是应用程序施加的限制

    使用SAX解析XML文件.XML文件有1.5G,程序抛出了这个问题: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 语法 ...

  9. LR(1)语法分析器生成器(生成Action表和Goto表)java实现(二)

    本来这次想好好写一下博客的...结果耐心有限,又想着烂尾总比断更好些.于是还是把后续代码贴上.不过后续代码是继续贴在BNF容器里面的...可能会显得有些臃肿.但目前管不了那么多了.先贴上来吧hhh.说 ...

  10. LR(1)语法分析器生成器(生成Action表和Goto表)java实现(一)

    序言 : 在看过<自己实现编译器链接器>源码之后,最近在看<编译器设计>,但感觉伪代码还是有点太浮空.没有掌握的感觉,也因为内网几乎没有LR(1)语法分析器生成器的内容,于是我 ...

随机推荐

  1. 一、新手必会Python基础

    博客内容: 1.基础语法 2.运算符 3.流程控制 4.列表.元组.字典.集合 5.字符串 6.文件操作 一.基础语法 1.标识符 命名规则: 以字母.下划线开头 其他部分由字母.数字或下划线组成 不 ...

  2. C++系统学习之七:类

    类的基本思想是数据抽象和封装. 数据抽象是一种依赖于接口和实现分离的编程技术.类的接口包括用户所能执行的操作:类的实现包括类的数据成员.负责接口实现的函数体以及定义类所需的各种私有函数. 封装实现了类 ...

  3. Comet OJ 热身赛-principal

    这题的话,我们分析一下,入栈的操作是: 栈空 栈顶元素和当前操作元素不属于同一类括号 栈顶元素和当前操作元素属于同一类括号,但是并不是左括号在前,右括号在后 上面三个条件有任意一个满足都应该入栈,如果 ...

  4. [UVA] 704 Colour Hash

    所谓"周界搜索",练习搜索的好题,双向宽搜/迭代加深均可,还有很多细节有待完善,判重有比set更优的结构,宽搜还没写,先存一下. //Writer:GhostCai &&a ...

  5. laravel中对加载进行优化

    在laravel中的模型与模型之间创建好关联关系会比较方便的方法 但是我们为了方便,有时也会忽略一些东西,比如: 我们在控制器中把整个一个文章对象传到了模板页面 在一次for循环下, 我们对数据进行了 ...

  6. python动态添加属性和方法

    ---恢复内容开始--- python动态添加属性: class Person(object): def __init__(self,newName,newAge): self.name = newN ...

  7. django第四天(路由别名,django2.x新特性和自定义转换器)

    django第四天 路由别名 1.路由别名: 给路由路径命名一个名字 url(r'^login/$',views.login,name = 'login') 2.为什么要用路由别名 ①当路由路径过长时 ...

  8. (原)neuq oj 1022给定二叉树的前序遍历和后序遍历确定二叉树的个数

    题目描述 众所周知,遍历一棵二叉树就是按某条搜索路径巡访其中每个结点,使得每个结点均被访问一次,而且仅被访问一次.最常使用的有三种遍历的方式: 1.前序遍历:若二叉树为空,则空操作:否则先访问根结点, ...

  9. c++ heap学习

    heap并不属于STL容器组件,它分为 max heap 和min heap,在缺省情况下,max-heap是优先队列(priority queue)的底层实现机制. 而这个实现机制中的max-hea ...

  10. 03005_Tomcat

    1.Tomcat下载 (1)Tomcat解压版:链接:Tomcat解压版 密码:0iw0 : (2)源码:链接:源码 密码:3o43 . 2.Tomcat的目录结构 (1)bin:脚本目录   ①启动 ...