一、实验目的
学生运用编译原理的知识在实验技能和方法自行设计实验方案并加以实现。
二、使用仪器、器材
计算机一台
操作系统:Windows10
编程软件:Intellij IDEA
三、实验内容及原理
1.实验内容:
输入任意一个正确的文法G[S],都能分析,并得出一个等价的LL(1)文法G[E],求出FIRST集和FOLLOW集,并求出LL(1)分析表。对输入串进行语法分析,判断其是否符合文法G[E]。
2.要求:
(1)输入任意一个正确的文法G[S],都能分析,并得出一个等价的LL(1)文法G[E];
(2)根据该LL(1)文法G[E]的文法规则建立LL(1)分析表;
(3)输出输入串分析过程。
3. 原理:
3.1 FIRST集构造原理:
3.2 FOLLOW集构造原理:
这里FOLLOW集的表述难以理解,可以转换成如下表述:
1.首先判断非终结符A是否是文法开始符,如果是,则 # ∈ FOLLOW(A)
2.查看A在哪几个产生式的右部出现
3.查看A是否位于产生式右部某候选式的最右端,
若是,则选用第三条规则
若否,则选用第二条规则且好看该候选项中A之后的符号串能否推导为空串,若能,则再次选用第三条规则
3.3 分析表构造原理:
一、主程序示意图
二、扫描子程序算法思想
1.构造FIRST集:
遍历每一个产生式
{
if(产生式右部第一个字符a为终结符)
{
// S➡a...
a 加入FIRST(S)
}
else
{
// S➡A1...
求FIRST(A1)
FIRST(A1)\{ε}加入FIRST(S)
// S➡A1A2...An
如果FIRST(A1)包含空串,则检查A2...An,将FIRST(Ai)\\{ε}加入FIRST(S),直到其中一个不包含空串
如果A1到An的FIRST都包含空串,则空串加入FIRST(A1)
}
}
2.构造FOLLOW集:
遍历每一个产生式右部包含S的产生式
{
if(S在产生式最右部)
{
// E → ...S,使用规则三
// FOLLOW(E) ∈ FOLLOW(S)
将FOLLOW(E)加入到FOLLOW(S)
}
else
{
// E → αSβ,使用规则二
// 将FIRST(β)\{ε}加入FOLLOW,如果FIRST(β)包含ε,则对β使用规则三
if(β 属于 Vt)
{
// FIRST(β) = {β}
直接将β加入FOLLOW(E)
}
else
{
FIRST(β)\{ε}加入FOLLOW(E)
if(FIRST(β)包含ε)
{
FOLLOW(β)加入FOLLOW(E)
}
}
}
}
3.构造分析表:
for(非终结符A:非终结符集)
{
获取FIRST(A)
对于FIRST集的每一个元素
找到对应产生式,加入分析表
如果FIRST集包含空串,则对于任意b(b∈vt)属于FOLLOW(A),A->空串加入到M[A,b]
}
4.语法分析程序:
四、实验过程原始记录
1.目录结构
2.类说明
该程序包含两个核心类,GrammarProcessUtil和MyGrammarProcessor。GrammarProcessUtil:处理FIRST、FOLLOW和分析表
MyGrammarProcessor:进行语法分析,判断某个句子是否符合语法
3.主要属性说明
/**
* 保存非终结符在 vnList中的位置
*/
private Map<String,Integer> vnMap = new HashMap<>();
/**
* 保存终结符在 vtList 中的位置
*/
private Map<String,Integer> vtMap = new HashMap<>();
/**
* 非终结符集
*/
private List<String> vnList = new ArrayList<>();
/**
* 终结符集
*/
private List<String> vtList = new ArrayList<>();
/**
* 分析表
*/
private String[][] table = null;
/**
* 保存非终结符的产生式
*/
private Map<String,List<String>> productionMap = new HashMap<>();
/**
* 产生式的分隔符
*/
private final String SEPARATOR = "→";
/**
* 候选式的分隔符
*/
private final String CANDIDATE_SEPARATOR = "|";
/**
* 空串
*/
private final String BLANK_STRING = "ε";
/**
* FIRST集
*/
private Map<String,Set<String>> first = new HashMap<>();
/**
* FOLLOW集
*/
private Map<String,Set<String>> follow = new HashMap<>();
- vnMap、vnList和vtMap、vtList是用来保存终结符与非终结符信息的。
其中Map用来保存字符存储在List的下标,List用来保存字符。 - table是一个二维数组,用来保存分析表,下标可根据Map来获得。
- productionMap用来保存每个非终结符对应的产生式的候选式(如S→a|Ba|C)则 productionMap中的数据为 键“S”对应的值为[“a”,“Ba”,“C”]
- first和follow分别为FIRST集与FOLLOW集
4.函数说明
红框内的函数是主要函数,都是私有函数:
- initFirst():初始化FIRST集
- getFirst(): 获取某个非终结符的FIRST集
- initFollow():初始化FOLLOW集
- getFollow():获取某个非终结符的FOLLOW集
- getTable(): 初始化分析表
- printXXX(): 打印
绿框内的函数是公开函数,提供给外部使用: - get():根据终结符与非终结符返回分析表中的内容
- getGrammarStart(): 返回文法开始符
其他函数是私有函数,则是起到一些辅助功能,具体可以查看代码中的注释
5.程序清单
/**
* 语法分析器工具类,处理FIRST集、FOLLOW集和分析表
* @Author DELL
* @create 2020/12/4 15:26
*/
public class GrammarProcessorUtil {
/**
* 保存非终结符在 vnList中的位置
*/
private Map<String,Integer> vnMap = new HashMap<>();
/**
* 保存终结符在 vtList 中的位置
*/
private Map<String,Integer> vtMap = new HashMap<>();
/**
* 非终结符集
*/
private List<String> vnList = new ArrayList<>();
/**
* 终结符集
*/
private List<String> vtList = new ArrayList<>();
/**
* 分析表
*/
private String[][] table = null;
/**
* 保存非终结符的产生式
*/
private Map<String,List<String>> productionMap = new HashMap<>();
/**
* 产生式的分隔符
*/
private final String SEPARATOR = "→";
/**
* 候选式的分隔符
*/
private final String CANDIDATE_SEPARATOR = "|";
/**
* 空串
*/
private final String BLANK_STRING = "ε";
/**
* FIRST集
*/
private Map<String,Set<String>> first = new HashMap<>();
/**
* FOLLOW集
*/
private Map<String,Set<String>> follow = new HashMap<>();
/**
* 将路径拼接为类路径
* @param path
* @return
*/
private String getClasspath(String path) {
return Thread.currentThread().getContextClassLoader().getResource(path).getPath();
}
/**
* 将产生式按照候选式的分隔符进行分割
* 例:str:"E'a|ε" => ["E'a","ε"]
* str:“E'a" => ["E'a"]
* @param str
* @return
*/
private List<String> splitBySeparator(String str) {
List<String> ans = new ArrayList<>();
if(!str.contains(CANDIDATE_SEPARATOR)) {
// 不包含分隔符,不可分割
ans.add(str);
return ans;
} else {
// 包含分隔符,进行分割
String[] split = str.split("\\|");
Collections.addAll(ans,split);
return ans;
}
}
/**
* 将字符串分割成一个个字符
* 注:主要是处理带单引号的字符
* 例:str:"E'aT" => ["E'","a","T"]
* @param str
* @return
*/
private List<String> getCharacter(String str) {
List<String> ans = new ArrayList<>();
int end = str.length();
for(int start = str.length()-1;start >= 0;start--) {
if(str.charAt(start) == '\'') {
continue;
} else {
String substring = str.substring(start, end);
ans.add(substring);
end = start;
}
}
return ans;
}
/**
* 判断字符串str是否完全包含 regex,返回下标
* 例:str:"aEb",regex="E" => 1
* str:"aE'b",regex="E" => -1
* str:"aE'b",regex="E'" => 1
* @param str
* @param regex
* @return
*/
private int contains(String str,String regex) {
char[] ch = str.toCharArray();
if(regex.length() == 2) {
for (int i = 0; i < ch.length - 1; i++) {
if (ch[i] == regex.charAt(0) && ch[i + 1] == regex.charAt(1)) {
return i;
}
}
} else if(regex.