语法分析 LL1实现

在grammarStr中设置好相应的文法,inStr中设置好相应的输入串(以#结尾)即可自动实现并输出构造First集、Follow集、预测分析表、预测分析总过程。

相关说明

  1. First集构造算法:
  2. Follow集构造算法
  3. Select集构造算法:
  4. 请注意本程序中并未单独为构造Select集编写相应的函数,而是通过GetFirstX()方法构造每条产生式右部的首符集。在构造预测分析表的过程中,如果某条产生式右部的首符集包括空,则不仅将该产生式右部加入到其首符集对应的预测分析表中,而且将产生式右部加入到左部非终结符的Follow集对应的预测分析表中。(这段话看不懂没关系,看代码就好了)

Java代码如下:

import java.util.*;

/**
 * class: --LL1: 实现LL(1)分析,构造分析预测程序(FIRST集-->FOLLOW集-->预测分析表-->stack预测分析)
 */

public class LL1 {
	static String[] grammarStr = { "E->TA", "A->+TA|ε", "T->FB", "B->*FB", "B->ε", "F->i", "F->(E)" };// 相关文法
	static HashMap<Character, ArrayList<String>> production = new HashMap<>();// 产生式
	static HashMap<Character, HashSet<Character>> FirstSet = new HashMap<>();// 构造FIRST集合
	static HashMap<String, HashSet<Character>> FirstSetX = new HashMap<>();// 生成任何符号串的first
	static HashMap<Character, HashSet<Character>> FollowSet = new HashMap<>();// 构造FOLLOW集合
	static String[][] table;// 预测分析表
	static HashSet<Character> VnSet = new HashSet<>();// 非终结符Vn集合
	static HashSet<Character> VtSet = new HashSet<>();// 终结符Vt集合
	static Stack<Character> stack = new Stack<>(); // 符号栈
	static String inStr = "i+i*i#";// 输入串
	static Character start = '0';
	static int index = 0;// 输入字符指针
	static String action = "";
	
	

	public static void main(String[] args) {
		// 求得production,终结符和非终结符,
		dividechar();
		// 求得所有符号的First集
		First();
		// 求得所有产生式右部的首符集
		for (Character c : VnSet) {
			ArrayList<String> l = production.get(c);
			for (String s : l)
				getFirstX(s);
		}
		// 计算Follow集合
		Follow();
		// 构造预测分析表
		creatTable();
		// 输出First集合、Follow集、预测分析表
		ouput();
		// 分析栈处理
		processLL1();
	}

	/**
	 * 生成产生式Map(production),划分终结符(vt)与非终结符(vn)
	 */
	static void dividechar() {
		// 开始符号
		start = grammarStr[0].charAt(0);
		// 生成产生式Map(production)
		for (String str : grammarStr) {
			// 将“|”相连的产生式分开
			String[] strings = str.split("->")[1].split("\\|");
			// 非终结符
			char Vch = str.charAt(0);
			ArrayList<String> list = production.containsKey(Vch) ? production.get(Vch) : new ArrayList<String>();
			for (String S : strings) {
				list.add(S);
			}
			// 将非终结符以及对应的所有产生式加入到production中
			production.put(Vch, list);
			// 加入非终结符
			VnSet.add(Vch);
		}
		// 寻找终结符
		for (Character ch : VnSet) {
			for (String str : production.get(ch)) {
				for (Character c : str.toCharArray()) {
					if (!VnSet.contains(c))
						VtSet.add(c);
				}
			}

		}

	}

	/**
	 * 生成非终结符的FIRST集的递归入口
	 */
	static void First() {
		// 循环求first直到first集合中元素不再添加
		int preFirstSize = 0;
		while (true) {
			int afterFirstSize = 0;
			// 遍历求每一个非终结符Vn的first集
			for (Character vn : VnSet) {
				getfisrst(vn);
			}
			// 一次计算后first集中元素数量
			for (Character chVn : VnSet) {
				afterFirstSize += FirstSet.get(chVn).size();
			}
			// 计算follow后元素数量没有变化
			if (preFirstSize == afterFirstSize) {
				break;
			}
			preFirstSize = afterFirstSize;
		}
	}

	/**
	 * 生成非终结符FIRST集的递归程序
	 */
	static void getfisrst(Character ch) {
		ArrayList<String> ch_production = production.get(ch);
		HashSet<Character> set = FirstSet.containsKey(ch) ? FirstSet.get(ch) : new HashSet<>();
		// 当ch为终结符
		if (VtSet.contains(ch)) {
			set.add(ch);
			FirstSet.put(ch, set);
			return;
		}
		// ch为非终结符
		for (String str : ch_production) {
			int i = 0;
			while (i < str.length()) {
				char tn = str.charAt(i);
				// 递归
				getfisrst(tn);
				HashSet<Character> tvSet = FirstSet.get(tn);
				// 将其first集加入左部
				for (Character tmp : tvSet) {
					if (tmp != 'ε')
						set.add(tmp);
				}
				// 若包含空串 处理下一个符号
				if (tvSet.contains('ε'))
					i++;
				// 否则退出 处理下一个产生式
				else
					break;
			}
			// 产生式右部都包括ε
			if (i == str.length())
				set.add('ε');
		}
		FirstSet.put(ch, set);
	}

	/**
	 * 生成任何符号串的first
	 */
	static void getFirstX(String s) {

		HashSet<Character> set = (FirstSetX.containsKey(s)) ? FirstSetX.get(s) : new HashSet<Character>();
		// 从左往右扫描该式
		int i = 0;
		while (i < s.length()) {
			char tn = s.charAt(i);
			if (!FirstSet.containsKey(tn))
				getfisrst(tn);
			HashSet<Character> tvSet = FirstSet.get(tn);
			// 将其非空 first集加入左部
			for (Character tmp : tvSet)
				if (tmp != 'ε')
					set.add(tmp);
			// 若包含空串 处理下一个符号
			if (tvSet.contains('ε'))
				i++;
			// 否则结束
			else
				break;
			// 到了尾部 即所有符号的first集都包含空串 把空串加入
			if (i == s.length()) {
				set.add('ε');
			}
		}
		FirstSetX.put(s, set);

	}

	/**
	 * 生成FOLLOW集
	 */
	static void Follow() {
		int preFollowSize = 0;
		while (true) {
			int afterFollowSize = 0;
			// 求得所有非终结符的Follow集
			for (Character character : VnSet) {
				getFollow(character);
			}
			// 一次计算后follow集中元素数量
			for (Character chVn : VnSet) {
				afterFollowSize += FollowSet.get(chVn).size();
			}
			// 计算follow后元素数量没有变化
			if (preFollowSize == afterFollowSize) {
				break;
			}
			preFollowSize = afterFollowSize;
		}
	}

	/**
	 * 求Follow集
	 */
	static void getFollow(char c) {
		ArrayList<String> list = production.get(c);
		HashSet<Character> setA = FollowSet.containsKey(c) ? FollowSet.get(c) : new HashSet<Character>();
		// 如果是开始符 添加 #
		if (c == start) {
			setA.add('#');
		}
		// 查找输入的所有产生式,确定c的后跟 终结符
		for (Character ch : VnSet) {
			ArrayList<String> l = production.get(ch);
			for (String s : l)
				for (int i = 0; i < s.length() - 1; i++)
					if (s.charAt(i) == c && VtSet.contains(s.charAt(i + 1)))
						setA.add(s.charAt(i + 1));
		}
		FollowSet.put(c, setA);
		// 处理c的每一条产生式
		for (String s : list) {
			int i = s.length() - 1;
			while (i >= 0) {
				char tn = s.charAt(i);
				// 只处理非终结符
				if (VnSet.contains(tn)) {
					// 都按 A->αBβ 形式处理
					// 若β不存在 followA 加入 followB
					// 若β存在,把β的非空first集 加入followB
					// 若β存在 且 first(β)包含空串 followA 加入 followB

					// 若β存在
					if (s.length() - i - 1 > 0) {
						String right = s.substring(i + 1);
						// 非空first集 加入 followB
						HashSet<Character> setF = null;
						if (right.length() == 1) {
							if (!FirstSet.containsKey(right.charAt(0)))
								getfisrst(right.charAt(0));
							setF = FirstSet.get(right.charAt(0));
						} else {
							// 先找出右部的first集
							if (!FirstSetX.containsKey(right))
								getFirstX(right);
							setF = FirstSetX.get(right);
						}
						HashSet<Character> setX = FollowSet.containsKey(tn) ? FollowSet.get(tn)
								: new HashSet<Character>();
						for (Character var : setF)
							if (var != 'ε')
								setX.add(var);
						FollowSet.put(tn, setX);

						// 若first(β)包含空串 ,则把followA 加入 followB
						if (setF.contains('ε')) {
							if (tn != c) {
								HashSet<Character> setB = FollowSet.containsKey(tn) ? FollowSet.get(tn)
										: new HashSet<Character>();
								for (Character var : setA)
									setB.add(var);
								FollowSet.put(tn, setB);
							}
						}
					}
					// 若β不存在,则把followA 加入 followB
					else {
						// A和B相同不添加
						if (tn != c) {
							HashSet<Character> setB = FollowSet.containsKey(tn) ? FollowSet.get(tn)
									: new HashSet<Character>();
							for (Character var : setA)
								setB.add(var);
							FollowSet.put(tn, setB);
						}
					}
					i--;
				}
				// 如果是终结符往前看 如 A->aaaBCDaaaa 此时β为 CDaaaa
				// 终结符不计算Follow集
				else
					i--;
			}
		}
	}

	/**
	 * 生成预测分析表
	 */
	static void creatTable() {
		Object[] VtArray = VtSet.toArray();
		Object[] VnArray = VnSet.toArray();
		// 预测分析表初始化
		table = new String[VnArray.length + 1][VtArray.length + 1];
		table[0][0] = "Vn\\Vt";
		// 初始化首行首列
		for (int i = 0; i < VtArray.length; i++)
			table[0][i + 1] = (VtArray[i].toString().charAt(0) == 'ε') ? "#" : VtArray[i].toString();
		for (int i = 0; i < VnArray.length; i++)
			table[i + 1][0] = VnArray[i] + "";
		// 全部置error
		for (int i = 0; i < VnArray.length; i++)
			for (int j = 0; j < VtArray.length; j++)
				table[i + 1][j + 1] = "error";

		// 插入生成式
		for (char A : VnSet) {
			ArrayList<String> l = production.get(A);
			for (String s : l) {
				HashSet<Character> set = FirstSetX.get(s);
				for (char a : set) {
					// 产生式右部首符集中如果有空,在这里跳过
					if (a == 'ε') {
						continue;
					}
					insert(A, a, s);
				}
				// 如果产生式右部的首符集包含空,即产生式右部所有符号的首符集都包括空,则将左部的后继符集加入选择符集中
				if (set.contains('ε')) {
					HashSet<Character> setFollow = FollowSet.get(A);
					// if (setFollow.contains('#'))
					// insert(A, '#', s);
					for (char b : setFollow)
						insert(A, b, s);
				}
			}
		}
	}

	/**
	 * 将生成式插入表中
	 */
	static void insert(char X, char a, String s) {
		if (a == 'ε')
			return;
		// a = '#';
		for (int i = 0; i < VnSet.size() + 1; i++) {
			if (table[i][0].charAt(0) == X)
				for (int j = 0; j < VtSet.size() + 1; j++) {
					if (table[0][j].charAt(0) == a) {
						table[i][j] = s;
						return;
					}
				}
		}
	}

	/**
	 * 执行LL1栈分析
	 */
	static void processLL1() {
		System.out.println("****************LL分析过程**********");
		System.out.println(
				"                                                 栈                   剩余输入串                         动作");
		stack.push('#');
		stack.push(start);
		char X = stack.peek();
		while (X != '#') {
			char a = inStr.charAt(index);
			// 匹配
			if (X == a) {
				action = "匹配 " + stack.peek();
				displayLL();
				stack.pop();
				index++;

			}
			// 预测分析表为error
			else if (find(X, a).equals("error")) {
				// boolean flag = false;
				// 如果X首符集包含空,直接弹出X,处理下一个元素
				if (FirstSet.get(X).contains('ε')) {
					action = X + "->ε";
					displayLL();
					stack.pop();
					// flag = true;
				}
			}
			// 预测分析表中为空,特殊处理
			else if (find(X, a).equals("ε")) {
				action = X + "->ε";
				displayLL();
				stack.pop();
			}
			// 预测分析表中找到或查无此项
			else {
				String str = find(X, a);
				if (str != "") {
					action = X + "->" + str;
					displayLL();
					stack.pop();
					int len = str.length();
					String pushStr = "";
					for (int i = len - 1; i >= 0; i--) {
						stack.push(str.charAt(i));
						pushStr += str.charAt(i);
					}
				}
				// a为非法符号
				else {
					action = "出错";
					displayLL();
					System.out.println("在处理该字符时发生错误 :" + inStr.charAt(index) + "\n再输入流的位置下标: " + index);
					return;
				}
			}
			X = stack.peek();
		}
		action = "接受";
		displayLL();
		System.out.println("匹配成功!");
		System.out.println();
	}

	/**
	 *
	 * @param X
	 *            非终结符
	 * @param a
	 *            终结符
	 * @return 预测分析表中对应内容,如果在预测分析表中没有找到返回空字符串
	 */
	static String find(char X, char a) {
		for (int i = 0; i < VnSet.size() + 1; i++) {
			if (table[i][0].charAt(0) == X)
				for (int j = 0; j < VtSet.size() + 1; j++) {
					if (table[0][j].charAt(0) == a)
						return table[i][j];
				}
		}
		return "";
	}

	static void displayLL() {
		// 输出 LL1单步处理
		Stack<Character> s = stack;
		System.out.printf("%23s", s);
		System.out.printf("%13s", inStr.substring(index));
		System.out.printf("%10s", action);
		System.out.println();
	}

	/**
	 * 打印first.follow集,预测分析表
	 */
	static void ouput() {
		System.out.println("***********First集********");
		for (Character c : VnSet) {
			HashSet<Character> set = FirstSet.get(c);
			System.out.print(c + "  :   ");
			for (Character var : set)
				System.out.print(var + " ");
			System.out.println();
		}
		System.out.println();

		System.out.println("**********Follow集*********");
		for (Character c : VnSet) {
			HashSet<Character> set = FollowSet.get(c);
			System.out.print(c + " :");
			for (Character var : set)
				System.out.print(var + " ");
			System.out.println();
		}
		System.out.println();

		System.out.println("**********LL1预测分析表********");
		for (int i = 0; i < VnSet.size() + 1; i++) {
			for (int j = 0; j < VtSet.size() + 1; j++) {
				System.out.printf("%6s", table[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值