壓縮C++簡單程序詞法分析後的文件(編譯原理實驗)

  繼續完成前面一篇「設計有窮自動機DFA實現C++簡單程序的詞法分析、掃描(編譯原理實驗)」詞法分析掃貓程序剩下來關於去除多餘空行、空格、註釋進行源程序壓縮的功能。java

按實驗要求(以下),這裏須要考慮下面帶星號*的第(3)(5)點:ios

 

實驗中用到的C++源程序以下圖:git

 

思路:緩存


其實也就是將源程序中的多餘空格、註釋、換行等都刪除,整理成單單一行的源代碼。
每次對掃描程序獲取到的Token進行判斷,根據上一個Token的類型(有關鍵字、標識符、數值、字符串、特殊符號)決定當前Token是否可以與上一個Token緊鄰,也即不加任何空格。
例如上面截圖中倒數第二行中的 else 和 cout 兩個關鍵字之間就必須有空格分開,不然代碼就會出錯了。針對上面這個簡單的C++源程序,觀察其DFA圖能夠得出如下特色:
一、關鍵字與標識符不能緊鄰,例如 int i中間必須有空格
二、關鍵字與關鍵字也不能緊鄰,如上所述
三、另外關鍵字與字符串也沒關係鄰app


對於以上樣例輸入,先進行詞法分析,而後將得到的Token壓縮並保存在StringBuilder對象中,在寫入到一個新的文件,最終再次對壓縮後的文件進行掃描,判斷壓縮先後的掃描結果是否一直。ide

程序輸出結果(包括壓縮後的源代碼)以下:測試

 

根據上面這三個特色,代碼實現以下(高亮部分是與上一篇源代碼不一樣之處):ui

  
  
           
  
  
  1. package lexical_analysis; 
  2.  
  3. import java.io.BufferedReader; 
  4. import java.io.File; 
  5. import java.io.FileOutputStream; 
  6. import java.io.FileReader; 
  7. import java.io.PrintWriter; 
  8.  
  9. public class Scanner_2 { 
  10.  
  11.     // 定義DFA中的全部狀態表 
  12. //  enum StateType {Start, Num, ID, EQ, NE, NM, NL,  
  13. //      Com, LineCom, MulCom1, MulCom2, Special, Done, Str}; 
  14.          
  15.     // 定義DFA中的全部狀態表 
  16.     private static final int Start = 1
  17.     private static final int Num = 2
  18.     private static final int ID = 3
  19.     private static final int EQ = 4
  20.     private static final int NE = 5
  21.     private static final int NM = 6
  22.     private static final int NL = 7
  23.     private static final int Coms = 8
  24.     private static final int LineCom = 9;  
  25.     private static final int MulCom1 = 10
  26.     private static final int MulCom2 = 11
  27.     private static final int Special = 12
  28.     private static final int Done = 13
  29.     private static final int Str = 14
  30.      
  31.     // Token類型,Initial爲初始類型 
  32.     private enum TokenType { 
  33.         Initial, ID, Special, Str, KeyWord 
  34.     }; 
  35.          
  36.     // 關鍵字 
  37.     private String[] keyWords = new String[] { 
  38.         "include""define""iostream""int""folat""double"
  39.         "main""if""else""for""while""do""goto""switch"
  40.         "case""static""cin""cout" 
  41.     }; 
  42.      
  43.     // 特殊字符 
  44.     private String [] special = {"{""}""[""]""("")"
  45.              "#"",""."";"":""\\"
  46.              "'""\""">>""<<""!=""="
  47.              "==""<="">=""++""--"}; 
  48.      
  49.     // 算術運算符 
  50.     private String [] arithmetic = {"+""-""-""/""%"}; 
  51.      
  52.     // 源代碼文件輸入流 
  53.     private BufferedReader sourceFile; 
  54.      
  55.     // 壓縮後的文件輸出流 
  56.     private PrintWriter compressedFileWriter; 
  57.     // 上一個Token的類型 
  58.     private TokenType preType = TokenType.Initial; 
  59.     // 緩存去除多餘空格、註釋後的源代碼 
  60.     private StringBuilder compressedStr = new StringBuilder(); 
  61.      
  62.     // 掃描行的最大字符數 
  63.     private static final int BUF_SIZE = 256
  64.     // 當前行的字符長度 
  65.     private int bufSize = 0
  66.     // 當前行 
  67.     private String eachLine; 
  68.     // 當前掃描行的字符序列 
  69.     private char [] lineBuf = new char[BUF_SIZE]; 
  70.     // 當前掃描的行數 
  71.     private int lineNum = 0
  72.     // 當前行的字符下標 
  73.     private int charPos = 0
  74.     // 是否已達文件尾 
  75.     private boolean isEOF = false
  76.  
  77.     /** 
  78.      * 每次掃描前都要初始化一些必要變量值 
  79.      */ 
  80.     private void initial(){ 
  81.         bufSize = 0
  82.         lineNum = 0
  83.         charPos = 0
  84.         isEOF = false
  85.     } 
  86.      
  87.     /** 
  88.      * 初始化並讀取源代碼文件 
  89.      * 掃描程序開始執行,直到讀取文件結束符EOF 
  90.      * @throws Exception 
  91.      */ 
  92.     private void scanning(String originalFile) throws Exception { 
  93.         this.sourceFile = new BufferedReader(new FileReader(originalFile)); 
  94.          
  95.         this.initial(); 
  96.         while(!isEOF) { 
  97.             getToken(); 
  98.         } 
  99.         System.out.println("========================> end scanning ..."); 
  100.     } 
  101.      
  102.     /** 
  103.      * 獲取下一個字符 
  104.      * @return 
  105.      * @throws Exception 
  106.      */ 
  107.     private char getNextChar() throws Exception { 
  108.         char nextChar = '\0'
  109.          
  110.         if(!(charPos < bufSize)) { 
  111.             if((eachLine = sourceFile.readLine()) != null) { 
  112.                 lineNum++; 
  113.                 System.out.println(lineNum + ": " + eachLine); 
  114.                 lineBuf = eachLine.toCharArray(); 
  115.                 bufSize = eachLine.length(); 
  116.                 charPos = 0
  117.                 nextChar = lineBuf[charPos++]; 
  118.             } else { 
  119.                 isEOF = true
  120.                 nextChar = '\0'
  121.             } 
  122.         } else { 
  123.             nextChar = lineBuf[charPos++]; 
  124.         } 
  125.         return nextChar; 
  126.     } 
  127.      
  128.     /** 
  129.      * 【按步長(step)】取消獲取下一個字符 
  130.      */ 
  131.     private void unGetNextChar(int step) { 
  132.         if(!isEOF) { 
  133.             charPos -= step; 
  134.         } 
  135.     } 
  136.      
  137.     /** 
  138.      * 獲取一個Token 
  139.      * @return 
  140.      * @throws Exception 
  141.      */ 
  142.     private String getToken() throws Exception { 
  143.         String tokenStr = ""
  144.         String currentToken = ""
  145.         int currentState = Start; 
  146.         boolean isSave; 
  147.          
  148.         // 不一樣時爲EOF和Done狀態 
  149.         while(currentState != Done && !isEOF) { 
  150.             char c = getNextChar(); 
  151.             isSave = true
  152.              
  153.             switch(currentState) { 
  154.                 case Start: 
  155.                     if(isDigit(c)) { 
  156.                         currentState = Num; 
  157.                     } else if(isLetter(c) || c == '.') { //點號是爲了處理頭文件iostream.h的格式 
  158.                         currentState = ID; 
  159.                     } else if(c == ' ' || c == '\t' || c == '\n') { 
  160.                         isSave = false
  161.                     } else if(c == '!') { 
  162.                         currentState = NE; 
  163.                     } else if(c == '=') { 
  164.                         currentState = EQ; 
  165.                     } else if(c == '<') { 
  166.                         currentState = NM; 
  167.                     } else if(c == '>') { 
  168.                         currentState = NL; 
  169.                     } else if(c == '/') { 
  170.                         currentState = Coms; 
  171.                         isSave = false
  172.                     } else if(c == '"') { 
  173.                         currentState = Str; 
  174.                     } else { 
  175.                         currentState = Done; 
  176. //                      if(isSingle(c)) { 
  177. //                          currentToken = "" + c; 
  178. //                          currentState = Done; 
  179. //                          isSave = false; 
  180. //                      } 
  181.                     } 
  182.                     break
  183.                 case Num: 
  184.                     if(!isDigit(c)) { 
  185.                         currentState = Done; 
  186.                         unGetNextChar(1); 
  187.                         isSave = false
  188.                     } 
  189.                     break
  190.                 case ID: 
  191.                     if(!isLetter(c) && !isDigit(c)) { 
  192.                         currentState = Done; 
  193.                         unGetNextChar(1); 
  194.                         isSave = false
  195.                     } 
  196.                     break
  197.                 case NE: 
  198.                     if(c != '=') { 
  199.                         currentState = Special; 
  200.                         unGetNextChar(2); 
  201.                         isSave = false
  202.                     } else { 
  203.                         currentState = Done; 
  204.                     } 
  205.                     break
  206.                 case NM: 
  207.                     if(c != '=' && c != '<') { 
  208.                         currentState = Special; 
  209.                         unGetNextChar(2); 
  210.                         isSave = false
  211.                     } else { 
  212.                         currentState = Done; 
  213.                     } 
  214.                     break
  215.                 case NL: 
  216.                     if(c != '=' && c != '>') { 
  217.                         currentState = Special; 
  218.                         unGetNextChar(2); 
  219.                         isSave = false
  220.                     } else { 
  221.                         currentState = Done; 
  222.                     } 
  223.                     break
  224.                 case EQ: 
  225.                     if(c != '=') { 
  226.                         currentState = Special; 
  227.                         unGetNextChar(2); 
  228.                         isSave = false
  229.                     } else { 
  230.                         currentState = Done; 
  231.                     } 
  232.                     break
  233.                 case Str: 
  234.                     if(c == '"') { 
  235.                         currentState = Done; 
  236.                     }  
  237.                     break
  238.                 case Coms: 
  239.                     isSave = false
  240.                     if(c == '/') { 
  241.                         currentState = LineCom; 
  242.                     } else if(c == '*') { 
  243.                         currentState = MulCom1; 
  244.                     } else { 
  245.                         currentState = Special; 
  246.                         unGetNextChar(1); 
  247.                     } 
  248.                     break
  249.                 case LineCom: 
  250.                     isSave = false
  251.                     if(c == '\n') { 
  252.                         currentState = Done; 
  253.                     } 
  254.                     break
  255.                 case MulCom2: 
  256.                     isSave = false
  257.                     if(c == '*') { 
  258.                         currentState = MulCom2; 
  259.                     } else if(c == '/') { 
  260.                         currentState = Done; 
  261.                     } else { 
  262.                         currentState = MulCom1; 
  263.                     } 
  264.                     break
  265.                 case Special: 
  266.                     if(c == '!' || c == '=' || c == '<' || c == '>') { 
  267. //                  if(isSpecialSingle(c)) { 
  268.                         currentToken = "" + c; 
  269.                         currentState = Done; 
  270.                         isSave = false
  271.                     } else { 
  272.                         currentToken = "Error"
  273.                         currentState = Done; 
  274.                     } 
  275.                     break
  276.                 default
  277.                     System.out.println(lineNum + " >> Scanner Bug : state = " + currentState); 
  278.                     currentState = Done; 
  279.                     currentToken = "Error"
  280.                     break
  281.             } 
  282.             if(isSave) { 
  283.                 tokenStr += c; 
  284.             } 
  285.             if(currentState == Done) { 
  286.                 currentToken = tokenStr; 
  287.                 printToken(currentToken); 
  288.             } 
  289.         } 
  290.         return currentToken; 
  291.     } 
  292.      
  293.     /** 
  294.      * 判斷是否爲字母 
  295.      * @param c 
  296.      * @return 
  297.      */ 
  298.     private boolean isLetter(char c) { 
  299.         if(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')){ 
  300.             return true
  301.         } 
  302.         return false
  303.     } 
  304.      
  305.     /** 
  306.      * 判斷是否爲數字 
  307.      * @param c 
  308.      * @return 
  309.      */ 
  310.     private boolean isDigit(char c) { 
  311.         if('0' <= c && c <= '9') { 
  312.             return true
  313.         } 
  314.         return false
  315.     } 
  316.      
  317.     /** 
  318.      * 打印時判斷是否爲【數值Num】 
  319.      * @param token 
  320.      * @return 
  321.      */ 
  322.     private boolean isNum(String token) { 
  323.         boolean flag = true
  324.         char [] chs = token.toCharArray(); 
  325.         int len = chs.length; 
  326.         for(int i = 0; i < len; i++) { 
  327.             if(!isDigit(chs[i])) { 
  328.                 flag = false
  329.             } 
  330.         } 
  331.         return flag; 
  332.     } 
  333.      
  334.     /** 
  335.      * 打印時判斷是否爲【特殊符號】 
  336.      */ 
  337.     private boolean isSpecial(String token) { 
  338.         int len = special.length; 
  339.         for(int i = 0; i < len; i++) { 
  340.             if(token.equals(special[i])) { 
  341.                 return true
  342.             } 
  343.         } 
  344.         return false
  345.     } 
  346.      
  347.     /** 
  348.      * 判斷是否爲算術運算符 
  349.      * @param token 
  350.      * @return 
  351.      */ 
  352.     private boolean isArithmetic(String token) { 
  353.         int len = arithmetic.length; 
  354.         for(int i = 0; i < len; i++) { 
  355.             if(token.equals(arithmetic[i])) { 
  356.                 return true
  357.             } 
  358.         } 
  359.         return false
  360.     } 
  361.      
  362.     /** 
  363.      * 打印時判斷是否爲【關鍵字】 
  364.      * @param token 
  365.      * @return 
  366.      */ 
  367.     private boolean isKeyWord(String token) { 
  368.         int len = keyWords.length; 
  369.         for(int i = 0; i < len; i++) { 
  370.             if(keyWords[i].equals(token)) { 
  371.                 return true
  372.             } 
  373.         } 
  374.         return false
  375.     } 
  376.      
  377.     /** 
  378.      * 判斷是否爲【單個字符】即 # * { } [ ] ( ) , . ; : ' 
  379.      * @param c 
  380.      * @return 
  381.      */ 
  382. //  private boolean isSingle(char c) { 
  383. //      char [] single = {'#', '*', '{', '}', 
  384. //                          '[', ']', '(', ')', 
  385. //                          ':', ';', '.', ',', 
  386. //                          '\''}; 
  387. //      int len = single.length; 
  388. //      for(int i = 0; i < len; i++) { 
  389. //          if(c == single[i]) { 
  390. //              return true; 
  391. //          } 
  392. //      } 
  393. //      return false; 
  394. //  } 
  395.      
  396.     /** 
  397.      * 判斷是否爲【單個的特殊字符】即 !   =   <   >  
  398.      * 由於這幾個屬於多義字符,能造成 !=  ==  <<  >> 
  399.      * @param c 
  400.      * @return 
  401.      */ 
  402. //  private boolean isSpecialSingle(char c) { 
  403. //      char [] special = {'!', '=', '<', '>'}; 
  404. //      int len = special.length; 
  405. //      for(int i = 0; i < len; i++) { 
  406. //          if(c == special[i]) { 
  407. //              return true; 
  408. //          } 
  409. //      } 
  410. //      return false; 
  411. //  } 
  412.      
  413.     /** 
  414.      * 按類別打印掃描獲得的Token 
  415.      * @param token 
  416.      */ 
  417.     private void printToken(String token) { 
  418.         if(isKeyWord(token)) { 
  419.             System.out.printf("%4d: %s --- %s\n", lineNum, token, "關鍵字"); 
  420.              
  421.             token = (preType == TokenType.KeyWord ? " " : "") + token; 
  422.             preType = TokenType.KeyWord;  
  423.             this.compressedStr.append(token); 
  424.              
  425.         } else if(isSpecial(token)) { 
  426.             System.out.printf("%4d: %s --- %s\n", lineNum, token,"特殊符號"); 
  427.              
  428.             preType = TokenType.Special; 
  429.             this.compressedStr.append(token); 
  430.              
  431.         } else if(isArithmetic(token)) { 
  432.             System.out.printf("%4d: %s --- %s\n", lineNum, token,"算術運算符"); 
  433.              
  434.             preType = TokenType.Special; 
  435.             this.compressedStr.append(token); 
  436.              
  437.         } else if(isNum(token)) { 
  438.             System.out.printf("%4d: %s --- %s\n", lineNum, token,"數值"); 
  439.              
  440.             preType = TokenType.Special; 
  441.             this.compressedStr.append(token); 
  442.              
  443.         } else if(token.startsWith("\"")) { 
  444.             System.out.printf("%4d: %s --- %s\n", lineNum, token,"字符串"); 
  445.              
  446.             token = (preType == TokenType.KeyWord ? " " : "") + token; 
  447.             this.compressedStr.append(token); 
  448.             preType = TokenType.Str; 
  449.              
  450.         } else { 
  451.             System.out.printf("%4d: %s --- %s\n", lineNum, token,"標識符"); 
  452.              
  453.             token = (preType == TokenType.KeyWord ? " " : "") + token; 
  454.             this.compressedStr.append(token); 
  455.             preType = TokenType.ID; 
  456.         }  
  457.     } 
  458.      
  459.     /** 
  460.      * 打印並將被壓縮後的源代碼寫入新的文件中 
  461.      */ 
  462.     public void printCompressedFile(String compressedFile) throws Exception { 
  463.         System.out.println(this.compressedStr); 
  464.         // 建立壓縮後的文件輸出流 
  465.         this.compressedFileWriter = new PrintWriter( 
  466.                 new FileOutputStream(new File(compressedFile))); 
  467.         // 寫入到新的文件 
  468.         this.compressedFileWriter.write(new String(this.compressedStr)); 
  469.         this.compressedFileWriter.flush(); 
  470.     } 
  471.      
  472.     /** 
  473.      * 測試 
  474.      */ 
  475.     public static void main(String[] args) throws Exception { 
  476.         Scanner_2 scanner = new Scanner_2(); 
  477.          
  478.         System.out.println("掃描未壓縮源代碼文件 >> "); 
  479.         scanner.scanning("cppSrc.cpp"); 
  480.          
  481.         System.out.println("\n壓縮以後的源代碼 >> ");        
  482.         scanner.printCompressedFile("afterCompressed.cpp"); 
  483.          
  484.         System.out.println("\n掃描壓縮後的源代碼文件 >> "); 
  485.         scanner.scanning("afterCompressed.cpp"); 
  486.     } 
相關文章
相關標籤/搜索