源码下载链接
(92条消息) 表驱动LL(1)语法分析程序-C++文档类资源-CSDN文库https://download.csdn.net/download/weixin_51187996/87659928
1 概述
1.1目的与意义
编译程序支撑着计算机核心系统,独立研究、设计并开发一个简单的编译程序或其部分功能,可以加深对编译理论和编译过程的理解。编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序,是编译程序的重要组成部分。通过设计、编制和调试一个典型的LL(1)语法分析方法,进一步掌握预测分析法的语法分析方法,有助于加深对编译程序的理解和实践。
1.2主要完成的任务
根据LL(1)分析法编写一个语法分析程序,输入已知文法,由程序自动构造文法的预测分析表。所开发的程序可适用于不同的文法和任意输入串,且能判断该文法是否为LL(1)文法。对输入的任意符号串,所编制的语法分析程序应能正确判断此串是否为文法的句子(句型分析),并可以输出分析过程。
1.3使用的开发工具
使用DEVC++作为本设计的开发工具。
1.4课程设计计划
表1.1 时间规划表
时间 | 任务 |
12月初 | 复习表驱动的LL(1)的基础知识,规划课程设计内容 |
12月12日 | 实现文法初始化及LL(1)文法判别模块 |
12月13日 | LL(1)文法判别模块 |
12月14日 | 实现预测分析表的构造及输入串分析 |
12月15日 | 程序调试及撰写课程设计报告,答辩 |
12月16日 | 完成所有材料并提交 |
2 使用的基本概念和原理(一些书上的概念,公式编辑器编辑的公式识别成图片了,CSDN复制粘贴图片不好使,一个个下载再放进来太麻烦了,自己看书补全吧
2.1 LL(1)分析法
LL(1)分析法又称预测分析法,是一种不带回溯的非递归自顶向下分析法。LL(1)的含义是:第一个L表明自顶向下分析是从左至右扫描输入串的;第二个L表明分析过程中将用最左推导;“1”表明只需向右查看一个符号就可以决定如何推导(即可知用哪一个产生式进行推导)。
2.2 自顶向下语法分析
从文法的开始符号出发,反复使用文法的产生式,寻找与输入符号串匹配的推导。
2.3 FIRST集合
是上下文无关文法,则对于,有对应的
,若,则规定。
2.4 FOLLOW集合
是上下文无关文法,。
,若有,则规定。
2.5 SELECT集合
是上下文无关文法,则对于,有对应的
SELECT集,若,则。
否则,。
2.6 LL(1)文法的判定
一个上下文无关文法为LL(1)文法的充分必要条件是,对每个非终结符A的两个不同产生式与 ,满足
3 总体设计
采用C++设计实现LL(1)语法分析器,数据结构上主要使用标准模板库,功能上主要分为4个模块,获取到文法产生式,判别文法是否是LL(1)文法,构造预测分析表,对输入串进行分析。
图3.1总体流程图
3.1 获取文法
在此模块中,从文本文档中获取到文法产生式,并对文法产生式进行分析,初始化得到文法的开始符号,非终结符,终结符等特征。
3.2 判别文法
要判别一个上下文无关文法是否是LL(1)文法,分为五步:
(1)求能推出的非终结符集
(2)计算FIRST集
(3)计算每个非终结符A的FOLLOW(A)集
(4)计算每个产生式A→β的SELECT(A→β)集
(5)按LL(1)文法的定义判别
3.3 构造预测分析表
根据判别文法过程中得到的SELECT集,把产生式填入分析表。分析表的列为终结符与‘#",分析表的行为非终结符。行列交点M[A,a]的填写法则如下:
(1)若,放入M[A,a]
(2)若无产生式对应M[A,a],表示出错
3.4 分析输入串
LL(1)分析法的原理
(1)初始化分析栈,将#和开始符号分别入栈,输入指针指向输入串的第一个字符。
(2)比较当前分析栈栈顶符号与当前输入符号:
- 如果栈顶符号为非终结符号,则到LL(1)分析表中查找(X.a),若存在可用产生式,则使用该产生式替换栈顶符号,转到②;若为错误信息,则输出错误信息,结束分析。
- 如果栈顶符号为终结符号,且与输入符号相同,则分析栈弹出栈顶符号,输入指针读入下一个输入符号,转到②;
(3)如果栈顶号为#且当前输入符号也为#,则分析成功,结束分析。
3.5 程序数据结构
enum
{
FU, SH, WD//012
};//规定大写字母为非终结符,E为空字符
char start;//开始符号
vector<char>Vn;//非终结符
vector<char>Vt;//终结符
vector<char>V;//文法字符
vector<string>G;//文法
vector<string>G1;//文法副本
map<char, int>isEVn;//非终结符标记数组
map<char, string>First;//每个文法符号的first集
map<string, string>First1;//每个字符串的first集
map<char, string>Follow;//每个非终结符的follow集
map<string, string>Select;//select集
map<char,map<char,string> >Anlysis;// 分析表
4 详细设计
4.1 获取文法
以只读方式,读取文本文档,默认文本中产生式的第一个符号为文法的开始符号,读取后的产生式处理后,存入文法副本,对形如的产生式做处理,变成,的形式,再存储到文法中。并更新文法副本,用于推导的非终结符集使用。扫描文法产生式,初始化终结符集和非终结符集。
4.2 求能推出的非终结符集
首先建立一个以文法的非终结符个数为上界的一维数组,其数组元素为非终结符,对应每一非终结符有一个标志位,用以记录能否推到出。其值有三种情况:“未定”、“是”、“否”。
计算能推出的非终结符步骤如下:
- 将数组X[]中对应每一非终结符的标记置初值为“未定”(WD)。
- 扫描文法中的产生式。
- 删除所有右部含有终结符的产生式,若这使得以某一非终结符为左部的所有产生式都被删除,则将数组中对应该非终结符的标记值改为“否”,说明该非终结符不能推出。
若某一非终结符的某一产生式右部为,则将数组中对应该非终结符的标志置为“是”,并从文法中删除该非终结符的所有产生式。
图4.1 scanA_s()函数流程图
- 扫描产生式右部的每一符号。
- 若所扫描到的非终结符在数组中对应的标志是“是”,则删去该非终结符;若这使产生式右部为空,则将产生式左部的非终结符在数组中对应的标志改为“是”,并删除以该非终结符为左部的所有产生式。
- 若所扫描到的非终结符号在数组中对应的标志是“否”,则删去该产生式;若这使产生式左部非终结符的有关产生式都被删去,则把在数组中该非终结符对应的标志改成“否”。
- 重复(3),直到扫描完一遍文法的产生式,数组中非终结符对应的特征再没有改变为止。
必须严格按照步骤进行,顺序不可颠倒。
图4.2 scan_s()函数流程图
4.3 计算FIRST集
- 根据FIRST集定义对每一文法符号计算FIRST(X)。
- 对,则。
- 扫描产生式,对,且识别到有产生式,则。
- 扫描产生式,对,识别到,则。
- 若,而识别的有产生式。当时(其中),则count标记记录下标,将
都包含在中。
- 当步骤四中所有,则。
图4.3get_First()函数流程图
反复使用上述2~5步,最后对每个符号的FIRST集合去除重复元素。
(2)求符号串的FIRST集合
求出每个文法符号的FIRST集合后,也就不难求出一个符号串的FIRST集合。
若符号串,当不能,则置。对产生式右部串,判断是否都含有空元素,记录最后一个FIRST集含有空元素的元素下标k,若该下标不等于右部串长度,即串元素的FIRST集合部分含有空,,则使得
当k等于右部串长度时,也即对任何都含有,则
图4.4 get_Firsts()函数流程图
4.4 计算FOLLOW集
对文法中每一个,计算FOLLOW(A)。
(1)设S为文法的开始符号,把{#}加人FOLLOW(S)中(这里#为句子括号),并初始化FOLLOW集均为空。
(2)循环查找,若识别到形如的产生式,则获取串,判断该串是否可推导出空,把 FIRST()的非空元素加入FOLLOW(B)中,FIRST()串可能尚未计算过,这里需要另计算FIRST(),将串中的每个符号的FIRST集中的非空元素加入其中,直到某符号推导不出空。
如果,则把FOLLOW(A)也加人FOLLOW(B)中,
图4.5 get_Follow()函数流程图
(3)重复使用(2),最后对FOLLOW集进行去重操作。
4.5 计算SELECT集
扫面文法的全部产生式,获取产生式右部串,存储在s中,同时进行分析,若不能推导出空,则修改标记。按照SELECT集定义对于产生式,若,则
否则,
4.6 判断LL(1)文法
按照定义判断是否是LL(1)文法,对比同一非终结符的产生式对应的SELECT集合是否相交。需要注意的是,在实际实现过程中需要对SELECT集的元素进行循环比较,不能简单的判断两集合是否相等,若相交则修改标记。
对标记进行判断,若不是LL(1)文法,则暂停程序。
4.7 构造预测分析表
SELECT集本身存储了产生式,在构造预测分析表时,直接遍历SELECT即可,将产生式左部的非终结符,对应的SELECT集内的元素,以及相应的产生式,按照定义存储在分析表中。
4.8 分析输入串
首先处理输入串,压入一个’#’号,分析栈压入’#’和文法开始符号。t存取当前栈顶元素。如果栈顶符号与输入串当前元素不匹配,则到LL(1)分析表中查找Anlysis(t,s[i]),若存在可用产生式,则获取到该产生式右部,替换栈顶符号(注意入栈顺序);若不存在产生式,则输出错误信息,结束分析。
如果栈顶符号与输入串当前元素相匹配,则分析栈弹出栈顶符号,输入串指针下标向下移动,读入下一个输入符号;若两者的匹配符号为’#’,则表示该串是此LL(1)文法的句子。
图4.6 anlysis()函数流程图