自顶向下语法分析器的设计与实现

目录

一、说明

二、程序功能及运行截图

功能

运行截图

三、算法逻辑和程序流程图

定义的主要变量或存储结构

(1)消除直接左递归

(2)求FIRST集合

(3)求FOLLOW集合

(4)判断是否是LL(1)文法

(5)构建预测分析表

(6)字符串分析过程

四、源代码

五、总结


一、说明

这是一个简单的自顶向下语法分析程序——LL(1) 语法分析程序。自顶向下分析也称面向目标的分析方法,也就是从文法的开始符出发,试图推导出与输入单词串相匹配的句子。

预测分析法(LL(1)方法)的基本思想是:从文法开始符S 出发,从左到右扫描源程序,每次通过向前查看 1 个字符,选择合适的产生式,生成句子的最左推导。

该程序代码具有一定的通用性,可通过以下几个样例来看。

作者的另一个《编译原理》实验代码:PL/0语言词法分析程序地址:词法分析器的编程实现(含源码)

二、程序功能及运行截图

功能:

  • 从文件读入文法自动区分 终结符号VT 和 非终结符号VN
  • 消除直接左递归、间接左递归和提取左公因子(未实现)
  • 自动生成FIRST和FOLLOW集合
  • 自动判断是否是LL(1)文法
  • 自动构建预测分析表
  • 输入要分析的单词串自动输出分析过程

(可根据自己需求借鉴相关模块函数的实现)

环境:

Java语言编写,在eclipse或者其他Java编译器上编译执行

运行截图:

样例0:

文法(含直接左递归):

E→T|E+T|E-T
T→F|T*F|T/F
F→(E)|i

输出:

输入单词串分析:i+i*i

样例①:

文法:

E→TE'
E'→+TE'|ε
T→FT'
T'→*FT'|ε
F→(E)|i

输出:

样例②:

文法:

S→AaS|BbS|d
A→a
B→ε|c

输入单词串分析:aabd

输出:

分析单词串:aaaa

输出:

样例③(含直接左递归):

文法:

S→a|^|(T)
T→T,S|S

输出:

样例④

E→TE'
E'→+E|ε
T→FT'
T'→T|ε
F→PF'
F'→*F'|ε
P→(E)|^|a|b

输出:

样例⑤

文法:

X→ABCd
A→a|ε
B→b|ε
C→c|ε

输出:

样例⑥:

非LL(1)文法:

S→AaS|a
A→a

输出:

三、算法逻辑和程序流程图

定义的主要变量或存储结构有:

 private static String START; // 开始符号private static HashSet<String> VN, VT; // 非终结符号集、终结符号集private static HashMap<String, ArrayList<ArrayList<String>>> MAP;// key:产生式左边 value:产生式右边(含多条)private static HashMap<String, String> oneLeftFirst;// "|" 分开的单条产生式对应的FIRST集合,用于构建预测分析表private static HashMap<String, HashSet<String>> FIRST, FOLLOW; // FIRST、FOLLOW集合private static String[][] FORM; // 存放预测分析表的数组,用于输出private static HashMap<String, String> preMap;// 存放预测分析表的map,用于快速查找
  • 非终结符号VN、终结符号VT存于Set,集合元素不重复。
  • 文法的每条产生式分割存于map中,键值对的数据结构,便于查找。其中产生式的左边为key,右边如果有多条就存于动态数组中,每个字符识别后同样存于动态数组中,所以value为ArrayList<ArrayList<String>>>,便于之后可能对单个字符的分析。
  • FIRST集合和FOLLOW集合同样以键值对Map的形式存放,其中FIRST中除了非终结符号VN的所有的FIRST集合外,oneLeftFirst在FIRST生成的同时,其也在同步构建,只不过表示方式不太一样,仅是为了便于构建预测分析表(第一行为Vt,第一列为Vn)的将Vn和与之对应的Vt(FIRST)组合起来为key,其value为当条产生式(“|”分割开的),该产生式的FIRST里有的Vt,如此以来,加速了预测分析表的构建。
  • String[][] FROM仅是为了方便输出,行与列之间的对应关系依然存于Map中,在对具体的单词串分析的时候,不可能每次都遍历一个二维数组,效率太低,而HashMap查询效率高。

大体流程图:

UML图:

(1)消除直接左递归

(2)求FIRST集合

对每一文法符号X∈(VnUVt)*

①若X∈Vt ,则FIRST(X)={X}。

②若X∈Vn ,且有产生式X→a,a∈Vt,则a∈FIRST(X)。

③若X∈Vn ,X→ε,则ε∈FIRST(X)

④若X∈Vn,Y1,Y2,...,Yi ∈VN,

且有产生式X→Y1,...,Yn。

若对于1≤i≤k≤n,都有Yi∈ε, 则FIRST(Yk+1)-{ε}∈FIRST(X)

(3)求FOLLOW集合

①对文法开始符号 S ,将“ # ” ( 结束标记) 置于 FOLLOW(S) 中。即 FOLLOW(S)= { # } 。 ( ∵有句型 #S#)

②若有A→aBb,则把FIRST(β)-{ε}加至FOLLOW(B)

③若有A→aB或A→aBb,而b∈εε∈FIRST(b));则把FOLLOW(A)加至FOLLOW(B)中。

(4)判断是否是LL(1)文法

①文法 不包含左递归 ;
②对于文法中的每一个非终结符 A的各个产生式的 侯选首字符集两两不相交 。
③即 对于产生式A→a| b
       若b ≠ ε, 则 FIRST( a ) ∩ FIRST( b )= Ф
       若 b =ε , 则 FIRST(A) ∩FOLLOW(A)=Ф
如果文法 G 满足以上条件,则称该文法 G 为 LL(1) 文法

(5)构建预测分析表

①若a∈FIRST(α),则把A→α放入M[A,a]中

②若ε∈FIRST(α),则对每个终结符b∈ FOLLOW(A),将A→ε放入M[A,b]中

③对空项置出错标记(或用空表示出错)

(6)字符串分析过程

①置栈的初态为#S(#:输入串的开始标记,S:文法开始符号),读入符号放a中;

②弹出栈顶符号放入x中;

③若x∈VT :

若 a=x :则把下一个输入符号读到 a 中,转②
若 a≠x :出错。

④若x=‘#’且a=‘#’:则分析结束,否则出错。

⑤若x∈VN:则查预测分析表M[A,a]:

若 M[ A,a ] 为空,则出错;
若 M[ A,a ] 有一个产生式 x→x 1 … x n ,则将产生式右部符号串以反序进栈。

四、源代码

请结合流程图看代码,定义的方法无顺序关系,调用顺序流程请看main方法

package exp;import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
import java.util.Stack;/*** * @Description:[自动去直接左递归、自动生成FIRST、FOLLOW集合、自动构建预测分析表、输入单词串给出推导过程]* @author kailang* @date 2020-4-25* */
public class Parser {public static final String PATH = "./grammar2";// 文法private static String START; // 开始符号private static HashSet<String> VN, VT; // 非终结符号集、终结符号集private static HashMap<String, ArrayList<ArrayList<String>>> MAP;// key:产生式左边 value:产生式右边(含多条)private static HashMap<String, String> oneLeftFirst;// "|" 分开的单条产生式对应的FIRST集合,用于构建预测分析表private static HashMap<String, HashSet<String>> FIRST, FOLLOW; // FIRST、FOLLOW集合private static String[][] FORM; // 存放预测分析表的数组,用于输出private static HashMap<String, String> preMap;// 存放预测分析表的map,用于快速查找public static void main(String[] args) {init(); // 初始化变量identifyVnVt(readFile(new File(PATH)));// 符号分类,并以key-value形式存于MAP中reformMap();// 消除左递归和提取左公因子findFirst(); // 求FIRST集合findFollow(); // 求FOLLOW集合// 断点测试
//      VN.toString();
//      VT.toString();
//      MAP.toString();
//      FOLLOW.toString();
//      FIRST.toString();
//      oneLeftFirst.toString();if (isLL1()) {preForm(); // 构建预测分析表// printAutoPre("aacbd"); // 示例推导System.out.println("请输入要分析的单词串:");Scanner in = new Scanner(System.in);printAutoPre(in.nextLine());in.close();}}// 变量初始化private static void init() {VN = new HashSet<>();VT = new HashSet<>();MAP = new HashMap<>();FIRST = new HashMap<>();FOLLOW = new HashMap<>();oneLeftFirst = new HashMap<>();preMap = new HashMap<>();}// 判断是否是LL(1)文法private static boolean isLL1() {System.out.println("\n正在判断是否是LL(1)文法....");boolean flag = true;// 标记是否是LL(1)文法Iterator<String> it = VN.iterator();while (it.hasNext()) {String key = it.next();ArrayList<ArrayList<String>> list = MAP.get(key);// 单条产生式if (list.size() > 1) // 如果单条产生式的左边包含两个式子以上,则进行判断for (int i = 0; i < list.size(); i++) {String aLeft = String.join("", list.get(i).toArray(new String[list.get(i).size()]));for (int j = i + 1; j < list.size(); j++) {String bLeft = String.join("", list.get(j).toArray(new String[list.get(j).size()]));if (aLeft.equals("ε") || bLeft.equals("ε")) { // (1)若b=ε,则要FIRST(A)∩FOLLOW(A)=φHashSet<String> retainSet = new HashSet<>();// retainSet=FIRST.get(key);//需要要深拷贝,否则修改retainSet时FIRST同样会被修改retainSet.addAll(FIRST.get(key));if (FOLLOW.get(key) != null)retainSet.retainAll(FOLLOW.get(key));if (!retainSet.isEmpty()) {flag = false;// 不是LL(1)文法,输出FIRST(a)FOLLOW(a)的交集System.out.println("\tFIRST(" + key + ") ∩ FOLLOW(" + key + ") = {"+ String.join("、", retainSet.toArray(new String[retainSet.size()])) + "}");break;} else {System.out.println("\tFIRST(" + key + ") ∩ FOLLOW(" + key + ") = φ");}} else { // (2)b!=ε若,则要FIRST(a)∩FIRST(b)= ФHashSet<String> retainSet = new HashSet<>();retainSet.addAll(FIRST.get(key + "→" + aLeft));retainSet.retainAll(FIRST.get(key + "→" + bLeft));if (!retainSet.isEmpty()) {flag = false;// 不是LL(1)文法,输出FIRST(a)FIRST(b)的交集System.out.println("\tFIRST(" + aLeft + ") ∩ FIRST(" + bLeft + ") = {"+ String.join("、", retainSet.toArray(new String[retainSet.size()])) + "}");break;} else {System.out.println("\tFIRST(" + aLeft + ") ∩ FIRST(" + bLeft + ") = φ");}}}}}if (flag)System.out.println("\t是LL(1)文法,继续分析!");elseSystem.out.println("\t不是LL(1)文法,退出分析!");return flag;}// 构建预测分析表FORMprivate static void preForm() {HashSet<String> set = new HashSet<>();set.addAll(VT);set.remove("ε");FORM = new String[VN.size() + 1][set.size() + 2];Iterator<String> itVn = VN.iterator();Iterator<String> itVt = set.iterator();// (1)初始化FORM,并根据oneLeftFirst(VN$VT,产生式)填表for (int i = 0; i < FORM.length; i++)for (int j = 0; j < FORM[0].length; j++) {if (i == 0 && j > 0) {// 第一行为Vtif (itVt.hasNext()) {FORM[i][j] = itVt.next();}if (j == FORM[0].length - 1)// 最后一列加入#FORM[i][j] = "#";}if (j == 0 && i > 0) {// 第一列为Vnif (itVn.hasNext())FORM[i][j] = itVn.next();}if (i > 0 && j > 0) {// 其他情况先根据oneLeftFirst填表String oneLeftKey = FORM[i][0] + "$" + FORM[0][j];// 作为key查找其First集合FORM[i][j] = oneLeftFirst.get(oneLeftKey);}}// (2)如果有推出了ε,则根据FOLLOW填表for (int i = 1; i < FORM.length; i++) {String oneLeftKey = FORM[i][0] + "$ε";if (oneLeftFirst.containsKey(oneLeftKey)) {HashSet<String> followCell = FOLLOW.get(FORM[i][0]);Iterator<String> it = followCell.iterator();while (it.hasNext()) {String vt = it.next();for (int j = 1; j < FORM.length; j++)for (int k = 1; k < FORM[0].length; k++) {if (FORM[j][0].equals(FORM[i][0]) && FORM[0][k].equals(vt))FORM[j][k] = oneLeftFirst.get(oneLeftKey);}}}}// 打印预测表,并存于Map的数据结构中用于快速查找System.out.println("\n该文法的预测分析表为:");for (int i = 0; i < FORM.length; i++) {for (int j = 0; j < FORM[0].length; j++) {if (FORM[i][j] == null)System.out.print(" " + "\t");else {System.out.print(FORM[i][j] + "\t");if (i > 0 && j > 0) {String[] tmp = FORM[i][j].split("→");preMap.put(FORM[i][0] + "" + FORM[0][j], tmp[1]);}}}System.out.println();}System.out.println();}// 输入的单词串分析推导过程public static void printAutoPre(String str) {System.out.println(str + "的分析过程:");Queue<String> queue = new LinkedList<>();// 句子拆分存于队列for (int i = 0; i < str.length(); i++) {String t = str.charAt(i) + "";if (i + 1 < str.length() && (str.charAt(i + 1) == '\'' || str.charAt(i + 1) == '’')) {t += str.charAt(i + 1);i++;}queue.offer(t);}queue.offer("#");// "#"结束// 分析栈Stack<String> stack = new Stack<>();stack.push("#");// "#"开始stack.push(START);// 初态为开始符号boolean isSuccess = false;int step = 1;while (!stack.isEmpty()) {String left = stack.peek();String right = queue.peek();// System.out.println(left+" "+right);// (1)分析成功if (left.equals(right) && right.equals("#")) {isSuccess = true;System.out.println((step++) + "\t#\t#\t" + "分析成功");break;}// (2)匹配栈顶和当前符号,均为终结符号,消去if (left.equals(right)) {String stackStr = String.join("", stack.toArray(new String[stack.size()]));String queueStr = String.join("", queue.toArray(new String[queue.size()]));System.out.println((step++) + "\t" + stackStr + "\t" + queueStr + "\t匹配成功" + left);stack.pop();queue.poll();continue;}// (3)从预测表中查询if (preMap.containsKey(left + right)) {String stackStr = String.join("", stack.toArray(new String[stack.size()]));String queueStr = String.join("", queue.toArray(new String[queue.size()]));System.out.println((step++) + "\t" + stackStr + "\t" + queueStr + "\t用" + left + "→"+ preMap.get(left + right) + "," + right + "逆序进栈");stack.pop();String tmp = preMap.get(left + right);for (int i = tmp.length() - 1; i >= 0; i--) {// 逆序进栈String t = "";if (tmp.charAt(i) == '\'' || tmp.charAt(i) == '’') {t = tmp.charAt(i-1)+""+tmp.charAt(i);i--;}else {t=tmp.charAt(i)+"";}if (!t.equals("ε"))stack.push(t);}continue;}break;// (4)其他情况失败并退出}if (!isSuccess)System.out.println((step++) + "\t#\t#\t" + "分析失败");}// 符号分类private static void identifyVnVt(ArrayList<String> list) {START = list.get(0).charAt(0) + "";// 存放开始符号for (int i = 0; i < list.size(); i++) {String oneline = list.get(i);String[] vnvt = oneline.split("→");// 用定义符号分割String left = vnvt[0].trim(); // 文法的左边VN.add(left);// 文法右边ArrayList<ArrayList<String>> mapValue = new ArrayList<>();ArrayList<String> right = new ArrayList<>();for (int j = 0; j < vnvt[1].length(); j++) { // 用 “|”分割右边if (vnvt[1].charAt(j) == '|') {VT.addAll(right);mapValue.add(right);// right.clear();// 清空之后,依然是同一个地址,需要重新new对象right = null;right = new ArrayList<>();continue;}// 如果产生式某字符的左边含有中文或英文的单引号,则视为同一个字符if (j + 1 < vnvt[1].length() && (vnvt[1].charAt(j + 1) == '\'' || vnvt[1].charAt(j + 1) == '’')) {right.add(vnvt[1].charAt(j) + "" + vnvt[1].charAt(j + 1));j++;} else {right.add(vnvt[1].charAt(j) + "");}}VT.addAll(right);mapValue.add(right);MAP.put(left, mapValue);}VT.removeAll(VN); // 从终结字符集中移除非终结符// 打印Vn、VtSystem.out.println("\nVn集合:\t{" + String.join("、", VN.toArray(new String[VN.size()])) + "}");System.out.println("Vt集合:\t{" + String.join("、", VT.toArray(new String[VT.size()])) + "}");}// 求每个非终结符号的FIRST集合 和 分解单个产生式的FIRST集合private static void findFirst() {System.out.println("\nFIRST集合:");Iterator<String> it = VN.iterator();while (it.hasNext()) {HashSet<String> firstCell = new HashSet<>();// 存放单个非终结符号的FIRSTString key = it.next();ArrayList<ArrayList<String>> list = MAP.get(key);// System.out.println(key+":");// 遍历单个产生式的左边for (int i = 0; i < list.size(); i++) {ArrayList<String> listCell = list.get(i);// listCell为“|”分割出来HashSet<String> firstCellOne = new HashSet<>();// 产生式左边用“ | ”分割的单个式子的First(弃用)String oneLeft = String.join("", listCell.toArray(new String[listCell.size()]));// System.out.println("oneLeft: "+oneLeft);if (VT.contains(listCell.get(0))) {firstCell.add(listCell.get(0));firstCellOne.add(listCell.get(0));oneLeftFirst.put(key + "$" + listCell.get(0), key + "→" + oneLeft);} else {boolean[] isVn = new boolean[listCell.size()];// 标记是否有定义为空,如果有则检查下一个字符isVn[0] = true;// 第一个为非终结符号int p = 0;while (isVn[p]) {// System.out.println(p+" "+listCell.size());if (VT.contains(listCell.get(p))) {firstCell.add(listCell.get(p));firstCellOne.add(listCell.get(p));oneLeftFirst.put(key + "$" + listCell.get(p), key + "→" + oneLeft);break;}String vnGo = listCell.get(p);//Stack<String> stack = new Stack<>();stack.push(vnGo);while (!stack.isEmpty()) {ArrayList<ArrayList<String>> listGo = MAP.get(stack.pop());for (int k = 0; k < listGo.size(); k++) {ArrayList<String> listGoCell = listGo.get(k);if (VT.contains(listGoCell.get(0))) { // 如果第一个字符是终结符号if (listGoCell.get(0).equals("ε")) {if (!key.equals(START)) { // 开始符号不能推出空firstCell.add(listGoCell.get(0));firstCellOne.add(listGoCell.get(0));oneLeftFirst.put(key + "$" + listGoCell.get(0), key + "→" + oneLeft);}if (p + 1 < isVn.length) {// 如果为空,可以查询下一个字符isVn[p + 1] = true;}} else { // 非空的终结符号加入对应的FIRST集合firstCell.add(listGoCell.get(0));firstCellOne.add(listGoCell.get(0));oneLeftFirst.put(key + "$" + listGoCell.get(0), key + "→" + oneLeft);}} else {// 不是终结符号,入栈stack.push(listGoCell.get(0));}}}p++;if (p > isVn.length - 1)break;}}FIRST.put(key + "→" + oneLeft, firstCellOne);}FIRST.put(key, firstCell);// 输出key的FIRST集合System.out.println("\tFIRST(" + key + ")={" + String.join("、", firstCell.toArray(new String[firstCell.size()])) + "}");}}// 求每个非终结符号的FLLOW集合private static void findFollow() {System.out.println("\nFOLLOW集合:");Iterator<String> it = VN.iterator();HashMap<String, HashSet<String>> keyFollow = new HashMap<>();ArrayList<HashMap<String, String>> vn_VnList = new ArrayList<>();// 用于存放/A->...B 或者 A->...Bε的组合HashSet<String> vn_VnListLeft = new HashSet<>();// 存放vn_VnList的左边和右边HashSet<String> vn_VnListRight = new HashSet<>();// 开始符号加入#keyFollow.put(START, new HashSet<String>() {private static final long serialVersionUID = 1L;{add(new String("#"));}});while (it.hasNext()) {String key = it.next();ArrayList<ArrayList<String>> list = MAP.get(key);ArrayList<String> listCell;// 先把每个VN作为keyFollow的key,之后在查找添加其FOLLOW元素if (!keyFollow.containsKey(key)) {keyFollow.put(key, new HashSet<>());}keyFollow.toString();for (int i = 0; i < list.size(); i++) {listCell = list.get(i);// (1)直接找非总结符号后面跟着终结符号for (int j = 1; j < listCell.size(); j++) {HashSet<String> set = new HashSet<>();if (VT.contains(listCell.get(j))) {// System.out.println(listCell.get(j - 1) + ":" + listCell.get(j));set.add(listCell.get(j));if (keyFollow.containsKey(listCell.get(j - 1)))set.addAll(keyFollow.get(listCell.get(j - 1)));keyFollow.put(listCell.get(j - 1), set);}}// (2)找...VnVn...组合for (int j = 0; j < listCell.size() - 1; j++) {HashSet<String> set = new HashSet<>();if (VN.contains(listCell.get(j)) && VN.contains(listCell.get(j + 1))) {set.addAll(FIRST.get(listCell.get(j + 1)));set.remove("ε");if (keyFollow.containsKey(listCell.get(j)))set.addAll(keyFollow.get(listCell.get(j)));keyFollow.put(listCell.get(j), set);}}// (3)A->...B 或者 A->...Bε(可以有n个ε)的组合存起来for (int j = 0; j < listCell.size(); j++) {HashMap<String, String> vn_Vn;if (VN.contains(listCell.get(j)) && !listCell.get(j).equals(key)) {// 是VN且A不等于Bboolean isAllNull = false;// 标记VN后是否为空if (j + 1 < listCell.size())// 即A->...Bε(可以有n个ε)for (int k = j + 1; k < listCell.size(); k++) {if ((FIRST.containsKey(listCell.get(k)) ? FIRST.get(listCell.get(k)).contains("ε"): false)) {// 如果其后面的都是VN且其FIRST中包含εisAllNull = true;} else {isAllNull = false;break;}}// 如果是最后一个为VN,即A->...Bif (j == listCell.size() - 1) {isAllNull = true;}if (isAllNull) {vn_VnListLeft.add(key);vn_VnListRight.add(listCell.get(j));// 往vn_VnList中添加,分存在和不存在两种情况boolean isHaveAdd = false;for (int x = 0; x < vn_VnList.size(); x++) {HashMap<String, String> vn_VnListCell = vn_VnList.get(x);if (!vn_VnListCell.containsKey(key)) {vn_VnListCell.put(key, listCell.get(j));vn_VnList.set(x, vn_VnListCell);isHaveAdd = true;break;} else {// 去重if (vn_VnListCell.get(key).equals(listCell.get(j))) {isHaveAdd = true;break;}continue;}}if (!isHaveAdd) {// 如果没有添加,表示是新的组合vn_Vn = new HashMap<>();vn_Vn.put(key, listCell.get(j));vn_VnList.add(vn_Vn);}}}}}}keyFollow.toString();// (4)vn_VnListLeft减去vn_VnListRight,剩下的就是入口产生式,vn_VnListLeft.removeAll(vn_VnListRight);Queue<String> keyQueue = new LinkedList<>();// 用栈或者队列都行Iterator<String> itVnVn = vn_VnListLeft.iterator();while (itVnVn.hasNext()) {keyQueue.add(itVnVn.next());}while (!keyQueue.isEmpty()) {String keyLeft = keyQueue.poll();for (int t = 0; t < vn_VnList.size(); t++) {HashMap<String, String> vn_VnListCell = vn_VnList.get(t);if (vn_VnListCell.containsKey(keyLeft)) {HashSet<String> set = new HashSet<>();// 原来的FOLLOW加上左边的FOLLOWif (keyFollow.containsKey(keyLeft))set.addAll(keyFollow.get(keyLeft));if (keyFollow.containsKey(vn_VnListCell.get(keyLeft)))set.addAll(keyFollow.get(vn_VnListCell.get(keyLeft)));keyFollow.put(vn_VnListCell.get(keyLeft), set);keyQueue.add(vn_VnListCell.get(keyLeft));// 移除已处理的组合vn_VnListCell.remove(keyLeft);vn_VnList.set(t, vn_VnListCell);}}}// 此时keyFollow为完整的FOLLOW集FOLLOW = keyFollow;// 打印FOLLOW集合Iterator<String> itF = keyFollow.keySet().iterator();while (itF.hasNext()) {String key = itF.next();HashSet<String> f = keyFollow.get(key);System.out.println("\tFOLLOW(" + key + ")={" + String.join("、", f.toArray(new String[f.size()])) + "}");}}// 消除直接左递归private static void reformMap() {boolean isReForm = false;// MAP是否被修改Set<String> keys = new HashSet<>();keys.addAll(MAP.keySet());Iterator<String> it = keys.iterator();ArrayList<String> nullSign = new ArrayList<>();nullSign.add("ε");while (it.hasNext()) {String left = it.next();boolean flag = false;// 是否有左递归ArrayList<ArrayList<String>> rightList = MAP.get(left);ArrayList<String> oldRightCell = new ArrayList<>(); // 旧产生的右边ArrayList<ArrayList<String>> newLeftNew = new ArrayList<>();// 存放新的左边和新的右边// 消除直接左递归for (int i = 0; i < rightList.size(); i++) {ArrayList<String> newRightCell = new ArrayList<>(); // 新产生式的右边if (rightList.get(i).get(0).equals(left)) {for (int j = 1; j < rightList.get(i).size(); j++) {newRightCell.add(rightList.get(i).get(j));}flag = true;newRightCell.add(left + "\'");newLeftNew.add(newRightCell);} else {for (int j = 0; j < rightList.get(i).size(); j++) {oldRightCell.add(rightList.get(i).get(j));}oldRightCell.add(left + "\'");}}if (flag) {// 如果有左递归,则更新MAPisReForm = true;newLeftNew.add(nullSign);MAP.put(left + "\'", newLeftNew);VN.add(left + "\'"); // 加入新的VNVT.add("ε"); // 加入ε到VTArrayList<ArrayList<String>> newLeftOld = new ArrayList<>();// 存放原先,但是产生新的右边newLeftOld.add(oldRightCell);MAP.put(left, newLeftOld);}// 间接左递归// 提取左公因子// 待续...}// 如果文法被修改,则输出修改后的文法if (isReForm) {System.out.println("消除文法的左递归:");Set<String> kSet = new HashSet<>(MAP.keySet());Iterator<String> itk = kSet.iterator();while (itk.hasNext()) {String k = itk.next();ArrayList<ArrayList<String>> leftList = MAP.get(k);System.out.print("\t" + k + "→");for (int i = 0; i < leftList.size(); i++) {System.out.print(String.join("", leftList.get(i).toArray(new String[leftList.get(i).size()])));if (i + 1 < leftList.size())System.out.print("|");}System.out.println();}}}// 从文件读文法public static ArrayList<String> readFile(File file) {System.out.println("从文件读入的文法为:");ArrayList<String> result = new ArrayList<>();try {BufferedReader br = new BufferedReader(new FileReader(file));String s = null;while ((s = br.readLine()) != null) {System.out.println("\t" + s);result.add(s.trim());}br.close();} catch (Exception e) {e.printStackTrace();}return result;}
}

tips:产生式的定义符号请以“→”,如果有两个符号组成的字符只允许含英文或中文的单引号,空符号请用这个“ ε ”,分割符号请用“ | ”,请不要添加多余的空格!

如果出现文件读入乱码,请用记事本改成这样的编码格式,(如果保存为txt,代码里的文件名记得加上.txt)

五、总结

虽然代码有点长,但逻辑上还是比较直白的。

由于构建的预测表不尽完善,仅是按FIRST、FOLLOW填表,无出错处理,所以在对输入的单词输出分析过程的时候,有可能陷入死循环,但是如果单词串是由该文法推导出来的话,一般来说,应该大概也许可能没什么问题!

希望能给你帮助!不理解的地方可以私信我!能否及时回复随缘。

版权所有,未经授权,严禁转载!

Github仓库地址

编译原理:LL(1)语法分析器的实现(内含代码详细注释)相关推荐

  1. 编译原理实验-LL1语法分析器(自动生成First集、Follow集求法)java实现

    编译原理实验-LL1语法分析器(自动生成First.Follow)java 博主在做实验时,参考众多他人代码,发现bug众多,在@moni_mm代码基础上,与伙伴把能看到的BUG都做出修正,同时增添了 ...

  2. 【编译原理】LR语法分析器的设计与实现

    LR语法分析器的设计与实现 本文为当时编译原理实验作业,要求用设计的思想完成,小题大做,仅供参考 文章目录 LR语法分析器的设计与实现 实验要求 实现功能 输入输出 样例 一.LR语法分析器问题定义 ...

  3. 【编译原理】词法分析器语法分析器

    简单编译器设计 采用Java语言对C++语言进行编译,具体的简单编译器设计 词法分析器-扫描器的设计与实现 基本符号表 状态转换图 代码实现 import java.io.*; import java ...

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

    实验内容 针对SysY语言中简单算术表达式文法G[E]: E→TE' E'→ATE'|ε T→FT' T'→MFT' |ε F→(E) | i A → + | - M → * | / 求解相应的FIR ...

  5. 编译原理之简单语法分析器(c语言)

    语法分析是编译过程的核心部分,其基本任务是根据语言的语法规则进行语法分析,如果不存在语法错误即给出正确的语法结果,并为语义分析和代码生成做准备. 语法分析器的两种方式 语法分析器的任务主要是确定是否可 ...

  6. 编译原理 实验2 语法分析器的构造

    [实验目的] 练习构造语法分析程序的方法,熟悉上下文无关文法的使用,加深对课堂教学的理解:提高词法分析方法的实践能力 [实验要求] 利用某一高级程序设计语言构造语法分析程序 [具体要求]对于给定的文法 ...

  7. 编译原理LR(1)语法分析器 C++实现

    LR(1)语法分析器 C++语言编写,已通过VS2019调试 文章目录 LR(1)语法分析器 一.测试结果 二.测试文件 三.核心代码 四.完整代码 感谢阅读! 如有错误,恳请指正! 一.测试结果 二 ...

  8. 编译原理-5-LL(1)语法分析器

    LL(1)语法分析器 1. 什么是LL(1)语法分析器 自顶向下的.递归下降的.预测分析的.适用于LL(1)文法的LL(1)语法分析器 自顶向下构建语法分析树 根节点是文法的起始符号SSS 每个中间节 ...

  9. 贵州大学-编译原理实验2-句法分析器

    贵州大学-编译原理实验2-句法分析器 考虑下面的C语言子集的文法,其中<>括起来的为非终结符,粗体为终结符. ® <statement_list> <statement_ ...

最新文章

  1. 【Android FFMPEG 开发】Android 中执行 FFMPEG 指令 ( 集成 FFmpegAndroid 框架 )
  2. 有关内存释放的一些问题
  3. Android系统自带样式(@android:style/)
  4. 中以什么开头仅可注释单行_Python从入门到精通第006课--注释与变量
  5. 跨模块中的分配内存问题
  6. cakephp 1.3 配置CakePHP
  7. Spring AOP实例——异常处理和记录程序执行时间
  8. python解析原理_Python 中 -m 的典型用法、原理解析与发展演变
  9. SpringMVC源码总结(八)类型转换PropertyEditor的背后
  10. Java Class对象
  11. 超三十万台设备感染银行木马、远程代码漏洞可攻击云主机|12月7日全球网络安全热点
  12. 软件质量管理体系-ISO 9000
  13. 施耐德 m340 编程手册_M251M340在自动化立体仓储的应用
  14. js调用数科阅读器_aspx调用js函数
  15. 基于ZYNQ的VGA驱动
  16. Windows11 下屏幕亮度自动调整的问题解决
  17. 扎拉赞恩 服务器 微信群,魔兽8.0剧透 回归的扎拉赞恩与沃金的骨灰
  18. [组合数学] NC13611树 (逆元的计算)
  19. 大文件数据传输会遇到哪些问题?
  20. pytorch_nicp编译

热门文章

  1. 2017年内大892数据结构部分参考答案
  2. 《深入理解计算机系统》学习记录
  3. 中国IT咨询公司的机会在哪里?
  4. linux下使用P4(命令行)
  5. 在Ubuntu 18.04安装福昕阅读器
  6. modbus测试软件
  7. C语言中静态变量的概念和用法
  8. 免费分享:5本安卓开发经典书籍,Android 7编程入门经典(第4版),Android底层驱动分析和移植,底层驱动分析和移植
  9. 快速校验 input内容规则
  10. 32位的md5校验程序