亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#詞法分析器之輸入緩沖和代碼定位的應用分析

2020-01-24 03:23:31
字體:
來源:轉載
供稿:網友
一、輸入緩沖

在介紹如何進行詞法分析之前,先來說說一個不怎么被提及的問題――怎么從源文件中讀取字符流。為什么這個問題這么重要呢?是因為在詞法分析中,對字符流是有要求的,它必須能夠支持回退操作(就是將多個字符放回到流中,以后會再次被讀?。?。

先來解釋下為什么需要支持回退操作,舉個簡單的例子來說,現在要對兩個模式進行匹配:

圖 1 流的回退過程

上面是一個簡單的匹配過程,僅為了展示回退過程,在后面實現 DFA 模擬器時會詳細解釋是如何匹配詞素的。

現在來看看 C# 中與輸入相關的類,有 Stream,它支持流的查找,但是只能以字節方式訪問;BinaryReader 和 TextReader 雖然支持讀取字符,但是又不能支持回退。所以,就必須自己完成這個輸入緩沖類了,大致思路就是以 TextReader 作為底層的字符輸入,然后由自己的類完成對回退能力的支持。

《編譯原理》上給出了一種緩沖區對的方法,簡單的說就是開辟兩個緩沖區,設緩沖區大小都是 N 個字符。每一次都將 N 個字符讀入到緩沖區中,并在這個緩沖區上實現字符操作。如果當前緩沖區的數據已經處理完畢,就將 N 個新字符讀入到另一個緩沖區中,接下來就換做操作新的緩沖區。

這樣的數據結構效率很高,而且只要維護合適的指針,就可以很容易的實現回退功能。不過它的緩沖區大小是固定的,新讀入的字符會覆蓋舊的字符。如果需要回退的字符數量過多(比如在分析很長的字符串時),就容易出現錯誤。我通過使用多個緩沖區解決了舊字符被覆蓋的問題――如果緩沖區不足了,就開辟新緩沖區,而不是覆蓋舊數據。

如果僅僅是不斷的添加緩沖區,那么占用的內存只會不斷增加,這樣是沒有什么意義的,因此我定義了三個釋放緩沖區的操作:Drop,Accept 和 AcceptToken。Drop 的作用是將當前位置之前的所有數據標記為無效(被拋棄),被標記無效的數據占用的緩沖區就被釋放掉,可以拿來被重復利用了;Accept 則會將標記為無效的數據以字符串形式返回,而不僅僅是簡單的拋棄;類似的,AcceptToken 是以 Token 形式返回被無效化的數據,是為了方便進行詞法分析。

這樣的數據結構比較類似于 STL 中的 deque,不過這里不需要隨機訪問和插入、刪除數據,僅會在數據的頭、尾進行操作,因此我直接將多個緩沖區使用雙向鏈表連成一個環,使用三個指針 current,first 和 last 指向鏈表中有數據的緩沖區,如下圖所示:

圖 2 多個緩沖區組成的鏈表,紅色的部分表示有數據,白色的部分沒有數據

其中,first 指向的是最早的數據緩沖區,last 指向的是最新的數據緩沖區,current 指向的是當前正在訪問的數據緩沖區,current 總是在 [first, last] 范圍之內。firstIndex 和 lastLen 之間紅色的部分,就是包含有效數據的緩沖區,idx 表示當前正在訪問的字符。白色的部分表示空緩沖區,或是緩沖區中的數據已無效。

當需要讀取下一個字符時,就從 current 中依次讀取數據,并將 idx 后移。如果 current 中的數據已經讀取完畢,則將 current 移向 last(這里用移向,是因為 current 和 last 之間可能有多個緩沖區),同時 idx 也要相應的移動。

圖 3 current 移向 last

如果需要繼續讀取字符,但是 current 中沒有新數據了,而此時 current 已經與 last 相同,表示緩沖區中已經沒有更新的數據,那么就需要從 TextReader 中讀取數據,放到新的緩沖區中,同時后移 current 和 last(需要保證 last 總是指向最新的緩沖區)。

圖 4 current 和 last 向后移

現在來看看回退操作。進行回退時,只需要將 current 向 first 的方向移動(同樣,current 和 first 之間可能有多個緩沖區)。

圖 5 回退操作

Drop 操作(Accept 和 AcceptToken 也同理)的實現也很簡單,只需要將 first 移動到 current 位置,將 firstIndex 移動到 idx 即可,這就表示 idx 之前的數據都看作無效數據

圖 6 Drop 操作

這里需要注意的就是,Drop 操作完成后,被無效化的數據就有可能會被新數據覆蓋,因此應該確定數據不再需要時再執行 Drop 操作。Drop 操作的效率很高(移動兩個引用),基本不用擔心會影響效率。

使用這種環形數據結構的優點是除了將字符填充到緩沖區之外,完全避免了數據的額外復制,無論是前進、回退還是 Drop 操作都只有指針(引用)操作,效率很高。當 Drop 比較及時時,僅會使用兩個緩沖區,不會額外的占用內存。當占用的緩沖區過多時,還能夠實現主動釋放多余的內存(這里現在沒有考慮)。

缺點就是實現起來會復雜些,需要仔細處理好 first、current 和 last 的關系,以及 firstIndex、index 和 lastLen 范圍限制,有時還會涉及到多個緩沖區的操作。

完整的代碼可見 SourceReader.cs

二、代碼定位

在對源代碼進行解析的時候,記錄每個 Token 對應的行號和列號顯然是很必要的工作,沒有人會喜歡面對一大堆 Error,而且還偏偏不告訴你到底是哪錯了……因此,我認為代碼定位絕對是詞法分析必備的功能,所以直接把這個功能內置到了 SourceReader 類中了。

下面來說明如何實現代碼定位。代碼定位包含三維數據:索引、行號和列號。索引是從 0 開始的字符索引,主要是方便程序進行處理;行號和列號則都是從 1 開始的,主要是為了人去看。

行定位比較簡單,Unix 的換行符是 '/n',Windows 的換行符是 "/r/n",所以直接統計 '/n' 的個數即可。

接下來是列定位。為了達到比較好的效果,需要考慮兩個因素:全角、半角字符和 Tab 字符。

一個中文字符(即全角字符)對應的是兩列,英文字符(半角字符)對應的則是一列,這樣在等寬字體下,每一列都是上下對齊的。在計算列數的時候,自然也應當如此,使用 Encoding.Default.GetByteCount() 而不是字符串的長度。不過這里我發現了一個內存問題(詳情參考這里),改用 Encoding.Default.GetEncoder() 的 GetByteCount 方法就可以了。

一個 Tab 字符的長度是不定的(一般是為 4 或 8,因人而異),所以定義了一個 TabSize 來表示 Tab 字符的寬度。那么,一個 Tab 字符就對應 TabSize 列么?并不是這樣的,雖然一般看來是這樣,但事實上,Tab 字符是讓下一字符對應的列總是為 TabSize 的整數倍再加 1。如果 TabSize = 4,那么它的行為如下圖所示,其中 a 和 bcc 后面都是有兩個 Tab 字符,bcccccc 和 bccccccc 后面都是有一個 Tab 字符,每個 Tab 字符我都用灰色箭頭標出來了。

圖 7 Tab 字符實例

所以,實際的列號應當使用下面的公式計算,其中 currentCol 是 Tab 字符所在的列,nextCol 就是下一字符所在的列:

nextCol = tabSize * (1 + (currentCol - 1) / tabSize) + 1;

代碼定位的計算方法有了,然后就是計算的時機。如果每次 Read 的時候都計算當前字符的位置,一是計算效率會略低,因為 GetByteCount 方法中,一次性計算較長一個字符數組的效率,差不多是多次計算長度為 1 的字符數組的一倍。二是回退的時候應該怎么辦?如果將之前的位置計算結果都保存起來,內存占用會是一個問題,如果不考慮的話,又無法根據當前字符的位置推算出前一個字符的位置(比如當前字符在第一列的話,前一個字符應該在第幾列?)。

綜合考慮之后,我決定將代碼位置的計算放到 Drop 操作(Accept 和 AcceptToken 也一樣)中,一個是向上面所說的,計算效率會略高,另一個是一般僅當識別出了一個 Token 后才需要為它定位,此時恰好是 Drop 或 AcceptToken 的時機,識別 Token 的過程中就是定位了也沒有什么用處。

我將代碼定位的功能單獨封裝到了 SourceLocator.cs 類中。

下一篇將會介紹詞法分析中用到的正則表達式,以及如何解析正則表達式。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲国产第一页| 自拍偷拍亚洲在线| 91精品国产综合久久香蕉| 91精品国产综合久久香蕉最新版| 久久免费成人精品视频| 精品国产一区二区三区久久狼黑人| 国产精自产拍久久久久久蜜| 亚洲男人天堂视频| 欧美激情videos| 91性高湖久久久久久久久_久久99| 九九久久综合网站| 久久精品国产久精国产思思| 久久久久国色av免费观看性色| 孩xxxx性bbbb欧美| 久久久99免费视频| 欧美裸体视频网站| 欧美成人高清视频| 欧美寡妇偷汉性猛交| 欧美成人一区二区三区电影| 欧美日韩美女视频| 日韩不卡在线观看| 亚洲欧美国产va在线影院| 国产69精品久久久久9999| 午夜精品一区二区三区在线视频| 欧美黑人巨大xxx极品| 性色av一区二区三区在线观看| 亚洲精品中文字幕有码专区| 欧美中文字幕在线观看| 日韩美女写真福利在线观看| 国产精品美女网站| 亚洲国产精品人人爽夜夜爽| 国产精品久久中文| 久热爱精品视频线路一| 国产精品永久免费视频| 日韩电影中文字幕一区| 91人成网站www| 5278欧美一区二区三区| 日韩av一区二区在线观看| 45www国产精品网站| 97在线看免费观看视频在线观看| 欧美精品一区在线播放| 欧美性生交xxxxx久久久| 久久久久久久久久国产精品| 欧美人与性动交a欧美精品| 在线午夜精品自拍| 色先锋资源久久综合5566| 欧美日韩久久久久| 欲色天天网综合久久| 久久久久久久国产| 亚洲一区精品电影| 国产精品99久久久久久人| 国产精品美女免费看| 亚洲自拍偷拍一区| 国产精品国产自产拍高清av水多| 麻豆成人在线看| 亚洲欧美一区二区激情| 亚洲日本中文字幕免费在线不卡| 麻豆一区二区在线观看| 色无极亚洲影院| 欧美性xxxxxxx| 另类色图亚洲色图| www.xxxx欧美| 狠狠色噜噜狠狠狠狠97| 欧美黑人一区二区三区| 91精品国产色综合久久不卡98| 国产成人精品视频在线观看| 国产精品美女av| 久久久精品免费| 亚洲美女免费精品视频在线观看| 日韩a**中文字幕| 国模gogo一区二区大胆私拍| 色噜噜国产精品视频一区二区| 国产欧美在线看| 按摩亚洲人久久| 日韩中文视频免费在线观看| 亚洲欧洲日产国码av系列天堂| 欧美国产精品日韩| 国产精品爽爽爽| 亚洲尤物视频网| 亚洲成人a级网| 91高清视频免费| 91网站免费看| 亚洲午夜精品久久久久久久久久久久| 日韩高清免费观看| 夜夜躁日日躁狠狠久久88av| 91丨九色丨国产在线| 91sao在线观看国产| 91精品国产91久久久久久久久| 欧美劲爆第一页| 日韩美女在线观看| 亚洲天堂av图片| 亚洲欧洲第一视频| 亚洲国产精品va在线| 国产精品一久久香蕉国产线看观看| 国产日韩欧美综合| 久久亚洲精品网站| 国产成人精品久久二区二区91| 亚洲第一中文字幕在线观看| 亚洲成avwww人| 欧美中文在线免费| 日韩美女免费视频| 亚洲天堂第二页| 欧美激情精品久久久久| 亚洲精品成人av| 日韩中文字幕免费看| 精品久久久国产精品999| 久久中文精品视频| 一本色道久久88精品综合| 九九热这里只有精品6| 亚洲精品不卡在线| 久久影院模特热| 久久成年人免费电影| 国产精品视频公开费视频| 国产在线观看91精品一区| 日韩少妇与小伙激情| 国产免费一区视频观看免费| 欧美黑人狂野猛交老妇| 尤物九九久久国产精品的分类| 精品久久久一区二区| 色多多国产成人永久免费网站| 亚洲一区二区免费在线| 国产成人精品亚洲精品| 亚洲欧美999| 国内精品400部情侣激情| 久久精品亚洲国产| 欧美精品久久久久久久| 亚洲视频欧洲视频| 国产视频在线一区二区| 欧美视频一二三| 亚洲欧洲中文天堂| 日韩在线观看免费av| 精品性高朝久久久久久久| 97精品国产91久久久久久| 欧美日本啪啪无遮挡网站| 欧美日韩精品二区| 欧美激情啊啊啊| 日韩免费在线免费观看| 97av在线视频| 亚洲精品丝袜日韩| 亚洲午夜未删减在线观看| 4438全国亚洲精品在线观看视频| 九九精品视频在线观看| 成人黄色免费网站在线观看| 91久久中文字幕| 欧美精品videosex牲欧美| 97视频免费观看| 国产成人精品日本亚洲| 国产成人久久久| 日本高清久久天堂| 日韩在线视频线视频免费网站| 欧美亚洲第一区| 中文字幕欧美在线| 亚洲人成电影网| 久久影院在线观看| 成人免费淫片视频软件| 国内精品久久久久伊人av| 97精品视频在线观看| 久久久国产精品x99av| 欧美乱妇高清无乱码| 亚洲综合国产精品| 国产精品pans私拍| 久久久久久久999| 久久影院在线观看| 一区二区三区视频免费|