1. 程式人生 > 其它 >編譯原理實驗LL1分析

編譯原理實驗LL1分析


//  下面是實驗三的內容 :

class LL1analysis extends JFrame implements ActionListener{


  JLabel labelGrammarInput ;
  JButton buttonOpenFile;
  JButton buttonCertainGrammar;
  JButton buttonSaveFile;

  JLabel labelWarning1;
  JLabel labelWarning2;
  JLabel labelWarning3;

  JTextArea textAreaGrammar;
  JButton buttonGenerateFirstSet;
  JButton buttonGenerateFollowSet;


  JLabel labelFirstSet;
  JLabel labelFollowSet;
  JTextArea textAreaFirstSet;
  JTextArea textAreaFollowSet;



  JLabel labelForecastAnalysisTable;
  JButton buttonGenerateForecastAnalysis;
  JTextArea textAreaForecastAnalysisTable;

  JLabel labelAnalysisSentence;
  JLabel labelWaitAnalosisSentence;
  JTextField textFieldWaitAnalysisSentence;
  JButton buttonAnalysis , buttonSingleStepShow , buttonOneChickDisplay;
  JLabel labelAnalysisResult ;
  JTextArea textAreaAnalysisResult;



  // 用於 實現功能的變數
  grammar currentGrammar ;


  boolean certainGrammar = false;



  boolean readyAnalysis = false;

  // 為句型 分析新增
  Stack<Character> analStack = new Stack<>();
  char[] sequence;
  int analIndex;

  boolean isEnd = false;




  String law = "*()+";
  LL1analysis(){
    init();
  }

  void init(){
    labelGrammarInput = new JLabel("文法輸入");
    buttonOpenFile =  new JButton("開啟檔案");
    buttonOpenFile.addActionListener(this::actionPerformed);
    buttonSaveFile = new JButton("儲存文法");
    buttonSaveFile.addActionListener(this::actionPerformed);
    buttonCertainGrammar = new JButton("確認文法");
    buttonCertainGrammar.addActionListener(this::actionPerformed);
    labelWarning1 = new JLabel("注意事項:請輸入滿足L(1)最簡判別的2型文法。-行一個產生式");
    labelWarning2 = new JLabel("注意事項:請輸入形式如S->A的產生式,空格用_表示,空用#表示");
    labelWarning3 = new JLabel("注意事項:開始符為第一個產生式的左部,非終結符用大寫字母表示");

    textAreaGrammar = new JTextArea(15,10);

    buttonGenerateFirstSet = new JButton("生成First集");
    buttonGenerateFirstSet.addActionListener(this::actionPerformed);
    buttonGenerateFollowSet = new JButton("生成Fllow集");
    buttonGenerateFollowSet.addActionListener(this::actionPerformed);

    labelFirstSet = new JLabel("First集");
    textAreaFirstSet = new JTextArea(20,15);
    labelFollowSet = new JLabel("Follow集");
    textAreaFollowSet = new JTextArea(20,15);

    labelForecastAnalysisTable = new JLabel("預測分析表");
    buttonGenerateForecastAnalysis = new JButton("構造預測分析");
    buttonGenerateForecastAnalysis.addActionListener(this::actionPerformed);
    textAreaForecastAnalysisTable = new JTextArea(20,15);
    labelAnalysisSentence = new JLabel("分析句子");
    labelWaitAnalosisSentence = new JLabel("待分析句子");
    textFieldWaitAnalysisSentence = new JTextField(15);
    buttonAnalysis = new JButton("分析");

    buttonAnalysis.addActionListener(this::actionPerformed);
    buttonSingleStepShow = new JButton("單步顯示");
    buttonSingleStepShow.addActionListener(this::actionPerformed);
    buttonOneChickDisplay = new JButton("一鍵顯示");
    buttonOneChickDisplay.addActionListener(this::actionPerformed);

    labelAnalysisResult = new JLabel("分析結果");
    textAreaAnalysisResult  = new JTextArea(20,15);


    /*
    整體 為一個一行兩列的結構
    第一列:
          為一個兩行一列的結構
            第一行: 為一個五個方向的結構:
                      上方: 為一個五行一列的結構  第二行用一個一行三列的結構包括三個按鈕  其餘都只有一個標籤
                      center 為一個textarea  ;south 為一個一行兩列的結構  包含兩個按鈕
            第二行: 為一個四行一列的結構: 分別是兩個標籤和兩個area
     */
    setLayout(new GridLayout(1,2));

    JPanel  panel1 = new JPanel();
    panel1.setLayout(new GridLayout(2,1));

    JPanel panel1_1 = new JPanel();
    panel1_1.setLayout(new BorderLayout());
    JPanel panel1_1_1 =  new JPanel();
    panel1_1_1.setLayout(new GridLayout(5,1));
    JPanel  panel1_1_1_1  = new JPanel();
    panel1_1_1_1.setLayout(new GridLayout(1,3));
    panel1_1_1_1.add(buttonOpenFile);
    panel1_1_1_1.add(buttonCertainGrammar);
    panel1_1_1_1.add(buttonSaveFile);
    panel1_1_1.add(labelGrammarInput);
    panel1_1_1.add(panel1_1_1_1);
    panel1_1_1.add(labelWarning1);
    panel1_1_1.add(labelWarning2);
    panel1_1_1.add(labelWarning3);
    panel1_1.add(panel1_1_1 , BorderLayout.NORTH);
    /*
    JScrollPane scroller = new JScrollPane(textAreaSourcePrograme);
    scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    panel1.add(scroller, BorderLayout.CENTER);//向視窗新增文字編輯區
    textAreaSourcePrograme.setWrapStyleWord(true);//設定單詞在一行不足容納時換行
    textAreaSourcePrograme.setLineWrap(true);//設定文字編輯區自動換行預設為true,即會"自動換行"
     */
    JScrollPane scrollPane1 = new JScrollPane(textAreaGrammar);
    scrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    panel1.add(scrollPane1, BorderLayout.CENTER);//向視窗新增文字編輯區
    textAreaGrammar.setWrapStyleWord(true);//設定單詞在一行不足容納時換行
    textAreaGrammar.setLineWrap(true);//設定文字編輯區自動換行預設為true,即會"自動換行"
    panel1_1.add(scrollPane1,BorderLayout.CENTER);
    JPanel panel1_1_2 = new JPanel();
    panel1_1_2.setLayout(new GridLayout(1,2));
    panel1_1_2.add(buttonGenerateFirstSet);
    panel1_1_2.add(buttonGenerateFollowSet);
    panel1_1.add(panel1_1_2 , BorderLayout.SOUTH);



    JPanel panel1_2 = new JPanel();
    panel1_2.setLayout(new GridLayout(2,1));
    JPanel panel1_2_1 = new JPanel();
    panel1_2_1.setLayout(new BorderLayout());
    panel1_2_1.add(labelFirstSet ,BorderLayout.NORTH );

    JScrollPane scrollPane3 = new JScrollPane(textAreaFirstSet);
    scrollPane3.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    panel1.add(scrollPane3, BorderLayout.CENTER);//向視窗新增文字編輯區
    textAreaFirstSet.setWrapStyleWord(true);//設定單詞在一行不足容納時換行
    textAreaFirstSet.setLineWrap(true);//設定文字編輯區自動換行預設為true,即會"自動換行"

    panel1_2_1.add(scrollPane3 , BorderLayout.CENTER);

    JPanel panel1_2_2 = new JPanel();
    panel1_2_2.setLayout(new BorderLayout());
    panel1_2_2.add(labelFollowSet ,BorderLayout.NORTH );
    JScrollPane scrollPane2 = new JScrollPane(textAreaFollowSet);
    scrollPane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    panel1.add(scrollPane2, BorderLayout.CENTER);//向視窗新增文字編輯區
    textAreaFollowSet.setWrapStyleWord(true);//設定單詞在一行不足容納時換行
    textAreaFollowSet.setLineWrap(true);//設定文字編輯區自動換行預設為true,即會"自動換行"
    panel1_2_2.add(scrollPane2 , BorderLayout.CENTER);

    panel1_2.add(panel1_2_1);
    panel1_2.add(panel1_2_2);

    panel1.add(panel1_1);
    panel1.add(panel1_2);

    this.add(panel1);



    /*
     第二列:
          為一個五個方向的結構:
                            上方: 用三行一列的  包含  標籤 按鈕  area
                            中間  用一個五個方向的結構
                                                    上: 一個標籤
                                                    中: 兩行一列  標籤 fild
                                                    下:  三行一列  三個按鈕
                            下方: 兩行一列: 標籤  area
     */

    JPanel panel2 = new JPanel();
    panel2.setLayout(new GridLayout(2,1));

    JPanel panel2_1 = new JPanel();
    panel2_1.setLayout(new BorderLayout());
    JPanel panel2_1_1 = new JPanel();
    panel2_1_1.setLayout(new GridLayout(1,2));
    panel2_1_1.add(labelForecastAnalysisTable);
    panel2_1_1.add(buttonGenerateForecastAnalysis);
    panel2_1.add(panel2_1_1 , BorderLayout.NORTH );

    JScrollPane scrollPane4 = new JScrollPane(textAreaForecastAnalysisTable);
    scrollPane4.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    panel1.add(scrollPane4, BorderLayout.CENTER);//向視窗新增文字編輯區
    textAreaForecastAnalysisTable.setWrapStyleWord(true);//設定單詞在一行不足容納時換行
    textAreaForecastAnalysisTable.setLineWrap(true);//設定文字編輯區自動換行預設為true,即會"自動換行"

    panel2_1.add(scrollPane4  , BorderLayout.CENTER);
    panel2_1.add(labelAnalysisSentence , BorderLayout.SOUTH);


    JPanel panel2_2 = new JPanel();
    panel2_2.setLayout(new BorderLayout());


//    JPanel panel2_2_1 = new JPanel();
//    panel2_2_1.setLayout(new GridLayout(2,1));
//    panel2_2_1.add(labelAnalysisSentence );

    JPanel panel2_2_2 = new JPanel();
    panel2_2_2.setLayout(new  BorderLayout());
    panel2_2_2.add(labelWaitAnalosisSentence ,BorderLayout.WEST);
    panel2_2_2.add(textFieldWaitAnalysisSentence , BorderLayout.CENTER);

    JPanel panel2_2_3 = new JPanel();
    panel2_2_3.setLayout(new GridLayout(1,3));
    panel2_2_3.add(buttonAnalysis);
    panel2_2_3.add(buttonSingleStepShow);
    panel2_2_3.add(buttonOneChickDisplay);

//    panel2_2.add(panel2_2_1 , BorderLayout.NORTH);
    panel2_2.add(panel2_2_2  , BorderLayout.CENTER);
    panel2_2.add(panel2_2_3  , BorderLayout.SOUTH);


    JPanel panel2_3 = new JPanel();
    panel2_3.setLayout(new BorderLayout());
    panel2_3.add(labelAnalysisResult , BorderLayout.NORTH);

    JScrollPane scrollPane5 = new JScrollPane(textAreaAnalysisResult);
    scrollPane5.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    panel1.add(scrollPane5, BorderLayout.CENTER);//向視窗新增文字編輯區
    textAreaAnalysisResult.setWrapStyleWord(true);//設定單詞在一行不足容納時換行
    textAreaAnalysisResult.setLineWrap(true);//設定文字編輯區自動換行預設為true,即會"自動換行"
    panel2_3.add(scrollPane5 , BorderLayout.CENTER);


    JPanel t = new JPanel();
    t.setLayout(new BorderLayout());
    t.add(panel2_2 , BorderLayout.NORTH);
    t.add(panel2_3 , BorderLayout.CENTER);
    panel2.add(panel2_1 );
    panel2.add(t );
//    panel2.add(panel2_3 );

    this.add(panel2);

    //設定視窗在螢幕上的位置、大小和可見性
    this.setLocation(100, 100);
    this.setSize(1000, 700);
    this.setVisible(true);
//    addWindowListener(new WindowAdapter() {
//      public void windowClosing(WindowEvent e) {
//        exitWindowChoose();
//      }
//    });
    this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);//使用 System exit 方法退出應用程式




//      測試專用:
    textAreaGrammar.setText("S->AB\n"
        + "S->bC\n"+"C->AD\n"+"C->b\nA->#\nA->b\nD->aS\nD->c\nB->#\nB->aD");

    buttonCertainGrammar.doClick();


//    textAreaGrammar.setText("E->TR\n"
//        + "R->+TR\n"
//        + "R->#\n"
//        + "T->TY\n"
//        + "Y->*FY\n"
//        + "Y->#\n"
//        + "F->(E)\n"
//        + "F->a");
//
//    buttonCertainGrammar.doClick();



    /*
    E->TR
R->+TR
R->#
T->TY
Y->*FY
Y->#
F->(E)
F->a
     */
  }



  boolean certainGrammar(){

    if (currentGrammar !=null){
      return currentGrammar.isL1L();
    }



    return  false;
  }


  @Override
  public void actionPerformed(ActionEvent e) {

    if (e.getSource() == buttonCertainGrammar){
      initGrammar();
      textAreaFirstSet.setText("");
      if (certainGrammar() ){
        textAreaFirstSet.append("合法");
      }else {
        textAreaFirstSet.append("不合法");
        currentGrammar = null;
      }
//      currentGrammar.show(textAreaFirstSet);

    }else if(e.getSource() == buttonGenerateFirstSet){
      // 生成First集
      if (currentGrammar !=null){
        currentGrammar.showFirstSet(textAreaFirstSet);
      }

    }else if (e.getSource() == buttonGenerateFollowSet){
      if (currentGrammar !=null){
        currentGrammar.showFollowSet(textAreaFollowSet);
      }
    }else if (e.getSource() == buttonGenerateForecastAnalysis){
      // 顯示  構造 預測分析表
      if (currentGrammar !=null){
        currentGrammar.showGenerateForecastTable(textAreaForecastAnalysisTable);
      }
    }else if (e.getSource() == buttonAnalysis){
      initAnalysis();
      if (readyAnalysis == false){
        textAreaAnalysisResult.append("無法啟動"+ "\n");
      }else {
        textAreaAnalysisResult.setText("");
        startAnalysis();
        textAreaAnalysisResult.append("準備完畢"+ "\n");

      }

    }else if (e.getSource() == buttonOneChickDisplay) {
      while (isEnd == false){
        oneStep();
      }
    }else if (e.getSource() == buttonSingleStepShow){
      oneStep();
    }else if (e.getSource() == buttonOpenFile){
      String ass = openGrammar();
      if (ass !=null){
        textAreaGrammar.setText("");
        textAreaGrammar.append(ass);
      }else {
        textAreaGrammar.append("錯誤");
      }
    }if (e.getSource() == buttonSaveFile){
      save();
    }

  }

  String openGrammar() {
    String str = null;
    JFileChooser fileChooser = new JFileChooser();
    fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
    fileChooser.setDialogTitle("開啟檔案");
    int result = fileChooser.showOpenDialog(this);
    if (result == JFileChooser.CANCEL_OPTION) {
      return null;
    }
    File fileName = fileChooser.getSelectedFile();
    if (fileName == null || fileName.getName().equals("")) {
      JOptionPane.showMessageDialog(this, "不合法的檔名", "不合法的檔名", JOptionPane.ERROR_MESSAGE);
    } else {
      try {

        FileInputStream fis = new FileInputStream(fileName);
        InputStreamReader isr = new InputStreamReader(fis, "gbk");//避免中文亂碼
        BufferedReader bfr = new BufferedReader(isr);

//        FileReader fr = new FileReader(fileName);
//        BufferedReader bfr = new BufferedReader(fr);
        String ans = "";
        int count = 0;
        if ((str = bfr.readLine()) != null) {
          count = 1;
        }
        while (count != 0) {
//          System.out.print(str);
          ans = ans.concat(str) + "\n";
          if ((str = bfr.readLine()) != null) {
            count = 1;
//            System.out.print("\n");
          } else {
            count = 0;
          }
        }

        return ans;
      } catch (Exception e) {

      }

    }
    return null;
  }


  void save( ){
    String str = null;
    JFileChooser fileChooser = new JFileChooser();
    fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
    //fileChooser.setApproveButtonText("確定");
    fileChooser.setDialogTitle("儲存");
    int result = fileChooser.showSaveDialog(this);
    File saveFileName = fileChooser.getSelectedFile();
    if (saveFileName == null || saveFileName.getName().equals("")) {
      JOptionPane.showMessageDialog(this, "不合法的檔名", "不合法的檔名", JOptionPane.ERROR_MESSAGE);
    } else {
      try {
        FileWriter fw = new FileWriter(saveFileName);
        BufferedWriter bfw = new BufferedWriter(fw);
        String ans = textAreaGrammar.getText();
        bfw.write(ans);
        bfw.flush();//重新整理該流的緩衝
        bfw.close();
      } catch (IOException ioException) {
      }
    }
  }


  void oneStep(){
    // 往前分析一步
    if (analIndex == sequence.length){
      isEnd  =true;
      textAreaAnalysisResult.append("分析結束\n");
      return;
    }

    char sour = analStack.peek();
    char des = sequence[analIndex];
    if (sour == des){
      analIndex++;
      analStack.pop();
      textAreaAnalysisResult.append("匹配" + sour + "\n");
      textAreaAnalysisResult.append("當前句子" + String.valueOf(sequence).substring(analIndex)+ "\n");
      textAreaAnalysisResult.append("當前分析棧" + analStack.toString()+ "\n");
      textAreaAnalysisResult.append("\n\n");
      return;
    }
    Map<Character , pUnit> now = currentGrammar.forecastAnalysisTable.get(new Character(sour));

    pUnit ans = now.get(new Character(des));
    if (ans == null){
      textAreaAnalysisResult.append("出錯"+ "\n");
      return;
    }

    textAreaAnalysisResult.append("匹配產生式"+ans.toString()+ "\n");
    analStack.pop();
    String  lll = ans.des;
    char[] lis = lll.toCharArray();
    int len = lis.length;
    int i = len-1;
    for (;i>=0 ; i--){
      if (lis[i] == '#'){
        break;
      }
      analStack.push(new Character(lis[i]));
    }

    textAreaAnalysisResult.append("當前句子" + String.valueOf(sequence).substring(analIndex)+ "\n");
    textAreaAnalysisResult.append("當前分析棧" + analStack.toString()+ "\n");
    textAreaAnalysisResult.append("\n\n");


  }

  void initAnalysis(){
    if (currentGrammar == null || currentGrammar.forecastAnalysisTable == null){
      readyAnalysis = false;
    }else {
      readyAnalysis = true;
    }

  }

  void startAnalysis(){
    // 啟動 分析
    String a = textFieldWaitAnalysisSentence.getText();
    a = a+"#";
    sequence = a.toCharArray();
    analIndex = 0;
    analStack.clear();
    analStack.push(new Character(new Character('#')));
    analStack.push(new Character(currentGrammar.S));
    isEnd = false;



  }

  void initGrammar(){
    String text = textAreaGrammar.getText();
    initGrammar(text);

  }

  void initGrammar(String text){
    String[] gra = text.split("\n");

    // 根據 輸入的產生式  初始化 文法
    // 先統計 終結符 和  非終結符

    grammar ans = new grammar();


    Set<Character> VN  = new HashSet<>();
    Set<Character> VT  = new HashSet<>();
    char[] all = text.toCharArray();
    // 先單獨抽出 S
    ans.S = all[0];
    for (char a : all){
      if (isBigLetter(a)){
        VN.add(new Character(a));
      }

      if (isSmallLetter(a)){
        VT.add(new Character(a));
      }
    }

    // 將集合 轉換為陣列   初始化  VN 和VT
    ans.VN = setToCharArray(VN);
    ans.VT = setToCharArray(VT);
//    System.out.println(VN);
//    System.out.println(VT);

    // 建立一個  非終結符 到下標的對映
    Map<Character , Integer> indexMap = new HashMap<>();
    int i=0;
    for (Character a : VN){
      indexMap.put(a , new Integer(i++));
    }

    // 下面  根據非終結符的數量   初始化P  然後遍歷文法  新增產生式
    ans.P = new ArrayList[VN.size()];
    for (i=0 ; i<VN.size() ; i++){
      ans.P[i] = new ArrayList<>();
    }

    // 下面  遍歷 新增
    for (String now : gra){
      // 每一行 是一個產生式
      now= now.replaceAll(" ","");
      char[] arr = now.toCharArray();
      char source = arr[0];
      String des = now.substring(3);
      pUnit  x = new pUnit(String.valueOf(source) , des);
      ans.P[ indexMap.get(source) ].add(x);

    }

    // 初始化完成
//    System.out.println("初始化結果");
//    System.out.println(ans.P);

    currentGrammar = ans;


//    currentGrammar.show();

  }


  //  判斷 是大寫字母 還是小寫字母
  boolean isBigLetter( char a){
    if (a >= 'A' && a<='Z') {
      return true;
    }

    return false;
  }

  boolean isSmallLetter( char a ){
    if (a >= 'a' && a<='z') {
      return true;
    }

    if (law.indexOf(String.valueOf(a)) !=-1){
      return true;
    }

    return false;
  }

  public  char[] setToCharArray(Set<Character> a){
    char[] ans = new char[a.size()];
    int inde = 0;
    for (Character s: a){
      ans[inde++] = s;
    }

    return ans;
  }



}

// 文法類
class grammar{

  char[] VN ;
  char[] VT;
  ArrayList<pUnit>[] P; // P[i]  以VN[i] 開頭

  static int UNKNOWN = -1;
  static int YES = 1;
  static int NO = 0;
  private Map<Character , Set<Character>>  firstSet = null;
  private boolean[] isIntroductionEmpty  = null;

  private Map<Character , Set<Character>>  followSet = null;
  private Map<String , Set<Character>>   selectSet = null;

  Map<Character , Map<Character , pUnit>> forecastAnalysisTable= null;
  char S;

  void showGenerateForecastTable(JTextArea panel){

    if (forecastAnalysisTable == null){
      generateForecastTable();
    }
    panel.setText("");
    Set<Character> VT = arrToSet(this.VT);
    VT.add(new Character('#'));
    for (char a : VN){
      panel.append(a + ": \n");

      Map<Character , pUnit> now = forecastAnalysisTable.get(new Character(a));
      for (char s : VT){
        if (now.containsKey(new Character(s))){
          // 包含
          panel.append("  "+String.valueOf(s) + "   ");
          panel.append( now.get(new Character(s)).toString() + "\n");
        }else {
          panel.append("  "+String.valueOf(s) + "   null" + "\n");
        }


      }

    }


  }


  boolean isL1L(){
    if (selectSet == null){
      getSelectSet();
    }

    // 判斷是不是  LL1

    int i=0;
    for (ArrayList<pUnit> a : P){
      // 第i個的所有產生式
      int count = 0;
      Set<Character> sc = new HashSet<>();
      for (pUnit b : a){
        if (selectSet.containsKey(b.toString())){
          Set<Character> now = selectSet.get(b.toString());
          sc.addAll(now);
          count += now.size();
        }

      }

      if (count != sc.size()){
        return false;
      }
      i++;
    }

    return true;
  }

  void generateForecastTable(){

    if (selectSet == null){
      getSelectSet();
    }

    if (forecastAnalysisTable != null){
      return;
    }

    Map<Character , Map<Character , pUnit>> table =  new HashMap<>();



    int i=0;
    for (ArrayList<pUnit> a : P){
      // 第i個的所有產生式
      Map<Character , pUnit> k = new HashMap<>();
      for (pUnit b : a){
        Set<Character> no = selectSet.get(b.toString());
        for(char ass : no){
          k.put(new Character(ass) , b);
        }
      }
      table.put( new Character(VN[i]) , k );
      i++;
    }

    this.forecastAnalysisTable  = table;





  }

  void showFirstSet(JTextArea panel){
    // 顯示  first集

    panel.setText("");

    Map<Character , Set<Character>> ans = getFirstSet();
    panel.append("顯示 各終結符/非終結符的first集合  \n");
    panel.append("\n");
    panel.append(ans.toString());


    panel.append("顯示 各產生式的first集合  \n");
    for (ArrayList<pUnit> a : P){
      for (pUnit b : a){
        panel.append(b.toString() + "  ");



//         這裡展示各產生式的右部符號串的first集合
        panel.append(getFirstSet(b).toString() + "\n");
//        System.out.println(b.toString());

      }
    }


  }

  void showFollowSet(JTextArea panel){
    panel.setText("");
    Map<Character , Set<Character>> an = getFollowSet();
    panel.append("顯示 follow集合  \n");
    panel.append("\n");
    panel.append(an.toString());
  }

  public grammar() {
//    P = new ArrayLis
  }
  void show(JTextArea panel){

    panel.setText("");
    for (ArrayList<pUnit> a : P){
      for (pUnit b : a){
        panel.append(b.toString() + "  ");


//         這裡展示各產生式的右部符號串的first集合
        panel.append(getFirstSet(b).toString() + "\n");
//        System.out.println(b.toString());

      }
    }




    // 顯示 first集合

//     這裡展示  first集合
    Map<Character , Set<Character>> ans = getFirstSet();
    panel.append("顯示 first集合  \n");
    panel.append("\n\n");
    panel.append(ans.toString());


    //     這裡展示  follow集合
    Map<Character , Set<Character>> an = getFollowSet();
    panel.append("顯示 follow集合  \n");
    panel.append("\n\n");
    panel.append(an.toString());



    panel.append("\n顯示 select集\n");
    for (ArrayList<pUnit> a : P){
      for (pUnit b : a){
        panel.append(b.toString() + "  ");


//         這裡展示各產生式的右部符號串的first集合
        panel.append(getSelectSet(b).toString() + "\n");
//        System.out.println(b.toString());

      }
    }




  }

  void show(){

    System.out.println(VT);
    System.out.println(VN);
    for (ArrayList<pUnit> a : P){
      for (pUnit b : a){
        System.out.print(b.toString() + "\n");
//        System.out.println(b.toString());

      }
    }


  }

  public grammar(char[] VN, char[] VT, ArrayList<pUnit>[] p, char s) {

    this.VN = VN;
    this.VT = VT;
    P = p;
    S = s;
  }

  void add( char source , String des ){
    int x = String.valueOf(VN).charAt(source);
    pUnit a = new pUnit(source , des);
    P[x].add(a);
  }

  boolean[] getIsIntroductionEmpty(){
    if (isIntroductionEmpty !=null){
      return isIntroductionEmpty;
    }
    int[] ans = new int[VN.length];
    boolean[] aans = new boolean[VN.length];

    int i;
    int l = VN.length;
    for (i=0 ; i<l ; i++){
      ans[i] = UNKNOWN;
    }

    grammar now = this.copy();

    // 刪除右部含有非終結符的產生式

    int j;
    for (i=0 ; i<l ; i++){
      for (j=0 ; j<now.P[i].size() ; j++){
        pUnit s = now.P[i].get(j);

        if (s.desIsEmpty()){
          ans[i]= YES;
//          System.out.println("fdsv");
          now.P[i].clear();
//          System.out.println("sd----");
        }
        if (s.desContainEndChar(VT)){
          now.P[i].remove(j);
        }
      }

      if (now.P[i].size() == 0 && ans[i] == UNKNOWN){
        ans[i] = NO;
      }

    }
//    now.show();

    // 重複掃描
    int count = 1;
    while (count ==1){
      count = 0;
      for (i=0 ; i<l ; i++){
//        System.out.println(i);
        for (j=0 ; j<now.P[i].size() ; j++){
          pUnit s = now.P[i].get(j);
//          System.out.println(s.toString());

//          now.P[i].remove(j);
//          System.out.print(s.des);
//          System.out.println("ds");
          if (s.desIsEmpty()){
            ans[i] = YES;
            count = 1;

            now.P[i].clear();
            break;
          }
          int z;
          for (z= 0 ; z<l ; z++){
            if (ans[z] == YES){
              s.des = s.des.replaceAll(String.valueOf(VN[z]) , "");
              if (s.desIsEmpty()){
                ans[i] = YES;
                count = 1;

                now.P[i].clear();
                break;
              }
            }
          }


        }
      }
    }

    for (i = 0 ; i<l ; i++){
      if (ans[i] == YES){
        aans[i] = true;
      }else {
        aans[i] = false;
      }
//      System.out.println(aans[i]);
    }


    isIntroductionEmpty = aans;

    return aans;

  }

  grammar copy(){
    grammar ans = new grammar();

    ans.VN = String.valueOf(this.VN).toCharArray();
    ans.VT = String.valueOf(this.VT).toCharArray();
    ans.S = this.S;
    ans.P = new ArrayList[VN.length];
    int i;
    int l = VN.length;
    for (i=0 ; i<l ; i++){
      ans.P[i] = new ArrayList<>();
      for (pUnit a:this.P[i] ){
        ans.P[i].add(new pUnit(a));
      }
    }

//    ans.show();

    return ans;

  }


  //  計算 每一個文法符號的 first集
  Map<Character , Set<Character>> getFirstSet() {
    if (firstSet !=null){
      return firstSet;
    }


    boolean[] isToEmpty = getIsIntroductionEmpty();
//    for (boolean a : isToEmpty){
//      System.out.println(a);
//    }

//    System.out.println(VT);
//    System.out.println(VN);



    Map<Character , Integer> indexMap = new HashMap<>();
    int i = 0;
//    for (char a :VT){
//      indexMap.put(new Character(a) , i++);
//    }
    for (char a :VN){
      indexMap.put(new Character(a) , i++);
    }

    Map<Character , Set<Character>> ans = new HashMap<>();

    // 先統計非終結符的first集
    for (char a : VT){
      Set<Character> x = new HashSet<>();
      x.add(new Character(a));
      ans.put(new Character(a) , x);
    }

    for (char a : VN){
      Set<Character> x = new HashSet<>();
      ans.put(new Character(a) , x);
    }

    int count = 1;


    Set<Character> VNSet = arrToSet(VN);
    Set<Character> VTSet = arrToSet(VT);

    // 這裡統計一個 可以推出空的集合  用於後面判斷
    Set<Character> toEmpty = new HashSet<>();

    for (i=0 ; i< VN.length ; i++){
      if (isToEmpty[i]){
        toEmpty.add(new Character(VN[i]));
      }
    }

    int  j;

    while (count == 1){
      count = 0;

      // 執行第二步   若X∈VN,且有Xa…,a∈ VT,則a∈FIRST(X);   第三步 若X∈VN,X ε ,則ε∈ FIRST(X);
      // 遍歷 產生式    X必定屬於VN   看右邊  是否 都是終結符
      i=0;
      for (ArrayList<pUnit> now : P){
        for (pUnit x : now){
          // 當前 為第i個非終結符的產生式
          Set<Character> cu = arrToSet(x.des.toCharArray());
          // 是否 全為 終結符   第二步
//          System.out.println(cu);
//          System.out.println(VT);
          if (VTSet.containsAll(cu) && ans.get(VN[i]).contains(new Character( x.des.charAt(0) )) == false){// 符合且未包含
            // 全部包含  即全為終結符
//            System.out.println(" 第二步 ");
            ans.get(VN[i]).add(new Character( x.des.charAt(0) ));
//            System.out.println(VTSet);
//            System.out.println(x.des.charAt(0));
            count = 1;
//            System.out.println("sdjk");
          }

          // 順便判斷 第三步
          if ( isToEmpty[i] == true  && ans.get(VN[i]).contains(new Character('#')) == false){
            // 該非終結符  可以推出空
            ans.get(VN[i]).add(new Character( '#'));
//            System.out.println("fds");
            count = 1;
          }
        }
        i++;

      }

      // 執行 第四步和第五步  4)若X∈VN,Y1,Y2,…,Yn都∈VN,且有XY1Y2…Yn 。當Y1,Y2,…,Yi-1都      ε時,(其中1<=i<=n),則FIRST(Y1)-{ε}, FIRST(Y2)-{ε},…,FIRST(Yi-1)-{ε},FIRST(Yi)都包含在FIRST(X)中。

      // 都是 非終結符

      i=0;
      for (ArrayList<pUnit> now : P){
        // 統計要新增的所有新的 元素 以判斷是否 有新的新增進去

        Set<Character> updateSet = new HashSet<>();// 要新增的    最後判斷 是否有新的
        for (pUnit x : now){
          // 當前 為第i個非終結符的產生式
          Set<Character> cu = arrToSet(x.des.toCharArray());
          // 是否 全為 非終結符   第四步
          if (VNSet.containsAll(cu)) {
            // 全部包含  即全為非終結符

            // 判斷  第四步 還是 第五步   即: -> 是否 全都可以推出空
            if (toEmpty.containsAll(cu)){
              // 全都可以推出空   第五步
              // 更新 updateSet
              for (Character aa : cu){
                for (Character as : ans.get(aa)){
                  if (as !='#'){
                    updateSet.add(aa);
                  }
                }
              }

              updateSet.add(new Character('#'));
              // 第五步



            }else {
              // 非 全都可以推出空   第四步

              char[] curr = x.des.toCharArray();
              for (int ii = 0 ; ii < curr.length ; ii++){
                if (toEmpty.contains(curr[ii])){
                  // 屬於 0-(i-1)
                  if (updateSet.contains(new Character('#'))){
                    updateSet.addAll(ans.get(curr[ii]));
                  }else {
                    updateSet.addAll(ans.get(curr[ii]));
                    updateSet.remove(new Character('#'));
                  }
                }else {
                  // 屬於 i
                  updateSet.addAll(ans.get(curr[ii]));
                  break;
                }
              }


            }

            // ok

          }else {

            // 這裡就是  含有 非終結符  也有 終結符
            char[] akd = x.des.toCharArray();
            for ( char ds : akd){
              // 非終結符
              if (VNSet.contains(new Character(ds))){
                if (ans.get(ds).contains('#')){
                  //可以退空
                  if (updateSet.contains('#')){
                    updateSet.addAll(ans.get(ds));
                  }else {
                    updateSet.addAll(ans.get(ds));
                    updateSet.remove('#');
                  }
                  continue;
                }else {
                  updateSet.addAll(ans.get(ds));
                  break;
                }
              }

              if (VTSet.contains(ds)){
                updateSet.add(new Character(ds));
                break;
              }

            }

          }



        }
        // 這裡遍歷完 第i的個非終結符的所有產生式   判斷 是否有更新

        if (ans.get(new Character(VN[i])).containsAll(updateSet)){
          // 包含全部
        }else {
          count = 1;
          ans.get(new Character(VN[i])).addAll(updateSet);
          // 更新
        }

        i++;
      }

//      System.out.println(ans.toString());

    }
    firstSet = ans;
    return ans;
  }


  public Set<Character>  arrToSet(char[] a){
    Set<Character> ans = new HashSet<>();

    for (char as : a){
      ans.add(new Character(as));
    }
    return ans;
  }

  Set<Character> getFirstSet(pUnit a){
    if (firstSet == null){
      getFirstSet();
    }
    //first集未初始化




    Set<Character> ans = new HashSet<>();
    //
    Map<Character , Integer> indexMap = new HashMap<>();
    int i = 0;
//    show();
    for (char aa :VN){
      indexMap.put(new Character(aa) , i++);
    }
//    System.out.println(indexMap);
    Set<Character> VNSet = arrToSet(VN);
    Set<Character> VTSet = arrToSet(VT);
    char[] des = a.des.toCharArray();
//    System.out.println(a.toString());
    if (des[0] == '#'){
      ans.add(new Character('#'));
      return ans;
    }
    for (char aa : des){
      if(VTSet.contains(aa)){
        ans.add(new Character(aa));
        return ans;
      }

      if (isIntroductionEmpty[ indexMap.get(aa) ] ){
        ans.addAll(firstSet.get(new Character(aa)));
        ans.remove(new Character('#'));
      }else {
        ans.addAll(firstSet.get(new Character(aa)));
        ans.remove(new Character('#'));
        return ans;

      }

    }

    ans.add(new Character('#'));



    return ans ;
  }


  Set<Character> getFirstSet(String a){
    return getFirstSet(new pUnit("2",a));
  }


  Map<Character , Set<Character>> getFollowSet(){

    if (followSet !=null){
      return followSet;
    }
    if (firstSet == null){
      getFirstSet();
    }
    Map<Character , Set<Character>> ans = new HashMap<>();
    // 由於要統計 每個  follow 都不在增大時  結束   所以   這裡統計一個總數
    int count = 0;// 所有follow 為空
    int i,j;
    int l = VN.length;
    // 初始化
    for( char a : VN){
      ans.put(new Character(a) , new HashSet<>());
    }

    // 設A為文法開始符號,把#加入FOLLOW(A)中,(#為句子括號)
    ans.get(new Character(S)).add(new Character('#'));
    int update = 1;


    Set<Character> VNSet = arrToSet(VN);
    Set<Character> VTSet = arrToSet(VT);

    while (count != update){
      count = update;
      // 遍歷 所有產生式
      i=0;
      for (ArrayList<pUnit> now : P){
        for (pUnit x : now){
          String des = x.des;
          char[] lis = des.toCharArray();
          char source = x.source.charAt(0);

          // 第二步
          /*
          2)若BαAβ是一個產生式,則把FIRST(β)的非空元素加入FOLLOW(A)中。
          如果β   ε,則把FOLLOW(B)也加入FOLLOW(A):
          因為當有形如Dα1Bβ1   BαAβ的產生式時,A、B、D∈VN, α1,β1,α,β∈V*,在推導過程中可能出現句型序列如
          S     …α1Bβ1…=>…α1αAββ1 …=> …α1αAβ1,
          故FIRST(β1)∈FOLLOW(B)和FIRST(β1) ∈ FOLLOW(A),所以FOLLOW(B)FOLLOW(A)
           */
          // 過濾掉 右側為空的 和 全為終結符的產生式
          if (des.equals("#" )|| VTSet.containsAll(arrToSet(lis)) ){
            continue;
          }

          // 含有 非終結符   檢測
          for(i=0 ;i<lis.length ; i++){


            if (VNSet.contains(lis[i])){
              // 是非終結符
              if (i== lis.length-1){
                // 最後一個
                ans.get(lis[i]).addAll( ans.get(new Character(source)) );
                continue;
              }
              String sub = des.substring(i+1);
              Set<Character> fir = getFirstSet(sub);
              //
              ans.get(lis[i]).addAll(fir);
              if (fir.contains(new Character('#'))){
                ans.get(lis[i]).addAll( ans.get(new Character(source)) );
              }else {

              }
            }
          }
        }
      }

      update = 0;
      for(char aaa: VN){
        update  = update+ ans.get(new Character(aaa)).size();
      }
    }

    this.followSet = ans;
    return ans;
  }


  Map<String , Set<Character>> getSelectSet() {

    if (selectSet != null){
      return selectSet;
    }

    if (firstSet == null){
      getFirstSet();
    }
    if (followSet == null){
      getFollowSet();
    }

    Map<String, Set<Character>> ans = new HashMap<>();

    for (ArrayList<pUnit> a : P) {
      for (pUnit b : a) {

//         這裡展示各產生式的右部符號串的first集合
        ans.put(b.toString(), getSelectSet(b));
//        System.out.println(b.toString());

      }
    }



    selectSet = ans;
    return ans;
  }

  Set<Character> getSelectSet( pUnit a ){
//    if (selectSet == null){
//     getSelectSet();
//    }




//    System.out.println(a.toString());
    if (a.des == "#"){
      return followSet.get(new Character(a.source.charAt(0)));
    }

    Set<Character> now = getFirstSet(a);
    if (now.contains(new Character('#'))){
      now.remove(new Character('#'));
      if (this.followSet ==  null){
        getFollowSet();
      }
      Set<Character> as = followSet.get(new Character(a.source.charAt(0)));
      now.addAll(as);
      return now;
    }else {
      return now;
    }
  }


}


class pUnit {
  String source;
  String des;

  public pUnit() {
  }


  @Override
  public String toString() {
    return source + "->" + des;
  }

  public pUnit(String source, String des) {
    this.source = source;
    this.des = des.replaceAll("\n","");
  }

  public pUnit(char source, String des) {
    this.source = String.valueOf(source);
    this.des = des;
  }

  public pUnit(pUnit a) {
    this.des = a.des;
    this.source = a.source;
  }

  boolean desContainEndChar(char[] VT){
    for (char a : VT){
      if (des.indexOf(a) !=-1){
        return true;
      }
    }

    return false;
  }

  boolean desIsEmpty(){
    if (des.equals("#") || des.equals("")){
      return true;
    }

    return false;
  }


}

這裡沒有新增main 函式ou 直接新建一個LL1analysis類就行了