在grammarStr中设置好相应的文法,inStr中设置好相应的输入串(以#结尾)即可自动实现并输出构造First集、Follow集、预测分析表、预测分析总过程。
相关说明
- First集构造算法:
- Follow集构造算法
- Select集构造算法:
- 请注意本程序中并未单独为构造Select集编写相应的函数,而是通过GetFirstX()方法构造每条产生式右部的首符集。在构造预测分析表的过程中,如果某条产生式右部的首符集包括空,则不仅将该产生式右部加入到其首符集对应的预测分析表中,而且将产生式右部加入到左部非终结符的Follow集对应的预测分析表中。(这段话看不懂没关系,看代码就好了)
Java代码如下:
import java.util.*;
/**
* class: --LL1: 实现LL(1)分析,构造分析预测程序(FIRST集-->FOLLOW集-->预测分析表-->stack预测分析)
*/
public class LL1 {
static String[] grammarStr = { "E->TA", "A->+TA|ε", "T->FB", "B->*FB", "B->ε", "F->i", "F->(E)" };// 相关文法
static HashMap<Character, ArrayList<String>> production = new HashMap<>();// 产生式
static HashMap<Character, HashSet<Character>> FirstSet = new HashMap<>();// 构造FIRST集合
static HashMap<String, HashSet<Character>> FirstSetX = new HashMap<>();// 生成任何符号串的first
static HashMap<Character, HashSet<Character>> FollowSet = new HashMap<>();// 构造FOLLOW集合
static String[][] table;// 预测分析表
static HashSet<Character> VnSet = new HashSet<>();// 非终结符Vn集合
static HashSet<Character> VtSet = new HashSet<>();// 终结符Vt集合
static Stack<Character> stack = new Stack<>(); // 符号栈
static String inStr = "i+i*i#";// 输入串
static Character start = '0';
static int index = 0;// 输入字符指针
static String action = "";
public static void main(String[] args) {
// 求得production,终结符和非终结符,
dividechar();
// 求得所有符号的First集
First();
// 求得所有产生式右部的首符集
for (Character c : VnSet) {
ArrayList<String> l = production.get(c);
for (String s : l)
getFirstX(s);
}
// 计算Follow集合
Follow();
// 构造预测分析表
creatTable();
// 输出First集合、Follow集、预测分析表
ouput();
// 分析栈处理
processLL1();
}
/**
* 生成产生式Map(production),划分终结符(vt)与非终结符(vn)
*/
static void dividechar() {
// 开始符号
start = grammarStr[0].charAt(0);
// 生成产生式Map(production)
for (String str : grammarStr) {
// 将“|”相连的产生式分开
String[] strings = str.split("->")[1].split("\\|");
// 非终结符
char Vch = str.charAt(0);
ArrayList<String> list = production.containsKey(Vch) ? production.get(Vch) : new ArrayList<String>();
for (String S : strings) {
list.add(S);
}
// 将非终结符以及对应的所有产生式加入到production中
production.put(Vch, list);
// 加入非终结符
VnSet.add(Vch);
}
// 寻找终结符
for (Character ch : VnSet) {
for (String str : production.get(ch)) {
for (Character c : str.toCharArray()) {
if (!VnSet.contains(c))
VtSet.add(c);
}
}
}
}
/**
* 生成非终结符的FIRST集的递归入口
*/
static void First() {
// 循环求first直到first集合中元素不再添加
int preFirstSize = 0;
while (true) {
int afterFirstSize = 0;
// 遍历求每一个非终结符Vn的first集
for (Character vn : VnSet) {
getfisrst(vn);
}
// 一次计算后first集中元素数量
for (Character chVn : VnSet) {
afterFirstSize += FirstSet.get(chVn).size();
}
// 计算follow后元素数量没有变化
if (preFirstSize == afterFirstSize) {
break;
}
preFirstSize = afterFirstSize;
}
}
/**
* 生成非终结符FIRST集的递归程序
*/
static void getfisrst(Character ch) {
ArrayList<String> ch_production = production.get(ch);
HashSet<Character> set = FirstSet.containsKey(ch) ? FirstSet.get(ch) : new HashSet<>();
// 当ch为终结符
if (VtSet.contains(ch)) {
set.add(ch);
FirstSet.put(ch, set);
return;
}
// ch为非终结符
for (String str : ch_production) {
int i = 0;
while (i < str.length()) {
char tn = str.charAt(i);
// 递归
getfisrst(tn);
HashSet<Character> tvSet = FirstSet.get(tn);
// 将其first集加入左部
for (Character tmp : tvSet) {
if (tmp != 'ε')
set.add(tmp);
}
// 若包含空串 处理下一个符号
if (tvSet.contains('ε'))
i++;
// 否则退出 处理下一个产生式
else
break;
}
// 产生式右部都包括ε
if (i == str.length())
set.add('ε');
}
FirstSet.put(ch, set);
}
/**
* 生成任何符号串的first
*/
static void getFirstX(String s) {
HashSet<Character> set = (FirstSetX.containsKey(s)) ? FirstSetX.get(s) : new HashSet<Character>();
// 从左往右扫描该式
int i = 0;
while (i < s.length()) {
char tn = s.charAt(i);
if (!FirstSet.containsKey(tn))
getfisrst(tn);
HashSet<Character> tvSet = FirstSet.get(tn);
// 将其非空 first集加入左部
for (Character tmp : tvSet)
if (tmp != 'ε')
set.add(tmp);
// 若包含空串 处理下一个符号
if (tvSet.contains('ε'))
i++;
// 否则结束
else
break;
// 到了尾部 即所有符号的first集都包含空串 把空串加入
if (i == s.length()) {
set.add('ε');
}
}
FirstSetX.put(s, set);
}
/**
* 生成FOLLOW集
*/
static void Follow() {
int preFollowSize = 0;
while (true) {
int afterFollowSize = 0;
// 求得所有非终结符的Follow集
for (Character character : VnSet) {
getFollow(character);
}
// 一次计算后follow集中元素数量
for (Character chVn : VnSet) {
afterFollowSize += FollowSet.get(chVn).size();
}
// 计算follow后元素数量没有变化
if (preFollowSize == afterFollowSize) {
break;
}
preFollowSize = afterFollowSize;
}
}
/**
* 求Follow集
*/
static void getFollow(char c) {
ArrayList<String> list = production.get(c);
HashSet<Character> setA = FollowSet.containsKey(c) ? FollowSet.get(c) : new HashSet<Character>();
// 如果是开始符 添加 #
if (c == start) {
setA.add('#');
}
// 查找输入的所有产生式,确定c的后跟 终结符
for (Character ch : VnSet) {
ArrayList<String> l = production.get(ch);
for (String s : l)
for (int i = 0; i < s.length() - 1; i++)
if (s.charAt(i) == c && VtSet.contains(s.charAt(i + 1)))
setA.add(s.charAt(i + 1));
}
FollowSet.put(c, setA);
// 处理c的每一条产生式
for (String s : list) {
int i = s.length() - 1;
while (i >= 0) {
char tn = s.charAt(i);
// 只处理非终结符
if (VnSet.contains(tn)) {
// 都按 A->αBβ 形式处理
// 若β不存在 followA 加入 followB
// 若β存在,把β的非空first集 加入followB
// 若β存在 且 first(β)包含空串 followA 加入 followB
// 若β存在
if (s.length() - i - 1 > 0) {
String right = s.substring(i + 1);
// 非空first集 加入 followB
HashSet<Character> setF = null;
if (right.length() == 1) {
if (!FirstSet.containsKey(right.charAt(0)))
getfisrst(right.charAt(0));
setF = FirstSet.get(right.charAt(0));
} else {
// 先找出右部的first集
if (!FirstSetX.containsKey(right))
getFirstX(right);
setF = FirstSetX.get(right);
}
HashSet<Character> setX = FollowSet.containsKey(tn) ? FollowSet.get(tn)
: new HashSet<Character>();
for (Character var : setF)
if (var != 'ε')
setX.add(var);
FollowSet.put(tn, setX);
// 若first(β)包含空串 ,则把followA 加入 followB
if (setF.contains('ε')) {
if (tn != c) {
HashSet<Character> setB = FollowSet.containsKey(tn) ? FollowSet.get(tn)
: new HashSet<Character>();
for (Character var : setA)
setB.add(var);
FollowSet.put(tn, setB);
}
}
}
// 若β不存在,则把followA 加入 followB
else {
// A和B相同不添加
if (tn != c) {
HashSet<Character> setB = FollowSet.containsKey(tn) ? FollowSet.get(tn)
: new HashSet<Character>();
for (Character var : setA)
setB.add(var);
FollowSet.put(tn, setB);
}
}
i--;
}
// 如果是终结符往前看 如 A->aaaBCDaaaa 此时β为 CDaaaa
// 终结符不计算Follow集
else
i--;
}
}
}
/**
* 生成预测分析表
*/
static void creatTable() {
Object[] VtArray = VtSet.toArray();
Object[] VnArray = VnSet.toArray();
// 预测分析表初始化
table = new String[VnArray.length + 1][VtArray.length + 1];
table[0][0] = "Vn\\Vt";
// 初始化首行首列
for (int i = 0; i < VtArray.length; i++)
table[0][i + 1] = (VtArray[i].toString().charAt(0) == 'ε') ? "#" : VtArray[i].toString();
for (int i = 0; i < VnArray.length; i++)
table[i + 1][0] = VnArray[i] + "";
// 全部置error
for (int i = 0; i < VnArray.length; i++)
for (int j = 0; j < VtArray.length; j++)
table[i + 1][j + 1] = "error";
// 插入生成式
for (char A : VnSet) {
ArrayList<String> l = production.get(A);
for (String s : l) {
HashSet<Character> set = FirstSetX.get(s);
for (char a : set) {
// 产生式右部首符集中如果有空,在这里跳过
if (a == 'ε') {
continue;
}
insert(A, a, s);
}
// 如果产生式右部的首符集包含空,即产生式右部所有符号的首符集都包括空,则将左部的后继符集加入选择符集中
if (set.contains('ε')) {
HashSet<Character> setFollow = FollowSet.get(A);
// if (setFollow.contains('#'))
// insert(A, '#', s);
for (char b : setFollow)
insert(A, b, s);
}
}
}
}
/**
* 将生成式插入表中
*/
static void insert(char X, char a, String s) {
if (a == 'ε')
return;
// a = '#';
for (int i = 0; i < VnSet.size() + 1; i++) {
if (table[i][0].charAt(0) == X)
for (int j = 0; j < VtSet.size() + 1; j++) {
if (table[0][j].charAt(0) == a) {
table[i][j] = s;
return;
}
}
}
}
/**
* 执行LL1栈分析
*/
static void processLL1() {
System.out.println("****************LL分析过程**********");
System.out.println(
" 栈 剩余输入串 动作");
stack.push('#');
stack.push(start);
char X = stack.peek();
while (X != '#') {
char a = inStr.charAt(index);
// 匹配
if (X == a) {
action = "匹配 " + stack.peek();
displayLL();
stack.pop();
index++;
}
// 预测分析表为error
else if (find(X, a).equals("error")) {
// boolean flag = false;
// 如果X首符集包含空,直接弹出X,处理下一个元素
if (FirstSet.get(X).contains('ε')) {
action = X + "->ε";
displayLL();
stack.pop();
// flag = true;
}
}
// 预测分析表中为空,特殊处理
else if (find(X, a).equals("ε")) {
action = X + "->ε";
displayLL();
stack.pop();
}
// 预测分析表中找到或查无此项
else {
String str = find(X, a);
if (str != "") {
action = X + "->" + str;
displayLL();
stack.pop();
int len = str.length();
String pushStr = "";
for (int i = len - 1; i >= 0; i--) {
stack.push(str.charAt(i));
pushStr += str.charAt(i);
}
}
// a为非法符号
else {
action = "出错";
displayLL();
System.out.println("在处理该字符时发生错误 :" + inStr.charAt(index) + "\n再输入流的位置下标: " + index);
return;
}
}
X = stack.peek();
}
action = "接受";
displayLL();
System.out.println("匹配成功!");
System.out.println();
}
/**
*
* @param X
* 非终结符
* @param a
* 终结符
* @return 预测分析表中对应内容,如果在预测分析表中没有找到返回空字符串
*/
static String find(char X, char a) {
for (int i = 0; i < VnSet.size() + 1; i++) {
if (table[i][0].charAt(0) == X)
for (int j = 0; j < VtSet.size() + 1; j++) {
if (table[0][j].charAt(0) == a)
return table[i][j];
}
}
return "";
}
static void displayLL() {
// 输出 LL1单步处理
Stack<Character> s = stack;
System.out.printf("%23s", s);
System.out.printf("%13s", inStr.substring(index));
System.out.printf("%10s", action);
System.out.println();
}
/**
* 打印first.follow集,预测分析表
*/
static void ouput() {
System.out.println("***********First集********");
for (Character c : VnSet) {
HashSet<Character> set = FirstSet.get(c);
System.out.print(c + " : ");
for (Character var : set)
System.out.print(var + " ");
System.out.println();
}
System.out.println();
System.out.println("**********Follow集*********");
for (Character c : VnSet) {
HashSet<Character> set = FollowSet.get(c);
System.out.print(c + " :");
for (Character var : set)
System.out.print(var + " ");
System.out.println();
}
System.out.println();
System.out.println("**********LL1预测分析表********");
for (int i = 0; i < VnSet.size() + 1; i++) {
for (int j = 0; j < VtSet.size() + 1; j++) {
System.out.printf("%6s", table[i][j] + " ");
}
System.out.println();
}
System.out.println();
}
}