编译器-FIRST集合(补充:左递归)

人生乱弹 2年前 (2024) admin
9 0

上一篇中实现的First函数没有考虑左递归,在这对此说明和实现
1.立即左递归
A -> Ab|a
1.两步或两步以上产生的左递归
A -> Bc|a
B -> Ab|d
 
前面的实现中,递归用first函数

public Set<Terminal> first(List<Symbol> tokens) {
first(token)
}
public Set<Terminal> first(Symbol token) {
first(tokens)
}

 
如果产生式存在左递归就会出现循环调用,得不到结果
那么需要做下面处理
1. 需要记录当前外层有没有处理这个符号,如果有则跳过
2. A -> Bc|a B->Ad | ε  
1)first(A) 
2)first(Bc)  并 first(a)
3)first(B) frist(c)  并  first(a) 
4)first(Ad) frist(ε) first(c) 并 first(a)
5)first(A) first(d) frist(ε) first(c) 并 first(a)
可以看第5步又需要first(A),因为外层正在处理,所以直接返回,但是返回前需要判断A是否为空,
如果不为空,则直接返回,如果为空,则继续first(d)
看两组产生式的结果 
 A -> Bc|a B->Ad| d | ε               A的first集合是 a, d, c, B的first集合 a d c
 A -> Bc|a B->Ad | d               A的first集合是 a, d, B的first集合 a d
可以看出  A -> Bc 对 frist(A)集合,如果A, B可以推出 ε 那么集合就多了c,否则 A->Bc 不影响 first(A)的结果
所以需要加过一个函数专门用来判断A是否可以推导出ε
 

/**
* tokens是否可以推导出ε
* @param tokens
* @param expands
* @return
*/
public boolean hasEpsilon(List<Symbol> tokens, Set<Symbol> expands) {
for (Symbol token : tokens) {
if (expands.contains(token)) {
return false;
}
if (Terminal.epsilon.equals(token)) {
return true;
}
if (token instanceof Terminal) {// 终结符直接返回
return false;
}

expands.add(token);
boolean hasEpsilon = false;
List<Production> productionList = getProduction((Nonterminal) token);
for (Production production : productionList) {
if (hasEpsilon(production.getBody(), expands)) {
hasEpsilon = true;
break;
}
}
if (!hasEpsilon) {
return false;
}
expands.remove(token);
}
return true;
}

最终First集合函数修改如下
 

/**
* P140 FIRST(X)集合
* @param tokens 正在计算的串
* @param expands 用于记录正在计算的串是由哪个非终结符展开得到的
* @return
*/
public Set<Terminal> first(List<Symbol> tokens, Set<Symbol> expands) {

// 是否全部包含ε
boolean allContainsEpsilon = true;
Set<Terminal> result = new HashSet<>();
for (Symbol token : tokens) {

Set<Symbol> epsilonExpands = new HashSet<>();
boolean hasEpsilon = hasEpsilon(Arrays.asList(token), epsilonExpands);

Set<Terminal> firstSet = first(token, expands);
if (firstSet == null) {
if (hasEpsilon) {
continue;
} else {
return result;
}
} else {
for (Terminal terminal : firstSet) {
if (terminal.equals(Terminal.of("ε"))) {
continue;
}
result.add(terminal);
}

if (!hasEpsilon) {
allContainsEpsilon = false;
break;
}
}
}

// 如果对于所有的j=1,2,...,k,ε在FIRST(Yj)中,那么将ε加入到FIRST(X)
if (allContainsEpsilon) {
result.add(Terminal.of("ε"));
}

return result;
}

 

文章来源

版权声明:admin 发表于 2024年1月21日 am3:31。
转载请注明:编译器-FIRST集合(补充:左递归) | 银库

相关文章

本站主题由 OneNav 一为主题强力驱动