看了网站的某些文章,发现许多人对规则一知半解就进行实验,虽然针对书上例子结果正确,但换一种文法、加两条语句、换一个输入就漏洞百出。
- 做实验前一定要把书弄懂
- 书上给的伪代码描述不清楚,尽信书不如无书
希望这篇文章能给大家带来帮助,错误欢迎指出。
代码在https://github.com/monimm/LLandLR
实验内容
根据某一文法编写LL(1)分析程序,对输入的句子进行语法分析。
SLR(1)的实验在此环境下完成
先贴实验结果
输入产生式,ε用~代替
“E->TK”, “K->+TK”, “K->~”, “T->FM”, “M->*FM”, “M->~”, “F->i”, “F->(E)”
后附源码:
实验要求
- 输入各个产生式
- 获取first集、follow集
- 构造LL(1)预测分析表
- 输入一个句子
- 根据LL(1)预测分析表对句子进行栈操作,输出分析结果
实验过程
构造first集(龙书4.4.2节有详细介绍)
First(α)定义为可从α推出的串的首符号集合 (α为任意文法符号串)
1.构造单个文法符号X的first集
- 如果X为终结符,First(X)=X
- 如果X->ε是产生式,把ε加入First(X)
- 如果X是非终结符,如X->YZW。从左往右扫描产生式右部,把First(Y)加入First(X)。如果First(Y)不包含ε,表示Y不可为空,便不再往后处理;如果First(Y)包含ε,表示Y可为空,则处理Z,依次类推。
2.构造文法符号串的first集,如:X->YZW;求YZW的first集
- 从左往右扫描该式,加入其非空first集:把First(Y)加入First(YZW)
- 若包含空串 处理下一个符号:如果First(Y)包含空串,便处理Z;不包含就退出;处理到尾部,即所有符号的first集都包含空串 把空串加入First(YZW)
构造follow集(龙书4.4.2节有详细介绍)
对于非终结符A,Follow(A)是可能在某些句型中紧跟在A后的终结符的集合
在计算First(X)集之后的基础上
1.$属于FOLLOW(S),S是开始符
2.查找输入的所有产生式,确定X后紧跟的终结符
3.如果存在A->αBβ,(α、β是任意文法符号串,A、B为非终结符),把first(β)的非空符号加入follow(B)
4.如果存在A->αB或A->αBβ 但first(β)包含空,把follow(A)加入follow(B)
栗子:X->YZWEFG 假设全为非终结符
处理G:
- 把first(G)加入follow(F);
- 把follow(X)加入follow(G)
- 如果first(G)包含空,把follow(X)加入follow(F),向前处理
处理F:
- 把first(FG)加入follow(E);
- 如果first(FG)包含空,把follow(X)加入follow(E),向前处理
依次处理E、W、Z、Y
构造预测分析表(龙书4.4.3节有详细介绍)
对于每个产生式A->α
- first(α)中的终结符a,把A->α加入M[A,a]
- 如果空串在first(α)中,说明可为空,找它的follow集,对于follow(A)中的终结符b,把A->α加入M[A,b]
- 如果空串在first(α)中,且 ‘$’
- 也在follow(A)中,说明A后可以接终结符,故把A->α加入M[A,$]中
执行分析(龙书算法4.34有详细介绍)
输入一个串,文法G的预测分析表,输出推导过程
$ 和 开始符S入栈
栈顶符号X,index指向分析串的待分析符号a
栈非空执行以下循环
- 如果X==a,表示匹配,符号栈弹出一个,index++指向下一个符号
- 否则,如果X是终结符,出错
- 否则,如果查表为error,出错
- 否则,查表正确,弹出栈顶符号,把其右部各个符号进栈
- 令X为栈顶符号
源码:
Tips:
- 用~代替ε
- first集是终结符的first集;firstX集是指任意符号串的first集
package compiler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
public class ll1 {
public static void main(String[] args) {
Test test = new Test();
test.getNvNt();
test.Init();
test.createTable();
test.analyzeLL();
test.ouput();
}
}
class Test {
public HashMap<Character, HashSet<Character>> firstSet = new HashMap<Character, HashSet<Character>>();
public HashMap<String, HashSet<Character>> firstSetX = new HashMap<String, HashSet<Character>>();
public Character S = 'E';
public HashMap<Character, HashSet<Character>> followSet = new HashMap<Character, HashSet<Character>>();
public HashSet<Character> VnSet = new HashSet<Character>();
public HashSet<Character> VtSet = new HashSet<Character>();
public HashMap<Character, ArrayList<String>> experssionSet = new HashMap<Character, ArrayList<String>>();
public String [] inputExperssion = { "E->TK", "K->+TK", "K->~", "T->FM", "M->*FM", "M->~", "F->i", "F->(E)"};
public String [][] table;
public Stack<Character> analyzeStatck = new Stack<Character>();
public String strInput = "i+i*i$";
public String action = "";
int index = 0;
public void Init() {
for (String e : inputExperssion) {
String[] str = e.split("->");
char c = str[0].charAt(0);
ArrayList<String> list = experssionSet.containsKey(c) ? experssionSet.get(c) : new ArrayList<String>();
list.add(str[1]);
experssionSet.put(c, list);
}
for (Character c : VnSet)
getFirst(c);
for (Character c : VnSet) {
ArrayList<String> l = experssionSet.get(c);
for (String s : l)
getFirst(s);
}
getFollow(S);
for (Character c : VnSet) {
getFollow(c);
}
}
public void getNvNt() {
for (String e : inputExperssion) {
String[] str = e.split("->");
VnSet.add(str[0].charAt(0));
}
for (String e : inputExperssion) {
String[] str = e.split("->");
String right = str[1];
for (int i = 0; i < right.length(); i++)
if (!VnSet.contains(right.charAt(i)))
VtSet.add(right.charAt(i));
}
}
public void getFirst(Character c) {
ArrayList<String> list = experssionSet.get(c);
HashSet<Character> set = firstSet.containsKey(c) ? firstSet.get(c) : new HashSet<Character>();
// c为终结符 直接添加
if (VtSet.contains(c)) {
set.add(c);
firstSet.put(c, set);
return;
}
// c为非终结符 处理其每条产生式
for (String s : list) {
// c 推出空串 直接添加
if (s == Character.toString('~')) {
set.add('~');
}
// X -> Y1Y2Y3… 情况
else {
// 从左往右扫描生成式右部
int i = 0;
while (i < s.length()) {
char tn = s.charAt(i);
//先处理防止未初始化
getFirst(tn);
HashSet<Character> tvSet = firstSet.get(tn);
// 将其first集加入左部
for (Character tmp : tvSet)
set.add(tmp);
// 若包含空串 处理下一个符号
if (tvSet.contains('~'))
i++;
// 否则退出 处理下一个产生式
else
break;
}
}
}
firstSet.put(c, set);
}
public void getFirst(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);
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);
}
public void getFollow(char c) {
ArrayList<String> list = experssionSet.get(c);
HashSet<Character> setA = followSet.containsKey(c) ? followSet.get(c) : new HashSet<Character>();
//如果是开始符 添加 $
if (c == S) {
setA.add('$');
}
//查找输入的所有产生式,确定c的后跟 终结符
for (Character ch : VnSet) {
ArrayList<String> l = experssionSet.get(ch);
for (String s : l)
for (int i = 0; i < s.length(); i++)
if (s.charAt(i) == c && i + 1 < s.length() && 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 && firstSet.containsKey(right.charAt(0)))
setF = firstSet.get(right.charAt(0));
else{
if(!firstSetX.containsKey(right)){
HashSet<Character> set = new HashSet<Character>();
firstSetX.put(right, set);
}
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
else i--;
}
}
}
public void createTable() {
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 = experssionSet.get(A);
for(String s : l){
HashSet<Character> set = firstSetX.get(s);
for (char a : set)
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);
}
}
}
}
public void analyzeLL() {
System.out.println("****************LL分析过程**********");
System.out.println(" Stack Input Action");
analyzeStatck.push('$');
analyzeStatck.push('E');
displayLL();
char X = analyzeStatck.peek();
while (X != '$') {
char a = strInput.charAt(index);
if (X == a) {
action = "match " + analyzeStatck.peek();
analyzeStatck.pop();
index++;
} else if (VtSet.contains(X))
return;
else if (find(X, a).equals("error"))
return;
else if (find(X, a).equals("~")) {
analyzeStatck.pop();
action = X + "->~";
} else {
String str = find(X, a);
if (str != "") {
action = X + "->" + str;
analyzeStatck.pop();
int len = str.length();
for (int i = len - 1; i >= 0; i--)
analyzeStatck.push(str.charAt(i));
} else {
System.out.println("error at '" + strInput.charAt(index) + " in " + index);
return;
}
}
X = analyzeStatck.peek();
displayLL();
}
System.out.println("analyze LL1 successfully");
System.out.println("****************LL分析过程**********");
}
public 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 "";
}
public void insert(char X, char a,String s) {
if(a == '~') 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;
}
}
}
}
public void displayLL() {
// 输出 LL1
Stack<Character> s = analyzeStatck;
System.out.printf("%23s", s);
System.out.printf("%13s", strInput.substring(index));
System.out.printf("%10s", action);
System.out.println();
}
public void ouput() {
System.out.println("*********first集********");
for (Character c : VnSet) {
HashSet<Character> set = firstSet.get(c);
System.out.printf("%10s",c + " -> ");
for (Character var : set)
System.out.print(var);
System.out.println();
}
System.out.println("**********first集**********");
System.out.println("*********firstX集********");
Set<String> setStr = firstSetX.keySet();
for (String s : setStr) {
HashSet<Character> set = firstSetX.get(s);
System.out.printf("%10s",s + " -> ");
for (Character var : set)
System.out.print(var);
System.out.println();
}
System.out.println("**********firstX集**********");
System.out.println("**********follow集*********");
for (Character c : VnSet) {
HashSet<Character> set = followSet.get(c);
System.out.print("Follow " + c + ":");
for (Character var : set)
System.out.print(var);
System.out.println();
}
System.out.println("**********follow集**********");
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("**********LL1预测分析表********");
}
}
参考文章:https://blog.csdn.net/puhaiyang/article/details/51793550,对其优化简化,并更正了一些错误。