LL(1)文法first集
2017-04-08 11:56
671 查看
LL(1) 文法 first集
思路: 利用递归(但普通递归的方法不能处理非终极符直接递归和间接递归的情况) 考虑到这种情况,见下方利用bfs遍历first集关系图的方法
第一步先计算可导空的非终极符集合
令first(X)=空
若 X->a... 则将a加入first(X)
若 X->ABC.. ,则计算first(A) ,first(B) ,first(C)..直到遇到第一个不导空的非终结符或者终极符,则将他们都加入first(X) 并除去^(空串)的情况
若 右部可以导空,将右部所有的非终结符的first集加入first(X),并加上空串的情况
利用bfs的方法
first集 思想: 利用图的广度遍历
对文法先进行预处理,求出每一个非终结符的出度
然后将待求的非终结符加入到队列,并标记为访问过,然后开始广度优先遍历,
队列为空,则停止遍历,若出队的是终极符,则加入到该非终结符的first集中
若出队的是非终结符,则将该非终结符中没有被访问过的出度加入到队列中
思路: 利用递归(但普通递归的方法不能处理非终极符直接递归和间接递归的情况) 考虑到这种情况,见下方利用bfs遍历first集关系图的方法
第一步先计算可导空的非终极符集合
令first(X)=空
若 X->a... 则将a加入first(X)
若 X->ABC.. ,则计算first(A) ,first(B) ,first(C)..直到遇到第一个不导空的非终结符或者终极符,则将他们都加入first(X) 并除去^(空串)的情况
若 右部可以导空,将右部所有的非终结符的first集加入first(X),并加上空串的情况
package first_collection; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * first集 * 第一步先计算可导空的非终极符集合 * 令first(X)=空 * 若 X->a... 则将a加入first(X) * 若 X->ABC.. ,则计算first(A) ,first(B) ,first(C)..直到遇到第一个不导空的非终结符或者终极符,则将他们都加入first(X) 并除去^(空串)的情况 * 若 右部可以导空,将右部所有的非终结符的first集加入first(X),并加上空串的情况 * * @author parting_soul * */ public class FirstCollection { private Set<Character> emptyNonTerminal; private List<Rule> ruleLists; private Set<Character> terminalC; private Set<Character> nonterminalC; private Map<String, Set<Character>> first; public FirstCollection() { ruleLists = new ArrayList<>(); terminalC = new HashSet<>(); nonterminalC = new HashSet<>(); init(); } /** * 输入产生式,求出可导空的非终结符 */ private void init() { inputRuleLists(); // 计算可导空的非终结符集合 EmptyNonterminalSet nonterminalSet = new EmptyNonterminalSet(ruleLists); nonterminalSet.solve(); emptyNonTerminal = nonterminalSet.getR(); first = new HashMap<String, Set<Character>>(); initRuleVisit(); } private void initRuleVisit() { for (Rule r : ruleLists) { r.isVisit = false; } } public void getTerminalFirst(char noTernimalLeft) { Rule r = null; Set<Character> terminalFirstSet = null; // 判断之前是否存在该非终结符的first集 if ((terminalFirstSet = first.get(noTernimalLeft + "")) == null) { // 没有计算过,新建一个集合用来保存该非终结符的first集 terminalFirstSet = new HashSet<>(); first.put(noTernimalLeft + "", terminalFirstSet); // System.out.println(noTernimalLeft + " create "); } for (int i = 0; i < ruleLists.size(); i++) { // 遍历所有的产生式,计算每一个左边为该非终结符产生式的first集 r = ruleLists.get(i); // 如果该产生式别计算过,直接选择下一个产生式 if (r.isVisit) continue; if (r.left.equals(noTernimalLeft + "")) { // System.out.println(r); // 找到左部为该非终极符的产生式,标记为访问过 r.isVisit = true; String right = r.right; for (int j = 0; j < right.length(); j++) { // 右部从左到右扫描每一个字符 char c = right.charAt(j); if (!isNonTerminal(c)) { // 如果是非终结符,直接加入到first集,然后停止计算 terminalFirstSet.add(c); // System.out.println(r.left + " -> " + r.right + " " + // c); break; } else { // 如果当前为非终结符,递归计算该非终结符的first集 getTerminalFirst(c); // System.out.println(r.left + " -> " + r.right + " " + // c + " nonterminal " + first.get(c + "")); // 将该非终结符的first集加入当前待求的first集中 terminalFirstSet.addAll(first.get(c + "")); // 除去空串 terminalFirstSet.remove('^'); if (!emptyNonTerminal.contains(c)) { // 如果当前非终结符不可导空,不用继续计算,跳出循环 break; } } } // 判断当前产生式最后一个字符是否可导空 char last = right.charAt(right.length() - 1); if (emptyNonTerminal.contains(last)) { // 可导空则加入空串 terminalFirstSet.add('^'); } } } } public Map<String, Set<Character>> getFirstCollection() { for (Character c : nonterminalC) { // System.out.println(" current " + c); // 遍历每一个非终结符,求出该非终结符的first集 getTerminalFirst(c); } return first; } public static void main(String[] args) { FirstCollection collection = new FirstCollection(); collection.printTerAndNon(); Map<String, Set<Character>> first = collection.getFirstCollection(); System.out.println(first); } /** * 输入产生式 */ private void inputRuleLists() { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("rule.txt")))); String str = null; while ((str = reader.readLine()) != null) { // System.out.println(str); saveRule(str); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } finally { reader = null; } } } } /** * 分析产生式 * * @param str */ private void saveRule(String str) { String[] strs = str.split("->"); // System.out.println(strs.length); if (strs.length == 2) { Rule r = new Rule(); r.left = strs[0]; r.right = strs[1]; if (isNonTerminal(r.left.charAt(0))) { nonterminalC.add(r.left.charAt(0)); for (int i = 0; i < r.right.length(); i++) { char c = r.right.charAt(i); if (!isNonTerminal(c)) { terminalC.add(c); } else { nonterminalC.add(c); } } ruleLists.add(r); } } } void printTerAndNon() { System.out.println("终结符:"); for (Character c : terminalC) { System.out.print(c + " "); } System.out.println(); System.out.println("非终结符:"); for (Character c : nonterminalC) { System.out.print(c + " "); } System.out.println(); System.out.println("产生式"); for (Rule r : ruleLists) { System.out.println(r.left + "->" + r.right); } System.out.println("可导空非终极符"); System.out.println(emptyNonTerminal); } /** * 是否为终极符 * * @param c * @return */ private boolean isNonTerminal(char c) { if (c >= 'A' && c <= 'Z') { return true; } return false; } } /** * 求出可导空的非终极符集合类 * * @author parting_soul * */ class EmptyNonterminalSet { private List<Rule> L; private Set<Character> R = new HashSet<>(); public EmptyNonterminalSet(List<Rule> L) { this.L = L; } public Set<Character> getR() { return R; } public void solve() { // 先扫描一遍所有的产生式,将右端为空的且左端为终结符的产生式左端加入集合R中(这里^号表示空) for (int i = 0; i < L.size(); i++) { Rule r = L.get(i); if (r.right.equals("^") && !isTerminator(r.left.charAt(0))) { R.add(r.left.charAt(0)); r.isVisit = true; } } int rLength = 0; while (rLength != R.size()) { rLength = R.size(); for (int i = 0; i < L.size(); i++) { Rule r = L.get(i); if (!r.isVisit && isBelongToR(r.right)) { R.add(r.left.charAt(0)); r.isVisit = true; } } } } /** * 判断产生式右端的所有字符是否属于R * * @return */ private boolean isBelongToR(String right) { for (int i = 0; i < right.length(); i++) { if (!R.contains(right.charAt(i))) { // System.out.println(right.charAt(i)); return false; } } return true; } // 判断是否是终结符 public boolean isTerminator(Character c) { return Character.isLowerCase(c); } public void print() { for (Rule r : L) { System.out.println(r); } } public void printR() { for (Character c : R) { System.out.print(c + " "); } System.out.println(); } } class Rule { String left; String right; boolean isVisit; @Override public String toString() { return "Rule [left=" + left + ", right=" + right + ", isVisit=" + isVisit + "]"; } }
利用bfs的方法
first集 思想: 利用图的广度遍历
对文法先进行预处理,求出每一个非终结符的出度
然后将待求的非终结符加入到队列,并标记为访问过,然后开始广度优先遍历,
队列为空,则停止遍历,若出队的是终极符,则加入到该非终结符的first集中
若出队的是非终结符,则将该非终结符中没有被访问过的出度加入到队列中
package first_collection_bfs; import java.io.*; import java.util.*; /** * first集 思想: 利用图的广度遍历 * 对文法先进行预处理,求出每一个非终结符的出度 * 然后将待求的非终结符加入到队列,并标记为访问过,然后开始广度优先遍历, * 队列为空,则停止遍历,若出队的是终极符,则加入到该非终结符的first集中 * 若出队的是非终结符,则将该非终结符中没有被访问过的出度加入到队列中 * * @author parting_soul */ public class FirstCollection { private Set<Character> emptyNonTerminal; private List<Rule> ruleLists; private Set<Character> terminalC; private Set<Character> nonterminalC; private Map<String, Set<Character>> first; private Map<Character, Set<Character>> map; private Queue<Character> queue; private Map<Character, Boolean> isVisistMap; public FirstCollection() { ruleLists = new ArrayList<>(); terminalC = new HashSet<>(); nonterminalC = new HashSet<>(); map = new HashMap<>(); queue = new LinkedList<>(); isVisistMap = new HashMap<>(); init(); } /** * 输入产生式,求出可导空的非终结符 */ private void init() { inputRuleLists(); // 计算可导空的非终结符集合 EmptyNonterminalSet nonterminalSet = new EmptyNonterminalSet(ruleLists); nonterminalSet.solve(); emptyNonTerminal = nonterminalSet.getR(); getMap(); first = new HashMap<>(); } /** * 预处理,得到每一个非终结符的出度 */ private void getMap() { for (char c : nonterminalC) { for (int j = 0; j < ruleLists.size(); j++) { Rule rule = ruleLists.get(j); if (rule.left.equals(c + "")) { Set<Character> characters = map.get(c); if (characters == null) { characters = new HashSet<>(); } for (int k = 0; k < rule.right.length(); k++) { char currentC = rule.right.charAt(k); if ("^".equals(currentC + "")) break; characters.add(currentC); if (!isNonTerminal(currentC) || !emptyNonTerminal.contains(currentC)) { break; } } map.put(c, characters); } } } } /** * bfs求非终结符first * * @param noTernimalLeft */ public void getNonTerminalFirstByBFS(char noTernimalLeft) { queue.clear(); //将待求的非终结符加入队列 queue.offer(noTernimalLeft); //标记为访问过 isVisistMap.put(noTernimalLeft, true); Set<Character> sets = new HashSet<>(); first.put(noTernimalLeft + "", sets); while (queue.size() != 0) { //队首出队 char currentC = queue.poll(); if (!isNonTerminal(currentC)) { //如果是终结符加入到该非终结符的first中 sets.add(currentC); continue; } //如果是非终结符,就遍历该非终结符的出度 Set<Character> characters = map.get(currentC); for (char c : characters) { if (!isNonTerminal(c)) { //如果是终结符,直接加入队列 queue.offer(c); continue; } //如果是非终结符就先判断是否访问过,没有访问过就入队,并标记为访问过 boolean isVisist = isVisistMap.get(c); if (isVisist) continue; isVisistMap.put(c, true); queue.offer(c); } } } public Map<String, Set<Character>> getFirstCollection() { for (Character c : nonterminalC) { // System.out.println(" current " + c); // 遍历每一个非终结符,求出该非终结符的first集 getNonTerminalFirstByBFS(c); //重置非终结符为未访问 initNonTernimalVisit(); //若该非终结符可导空,加入^ if (emptyNonTerminal.contains(c)) { first.get(c.charValue() + "").add('^'); } } return first; } private void initNonTernimalVisit() { for (char c : nonterminalC) { isVisistMap.put(c, false); } } public static void main(String[] args) { FirstCollection collection = new FirstCollection(); collection.printTerAndNon(); Map<String, Set<Character>> first = collection.getFirstCollection(); System.out.println(first); } /** * 输入产生式 */ private void inputRuleLists() { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("rule1.txt")))); String str = null; while ((str = reader.readLine()) != null) { // System.out.println(str); saveRule(str); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } finally { reader = null; } } } } /** * 分析产生式 * * @param str */ private void saveRule(String str) { String[] strs = str.split("->"); // System.out.println(strs.length); if (strs.length == 2) { Rule r = new Rule(); r.left = strs[0]; r.right = strs[1]; if (isNonTerminal(r.left.charAt(0))) { nonterminalC.add(r.left.charAt(0)); isVisistMap.put(r.left.charAt(0), false); for (int i = 0; i < r.right.length(); i++) { char c = r.right.charAt(i); if (!isNonTerminal(c)) { terminalC.add(c); } else { nonterminalC.add(c); } } ruleLists.add(r); } } } void printTerAndNon() { System.out.println("终结符:"); for (Character c : terminalC) { System.out.print(c + " "); } System.out.println(); System.out.println("非终结符:"); for (Character c : nonterminalC) { System.out.print(c + " "); } System.out.println(); System.out.println("产生式"); for (Rule r : ruleLists) { System.out.println(r.left + "->" + r.right); } System.out.println("可导空非终极符"); System.out.println(emptyNonTerminal); } /** * 是否为非终极符 * * @param c * @return */ private boolean isNonTerminal(char c) { if (c >= 'A' && c <= 'Z') { return true; } return false; } } /** * 求出可导空的非终极符集合类 * * @author parting_soul */ class EmptyNonterminalSet { private List<Rule> L; private Set<Character> R = new HashSet<>(); public EmptyNonterminalSet(List<Rule> L) { this.L = L; } public Set<Character> getR() { return R; } public void solve() { // 先扫描一遍所有的产生式,将右端为空的且左端为终结符的产生式左端加入集合R中(这里#号表示空) for (int i = 0; i < L.size(); i++) { Rule r = L.get(i); if (r.right.equals("^") && !isTerminator(r.left.charAt(0))) { R.add(r.left.charAt(0)); r.isVisit = true; } } int rLength = 0; while (rLength != R.size()) { rLength = R.size(); for (int i = 0; i < L.size(); i++) { Rule r = L.get(i); if (!r.isVisit && isBelongToR(r.right)) { R.add(r.left.charAt(0)); r.isVisit = true; } } } } /** * 判断产生式右端的所有字符是否属于R * * @return */ private boolean isBelongToR(String right) { for (int i = 0; i < right.length(); i++) { if (!R.contains(right.charAt(i))) { // System.out.println(right.charAt(i)); return false; } } return true; } // 判断是否是终结符 public boolean isTerminator(Character c) { return Character.isLowerCase(c); } public void print() { for (Rule r : L) { System.out.println(r); } } public void printR() { for (Character c : R) { System.out.print(c + " "); } System.out.println(); } } class Rule { String left; String right; boolean isVisit; @Override public String toString() { return "Rule [left=" + left + ", right=" + right + ", isVisit=" + isVisit + "]"; } }
相关文章推荐
- LL(1)文法判别之First集合、Follow集合、Select集合求法
- [转]LL(1)文法判别之First集合、Follow集合、Select集合求法
- LL(1)文法分析表的构造和分析过程示例
- 编译原理(六) LL(1)文法分析法(分析过程的C++实现)
- 编译器设计:文法与LL(1)
- 编译原理:first集合和follow集合的求法及LL(1)文法判定
- javacc 中Non-LL(1) 文法处理技巧之LOOKAHEAD
- 编译原理实验4——LL(1)文法分析
- 自上而下的语法分析,LL(1)文法(消除左递归,提取左因子)
- 如何求LL(1)文法的 FIRST集、FOLLOW集 和 SELECT集
- LL(1)语法分析之first、follow的实现
- Formal Languages and Compilers-LL(1),FIRST and FOLLOW
- 编译原理中LL(1)文法求FIRST集和FOLLOW集的方法
- LL(1)文法
- LL(1)文法判断
- 上一个计算器有bug,还是用语法制导的方法计算表达式,这个没有bug,用的是LL(1)表达式文法,以=号结束
- Formal Languages and Compilers-LL(1),FIRST and FOLLOW
- HDU1409-LL(1)文法
- LL(1)文法判定
- 如何判断文法是LL(1)SLR(1)LR(1)LALR(1)的?