First集
- 求文法符号串所可能推导出的符号串中第一个
终结符
的集合 - 文法符号串的First集可能有以下的情况:[这里假设所有
非终结符
为大写字母,终结符
为小写字母]- 单个以终结符号开头的符号串的First集合
例如 最简单的一种情况 若 S -> aB..., 那么可以说First(S) ={a} 若 S-> ε , 当S推导出空串时,则 First(s) = {ε}
- 单个以非终结符开头的符号串的First集合
S->A..., 此时我们需要求出First(A)来作为S的First集(前提是First(A)中不含空串)
- 多个符号形成的符号串的First集合
S->aA... S->b S->BC.. 以上可以写为 S->aA...|b|BC... 此时 First(S) -> {a}U{b} U First(B)
注意!!
上述若First(B) ={ε,…}即B的First集中有空串时,还要并上First(C)
,若First(C)
={ε,…},则需要并上下一符号的First集,以此类推
- 单个以终结符号开头的符号串的First集合
Follow集
- 文法符号串后面可能紧跟的
终结符
的集合,注意, ε不能存在于Follow集中
- 对于文法的开始符号S, 将
#
置于Follow(S)中我们来看下简单的小例子: 文法的开始符号为S, S->aB C->SD D->aB|cC 由于S是开始符号,而且在下一个产生式中D紧跟在S后边,所以 Follow(S)={#} U First(D)={#,a,c}
注意!!,上面例子中 First(D)不含ε, 若含有ε,则需要将Follow(D)-{ε}
- 为了加深印象,我们对上面的示例进行改造:
S->aB C->SDE D->ε|aB|cC E->e 由于First(D)中含有ε,当选择 D->ε时,E也会跟在S后边,所以需要加上First(E),同时去除First(D)中的ε 此时 Follow(S) = (First(D)-{ε}) U First(E)U{#} = {#,a,c,e}
Select集
- Select集相对比较容易计算,它表示的是一条产生式的可选集,对于A->a
- 如果 ε ∉ \notin ∈/ First(a),则 Select(A->a) = First(a)
- 如果 ε ∈ \in ∈ First(a),则Select(A-a) = (First(a) - {ε}) U Follow(A)
- 示例
S->aB C->SDE D->ε|aB|cC E->e 对于产生式 S-> aB, 由于First(aB)不含 ε, 所以Select(S-aB) = First(aB)={a} 假使: S->AB A->a|ε 则 Select(S->AB) = (First(AB) -{ε}) U Follow(S)
注!!Select集在我们LL(1)文法的构建预测分析表中起着非常关键的作用
编码实现
package FirstandFollow;
import java.util.*;
public class clearRecursion {
class P { // 表示一条产生式
String key; // 产生式的左部
String[] value=new String[]{}; // 产生式的右部
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String[] getValue() {
return value;
}
public void setValue(String value) {
this.value = value.split("\\|");
}
public void addValue(String value) {
this.value = insert(value, getValue());
}
public void addValue(String[] values) {
for (String value : values) {
addValue(value);
}
}
public void deleteValue(String value) {
this.value = delete(value, getValue());
}
public int getCount() {
return this.value.length;
}
private String[] delete(String value, String[] values) {
int length = values.length;
String[] newValues = new String[length-1];
int index = 0;
for (int i = 0; i< length; i++) {
if (values[i].equals(value)) {
index = i;
break;
}
}
System.arraycopy(values,0,newValues,0,index);
if (index < length -1) {
System.arraycopy(values, index+1,newValues,index,newValues.length-index);
}
return newValues;
}
private String[] insert(String value, String[] values) {
int length = values.length;
String[] newValues = new String[length + 1];
newValues[length] = value;
System.arraycopy(values, 0, newValues, 0, length);
return newValues;
}
// 设置 右部规则的数量
// public void setCount(int count) {
// if (count == 0) { // 如果没有设置, 根据value自动算出有几组规则
// this.count = value.split("\\|").length;
// } else { // 设置的话就赋值
// this.count = count;
// }
// }
}
class G { // 用于表示该文法
String[] VN; // 表示非终结符的集合
String[] VT; // 表示终结符的集合
List<P> pList = new ArrayList<>(); // 产生式的集合
String start; // 定义开始符号
public String[] getVN() {
return VN;
}
public void setVN(String VN) {
this.VN = VN.split("");
}
public void addVN(String VN) {
this.VN = insert(VN,this.VN);
}
public String[] getVT() {
return VT;
}
public void setVT(String VT) {
this.VT = VT.split("");
}
public List<P> getpList() {
return pList;
}
public void setpList(List<P> pList) {
this.pList = pList;
}
public void addP(P p) {
this.pList.add(p);
}
public String getStart() {
return start;
}
public void setStart(String start) {
this.start = start;
}
public int getpCount() {
return pList.size();
}
public P findPbyKey(String key) {
P result = null;
for (P p: pList) {
if (p.getKey().equals(key)) {
result = p;
}
}
return result;
}
// 向数组中插入元素,插入到最后一个
private String[] insert(String VN, String[] VNarray) {
int length = VNarray.length;
String[] newVNarray = new String[length + 1];
newVNarray[length] = VN;
System.arraycopy(VNarray, 0, newVNarray, 0, VNarray.length);
return newVNarray;
}
}
public G g = new G();
/**
* 初始化 文法
* E->E+T|T
* T->T*F|F
* F->i|(E)
*
*/
public void InitG() {
// g.setVN("SQR"); // 设置 非终结符
// g.setVT("abc"); // 设置终结符
// g.setStart("S"); // 设置开始符号
//
//
// P p1 = new P();
//
// // S->Qc|c
// p1.setKey("S");
// p1.setValue("Qc|c");
//
// g.addP(p1);
//
// P p2 = new P();
// // Q->Rb|b
// p2.setKey("Q");
// p2.setValue("Rb|b");
// g.addP(p2);
//
//
// P p3 =new P();
// // R->Sa|a
// p3.setKey("R");
// p3.setValue("Sa|a");
// g.addP(p3);
g.setVN("ETF");
g.setVT("+*i()");
P p1 = new P();
// E->E+T|T
p1.setKey("E");
p1.setValue("E+T|T");
P p2 = new P();
// T -> T*F|F
p2.setKey("T");
p2.setValue("T*F|F");
P p3 = new P();
// F->i|(E)
p3.setKey("F");
p3.setValue("i|(E)");
g.addP(p1);
g.addP(p2);
g.addP(p3);
}
/**
* 大致思路:
* 如果有间接左递归,先转化为直接左递归
* 消除直接左递归
*
* 消除左递归
* 立即左递归
* S-> Sa|bc
*
* S-bcS'
* S'->aS'|ε // ε 表示为空
*
* 非立即左递归
* S-> aB|Bb
* B->Sc|d
*
* 变为立即左递归
* S-> aSc|ad|Sc|db
* S->aScS'|dbS'|adS'
* S'->cS'|ε
*
*/
// 消除左递归 求 first follow
public void clearLeftRecursion() {
InitG(); // 初始化文法G
transformP(g); // 将间接左递归转化为直接左递归
clearRecursion(g); // 消除直接左递归
System.out.println("左递归的结果:");
showResult(g); // 展示左递归结果
System.out.println("first集:");
getFirstEpisode(g); // 求First 集
System.out.println("follow 集:");
getFollowEpisode(g); // 求Follow集
System.out.println("select集:");
getSelectEpisode(g); // 求Select集
}
Map<String, Set<String>> select = new TreeMap<>();
// 获取并打印select集
public void getSelectEpisode(G g){
for (int i =0; i<g.getpCount();i++) {
getSelect(g.getpList().get(i));
}
for (Map.Entry<String,Set<String>> entry : select.entrySet()) {
System.out.println("Select(" + entry.getKey()+")=" +entry.getValue()+"");
}
}
private void getSelect(P p) {
for (int i= 0; i<p.getValue().length;i++) {
String key = p.getKey()+"->"+p.getValue()[i]; // 定义 Select(A->BC) 的key值
Set<String> selectSet = new TreeSet<>(); // 放置到这里
for (int j = 0; j<p.getValue()[i].length(); j++) {
String node = p.getValue()[i].charAt(j) + "";
if (j+1<p.getValue()[i].length()&&p.getValue()[i].charAt(j+1)=='\''){
node += p.getValue()[i].charAt(j+1);
++j;
}
if (isVNorVT(node,g.getVT())) { // 如果是终结符
selectSet.add(node);
break;
}
if (node.equals("ε")) {
selectSet.addAll(follow.get(p.getKey()));
break;
}
if (first.containsKey(node)) { // 表明是非终结符
selectSet.addAll(first.get(node));
if (!first.get(node).contains("ε")) {
// selectSet.addAll(first.get(node));
break;
}
if (first.get(node).contains("ε")&&j==p.getValue()[i].length()-1) {
// selectSet.addAll(first.get(node));
selectSet.addAll(follow.get(p.getKey()));
break;
}
// selectSet.addAll(first.get(node));
}
}
selectSet.remove("ε");
select.put(key, selectSet);
}
}
Map<String, Set<String>> follow = new TreeMap<>();
// 获取并打印 follow集
public void getFollowEpisode(G g) {
for (int i = 0; i<g.getpCount();i++) {
// 获取
String key = g.getpList().get(i).getKey();
if (i==0) {
follow.put(key , Collections.singleton("#"));
}
getFollow(key);
}
for (Map.Entry<String,Set<String>> entry : follow.entrySet()) {
System.out.println("Follow(" + entry.getKey()+")=" +entry.getValue()+"");
}
}
// 获取单个产生式的 follow集
public void getFollow(String key) {
Set<String> followSet = new TreeSet<>();
// 遍历所有的产生式
for (int i =0; i<g.getpCount(); i++) {
P p = g.getpList().get(i);
for (int j = 0; j<p.getValue().length; j++) {
int index = p.getValue()[j].indexOf(key); // 找到此非终结符对应的下标
while (index != -1) { // 如果有此非终结符
int nextIndex = index + 1; // 便于循环
if (key.length()==1&&index+1 < p.getValue()[j].length() && p.getValue()[j].charAt(index+1)== '\'') { // 如果是 E' 之类的形式
index = p.getValue()[j].indexOf(key, index+1); 恰巧 key 是 E 的情况, 需要跳过此处 从 E' 之后 重新检索
continue;
}
// 开始计算 key 之后的 情况
index = index + key.length();
if (index == p.getValue()[j].length()) { // 如果 结果为 A-> ..E , 即 E 在 产生式的最后
if (follow.get(p.getKey()) == null) { // 如果 不能获取到 目前A的follow集, 需要进行求取
if (p.getKey().equals(key)&&key.length()==2) { // R' -> ...R'的情况, 如果是 R->..R 呢?
String newKey = key.charAt(0)+"";
if (follow.get(newKey)== null) {
getFollow(newKey);
}
if (follow.get(key)==null) {
follow.put(key, follow.get(newKey));
} else {
follow.get(key).addAll(follow.get(newKey));
}
}
getFollow(p.getKey());
}
followSet.addAll(follow.get(p.getKey())); // 之后加入
if (follow.get(key)== null) { // 如果follow 集中没有此数据
follow.put(key,followSet);
} else {
follow.get(key).addAll(followSet);
}
} else { // 如果后面跟的还有 A-> ..E..
String node = p.getValue()[j].charAt(index) + ""; // 获得下一个 node
if (index+1 < p.getValue()[j].length()&& p.getValue()[j].charAt(index + 1) == '\''){
node += p.getValue()[j].charAt(index + 1);
++index;
}
if (isVNorVT(node, g.getVT())) { // 如果是终结符
followSet.add(node);
if (follow.get(key)== null) { // 如果follow 集中没有此数据
follow.put(key,followSet);
} else {
followSet.addAll(follow.get(key));
follow.put(key,followSet);
// follow.get(key).addAll(followSet);
}
} else if (first.containsKey(node)) { // 如果是 非终结符
if (first.get(node).contains("ε")) { // 如果出现 A-> ..EB 而B的First集中有ε //需要去掉
Set<String> tmpSet = new TreeSet<>();
getFirstSetForFollow(tmpSet, p.getValue()[j], index,p.getKey());
tmpSet.remove("ε");
followSet.addAll(tmpSet);
if (follow.get(key)== null) { // 如果follow 集中没有此数据
follow.put(key,followSet);
} else {
follow.get(key).addAll(followSet);
}
} else {
followSet.addAll(first.get(node));
}
}
}
index = p.getValue()[j].indexOf(key, nextIndex);
}
}
}
}
/**
* 为求follow集而获取所有的first
* @param tmpSet
* @param value
* @param index
*/
private void getFirstSetForFollow(Set<String> tmpSet, String value, int index,String key) {
if (index >= value.length()) {
return;
}
if (value.charAt(index)=='\''){
--index;
}
String node = value.charAt(index) + "";
if (index+1 < value.length() && value.charAt(index+1)=='\'') {
node += value.charAt(index+1);
++index;
}
if (first.containsKey(node)) {
tmpSet.addAll(first.get(node));
if (first.get(node).contains("ε")){
if (index==value.length()-1){ // 如果直到最后都是ε, 证明key->ε, 需要加入 follow(key)
if (follow.get(key)== null) {
getFollow(key);
}
tmpSet.addAll(follow.get(key));
}
getFirstSetForFollow(tmpSet, value, index+1,key);
}
} else {
tmpSet.add(node);
}
}
public Map<String, Set<String>> first = new TreeMap<>(); //所求的first集
// 求First集的方法
private void getFirstEpisode(G g) {
for (int i =0; i< g.getpCount(); i++ ) { //遍历每条产生式
P p = g.getpList().get(i);
getFirst(p);
}
for (Map.Entry<String,Set<String>> entry : first.entrySet()) {
System.out.println("First(" + entry.getKey()+")=" +entry.getValue()+"");
}
}
// 求产生式的First集
private Set<String> getFirst(P p) {
if (first.containsKey(p.getKey())) {
return first.get(p.getKey());
}
Set<String> set = new TreeSet<>();
// 循环产生式
for (int i=0; i< p.getValue().length;i++) {
Set<String> set2 = new TreeSet<>();
for (int j= 0; j< p.getValue()[i].length(); j++) {
String node = p.getValue()[i].charAt(j) + "";
if (isVNorVT(node, g.getVT())){ // 如果是终结符
set2.add(node);
break;
} else if (isVNorVT(node, g.getVN())){ // 如果是非终结符
if (j+1<p.getValue()[i].length()&&p.getValue()[i].charAt(j+1)=='\'') { // 防止出现 "E'"之类的情况
node += p.getValue()[i].charAt(j+1);
++j;
}
P result = g.findPbyKey(node);
if (result!=null) { // 能够找到
Set<String> newSet = getFirst(result);
if (newSet.contains("ε")&&j==p.getValue()[i].length()-1){ // S->ABC,只有当 A->ε B->ε C->ε, 才可以说 ε属于first(S)
set2.addAll(newSet);
}
if (newSet.contains("ε")&&j< p.getValue()[i].length() - 1) {
newSet.remove("ε");
set2.addAll(newSet);
}
if (!newSet.contains("ε")){ // 如果不包含 ε , 结束
set2.addAll(newSet);
break;
}
}else {
try {
throw new Exception("所获取的p为null");
} catch (Exception e) {
e.printStackTrace();
}
}
} else if (node.equals("ε")&&node.length()==p.getValue()[i].length()){ // 如果可以直接推导出 S-> ε
set2.add("ε"); // 之后还需要循环去找 下一个 符号
}
}
set.addAll(set2);
}
first.put(p.getKey(), set);
return set;
}
// 判断是终结符还是非终结符
private boolean isVNorVT(String charac , String[] value) {
boolean bool = false;
for (String val:value) {
if (charac.equals(val)) {
bool = true;
}
}
return bool;
}
// 将间接左递归转化为直接左递归
private void transformP(G g) {
for (int i = 0; i<g.getpCount();i++) {
P p = g.getpList().get(i);
for (int j =0; j<i;j++) {
replaceP(p, g.getpList().get(j));
}
}
}
// 可能代替g.getpList.get(i)的产生式
private void replaceP(P p1, P p2) {
// 获取p2的左部非终结符
String key = p2.getKey();
// 获取p1的右步文法规则
String[] values1 = p1.getValue();
// 获取p2的右部文法规则
String[] values2= p2.getValue();
for (String s : values1) {
if (s.substring(0, 1).equals(key)) { // 进行对比,如果相同
String value1 = s.substring(1);
String[] newValues1 = new String[values2.length];
for (int j = 0; j < values2.length; j++) {
newValues1[j] = values2[j] + value1;
}
p1.deleteValue(s);
p1.addValue(newValues1);
}
}
}
// 消除直接左递归
private void clearRecursion(G g) {
for (int i=0; i< g.getpCount(); i++) {
P p = g.getpList().get(i);
String key = p.getKey(); // 获取左部非终结符
String [] values = p.getValue(); // 获取规则
boolean has = false; // 是否符合直接左递归
for (String value : values) {
String character = value.substring(0, 1); // 获取第一个符号
if (character.equals(key)) { // 与左部相同
putInP(key, value);
p.deleteValue(value);
has = true;
}
}
if (has&&p.getValue().length>0) {
for (int k =0; k<p.getValue().length;k++) {
p.value[k]+=key+"'";
}
}
}
}
/**
* 展示左递归后的结果
*/
private void showResult(G g) {
// 将结果打印
for (int m =0; m<g.getpCount(); m++) {
P p = g.getpList().get(m);
StringBuilder value = new StringBuilder();
for (int n =0; n<p.getValue().length; n++) {
value.append(p.getValue()[n]).append("|");
}
System.out.println(p.getKey()+"->"+ value.substring(0,value.length()-1));
}
}
/**
* 将一条文法规则放入新的产生式中 , 即
* E->E+T|T
* 转化为
* E->TE'
* E'->+TE'|ε // putInP()
*/
private void putInP(String key, String value) {
key = key+"'"; // 新符号
value = value.substring(1) + key;
boolean bool = true;
for (int i =0; i<g.getpList().size(); i++) {
if (g.getpList().get(i).getKey().equals(key)) { // 判断产生式中是否有此新符号,有则直接添加
g.getpList().get(i).addValue(value);
bool = false;
}
}
if (bool) { // 没有的话,新建
P p = new P();
p.setKey(key);
p.addValue(value);
p.addValue("ε");
g.addP(p);
}
}
public static void main(String[] args) {
clearRecursion episode = new clearRecursion();
episode.clearLeftRecursion();
}
}
检测
-
输入: E->E+T|T T->T*F|F F->i|(E)
- 结果
- 输入
S->Qc|c
Q->Rb|b
R->Sa|a
- 结果
测试完毕
参考博客.