Thinker

            - long way to go...

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            24 隨筆 :: 0 文章 :: 143 評論 :: 0 Trackbacks
          詞法分析是編譯器實現的第一步。主要是分析輸入的源程序(字符串),輸出該字符串中出現的所有的合法的單詞。例如:int a = 3 + 5;經過詞法分析會輸出 int,a,=,3,+,5和;這七個單詞。實現詞法分析器的官方做法是:
              1.寫出各個單詞的正規式(正則表達式);
              2.根據正規式構造NFA(不確定的有限自動機);
              3.將NFA轉換DFA(確定的有限自動機);
              4.根據DFA就可以實現詞法分析器,寫出程序。

          下面用實例來說明上面的各個步驟:
              假設我們要實現一個很簡單的腳本,該腳本中只有兩種類型的單詞,一種就是變量,變量名的規則就是以字母開頭后面緊跟0個或多個字母或數字的的字符串,如 a 和 a12d3 等;另一種就是操作符,很簡單只有 &,|,~,(,),==,!= 這幾個。給出幾個腳本的實例:result1 & result2,rst1&(rst2|rst3),answer1 | (~answer2)。

          按照上面的步驟,讓我們來看一下如何實現這個詞法分析器:
          第一步:寫出正規式
              變量的正規式是:letter(letter|digit)*
              操作符的正規式:&,|,~,(,)。由于操作符都是固定字符的,所以正規式就是它本身。
          第二步:根據正規式構造NFA
              正規式構造NFA的基礎是先構造正規式中每個字符的NFA,如變量的正規式中只有兩個字符 letter 和 digit,他們的正規式分別是:
                           
          根據構造|形式的NFA的公式可以構造 letter|digit 的NFA如下圖:

          (letter|digit)*的NFA為:

          letter(letter|digit)*的NFA為:

          第三步:將NFA轉換為DFA
              先是將所有通過ε可以到達的狀態合并,由上圖NFA可以看出1,2,3,4,6,9都是通過ε可以直接用箭頭連接的,以此類推5,8,9,3,4,6和7,8,9,3,4,6都是可以合并稱一個狀態的,如下圖:

          此圖用新的DFA表示如下:

             由于生成的DFA的狀態過多,需要將上面的DFA最小化,生成狀態數最少的DFA,最小化DFA的過程就是將那些無論怎樣轉換都仍然轉換到本組合內部的狀態組合合并,如上圖{B,C,D}這三個狀態組合稱狀態組無論經過letter還是digit轉換都仍然轉換到該組合,那么就可以將這三個狀態合并,如下圖:
                  用新狀態表示為:      

          腳本中變量的DFA已經構造好了。
          由于操作符都是固定字符,所以DFA比較簡單,下面給出幾個的圖示例子:




          現在我們將各個單詞的DFA組合在一起,大致類似下圖:

          實際上,由于這種很簡單的例子不必套用這種正規的步驟來得到DFA,可以直接把圖畫出來。首先畫一個開始狀態(Start)和一個結束狀態(Done),然后再考慮各個單詞之間的關系將其分類,這種分類的過程就看你的經驗了,依據各個單詞挨個字符被識別的過程即可畫出類似上圖的DFA,然后再根據這個DFA寫出詞法分析的程序來就非常的簡單了。
          下面給出根據這個DFA的寫出的大概程序,這段代碼可執行,但是沒有考慮各種意外、出錯以及相關優化處理等,所以僅供參考,請斟酌使用:
            1 
            2 public class ScriptScanner
            3 {
            4   private String scriptInput = null;
            5 
            6   /**
            7    * @param scriptInput
            8    */
            9   public ScriptScanner(String scriptInput)
           10   {
           11     this.scriptInput = scriptInput;
           12   }
           13 
           14   /** 標記當前讀取段的開始位置 */
           15   private int start_read_pos = 0;
           16 
           17   /** 當前位置 */
           18   private int current_pos = 0;
           19 
           20   private char readChar()
           21   {
           22     if (scriptInput == null || current_pos >= scriptInput.length())
           23       return EOF;
           24 
           25     return scriptInput.charAt(current_pos);
           26   }
           27 
           28   public Token getNextToken()
           29   {
           30     Token currentToken = null;
           31 
           32     int state = STATE_START;
           33 
           34     start_read_pos = current_pos;
           35 
           36     while (state != STATE_DONE)
           37     {
           38       char ch = readChar();
           39 
           40       switch (state)
           41       {
           42         case STATE_START:
           43         {
           44           if (Character.isLetter(ch))
           45           {
           46             state = STATE_IN_VAR;
           47           }
           48           else if (ch == '=')
           49           {
           50             state = STATE_IN_EQUAL;
           51           }
           52           else if (ch == '<')
           53           {
           54             state = STATE_IN_NOTEQUAL;
           55           }
           56           else
           57           {
           58             state = STATE_DONE;
           59 
           60             switch (ch)
           61             {
           62               case EOF:
           63                 currentToken = new Token(TokenType.EOF);
           64                 break;
           65 
           66               case '&':
           67                 currentToken = new Token(TokenType.AND);
           68                 break;
           69 
           70               case '|':
           71                 currentToken = new Token(TokenType.OR);
           72                 break;
           73 
           74               case '~':
           75                 currentToken = new Token(TokenType.NOT);
           76                 break;
           77 
           78               case '(':
           79                 currentToken = new Token(TokenType.LPAREN);
           80                 break;
           81 
           82               case ')':
           83                 currentToken = new Token(TokenType.RPAREN);
           84                 break;
           85 
           86               default:
           87                 currentToken = new Token(TokenType.ERROR, "無法識別的字符");
           88                 break;
           89             }
           90 
           91           } // End: else
           92 
           93           current_pos ++// 開始狀態下除 EOF 外都需要將位置后移
           94 
           95           break;
           96 
           97         } // End: case STATE_START
           98 
           99         case STATE_IN_EQUAL:
          100         {
          101           state = STATE_DONE;
          102 
          103           if (ch == '=')
          104           {
          105             currentToken = new Token(TokenType.EQUAL);
          106           }
          107           else
          108           {
          109             currentToken = new Token(TokenType.ERROR, "錯誤的運算符");
          110           }
          111 
          112           current_pos ++;
          113 
          114           break;
          115 
          116         } // End: case STATE_IN_EQUAL
          117 
          118         case STATE_IN_NOTEQUAL:
          119         {
          120           state = STATE_DONE;
          121 
          122           if (ch == '>')
          123           {
          124             currentToken = new Token(TokenType.NOTEQUAL);
          125           }
          126           else
          127           {
          128             currentToken = new Token(TokenType.ERROR, "錯誤的運算符");
          129           }
          130 
          131           current_pos ++;
          132 
          133           break;
          134 
          135         } // End: case STATE_IN_NOTEQUAL
          136 
          137         case STATE_IN_VAR:
          138         {
          139           if (! Character.isLetterOrDigit(ch))
          140           {
          141             state = STATE_DONE;
          142 
          143             String value = scriptInput.substring(start_read_pos, current_pos);
          144 
          145             currentToken = new Token(TokenType.ID, value);
          146           }
          147           else
          148           {
          149             current_pos ++;
          150           }
          151 
          152           break;
          153 
          154         } // End: case STATE_IN_VAR
          155 
          156         default:
          157         {
          158           state = STATE_DONE;
          159 
          160           currentToken = new Token(TokenType.ERROR);
          161         }
          162       } // End: switch (state)
          163     }
          164 
          165     return currentToken;
          166   }
          167 
          168   public final static char EOF = '\0';
          169 
          170   /*
          171    * 定義 DFA 的狀態。
          172    */
          173 
          174   /** 開始狀態 */
          175   public final static int STATE_START = 0;
          176 
          177   /** 當前 Token 是變量 */
          178   public final static int STATE_IN_VAR = 1;
          179 
          180   /** 當前 Token 是 "==" */
          181   public final static int STATE_IN_EQUAL = 2;
          182 
          183   /** 當前 Token 是 "<>" */
          184   public final static int STATE_IN_NOTEQUAL = 3;
          185 
          186   /** 當前 Token 結束 */
          187   public final static int STATE_DONE = 4;
          188 }
          189 
          從代碼中可以看出,預先根據DFA定義了5個狀態:
          STATE_START,STATE_IN_VAR,STATE_IN_EQUAL,STATE_IN_NOTEQUAL,STATE_DONE,然后每個記號(Token)都是從STATE_START開始到STATE_DONE結束,中間根據輸入字符的不同在各個狀態中不斷的轉換,直到識別出記號或者錯誤為止。由此可見只要畫好DFA寫代碼就簡單多了。
              有興趣的朋友可以研究一下語法分析,生成語法樹,根據語法樹求值;也可以研究一下利用中綴表達式求值。

          http://www.aygfsteel.com/qujinlong123/
          posted on 2007-05-08 13:53 Long 閱讀(15982) 評論(16)  編輯  收藏 所屬分類: Java

          評論

          # re: 詞法分析(字符串分析) 2007-05-08 15:17 dennis
          使用antlr或者javacc來生成詞法分析器比較簡單,自己寫實在是很麻煩的事情。  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2007-05-09 10:02 BeanSoft
          支持一下, 期待能作出國人自己的 IDE 和 語言...呵呵  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2007-05-09 12:43 CowNew開源團隊
          兄弟確實很牛!!!  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2007-05-25 13:04 tripper
          牛人!基礎的東西~  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2007-07-05 16:41 sayigood
          有完整的NFA->DFA的代碼嗎?
            回復  更多評論
            

          # re: 詞法分析(字符串分析) 2007-07-05 17:09 Long
          @sayigood
          NFA->DFA的代碼?  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2007-10-22 09:47 逍貓
          最近在看關于這方面的東西,自己也做了個詞法分析器,功能實現了,但總覺得規范上差了些。今天看了此文,有種徹悟的感覺,深表感謝!!  回復  更多評論
            

          # re: 詞法分析(字符串分析)[未登錄] 2008-01-05 00:38
          @sayigood
          講的很透徹了,但是例子多少有些掃興,沒有給出正則表達到dfa的轉換.能否給出nfa->dfa的轉化 及dfa最小化的代碼,另外能否給一段正則表達式->dfa的轉換,這樣就有lex的樣子了 ,本人做語義屬性方面學習有空交流
            回復  更多評論
            

          # re: 詞法分析(字符串分析) 2008-06-26 11:08 又四草
          一個頁面就比陳亦云那一章都清楚,害我不得不留言  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2009-05-02 09:45 rdc
          請問你的圖是用什么畫得,謝謝  回復  更多評論
            

          # re: 詞法分析(字符串分析)[未登錄] 2009-10-15 15:06 aa
          感謝樓主一下。。  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2010-01-05 17:47 qqj
          謝謝  回復  更多評論
            

          # re: 詞法分析(字符串分析)[未登錄] 2011-05-13 08:31 gemini
          確實是好清楚啊  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2011-11-06 14:38 Java_learner
          求語法分析  回復  更多評論
            

          # re: 詞法分析(字符串分析)[未登錄] 2014-07-07 10:23 123
          好文要頂  回復  更多評論
            

          # re: 詞法分析(字符串分析) 2016-01-16 08:16 qw
          /61
            回復  更多評論
            

          主站蜘蛛池模板: 冀州市| 昌吉市| 肥东县| 高密市| 开远市| 邻水| 墨竹工卡县| 云安县| 岳普湖县| 饶阳县| 鄂温| 靖西县| 蛟河市| 玛沁县| 高要市| 喀喇沁旗| 屏东市| 玉山县| 乌海市| 连平县| 阿瓦提县| 绩溪县| 雅江县| 康保县| 米林县| 满城县| 敦化市| 丰都县| 博乐市| 宁城县| 郸城县| 梧州市| 江安县| 景宁| 肃宁县| 米泉市| 霍城县| 林芝县| 汝阳县| 上栗县| 星座|