任务描述

本关任务:加深对语法分析器工作过程的理解;加强对预测分析法实现语法分析程序的掌握;能够采用一种编程语言实现简单的语法分析程序;能够使用自己编写的分析程序对简单的程序段进行语法翻译。

相关知识

为了完成本关任务,你需要掌握:用预测分析法编制语法分析程序。

自上而下的语法分析器

语法分析在编译中是一个重要的环节,语法分析可以分为自上而下分析和自下而上分析两种方式。

自上而下分析法是从文法开始符号开始,不断进行推导,直到推导所得的符号串与输入串相同为止。简单来解释这句话:我们有一个既定的文法,和一个需要分析的符号串。接下来我们从文法的开始符号出发,反复地使用文法规定的一些产生式匹配符号串的每一个符号,直到所得的符号串和待分析的符号串相同,则为分析成功,反之匹配不成功。

下面我们举例来进行说明:
我们有文法G(E):

E→aF
F→b|c 

以及待输入的字符 ab,从文法开始符号E出发,E→aF,a匹配成功后,指针指向F,找非终结符F的产生式合适的候选式 b匹配,于是匹配成功。

想要对一个文法进行自上而下的分析,要消除文法的二义性,消除左递归,提取左公共因子,计算FIRST集合和FOLLOW集合,判断文法是否为LL(1)型文法,一个文法经过这些步骤,并且是LL(1)文法,则可以用LL(1)分析法的具体实现去分析。我们在后续的实验步骤中会进行该过程的一一实验。

预测分析法

预测分析法是LL(1)分析法的一种实现方法,它通过一张表来关联非终结符和终结符,这张表就是预测分析表,预测分析表可以说是预测分析法的核心部分。我们在进行该方法时,首先要构建预测分析表。

首先介绍一下预测分析表的结构,简单来说他就是一张表,表的两个属性分别是非终结符和终结符(包括‘#’),形如:
其中对应的内容是产生式的形式,若是没有产生式则可以写入标记来表示其匹配不到具体的产生式,进入报错处理程序。

在根据文法构造预测分析表时需要扫描全部产生式:

计算产生式的右部的FIRST集合,如果求出来的FIRST集合中包含终结符,那么就把这条产生式放入对应非终结符和终结符的格子中
如果ε在FIRST集合中,则计算该非终结符的FOLLOW集合,如果求出来的FOLLOW集合中包含终结符,那么就把这条产生式放入对应的非终结符和终结符的格子中
当预测分析表构造完成后,我们便可以运用预测分析法进行语法分析了。我们使用用一个栈来存放过程数据,主要步骤如下:

获取栈顶的元素A,获取输入串目前指针指向的元素a
若A = ‘#’ ,a = ‘#’ 则匹配成功
若A = a 但是A和a不为’#’,则pop栈顶元素,输入串指针+1
若A为非终结符,这查询预测分析表,把由A和a确定的产生式的右部从右往左依次压入到栈中,若右部是ε,那就不做操作
查找预测分析表得到预设的出错字符则调用error()

实验步骤

根据语法分析器的一般功能,我们的程序应具有如下的要求及实验步骤:

定义目标语言的语法规则;
求解预测分析方法需要的符号集和分析表;
依次读入测试用例,根据预测分析的方法进行语法分析,直到源程序结束;该部分具体见上。
对遇到的语法错误做出错误处理。
下面我们对步骤1,2进行说明。

目标语言的语法规则

该部分重点在于消除文法的二义性,消除左递归,提取左公共因子。想要让程序自动地消除左递归,具体的做法如下:
1.把文法的所有非终结符进行排序S = [‘A’,‘B’,…]
2.做一个嵌套循环:
其中S[k]为排在S[j]之后的非终结符,bunch_i为非终结符和终结符组成的串

for j in range(len(s)):
        for k in range(j):
            原产生式中:S[j]→S[k]bunch_1的S[k]用其对应的产生式代替
            S[k]→bunch_2|bunch_3|...
            推出:S[j]→bunch_2 bunch_1|bunch_3 bunch_1|...
            如此,做完循环后该文法若有间接左递归,就将其转换成直接左递归了
            消除直接左递归,具体做法见上文
循环结束后删除无用产生式 
求解预测分析方法需要的符号集和分析表

符号集主要是构造FIRST集和FOLLOW集,其主要目的是为了消除回溯,也就是选择候选式的时候能够更加准确,当然,这些集合在别的地方也有用处。

构造FIRST集: 扫描所有的产生式,对于每一个X,连续使用以下规则,直至每个集合的FIRST集合不再增大为止,每一遍扫描所有产生式如果有FIRST集合变化则重新扫描:

如果X是终结符,则FIRST(X) = {X}.
如果X是非终结符 ,且有产生式 X → a… 则把a加入到FIRST集合中. 若 X → ε也是其中一条产生式,则把ε也加入到FIRST集合中.
如果X是非终结符,且X → Y…是一条产生式,其中Y是非终结符,那么则把FIRST(Y) \ {ε}(即FIRST集合去除ε)加入到FIRST(X)中.
构造FOLLOW集: 扫描所有产生式.对于文法G的每一个非终结符A构造FOLLOW(A)可以连续使用以下规则,直至每一个FOLLOW集合不在增大为止:

对于文法的开始符号S,置#于FOLLOW(S)中.
若有产生式 A → αBβ,其中α和β是非终结符和终结符构成的串,B为非终结符,则把FIRST(β) \ {ε}加入到FOLLOW(B)中.
若有产生式 A → αB 或 A → αBβ,ε∈FIRST(β) ,则把FOLLOW(A)加入到FOLLOW(B)中.
分析表的构成见上所述。

编程要求

根据提示,在右侧编辑器Begin和End之间依次完成划分产生式、消除左递归、计算FIRST集、计算follow集、构建LL(1)分析表等代码,然后点击测评,运行程序,进行结果对比。

测试说明

测试输入的格式为:

E T F
+ * ( ) i
E->E+T|T
T->T*F|F
F->(E)|i
(i)*i
(i)*iii

平台会对你编写的代码进行测试.

开始你的任务吧,祝你成功!

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class ex2 {
    //非终结符集
    static List<String> noTerminal = new ArrayList<>();
    //终结符集
    static List<String> terminal = new ArrayList<>();
    //语法规则存储
    static String[][] grammarRules;
    //消除左递归后语法规则存储
    static String[][] grammar_rules;
    static String[][] newGrammer;
    //文法开始符号
    static String init;
    //FIRST集
    static String[][] firstSet;
    //FOLLOW集
    static String[][] followSet;
    //LL(1)分析表
    static int[][] analysis;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        //读入非终结符
        //System.out.println("请输入非终结符: ");
        String noTer = new String("ETF");
        for (int i = 0; i < noTer.length(); i++){
            noTerminal.add(String.valueOf(noTer.charAt(i)));
        }
        //读入终结符
        terminal.add("#");
        //System.out.println("请输入终结符: ");
        String ter = new String("+*()i");
        for (int i = 0; i < ter.length(); i++){
            terminal.add(String.valueOf(ter.charAt(i)));
        }
        //初始化
        grammarRules = new String[noTer.length()][2];
        grammar_rules = new String[noTer.length() * 2][2];
        newGrammer = new String[noTerminal.size() * 5][3];
        //输入语法规则
        //System.out.println("请输入语法规则:");
        String grammar = new String("E->E+T|T");
        init = String.valueOf(grammar.charAt(0));
        divide(grammar);
        eliminate(grammar);

        String grammar1 = new String("T->T*F|F");
        divide(grammar1);
        eliminate(grammar1);

        String grammar2 = new String("F->(E)|i");
        divide(grammar2);
        eliminate(grammar2);
        //请输入测试用例
        System.out.println("请输入测试用例:");
        String test = input.nextLine();
        //输出信息
        System.out.println("------------------基本信息-----------------------");
        System.out.println("产生式:");
        output(grammarRules);
        System.out.println("终结符:");
        for (int i = 0; i < terminal.size(); i++){
            System.out.print(terminal.get(i) + "     ");
        }
        System.out.println();
        System.out.println("非终结符:");
        for (int i = 0; i < noTerminal.size(); i++){
            System.out.print(noTerminal.get(i) + "     ");
        }
        System.out.println();
        System.out.println("读取测试:");
        System.out.println(test);
        System.out.println("-------------------消除左递归---------------------");
        System.out.println("产生式:");
        output(grammar_rules);
        System.out.println("----------------FIRST集和FOLLOW集-----------------");
        firstSet = new String[noTerminal.size()][terminal.size()];
        for (int i = 0; i < noTerminal.size(); i++){
            first(noTerminal.get(i));
            System.out.print("FIRST(" + noTerminal.get(i) + ") = " );
            for (int j = 0; j < terminal.size(); j++){
                if (firstSet[i][j] != null){
                    System.out.print(firstSet[i][j] + " ");
                }
            }
            System.out.println();
        }
        followSet = new String[noTerminal.size()][terminal.size()];
        for (int i = 0; i < noTerminal.size(); i++){
            follow(noTerminal.get(i));
            System.out.print("FOLLOW(" + noTerminal.get(i) + ") = ");
            for (int j = 0; j < terminal.size(); j++){
                if (followSet[i][j] != null){
                    System.out.print(followSet[i][j] + " ");
                }
            }
            System.out.println();
        }
        System.out.println("----------------LL(1)分析表-----------------");
        analysis = new int[noTerminal.size()][terminal.size()];
        table();
        System.out.print("      ");
        for (int i = 0; i < terminal.size(); i++){
            System.out.print(terminal.get(i) + "     ");
        }
        System.out.println();
        for (int i = 0; i < noTerminal.size(); i++){
            System.out.print(noTerminal.get(i) + "     ");
            for (int j = 0; j < terminal.size(); j++){
                if (analysis[i][j] != 0) {
                    System.out.print(analysis[i][j] + "     ");
                }else{
                    System.out.print("      ");
                }
            }
            System.out.println();
        }
        System.out.println("--------------------分析栈-------------------");
        System.out.println(String.format("%-10s","分析栈") + String.format("%-10s","剩余字符串"));
        analysisStack(test);
    }

	/********Beign********/
    /*** 划分出产生式*/
    static void divide(String grammar){

    }
    /********End********/

	/********Beign********/
    /*** 消除左递归*/
    static void eliminate(String grammar){

    }
    /********End********/

    /*** 输出产生式*/
    static void output(String[][] array){
        int k = 0;
        for (int i = 0; i < array.length; i++){
            for (int j = 0; j < array[i].length; j++){
                if (array[i][j] != null) {
                    newGrammer[k][0] = String.valueOf(k+1);
                    newGrammer[k][1] = noTerminal.get(i);
                    newGrammer[k][2] = array[i][j];
                    System.out.println(newGrammer[k][0] + "     " + newGrammer[k][1] + "     " + newGrammer[k][2]);
                    k++;
                }
            }
        }
    }

	/********Beign********/
    /*** 计算FIRST集*/
    static void first(String noTer){
        
    }
    /********End********/

    /********Beign********/
    /*** 计算follow集*/
    static void follow(String noTer){
        
    }
	/********End********/

    /********Beign********/
    /*** 构建LL(1)分析表*/
    static void table(){
        
    }
	/********End********/

    /*** 输出分析栈*/
    static void analysisStack(String test){
        List<String> stack = new ArrayList<>();
        stack.add("#");
        stack.add("E");
        String outStack = test + "#";
        boolean flag = true;
        while (flag){
            String inStack = "";
            for (int i = 0; i < stack.size(); i++){
                inStack = inStack + stack.get(i);
            }
            System.out.println(String.format("%-10s",inStack) + String.format("%-10s",outStack));
            String NT = stack.get(stack.size()-1);
            String T = String.valueOf(outStack.charAt(0));
            if (NT.equals(T) && NT.equals("#")){
                System.out.println("分析成功");
                break;
            } else if (NT.equals(T)){
                stack.remove(stack.size()-1);
                outStack = outStack.substring(1);
            } else {
                if (analysis[noTerminal.indexOf(NT)][terminal.indexOf(T)] > 0){
                    int num = analysis[noTerminal.indexOf(NT)][terminal.indexOf(T)];
                    String s = newGrammer[num-1][2];
                    stack.remove(stack.size()-1);
                    for (int i = s.length()-1; i >= 0; i--){
                        if (!s.equals("-")) {
                            stack.add(String.valueOf(s.charAt(i)));
                        }
                    }
                }else {
                    System.out.println("ERROR: 分析出错");
                }
            }
        }
    }
}

通关代码

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class ex2 {
    //非终结符集
    static List<String> noTerminal = new ArrayList<>();
    //终结符集
    static List<String> terminal = new ArrayList<>();
    //语法规则存储
    static String[][] grammarRules;
    //消除左递归后语法规则存储
    static String[][] grammar_rules;
    static String[][] newGrammer;
    //文法开始符号
    static String init;
    //FIRST集
    static String[][] firstSet;
    //FOLLOW集
    static String[][] followSet;
    //LL(1)分析表
    static int[][] analysis;
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        //读入非终结符
        //System.out.println("请输入非终结符: ");
        String noTer = new String("ETF");
        for (int i = 0; i < noTer.length(); i++){
            noTerminal.add(String.valueOf(noTer.charAt(i)));
        }
        //读入终结符
        terminal.add("#");
        //System.out.println("请输入终结符: ");
        String ter = new String("+*()i");
        for (int i = 0; i < ter.length(); i++){
            terminal.add(String.valueOf(ter.charAt(i)));
        }
        //初始化
        grammarRules = new String[noTer.length()][2];
        grammar_rules = new String[noTer.length() * 2][2];
        newGrammer = new String[noTerminal.size() * 5][3];
        //输入语法规则
        //System.out.println("请输入语法规则:");
        String grammar = new String("E->E+T|T");
        init = String.valueOf(grammar.charAt(0));
        divide(grammar);
        eliminate(grammar);
        String grammar1 = new String("T->T*F|F");
        divide(grammar1);
        eliminate(grammar1);
        String grammar2 = new String("F->(E)|i");
        divide(grammar2);
        eliminate(grammar2);
        //请输入测试用例
        System.out.println("请输入测试用例:");
        String test = input.nextLine();
        //输出信息
        System.out.println("------------------基本信息-----------------------");
        System.out.println("产生式:");
        output(grammarRules);
        System.out.println("终结符:");
        for (int i = 0; i < terminal.size(); i++){
            System.out.print(terminal.get(i) + "     ");
        }
        System.out.println();
        System.out.println("非终结符:");
        for (int i = 0; i < noTerminal.size(); i++){
            System.out.print(noTerminal.get(i) + "     ");
        }
        System.out.println();
        System.out.println("读取测试:");
        System.out.println(test);
        System.out.println("-------------------消除左递归---------------------");
        System.out.println("产生式:");
        output(grammar_rules);
        System.out.println("----------------FIRST集和FOLLOW集-----------------");
        firstSet = new String[noTerminal.size()][terminal.size()];
        for (int i = 0; i < noTerminal.size(); i++){
            first(noTerminal.get(i));
            System.out.print("FIRST(" + noTerminal.get(i) + ") = " );
            for (int j = 0; j < terminal.size(); j++){
                if (firstSet[i][j] != null){
                    System.out.print(firstSet[i][j] + " ");
                }
            }
            System.out.println();
        }
        followSet = new String[noTerminal.size()][terminal.size()];
        for (int i = 0; i < noTerminal.size(); i++){
            follow(noTerminal.get(i));
            System.out.print("FOLLOW(" + noTerminal.get(i) + ") = ");
            for (int j = 0; j < terminal.size(); j++){
                if (followSet[i][j] != null){
                    System.out.print(followSet[i][j] + " ");
                }
            }
            System.out.println();
        }
        System.out.println("----------------LL(1)分析表-----------------");
        analysis = new int[noTerminal.size()][terminal.size()];
        table();
        System.out.print("      ");
        for (int i = 0; i < terminal.size(); i++){
            System.out.print(terminal.get(i) + "     ");
        }
        System.out.println();
        for (int i = 0; i < noTerminal.size(); i++){
            System.out.print(noTerminal.get(i) + "     ");
            for (int j = 0; j < terminal.size(); j++){
                if (analysis[i][j] != 0) {
                    System.out.print(analysis[i][j] + "     ");
                }else{
                    System.out.print("      ");
                }
            }
            System.out.println();
        }
        System.out.println("--------------------分析栈-------------------");
        System.out.println(String.format("%-10s","分析栈") + String.format("%-10s","剩余字符串"));
        analysisStack(test);
    }
    /********Beign********/
    /*** 划分出产生式*/
    static void divide(String grammar){
        int fla = grammar.indexOf("|");
        int flag = grammar.indexOf(">");
        String a = String.valueOf(grammar.charAt(0));
        if (fla != -1){
            grammarRules[noTerminal.indexOf(a)][0] = grammar.substring(flag+1, fla);
            grammarRules[noTerminal.indexOf(a)][1] = grammar.substring(fla+1);
        } else {
            grammarRules[noTerminal.indexOf(a)][0] = grammar.substring(flag+1);
        }
    }
    /********End********/
    /********Beign********/
    /*** 消除左递归*/
    static void eliminate(String grammar){
        int fla = grammar.indexOf("|");
        int flag = grammar.indexOf(">");
        //如果存在左递归
        if (grammar.charAt(0) == grammar.charAt(flag+1)){
            String a = String.valueOf(Character.toLowerCase(grammar.charAt(0)));
            noTerminal.add(a);
            if (!terminal.contains("-")){
                terminal.add("-");
            }
            String s = "";
            for (int i = flag; i < grammar.length(); i++){
                s = String.valueOf(grammar.charAt(i));
                flag = terminal.indexOf(s);
                if (flag != -1) {
                    flag = grammar.indexOf(s);
                    break;
                }
            }
            s = s + grammar.substring(flag+1, fla) + a;
            grammar_rules[noTerminal.indexOf(a)][0] = s;
            grammar_rules[noTerminal.indexOf(a)][1] = "-";
            grammar_rules[noTerminal.indexOf(String.valueOf(grammar.charAt(0)))][0] = grammar.substring(fla+1) + a;
        } else {
            grammar_rules[noTerminal.indexOf(String.valueOf(grammar.charAt(0)))][0] = grammarRules[noTerminal.indexOf(String.valueOf(grammar.charAt(0)))][0];
            grammar_rules[noTerminal.indexOf(String.valueOf(grammar.charAt(0)))][1] = grammarRules[noTerminal.indexOf(String.valueOf(grammar.charAt(0)))][1];
        }
    }
    /********End********/
    /*** 输出产生式*/
    static void output(String[][] array){
        int k = 0;
        for (int i = 0; i < array.length; i++){
            for (int j = 0; j < array[i].length; j++){
                if (array[i][j] != null) {
                    newGrammer[k][0] = String.valueOf(k+1);
                    newGrammer[k][1] = noTerminal.get(i);
                    newGrammer[k][2] = array[i][j];
                    System.out.println(newGrammer[k][0] + "     " + newGrammer[k][1] + "     " + newGrammer[k][2]);
                    k++;
                }
            }
        }
    }
    /********Beign********/
    /*** 计算FIRST集*/
    static void first(String noTer){
        String s = "";
        int flag = noTerminal.indexOf(noTer);
        for (int i = 0; i < 2; i++){
            if (grammar_rules[flag][i] != null) {
                String str = grammar_rules[flag][i];
                String initials = String.valueOf(str.charAt(0));
                if (noTerminal.contains(initials)){
                    first(initials);
                    for (int j = 0; j < terminal.size(); j++){
                        int temp = noTerminal.indexOf(initials);
                        if (firstSet[temp][j] != null){
                            firstSet[flag][j] = firstSet[temp][j];
                        }
                    }
                } else {
                    firstSet[flag][terminal.indexOf(initials)] = String.valueOf(str.charAt(0));
                }
            }
        }
    }
    /********End********/
    /********Beign********/
    /*** 计算follow集*/
    static void follow(String noTer){
        for (int i = 0; i < noTerminal.size(); i++){
            for (int j = 0; j < 2;j++){
                if (grammar_rules[i][j] != null){
                    String str = grammar_rules[i][j];
                    //文法开始符号
                    if (noTer.equals(init)) {
                        followSet[noTerminal.indexOf(init)][terminal.indexOf("#")] = "#";
                    }
                    int index = str.indexOf(noTer);
                    //noTer不在最后位置
                    if (index != -1 && index != str.length() - 1){
                        String ch = String.valueOf(str.charAt(index + 1));
                        //ch是非终结符
                        if (noTerminal.contains(ch)){
                            for (int k = 0; k < terminal.size(); k++){
                                if (firstSet[noTerminal.indexOf(ch)][k] != null && !firstSet[noTerminal.indexOf(ch)][k].equals("-")){
                                    followSet[noTerminal.indexOf(noTer)][k] = firstSet[noTerminal.indexOf(ch)][k];
                                    for (int l = 0; l < terminal.size(); l++){
                                        if (followSet[i][l] != null){
                                            followSet[noTerminal.indexOf(noTer)][l] = followSet[i][l];
                                        }
                                    }
                                }
                            }
                        } else {
                            //ch不是非终结符
                            followSet[noTerminal.indexOf(noTer)][terminal.indexOf(ch)] = ch;
                        }
                    } else if (index > 0 && index == str.length() - 1){
                        //noTer在最后位置,防止死循环
                        if (!noTerminal.get(i).equals(noTer)) {
                            follow(noTerminal.get(i));
                            for (int k = 0; k < terminal.size(); k++){
                                if (followSet[i][k] != null){
                                    followSet[noTerminal.indexOf(noTer)][k] = followSet[i][k];
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /********End********/
    /********Beign********/
    /*** 构建LL(1)分析表*/
    static void table(){
        int index = 0;
        for (int i = 0; i < noTerminal.size(); i++){
            int num = 0, temp = 0;
            int[] flag = new int[terminal.size()];
            for (int j = index; j < newGrammer.length; j++){
                if (newGrammer[index][1].equals(newGrammer[j][1])){
                    flag[temp] = Integer.valueOf(newGrammer[j][0]);
                    num++;
                    temp++;
                }
            }
            index += num;
            if (num == 1){
                for (int j = 0; j < firstSet[i].length; j++){
                    if (firstSet[i][j] != null){
                        analysis[i][j] = flag[0];
                    }
                }
            } else {
                temp = 0;
                for (int j = 0; j < firstSet[i].length; j++){
                    if (firstSet[i][j] != null) {
                        if (!firstSet[i][j].equals("-")){
                            analysis[i][j] = flag[temp];
                            temp++;
                        } else {
                            for (int f = 0; f < followSet[i].length; f++){
                                if (followSet[i][f] != null){
                                    analysis[i][f] = flag[temp];
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /********End********/
    /*** 输出分析栈*/
    static void analysisStack(String test){
        List<String> stack = new ArrayList<>();
        stack.add("#");
        stack.add("E");
        String outStack = test + "#";
        boolean flag = true;
        while (flag){
            String inStack = "";
            for (int i = 0; i < stack.size(); i++){
                inStack = inStack + stack.get(i);
            }
            System.out.println(String.format("%-10s",inStack) + String.format("%-10s",outStack));
            String NT = stack.get(stack.size()-1);
            String T = String.valueOf(outStack.charAt(0));
            if (NT.equals(T) && NT.equals("#")){
                System.out.println("分析成功");
                break;
            } else if (NT.equals(T)){
                stack.remove(stack.size()-1);
                outStack = outStack.substring(1);
            } else {
                if (analysis[noTerminal.indexOf(NT)][terminal.indexOf(T)] > 0){
                    int num = analysis[noTerminal.indexOf(NT)][terminal.indexOf(T)];
                    String s = newGrammer[num-1][2];
                    stack.remove(stack.size()-1);
                    for (int i = s.length()-1; i >= 0; i--){
                        if (!s.equals("-")) {
                            stack.add(String.valueOf(s.charAt(i)));
                        }
                    }
                }else {
                    System.out.println("ERROR: 分析出错");
                }
            }
        }
    }
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐