static HashMap<String, ArrayList<String>> follow = new HashMap<>();
时间: 2024-05-31 11:15:08 浏览: 139
这是一个静态变量,在Java中,静态变量属于类而不是对象,可以通过类名直接访问。该变量名为follow,类型为HashMap<String, ArrayList<String>>,即键为字符串类型,值为字符串类型的ArrayList。这个HashMap用于存储每个字符串对应的一个字符串列表,可以用来表示关注某个用户的所有粉丝或者关注某个用户所关注的所有人。
相关问题
假如你是一个高级程序员,请用Java编写具体代码实现下面功能 创建一个 GUI 界面,让用户输入语法 G。您可以使用 Java Swing 来创建用户界面。 创建一个 CFG 类,用于处理输入语法 G 的左递归和公共左因子分解。在该类中,您可以添加方法来计算每个非终端符号的 FIRST 和 FOLLOW 集。 实现 LL(1) 解析器算法。您可以使用一个解析器类来实现此算法。该算法需要 LL(1) 解析表来预测下一个输入标记是哪个。可以使用一个二维数组来表示解析表。每个单元格都对应一个非终结符和一个终结符。解析表中的每个元素包含一个产生式,该产生式来标识生成哪个非终极符号的语法树。 将 CFG 类和解析器类与 GUI 类集成,以便用户可以输入要解析的句子,并且输出可以显示在屏幕上。您可以将 CFG 类和解析器类作为 GUI 类的成员变量,并在 GUI 类中添加按钮和文本框,以便用户可以输入要解析的句子并将打印出来的FIRST、FOLLOW集和预测分析表输出显示在屏幕上。
好的,以下是我给出的Java代码实现:
首先,我们创建一个GUI界面,使用Java Swing来创建。代码如下:
```java
import javax.swing.*;
import java.awt.*;
public class CFG_GUI {
private JFrame frame;
private JPanel panel;
private JLabel inputLabel;
private JTextField inputField;
private JButton submitButton;
private JTextArea outputArea;
private JScrollPane scrollPane;
public CFG_GUI() {
frame = new JFrame("CFG Parser");
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
panel.setLayout(new BorderLayout());
inputLabel = new JLabel("Input Grammar:");
inputField = new JTextField(50);
submitButton = new JButton("Parse");
outputArea = new JTextArea(30, 50);
outputArea.setEditable(false);
scrollPane = new JScrollPane(outputArea);
JPanel inputPanel = new JPanel();
inputPanel.add(inputLabel);
inputPanel.add(inputField);
inputPanel.add(submitButton);
panel.add(inputPanel, BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);
frame.add(panel);
frame.setVisible(true);
}
public String getInput() {
return inputField.getText();
}
public void setOutput(String output) {
outputArea.setText(output);
}
public void addSubmitListener(ActionListener listener) {
submitButton.addActionListener(listener);
}
}
```
接下来,我们创建一个CFG类,用于处理输入语法G的左递归和公共左因子分解,并添加方法来计算每个非终端符号的FIRST和FOLLOW集。代码如下:
```java
import java.util.*;
public class CFG {
private Map<String, List<String>> productions;
public CFG(String input) {
productions = new HashMap<>();
String[] lines = input.split("\n");
for (String line : lines) {
String[] tokens = line.split(" -> ");
String nonterminal = tokens[0];
String[] rhs = tokens[1].split("\\|");
List<String> productionsList = new ArrayList<>(Arrays.asList(rhs));
productions.put(nonterminal, productionsList);
}
}
public Map<String, Set<String>> getFirst() {
Map<String, Set<String>> first = new HashMap<>();
for (String nonterminal : productions.keySet()) {
first.put(nonterminal, new HashSet<>());
}
boolean changed;
do {
changed = false;
for (String nonterminal : productions.keySet()) {
for (String production : productions.get(nonterminal)) {
String[] symbols = production.split("\\s+");
int i = 0;
while (i < symbols.length) {
String symbol = symbols[i];
if (isTerminal(symbol)) {
changed |= first.get(nonterminal).add(symbol);
break;
} else {
Set<String> firstOfSymbol = first.get(symbol);
changed |= first.get(nonterminal).addAll(firstOfSymbol);
if (!firstOfSymbol.contains("")) {
break;
}
}
i++;
}
if (i == symbols.length) {
changed |= first.get(nonterminal).add("");
}
}
}
} while (changed);
return first;
}
public Map<String, Set<String>> getFollow(Map<String, Set<String>> first) {
Map<String, Set<String>> follow = new HashMap<>();
for (String nonterminal : productions.keySet()) {
follow.put(nonterminal, new HashSet<>());
}
follow.get("S").add("$");
boolean changed;
do {
changed = false;
for (String nonterminal : productions.keySet()) {
for (String production : productions.get(nonterminal)) {
String[] symbols = production.split("\\s+");
for (int i = 0; i < symbols.length; i++) {
String symbol = symbols[i];
if (isNonterminal(symbol)) {
Set<String> followOfSymbol = follow.get(symbol);
int j = i + 1;
while (j < symbols.length) {
Set<String> firstOfNextSymbol = first.get(symbols[j]);
changed |= followOfSymbol.addAll(firstOfNextSymbol);
if (!firstOfNextSymbol.contains("")) {
break;
}
j++;
}
if (j == symbols.length) {
changed |= followOfSymbol.addAll(follow.get(nonterminal));
}
}
}
}
}
} while (changed);
return follow;
}
public boolean isNonterminal(String symbol) {
return symbol.matches("[A-Z].*");
}
public boolean isTerminal(String symbol) {
return !isNonterminal(symbol);
}
public String eliminateLeftRecursion() {
List<String> nonterminals = new ArrayList<>(productions.keySet());
StringBuilder output = new StringBuilder();
for (int i = 0; i < nonterminals.size(); i++) {
String nonterminal = nonterminals.get(i);
output.append(nonterminal).append(" -> ");
List<String> productionsList = productions.get(nonterminal);
List<String> alpha = new ArrayList<>();
List<String> beta = new ArrayList<>();
for (String production : productionsList) {
String[] symbols = production.split("\\s+");
if (symbols[0].equals(nonterminal)) {
alpha.add(production.substring(nonterminal.length() + 3));
} else {
beta.add(production);
}
}
if (alpha.isEmpty()) {
output.append(String.join(" | ", beta)).append("\n");
continue;
}
String newNonterminal = nonterminal + "'";
output.append(String.join(" | ", beta)).append(" ").append(newNonterminal).append("\n");
List<String> newProductions = new ArrayList<>();
for (String b : beta) {
newProductions.add(b + " " + newNonterminal);
}
newProductions.add("");
List<String> newAlphaProductions = new ArrayList<>();
for (String a : alpha) {
newAlphaProductions.add(a.substring(1) + " " + newNonterminal);
}
newAlphaProductions.add("");
productions.put(nonterminal, new ArrayList<>(newProductions));
productions.put(newNonterminal, new ArrayList<>(newAlphaProductions));
nonterminals.add(i + 1, newNonterminal);
}
return output.toString();
}
public String factorize() {
List<String> nonterminals = new ArrayList<>(productions.keySet());
StringBuilder output = new StringBuilder();
for (int i = 0; i < nonterminals.size(); i++) {
String nonterminal = nonterminals.get(i);
List<String> productionsList = productions.get(nonterminal);
Map<String, List<String>> groups = new HashMap<>();
for (String production : productionsList) {
String symbol = production.split("\\s+")[0];
if (!groups.containsKey(symbol)) {
groups.put(symbol, new ArrayList<>());
}
groups.get(symbol).add(production);
}
List<String> newProductions = new ArrayList<>();
for (String symbol : groups.keySet()) {
List<String> group = groups.get(symbol);
if (group.size() > 1) {
String newNonterminal = nonterminal + "'";
output.append(newNonterminal).append(" -> ").append(symbol).append(" ").append(newNonterminal).append("\n");
productions.put(newNonterminal, new ArrayList<>(group));
newProductions.add(symbol + " " + newNonterminal);
nonterminals.add(i + 1, newNonterminal);
} else {
newProductions.addAll(group);
}
}
productions.put(nonterminal, newProductions);
}
return output.toString();
}
}
```
最后,我们实现LL(1)解析器算法,使用一个解析器类来实现此算法。代码如下:
```java
import java.util.*;
public class Parser {
private String[][] parsingTable;
private Map<String, List<String>> productions;
public Parser(Map<String, Set<String>> first, Map<String, Set<String>> follow, CFG cfg) {
Set<String> terminals = new HashSet<>();
Set<String> nonterminals = new HashSet<>(cfg.productions.keySet());
productions = cfg.productions;
for (String nonterminal : nonterminals) {
terminals.addAll(first.get(nonterminal));
terminals.addAll(follow.get(nonterminal));
}
terminals.remove("");
parsingTable = new String[nonterminals.size() + 1][terminals.size() + 1];
for (int i = 0; i < parsingTable.length; i++) {
for (int j = 0; j < parsingTable[0].length; j++) {
parsingTable[i][j] = "";
}
}
for (String nonterminal : nonterminals) {
int i = getIndex(nonterminal, nonterminals);
for (String production : productions.get(nonterminal)) {
Set<String> firstOfProduction = getFirstOfProduction(production, first);
for (String symbol : firstOfProduction) {
if (!symbol.equals("")) {
int j = getIndex(symbol, terminals);
parsingTable[i][j] = production;
}
}
if (firstOfProduction.contains("")) {
for (String symbol : follow.get(nonterminal)) {
int j = getIndex(symbol, terminals);
parsingTable[i][j] = production;
}
}
}
}
}
public boolean parse(String input) {
Stack<String> stack = new Stack<>();
stack.push("$");
stack.push("S");
String[] tokens = input.split("\\s+");
int i = 0;
while (!stack.empty()) {
String top = stack.pop();
if (isTerminal(top)) {
if (!top.equals(tokens[i])) {
return false;
}
i++;
} else if (isNonterminal(top)) {
int row = getIndex(top, productions.keySet());
int col = getIndex(tokens[i], parsingTable[0]);
String production = parsingTable[row][col];
if (production.equals("")) {
return false;
}
String[] symbols = production.split("\\s+");
for (int j = symbols.length - 1; j >= 0; j--) {
stack.push(symbols[j]);
}
}
}
return true;
}
public Set<String> getFirstOfProduction(String production, Map<String, Set<String>> first) {
String[] symbols = production.split("\\s+");
Set<String> firstOfProduction = new HashSet<>();
int i = 0;
while (i < symbols.length) {
String symbol = symbols[i];
if (isTerminal(symbol)) {
firstOfProduction.add(symbol);
break;
} else {
Set<String> firstOfSymbol = first.get(symbol);
firstOfProduction.addAll(firstOfSymbol);
if (!firstOfSymbol.contains("")) {
break;
}
}
i++;
}
if (i == symbols.length) {
firstOfProduction.add("");
}
return firstOfProduction;
}
public boolean isNonterminal(String symbol) {
return symbol.matches("[A-Z].*");
}
public boolean isTerminal(String symbol) {
return !isNonterminal(symbol);
}
public int getIndex(String symbol, Set<String> set) {
int i = 0;
for (String s : set) {
if (s.equals(symbol)) {
return i;
}
i++;
}
return -1;
}
public int getIndex(String symbol, String[] array) {
int i = 0;
for (String s : array) {
if (s.equals(symbol)) {
return i;
}
i++;
}
return -1;
}
}
```
最后,我们将CFG类和解析器类与GUI类集成,以便用户可以输入要解析的句子,并且输出可以显示在屏幕上。代码如下:
```java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;
import java.util.Set;
public class Main {
public static void main(String[] args) {
CFG_GUI gui = new CFG_GUI();
gui.addSubmitListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String input = gui.getInput();
CFG cfg = new CFG(input);
String output = "";
output += "Original Grammar:\n" + input + "\n\n";
output += "Eliminated Left Recursion:\n" + cfg.eliminateLeftRecursion() + "\n";
output += "Factored Grammar:\n" + cfg.factorize() + "\n";
Map<String, Set<String>> first = cfg.getFirst();
output += "FIRST Sets:\n";
for (String nonterminal : first.keySet()) {
output += nonterminal + ": " + first.get(nonterminal) + "\n";
}
output += "\n";
Map<String, Set<String>> follow = cfg.getFollow(first);
output += "FOLLOW Sets:\n";
for (String nonterminal : follow.keySet()) {
output += nonterminal + ": " + follow.get(nonterminal) + "\n";
}
output += "\n";
Parser parser = new Parser(first, follow, cfg);
output += "Parsing Table:\n";
output += String.format("%-6s", "");
Set<String> terminals = parser.getIndex("", first).isEmpty() ? first.get("S") : first.get("");
for (String terminal : terminals) {
output += String.format("%-25s", terminal);
}
output += "\n";
for (String nonterminal : cfg.productions.keySet()) {
output += String.format("%-6s", nonterminal);
for (String terminal : terminals) {
String production = parser.parsingTable[parser.getIndex(nonterminal, cfg.productions.keySet())][parser.getIndex(terminal, parser.parsingTable[0])];
if (!production.equals("")) {
output += String.format("%-25s", nonterminal + " -> " + production);
} else {
output += String.format("%-25s", "");
}
}
output += "\n";
}
output += "\n";
boolean success = parser.parse(gui.getInput());
output += "Parsing Result: " + (success ? "Accepted" : "Rejected");
gui.setOutput(output);
}
});
}
}
```
用java实现语法分析器(要求有界面),并实现预测分析法,不能使用JavaFX,并完成以下要求:处理文法中可能存在的左递归和公共左因子问题;对文法中的每个非终结符自动生成并打印输出:① FIRST集;② FOLLOW集;判断处理后的文法是否为LL(1)文法,如果是, 自动生成并打印输出其预测分析表;5) 模拟分析过程。如输入一个句子,如果该句子合法则输出与句子 对应的语法树;能够输出分析过程中每一步符号 栈的变化情况。如果该句子非法则进行相应的报错处理直接给出代码
以下是一个简单的Java语法分析器的实现,实现了预测分析法,处理了文法中可能存在的左递归和公共左因子问题,并对文法中的每个非终结符自动生成并打印输出:FIRST集、FOLLOW集,并判断处理后的文法是否为LL(1)文法,如果是,自动生成并打印输出其预测分析表,并能够输出分析过程中每一步符号栈的变化情况。如果该句子非法则进行相应的报错处理。
请注意,这只是一个简单的示例代码,您需要进一步完善和优化它以适应您的具体需求。
```java
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class SyntaxAnalyzer extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private JTextArea textArea;
private JButton openButton;
private JButton parseButton;
private JFileChooser fileChooser;
private Map<String, List<String>> grammar;
private Map<String, Set<String>> firstSets;
private Map<String, Set<String>> followSets;
private Map<String, Map<String, String>> predictionTable;
private List<String> nonterminals;
private List<String> terminals;
public SyntaxAnalyzer() {
super("Syntax Analyzer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
JPanel buttonPanel = new JPanel();
openButton = new JButton("Open");
openButton.addActionListener(this);
buttonPanel.add(openButton);
parseButton = new JButton("Parse");
parseButton.addActionListener(this);
buttonPanel.add(parseButton);
add(buttonPanel, BorderLayout.NORTH);
textArea = new JTextArea();
JScrollPane jScrollPane = new JScrollPane(textArea);
add(jScrollPane, BorderLayout.CENTER);
fileChooser = new JFileChooser(".");
}
public static void main(String[] args) {
SyntaxAnalyzer analyzer = new SyntaxAnalyzer();
analyzer.setVisible(true);
}
private void readGrammar(String filename) throws Exception {
grammar = new HashMap<String, List<String>>();
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line = null;
while ((line = reader.readLine()) != null) {
String[] parts = line.split(" -> ");
String lhs = parts[0].trim();
String[] rhss = parts[1].split("\\|");
List<String> rhsList = new ArrayList<String>();
for (String rhs : rhss) {
rhsList.add(rhs.trim());
}
grammar.put(lhs, rhsList);
}
reader.close();
}
private void eliminateLeftRecursion() {
nonterminals = new ArrayList<String>(grammar.keySet());
for (int i = 0; i < nonterminals.size(); i++) {
String A = nonterminals.get(i);
for (int j = 0; j < i; j++) {
String B = nonterminals.get(j);
List<String> AProductions = grammar.get(A);
List<String> BProductions = grammar.get(B);
List<String> newAProductions = new ArrayList<String>();
List<String> newBProductions = new ArrayList<String>();
for (String production : AProductions) {
if (production.startsWith(B)) {
for (String bProduction : BProductions) {
newAProductions.add(bProduction + production.substring(1));
}
} else {
newAProductions.add(production);
}
}
grammar.put(A, newAProductions);
}
List<String> AProductions = grammar.get(A);
boolean leftRecursive = false;
for (String production : AProductions) {
if (production.startsWith(A)) {
leftRecursive = true;
break;
}
}
if (leftRecursive) {
List<String> newAProductions = new ArrayList<String>();
List<String> newAProductions = new ArrayList<String>();
for (String production : AProductions) {
if (production.startsWith(A)) {
newAProductions.add(production.substring(1) + A + "'");
} else {
newAProductions.add(production + A + "'");
}
}
newAProductions.add("epsilon");
grammar.put(A, newAProductions);
grammar.put(A + "'", newAProductions);
}
}
}
private void eliminateCommonLeftFactor() {
nonterminals = new ArrayList<String>(grammar.keySet());
for (int i = 0; i < nonterminals.size(); i++) {
String A = nonterminals.get(i);
List<String> AProductions = grammar.get(A);
for (int j = 0; j < AProductions.size(); j++) {
String aProduction = AProductions.get(j);
for (int k = j + 1; k < AProductions.size(); k++) {
String bProduction = AProductions.get(k);
int l = 0;
while (l < aProduction.length() && l < bProduction.length()
&& aProduction.charAt(l) == bProduction.charAt(l)) {
l++;
}
if (l > 0) {
String C = A + "_" + l;
String newAProduction = aProduction.substring(0, l) + C;
String newBProduction = bProduction.substring(0, l) + C;
List<String> CProductions = new ArrayList<String>();
CProductions.add(aProduction.substring(l));
CProductions.add(bProduction.substring(l));
grammar.put(C, CProductions);
AProductions.set(j, newAProduction);
AProductions.set(k, newBProduction);
}
}
}
grammar.put(A, AProductions);
}
}
private void computeFirstSets() {
firstSets = new HashMap<String, Set<String>>();
nonterminals = new ArrayList<String>(grammar.keySet());
terminals = new ArrayList<String>();
for (List<String> productions : grammar.values()) {
for (String production : productions) {
for (int i = 0; i < production.length(); i++) {
char symbol = production.charAt(i);
if (symbol >= 'A' && symbol <= 'Z') {
break;
} else if (i == production.length() - 1) {
terminals.add("" + symbol);
}
}
}
}
for (String terminal : terminals) {
Set<String> firstSet = new HashSet<String>();
firstSet.add(terminal);
firstSets.put(terminal, firstSet);
}
for (String nonterminal : nonterminals) {
firstSets.put(nonterminal, new HashSet<String>());
}
boolean changed = true;
while (changed) {
changed = false;
for (String nonterminal : nonterminals) {
List<String> productions = grammar.get(nonterminal);
for (String production : productions) {
boolean allNullable = true;
for (int i = 0; i < production.length(); i++) {
char symbol = production.charAt(i);
if (symbol >= 'A' && symbol <= 'Z') {
Set<String> symbolFirstSet = firstSets.get("" + symbol);
if (!symbolFirstSet.contains("epsilon")) {
allNullable = false;
firstSets.get(nonterminal).addAll(symbolFirstSet);
break;
} else {
symbolFirstSet.remove("epsilon");
firstSets.get(nonterminal).addAll(symbolFirstSet);
}
} else {
firstSets.get(nonterminal).add("" + symbol);
allNullable = false;
break;
}
}
if (allNullable) {
firstSets.get(nonterminal).add("epsilon");
}
}
Set<String> oldFirstSet = new HashSet<String>(firstSets.get(nonterminal));
for (String first : oldFirstSet) {
if (first.equals("epsilon")) {
continue;
}
for (int i = 0; i < productions.size(); i++) {
String production = productions.get(i);
if (first.equals("" + production.charAt(0))) {
firstSets.get(nonterminal).addAll(firstSets.get("" + production.charAt(0)));
if (i == productions.size() - 1 && oldFirstSet.contains("epsilon")) {
firstSets.get(nonterminal).add("epsilon");
}
}
}
}
if (!oldFirstSet.equals(firstSets.get(nonterminal))) {
changed = true;
}
}
}
}
private void computeFollowSets() {
followSets = new HashMap<String, Set<String>>();
nonterminals = new ArrayList<String>(grammar.keySet());
terminals.add("$");
for (String nonterminal : nonterminals) {
followSets.put(nonterminal, new HashSet<String>());
}
followSets.get(nonterminals.get(0)).add("$");
boolean changed = true;
while (changed) {
changed = false;
for (String nonterminal : nonterminals) {
List<String> productions = grammar.get(nonterminal);
for (String production : productions) {
for (int i = 0; i < production.length(); i++) {
char symbol = production.charAt(i);
if (symbol >= 'A' && symbol <= 'Z') {
boolean allNullable = true;
for (int j = i + 1; j < production.length(); j++) {
char nextSymbol = production.charAt(j);
if (nextSymbol >= 'A' && nextSymbol <= 'Z') {
followSets.get("" + symbol).addAll(firstSets.get("" + nextSymbol));
if (!firstSets.get("" + nextSymbol).contains("epsilon")) {
allNullable = false;
break;
}
} else {
followSets.get("" + symbol).add("" + nextSymbol);
allNullable = false;
break;
}
}
if (allNullable) {
followSets.get("" + symbol).addAll(followSets.get(nonterminal));
}
}
}
}
}
for (String nonterminal : nonterminals) {
Set<String> oldFollowSet = new HashSet<String>(followSets.get(nonterminal));
for (String production : grammar.get(nonterminal)) {
for (int i = production.length() - 1; i >= 0; i--) {
char symbol = production.charAt(i);
if (symbol >= 'A' && symbol <= 'Z') {
if (i == production.length() - 1) {
followSets.get("" + symbol).addAll(followSets.get(nonterminal));
}
boolean allNullable = true;
for (int j = i + 1; j < production.length(); j++) {
char nextSymbol = production.charAt(j);
if (nextSymbol >= 'A' && nextSymbol <= 'Z') {
followSets.get("" + symbol).addAll(firstSets.get("" + nextSymbol));
if (!firstSets.get("" + nextSymbol).contains("epsilon")) {
allNullable = false;
break;
}
} else {
followSets.get("" + symbol).add("" + nextSymbol);
allNullable = false;
break;
}
}
if (allNullable) {
followSets.get("" + symbol).addAll(followSets.get(nonterminal));
}
}
}
}
if (!oldFollowSet.equals(followSets.get(nonterminal))) {
changed = true;
}
}
}
}
private void buildPredictionTable() {
predictionTable = new HashMap<String, Map<String, String>>();
nonterminals = new ArrayList<String>(grammar.keySet());
terminals = new ArrayList<String>();
for (List<String> productions : grammar.values()) {
for (String production : productions) {
for (int i = 0; i < production.length(); i++) {
char symbol = production.charAt(i);
if (symbol >= 'A' && symbol <= 'Z') {
break;
} else if (i == production.length() - 1) {
terminals.add("" + symbol);
}
}
}
}
terminals.add("$");
for (String nonterminal : nonterminals) {
Map<String, String> row = new HashMap<String, String>();
for (String terminal : terminals) {
row.put(terminal, "");
}
predictionTable.put(nonterminal, row);
}
for (String nonterminal : nonterminals) {
List<String> productions = grammar.get(nonterminal);
for (String production : productions) {
Set<String> firstSet = computeFirstSet(production);
for (String terminal : firstSet) {
if (!terminal.equals("epsilon")) {
predictionTable.get(nonterminal).put(
阅读全文