编译原理 - LL(1)语法分析器的设计与实现

源代码

1.基本原理

FIRST、FOLLOW、SELECT三个集合构建规则,预测分析过程如下:

1、FIRST集合

计算文法符号 X 的 FIRST(X),不断运用以下规则直到没有新终结符号或 ε可以被加入为止 :
(1)如果 X 是一个终结符号,那么 FIRST(X) = X。
(2)如果 X 是一个非终结符号,且 X ->Y1 Y2 … Yk是一个产生式,其中 k≥1,那么如果对于某个i,a在 FIRST(Y1)、FIRST(Y2)… FIRST(Yi-1)中,就把a加入到 FIRST(X) 中。
(3)如果 X ->ε是一个产生式,那么将ε加入到 FIRST(X)中。

2、FOLLOW集合

计算文法符号 X 的 FOLLOW(X) ,不断运用以下规则直到没有新终结符号可以被加入任意FOLLOW集合为止 :
(1)将#加入到FOLLOW(X)中,其中S是开始符号,而#是输出右端的结束标记。
(2)如果存在一个产生式S->αXβ,那么将集合FIRST(β)中除ε外的所有元素加入到FOLLOW(X)当中。
(3)如果存在一个产生式 S->αX , 或者S->αXβ且FIRST(β)中包含ε , 那么将集合FOLLOW(S)中的所有元素加入到集合FOLLOW(X)中。

3、SELECT集合

在这里插入图片描述

4、预测分析过程

分析栈中push“#”与开始符,然后根据分析栈中最顶部的元素与分析字符串最左边的字符进行对比,如果相同则将分析栈顶部元素出栈,然后分析字符串中下一个元素;若不匹配则用分析栈顶部元素与字符串当前元素在分析表中查找对应产生式,将栈顶元素出栈,将产生式右部字符依次入栈,若没找到对应产生式则报错返回。喜欢处理分析栈内的非终结符,知道栈内元素只剩“#”分析成功结束。

2.数据结构分析

  • 我们知道First,Follow集合内的终结符是不重复的,那么对于表达First(x)->{}的情况,我们可以得出,’{ }’内的数据结构应该满足不重复的特性,即Set去重

  • 其次,存放First(x)->{}类型的数据结构,必须满足key->value的形式,且是一个集合,因为存放的不只是一个类似于First(x)->{}的结构,其中key是文法的非终结符,value是该终结符所求出来对应的终结符

  • 关于分析表的结构,我采用的是一个String类型的二维数组,因为表就是一个二维的结构

  • 同时,为了达到闭包的效果,我们需要Set来存储文法对应的终结符和非终结符,每次完成一个循环,就需要到该Set中去查看,是否完成了First集合内的所有终结符或非终结符集合包含了文法对应集合内的所有元素

  • 预测分析采用数据结构常用的栈结构

3.数据结构的具体实现

采用JAVA语言内置数据结构Map,Set,Stack

3.1 HashMap数据结构

对应First(x)->{},即key->value形式

HashMap特点为:

  • 存储时:他们会找到相同的bucket位置,发生碰撞,因为HashMap使用链表存储对象(每个Map.Entry都有一个next指针),这个Entry会存储在链表中。

  • 获取时:会用hashCode找到bucket位置,然后调用key.equals()方法找到链表中正确的节点.最终找到要找的值对象.

  • 产生hash碰撞的元素会放到相同hash值里的链表下

3.2 Set数据结构

Set集合内的元素是不重复的,即Set集合有去重的功能
Set数据结构对应文法的终结符集合,其实现原理是对每个要存入集合的元素计算hash值,再放入集合的相应位置;
其底层实现其实还是HashMap,只不过放置的Value元素变为了一个全部相同的哑值;

简而言之,就是如若发生hash冲突,则一定会覆盖原来的值,而不再去比较其Value是否相等,去掉了每个桶后面的链表;
Set主要用在两处,一处是初始化文法所有的不重复的终结符集合,第二处是求出First和Follow相应的终结符集合;

4.过程的具体实现

第一步,解析文法,将存储的文法的终结符和非终结符全部提取出来放入如下Set集合;
private TreeSet nvSet;
private TreeSet ntSet;

第二步,求First集合,在此过程按照以上算法,每完成一次循环,查看First集合内的所有终结符看看是否包含上面存储到nvSet内的所有元素,以达成闭包的效果;并将结果存入Map集合,Key值为文法左部非终结符,Value为对应求出的终结符,并存入一个新的集合Set

private HashMap<Character, TreeSet> firstMap;

第三步,求Follw集合,依据上面的First集合,完成Follow集合的运算,存入同上;

private HashMap<Character, TreeSet> followMap;

第四步,先得出求Select集合,再构造生成预测分析表;

private TreeMap<Character, HashMap<String, TreeSet>> selectMap;

private String[][] analyzeTable;

第五步,预测分析,利用栈结构和生成的预测分析表完成LL(1)预测分析;


代码:

1.产生的结果主要存储于以下数据结构内

    //分析表
private String[][] analyzeTable;

    /**
     * Select集合
     */
    private TreeMap<Character, HashMap<String, TreeSet<Character>>> selectMap;

    /**
     * LL(1)文法产生集合
     */
    private ArrayList<String> gsArray;

    /**
     * 表达式集合
     */
    private HashMap<Character, ArrayList<String>> expressionMap;

    /**
     * 开始符
     */
    private Character s;

    /**
     * Vn非终结符集合
     */
    private TreeSet<Character> nvSet;

    /**
     * Vt终结符集合
     */
    private TreeSet<Character> ntSet;

    /**
     * First集合
     */
    private HashMap<Character, TreeSet<Character>> firstMap;

    /**
     * Follow集合
     */
    private HashMap<Character, TreeSet<Character>> followMap;

2.初始化文法

        gsArray.add("E->TM");
        gsArray.add("M->+TF");
        gsArray.add("M->ε");
        gsArray.add("T->FN");
        gsArray.add("N->*FN");
        gsArray.add("N->ε");
        gsArray.add("F->(E)");
        gsArray.add("F->i");

3.获取文法的非终结符集合与终结符集合

 public void getNvNt() {
        for (String gsItem : gsArray) {
            String[] nvNtItem = gsItem.split("->");
            String charItemStr = nvNtItem[0];
            char charItem = charItemStr.charAt(0);
            // nv在左边
            nvSet.add(charItem);
        }
        for (String gsItem : gsArray) {
            String[] nvNtItem = gsItem.split("->");
            // nt在右边
            String nvItemStr = nvNtItem[1];
            // 遍历每一个字
            for (int i = 0; i < nvItemStr.length(); i++) {
                char charItem = nvItemStr.charAt(i);
                if (!nvSet.contains(charItem)) {
                    ntSet.add(charItem);
                }
            }
        }
    }

4.初始化表达式集合

 public void initExpressionMaps() {
        expressionMap = new HashMap<Character, ArrayList<String>>();
        for (String gsItem : gsArray) {
            String[] nvNtItem = gsItem.split("->");
            String charItemStr = nvNtItem[0];
            String charItemRightStr = nvNtItem[1];
            char charItem = charItemStr.charAt(0);
            if (!expressionMap.containsKey(charItem)) {
                ArrayList<String> expArr = new ArrayList<String>();
                expArr.add(charItemRightStr);
                expressionMap.put(charItem, expArr);
            } else {
                ArrayList<String> expArr = expressionMap.get(charItem);
                expArr.add(charItemRightStr);
                expressionMap.put(charItem, expArr);
            }
        }
    }

5.获取 First 集合

 public void getFirst() {
        // 遍历所有Nv,求出它们的First集合
        Iterator<Character> iterator = nvSet.iterator();
        while (iterator.hasNext()) {
            Character charItem = iterator.next();
            ArrayList<String> arrayList = expressionMap.get(charItem);
            for (String itemStr : arrayList) {
                boolean shouldBreak = false;
                // Y1Y2Y3...Yk
                for (int i = 0; i < itemStr.length(); i++) {
                    char itemitemChar = itemStr.charAt(i);
                    TreeSet<Character> itemSet = firstMap.get(charItem);
                    if (null == itemSet) {
                        itemSet = new TreeSet<Character>();
                    }
                    shouldBreak = calcFirst(itemSet, charItem, itemitemChar);
                    if (shouldBreak) {
                        break;
                    }
                }
            }
        }
    }

6.获取 Follow 集合

public void getFollow() {
    for (Character tempKey : nvSet) {
        TreeSet<Character> tempSet = new TreeSet<Character>();
        followMap.put(tempKey, tempSet);
    }
    // 遍历所有Nv,求出它们的First集合
    Iterator<Character> iterator = nvSet.descendingIterator();

    while (iterator.hasNext()) {
        Character charItem = iterator.next();
        System.out.println("charItem:" + charItem);
        Set<Character> keySet = expressionMap.keySet();
        for (Character keyCharItem : keySet) {
            ArrayList<String> charItemArray = expressionMap.get(keyCharItem);
            for (String itemCharStr : charItemArray) {
                System.out.println(keyCharItem + "->" + itemCharStr);
                TreeSet<Character> itemSet = followMap.get(charItem);
                calcFollow(charItem, charItem, keyCharItem, itemCharStr, itemSet);
            }
        }
    }
}

7.获取 Select 集合

  public void getSelect() {
        // 遍历每一个表达式
        // HashMap<Character, HashMap<String, TreeSet<Character>>>
        Set<Character> keySet = expressionMap.keySet();
        for (Character selectKey : keySet) {
            ArrayList<String> arrayList = expressionMap.get(selectKey);
            // 每一个表达式
            HashMap<String, TreeSet<Character>> selectItemMap = new HashMap<String, TreeSet<Character>>();
            for (String selectExp : arrayList) {
                /**
                 * 存放select结果的集合
                 */
                TreeSet<Character> selectSet = new TreeSet<Character>();
                // set里存放的数据分3种情况,由selectExp决定
                // 1.A->ε,=follow(A)
                if (TextUtil.isEmptyStart(selectExp)) {
                    selectSet = followMap.get(selectKey);
                    selectSet.remove('ε');
                    selectItemMap.put(selectExp, selectSet);
                }
                // 2.Nt开始,=Nt
                // <br>终结符开始
                if (TextUtil.isNtStart(ntSet, selectExp)) {
                    selectSet.add(selectExp.charAt(0));
                    selectSet.remove('ε');
                    selectItemMap.put(selectExp, selectSet);
                }
                // 3.Nv开始,=first(Nv)
                if (TextUtil.isNvStart(nvSet, selectExp)) {
                    selectSet = firstMap.get(selectKey);
                    selectSet.remove('ε');
                    selectItemMap.put(selectExp, selectSet);
                }
                selectMap.put(selectKey, selectItemMap);
            }
        }
    }

8.生成预测分析表,并将First,Follow,Select集合和预测分析表写入文件

public void genAnalyzeTable() throws Exception {
Object[] ntArray = ntSet.toArray();
Object[] nvArray = nvSet.toArray();
// 预测分析表初始化
analyzeTable = new String[nvArray.length + 1][ntArray.length + 1];
System.out.println("====================\n预测分析表\n====================");

File outputFile = new File("D:\\ideal\\algorithm\\src\\main\\resources\\analysetable.txt");
try (Writer writer = new FileWriter(outputFile)) {
    System.out.println("==============First集合=================");
    writer.write("==============First集合================="+"\n");
    for (Character key : firstMap.keySet()) {
        writer.write(firstMap.get(key).toString()+"\n");
        System.out.println(firstMap.get(key));
    }
      System.out.println("==============First集合=================");
        writer.write("==============First集合=================" + "\n");
        for (Character key : firstMap.keySet()) {
            writer.write(key +" -> "+ firstMap.get(key).toString() + "\n");
            System.out.println(firstMap.get(key));
        }
        System.out.println("==============Follow集合=================");
        writer.write("==============Follow集合=================" + "\n");
        for (Character key : followMap.keySet()) {
            writer.write(key +" -> "+followMap.get(key).toString() + "\n");
            System.out.println(followMap.get(key));
        }
        System.out.println("==============Select集合=================");
        writer.write("==============Select集合=================" + "\n");
        for (Character key : selectMap.keySet()) {
            writer.write(key +" -> "+selectMap.get(key).toString() + "\n");
            System.out.println(selectMap.get(key));
        }

    writer.write("====================\n预测分析表\n====================\n");
    // 输出一个占位符
    System.out.print("表" + "\t");
    writer.write("表" + "\t");
    analyzeTable[0][0] = "Nv/Nt";

    // 初始化首行
    for (int i = 0; i < ntArray.length; i++) {
        if (ntArray[i].equals('ε')) {
            ntArray[i] = '#';
        }
        writer.write(ntArray[i] + "\t\t");
        System.out.print(ntArray[i] + "\t\t");

        analyzeTable[0][i + 1] = ntArray[i] + "";
    }

    System.out.println("");
    writer.write("\n");
    for (int i = 0; i < nvArray.length; i++) {
        // 首列初始化
        writer.write(nvArray[i] + "\t");
        System.out.print(nvArray[i] + "\t");

        analyzeTable[i + 1][0] = nvArray[i] + "";
        for (int j = 0; j < ntArray.length; j++) {
            String findUseExp = TextUtil.findUseExp(selectMap, Character.valueOf((Character) nvArray[i]),
                    Character.valueOf((Character) ntArray[j]));
            if (null == findUseExp) {
                writer.write("空\t\t");
                System.out.print("空\t\t");

                analyzeTable[i + 1][j + 1] = "";
            } else {
                writer.write(nvArray[i] + "->" + findUseExp + "\t");
                System.out.print(nvArray[i] + "->" + findUseExp + "\t");

                analyzeTable[i + 1][j + 1] = nvArray[i] + "->" + findUseExp;
            }
        }
        writer.write("\n");
        System.out.println();

    }

} catch (Exception e) {
    e.printStackTrace();
}

}

  1. 预测分析
public void analyze() {
        analyzeProduces = new ArrayList<AnalyzeProduce>();
       
        // 开始符进栈
        analyzeStatck.push(startChar);

        System.out.println("====================\nLL(1)文法分析过程\n====================");
        System.out.println("开始符:" + startChar);
        System.out.println("序号\t\t符号栈\t\t\t输入串\t\t\t所用产生式");
        int index = 0;
        // 开始分析
        // while (analyzeStatck.peek() != '#' && str.charAt(0) != '#') {
        while (!analyzeStatck.empty()) {
            index++;
            if (analyzeStatck.peek() != str.charAt(0)) {

                // 到分析表中找到这个产生式
                String nowUseExpStr = TextUtil.findUseExp(ll1Grammar.getSelectMap(), analyzeStatck.peek(), str.charAt(0));

                //打印表格注意, 制表符的个数
                if (analyzeStatck.size()==1){
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t\t\t\t" + str + "\t\t\t"
                            + analyzeStatck.peek() + "->" + nowUseExpStr);
                }else if (analyzeStatck.size()==2){
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t\t\t" + str + "\t\t\t"
                            + analyzeStatck.peek() + "->" + nowUseExpStr);
                }else if (analyzeStatck.size()==3){
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t\t" + str + "\t\t\t"
                            + analyzeStatck.peek() + "->" + nowUseExpStr);
                }else {
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t" + str + "\t\t\t"
                            + analyzeStatck.peek() + "->" + nowUseExpStr);
                }

                AnalyzeProduce produce = new AnalyzeProduce();
                produce.setIndex(index);
                produce.setAnalyzeStackStr(analyzeStatck.toString());
                produce.setStr(str);
                if (null == nowUseExpStr) {
                    produce.setUseExpStr("无法匹配!");
                } else {
                    produce.setUseExpStr(analyzeStatck.peek() + "->" + nowUseExpStr);
                }
                analyzeProduces.add(produce);
                // 将之前的分析栈中的栈顶出栈
                analyzeStatck.pop();
                // 将要用到的表达式入栈,反序入栈
                if (null != nowUseExpStr && nowUseExpStr.charAt(0) != 'ε') {
                    for (int j = nowUseExpStr.length() - 1; j >= 0; j--) {
                        char currentChar = nowUseExpStr.charAt(j);
                        analyzeStatck.push(currentChar);
                    }
                }
                continue;
            }
            // 如果可以匹配,分析栈出栈,串去掉一位
            if (analyzeStatck.peek() == str.charAt(0)) {

                if (analyzeStatck.size()==1){
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t\t\t\t" + str + "\t\t\t" + "“"
                            + str.charAt(0) + "”匹配");
                }else if (analyzeStatck.size()==2){
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t\t\t" + str + "\t\t\t" + "“"
                            + str.charAt(0) + "”匹配");
                }else if (analyzeStatck.size()==3){
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t\t" + str + "\t\t\t" + "“"
                            + str.charAt(0) + "”匹配");
                }else {
                    System.out.println(index + "\t\t" + analyzeStatck.toString() + "\t" + str + "\t\t\t" + "“"
                            + str.charAt(0) + "”匹配");
                }

                AnalyzeProduce produce = new AnalyzeProduce();
                produce.setIndex(index);
                produce.setAnalyzeStackStr(analyzeStatck.toString());
                produce.setStr(str);
                produce.setUseExpStr("“" + str.charAt(0) + "”匹配");
                analyzeProduces.add(produce);
                analyzeStatck.pop();
                str = str.substring(1);
                continue;
            }
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值