1. 程式人生 > >編譯原理動手實操之一個輸入系統的實現

編譯原理動手實操之一個輸入系統的實現

大家好,歡迎大家來到coding迪斯尼,願天下沒有難學的知識

閱讀部落格的朋友可以到我的網易雲課堂中,通過視訊的方式檢視程式碼的除錯和執行過程:

這篇文章是我課程中的系列講稿,也是編譯原理動手實操系列的文章。

在我們南方有一句老話叫:無雞不成宴。如果我們把編譯原理當成技術大餐的話,

那我可以說:無碼不成席。程式碼永遠是體驗原理的最好方式。

雞和碼放一次,不知會不會讓大家想起硬碟上的女神.

本節的主要內容,用java實現了上一節所描述的輸入系統,程式碼永遠是掌握理論的唯一方式,本章所講述的程式碼可以在以下連結下載:

大家開啟專案後先看目錄結構:


Input.java 是專案的主體,該檔案是輸入系統的具體實現。FileHandler是一組輸入流讀取介面,輸入系統根據它提供的介面,從輸入流中獲取字元資訊。DiskFileHandler, StdInHandler 是FileHandler的具體實現。

開啟Input.java, 按照小學生作文開頭正規化,我要說的是:首先映入大家眼簾的是:


開始先以一組常量定義了輸入系統的各種屬性,很多屬性我都加上了註釋,有幾個變數我需要強調,Eof_read 表示輸入流中是否還有可讀的資訊,如果輸入流來自於檔案,那麼當讀到檔案末尾時Eof_read 設定為true.

我們需要注意,當輸入流中沒有多餘資訊時,我們的緩衝區中有可能還有沒有讀取的資訊,因為我們是將資訊從輸入流放入緩衝區後再從緩衝區中取出資訊進行處理的。

noMoreChars 返回true則表明,緩衝區和輸入流都沒有可讀資訊了。

大家可能會發覺 End_Buf 是緩衝區的邏輯結束地址,按照前面講的,在它後面其實還有一段浪費的可用記憶體,為何這裡它直接等於實際的記憶體結束地址呢,真實原因是在後面的程式碼中,它的值會做相應調整。

在接下來的程式碼中,有一個函式是ii_newfile, 該介面用於決定輸入流是磁碟檔案還是控制檯,如果ii_newfile的輸入引數filename 不是null 那麼就以磁碟檔案作為輸入流,要是null,就以控制檯為輸入流.要注意ii_newfile並沒有做將資料從輸入流讀入緩衝區的操作,它僅僅是初始化一些指標或變數。真正的將輸入流讀入緩衝區的是ii_advance函式,該函式的作用是從緩衝區中讀取字元資料。

將資料讀入緩衝區是比較耗時的操作,因此輸入系統會等待外部真正請求資料時,才好觸發資料讀取操作。

大家還記得上節講的,當Next指標越過Danger時將會引發Flush操作,也就是將資料從輸入流中讀入緩衝區。在ii_newfile的初始化中,我們特意將Next指標設成緩衝區的末尾,當ii_advance第一次執行時,發現Next越過了Danger,於是引發Flush操作,這樣,資料就從輸入流寫入緩衝區了。

在程式碼中還有一些簡單函式:ii_text(), ii_length(), ii_lineno() 用於返回當前要分析的字串,字串的長度,和所在的行號。Ii_ptext(), ii_plength(), ii_plineno() 用於返回上一個被解析的字串。 

有一個函式需要注意的是ii_flush, 該函式負責執行flush 操作,它先把緩衝區中還沒有讀取的資料向左平移,接著從輸入流中讀入資料,填充平移後產生的可用空間:


大家看程式碼中的註釋基本可以略知一二,ii_flush要做的是將未讀取的區域,向左平移到Start_buf處,平移的距離就是shift_amt, 未讀取區域的長度是copy_amt, shift_amt + copy_amt 就等於End_buf. ii_flush 先判斷Next指標是否在DANGER邊界後面,然後才會執行平移寫入操作。如果Next在DANGER前面,但是傳進來的引數force為true,那麼也會強制進行平移寫入操作。再繼續看函式程式碼:


接下來通過java庫函式arraycopy 進行資料的平移操作,left_edge其實就是未讀取區域的起始地址,平移後,從緩衝區的開頭直到copy_amt處的資料都是未讀取資料,因此從輸入流填入資料是要從緩衝區的copy_amt處之後才開始,ii_fillbuf的作用就是將資料從輸入流寫入緩衝區。平移後,一些指標也要做相應的調整。

接下來再看看ii_fillbuffer函式:


ii_fillbuffer 先從fileHandler的read 函式中獲取資訊,也就是從輸入流中讀入資料,每次讀入資料的數量用need表示,need是MAXLEX的整數倍。got 返回的是讀到的資料量,如果讀到的資料少於想要讀取的資料,那就表明輸入流中已經沒有多餘的資訊可讀了。

最後我們再看看,整個程式跑去來是什麼樣子的,開啟InputSystem.java


當runStdinExample() 執行時,ii_newfile 輸入為null, 也就是輸入系統要從控制檯做為輸入流,當執行ii_newfile時,程式要求使用者從控制檯輸入資訊,我們輸入如下:


回車後,程式由ii_mark_start開始往下走, printWord() 打出第一個字串也就是typedef, 於是緩衝區中的資料及各個指標的情況如圖所示。

Ii_mark_prev() 將當期的字串設定為“上一個”字串,再往下走:


第二次printWord()打印出的字串是“int”, 此時緩衝區及各指標變數的狀態如上圖所示。

最後顯示的是ii_ptext()輸出的是上一個字串,也就是typedef

ii_text()輸出的是當期字串,也就是 int:

整個過程執行結果如下:


輸入系統到此就介紹結束了,閱讀部落格的朋友可以到我的網易雲課堂中,通過視訊的方式檢視程式碼的除錯和執行過程:


在下一講,我們將討論詞法分析的演算法細節。