编译原理之First集,Follow集.Select集

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集,以此类推

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
  • 结果
    在这里插入图片描述

测试完毕

参考博客.

源码地址.注:本项目尚属半成品,后续会更新LL1文法与LR文法

猜你喜欢

转载自blog.csdn.net/qq_40552152/article/details/105437469