// 看了看网上挺多都是非递归,写了很长
// 想要个递归的,短点的
// 到处瞎逛的时候发现了这个,感觉写的很好,我通读了一遍,补了点注释,让大家借鉴下..
// 忘了是从哪里扒来的.. 如果有需要删除请联系我
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.StringTokenizer;
public class Ex3 {
public List<String[]> in = new ArrayList<String[]>(); // 这存放的是拆分后最简单的生成子式
public List<String[]> result = new ArrayList<String[]>(); // 包括左推导符和其 First Set
public void start() {
this.preprocess(); // 预处理
this.process(); // 处理逻辑
this.output(); // 输出
}
private void preprocess() {
Scanner sc = new Scanner(System.in);
System.out.println("请分行输入一个完整文法:(end结束)");
String sLine = "";
sLine = sc.nextLine();
while (!sLine.startsWith("end")) {
String[] strings = sLine.split("->"); // 默认的推导符
if (strings.length == 1)
strings = sLine.split("→"); // 考虑到推导符不同,再分一次
if (strings.length == 1) {
System.out.println("文法有误");
System.exit(0);
}
if (!Character.isUpperCase(strings[0].charAt(0))) {
System.out.println("文法有误"); // 左推导符号不为终结符
System.exit(0);
}
StringTokenizer fx = new StringTokenizer(strings[1], "|");
while (fx.hasMoreTokens()) {
String[] sub = new String[2]; // 将可能的多条分支分解成一条条产生子式(subProduction)
sub[0] = strings[0]; // 左边依旧放非终结符
sub[1] = fx.nextToken(); // 右边放多条分支的其中一条
in.add(sub);
}
sLine = sc.nextLine();
}
}
public void process() {
for (int i = 0; i < in.size(); i++) {
boolean isFirstSub = true; // 是否是该非终结符的第一个产生子式
for (int j = 0; j < i; j++)
if (in.get(j)[0].equals(in.get(i)[0])) // 遍历比较当前i锚点之前的所有产生式的头部
isFirstSub = false; // 当前i指向的非第一产生子式
if (isFirstSub) {
ArrayList<String> firstSet;
firstSet = this.getFirst(in.get(i)[0]); // 求出 First
firstSet.add(0, in.get(i)[0]); // 构造完毕,数组可以随意更改,插入 Vn
String[] vn_firstSet = firstSet.toArray(new String[0]); // 构造 vn-firstSet
result.add(vn_firstSet); // 放入结果集中
}
}
}
/*
* 递归函数
* s 可以表示非终结符 或者 终结符
* s 为终结符,则返回自身
* s 为非终结符,则返回其 First Set
* */
public ArrayList<String> getFirst(String s) {
ArrayList<String> result = new ArrayList<String>();
ArrayList<String> middle = new ArrayList<String>();
if (Character.isUpperCase(s.charAt(0))) { // 递归时判定是否为非终结符,只看第一位是考虑 E'
for (int i = 0; i < in.size(); i++) {
String[] sub = in.get(i);
if (s.equals(sub[0])) { // 确定当前要处理的子式
int k = 0;
middle = getFirst(sub[1].charAt(k) + ""); // 递归求右式一位的 First
for (int j = 0; j < middle.size(); j++) {
result.add(middle.get(j)); // 将右边一位的 First 据为己有
while (middle.get(j).equals("ε") && (++k < sub[1].length())) {
// 对于可能出现 ε 的情况,就是有可能要往下递归的情况
// 判断当前递归是否是最后一轮递归
// 不是,则进入循环,继续递归
middle = getFirst(sub[1].charAt(k) + ""); // 递归取右边第k+1位的First k>=1
for (int l = 0; l < middle.size(); l++) {
if (!middle.get(l).equals("ε"))
// 如果为空,说明:1.非空元素已经全部取完 2.需要判断是否往下递归,即跳出循环
result.add(middle.get(l));
}
}
}
}
}
} else {
result.add(s);
}
return result;
}
private void output() {
System.out.println("\nFirst Set:");
for (int i = 0; i < result.size(); i++) {
// get non-terminal
String[] vn_firstSet = result.get(i);
System.out.print("First(" + vn_firstSet[0] + ")={");
for (int j = 1; j < vn_firstSet.length; j++) {
// First Set content
System.out.print(vn_firstSet[j]);
if (j < vn_firstSet.length - 1)
System.out.print(",");
}
System.out.println("}");
}
}
public static void main(String[] args) {
Ex3 ex3 = new Ex3();
ex3.start();
}
}
Mess_ LL(1) First Set——递归实现
最新推荐文章于 2020-08-25 12:38:58 发布