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

上一篇中实现的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;
    }

 

posted @ 2024-01-17 23:59  kashin05  阅读(44)  评论(0编辑  收藏  举报