譯者前言
一直對詞法分析與解析的話題比較感興趣,最近發現了好幾篇相關的優秀文章,準備好好翻譯和研究下。我的理解,詞法分析與解析的應用還是比較廣泛的,無論簡單的配置檔案、各種模板語言、還是我們每天在寫程式語言都離不開它。
本篇文章乙個系列文章的第一篇,主要介紹的是詞法分析與解析的一些基礎概念,包括什麼是詞法分析,什麼是解析,token 如何表示等等。
正文如下:
從今天開始,我將會用三篇文章介紹在 go 中如何構建乙個簡單的詞法分析與直譯器。文中介紹的內容主要是基於 rob pike 在 2011 年關於 lexical scanning in go 的演講。這個系列文章最終會包含乙個功能完善的**,它可用於 ini 型別檔案的解析。
基礎概念的介紹,如什麼是詞法分析、解析,以及案例的一部分介紹;
概要詞法分析與解析是個比較複雜的話題,但這並不意味著我們無法一點點剖析和掌握它。為了幫助大家更好地了解它,接下來,我將會構建乙個簡單的 ini 檔案解析器。這個解析器輸入的是文字字串,返回的是經過結構化處理的結果,結果包含多個 section 和 key/value。我將用 go 實現它。
為什麼選擇 ini 檔案?主要是因為它的簡單性,結構容易理解。例如,下面就是乙個簡單的 ini 內容樣例:
[setionname]
key1=value 1
key2=value 2
樣例中主要涉及了三個元素,充分理解它們對於我們如何設計 ini 直譯器是非常有幫助的。
段: sections
鍵: keys
值: values
key/value 屬於 section,每個 section 可能不止乙個 key/value,每個 ini 檔案可以包含多個 section。這是一種非常簡單但非常高效的結構,特別適用於儲存配置資訊。
上面的內容將會被解析為結構化資料,我們可以提前看下處理後的資料的 json 格式,如下:
"filename": "sample.ini",
"sections": [
"name": "sectionname",
"keyvaluepairs": [
"key": "key1",
"value": "value 1"
"key": "key2",
"value": "value 2"
什麼是詞法分析
詞法分析在 wiki 中的定義是 "將字串轉化為一系列 token 的過程,即,一系列有意義的字串"。詞法分析通常是在編譯或執行之前執行。例如,php 是一種解釋型語言,當你訪問乙個由 php 開發的站點,php 直譯器將負責 php **的執行,並把生成的 html 返回給瀏覽器。php **先會經過詞法分析得到一系列有意義的 token。之後,php 直譯器會按照這些 token 執行接下來的操作,比如將 token 結果快取,以及執行具體工作等。
什麼是 token
token 是用於描述與歸類從文字中分解出來的元素的一種結構。例如,之前的例子中,section 可歸類為 token_section,key 可以歸類為 token_key。這種結構通常被用於追蹤元素類類別和值。比如,前面的例子中,名為 sectionname 的 section,在 token 中的結構是如下表示:
"type": token_section,
"value": "sectionname"
解析器、直譯器或編譯器將會根據得到 token 決定如何執行、編譯或生成**/資料。
什麼是解析
詞法分析器將輸入文字拆分,並返回一系列結構化的 token。但 token 本身並沒有什麼價值,如此便引出了解析的概念。解析是指對 tokens 進行語法分析的過程,它可以確保輸入的文字的可用性和有意義的。
例如,下面的樣例就是非可用 ini 段 section。
[sectionname]=hi there
這段文字在經過詞法分析後,將會得到一系列的 token,它們將被用於 section、等於號和字串的表示。這是詞法分析的職責所在。而解析器則是決定它們是否有意義,即是否符合語法。對於 ini 格式而言,這些 token 並不可用。我們實現的解析器將會從 channel 中接收 token,建立相應的資料結構,包含 section 和 key/value。
逐步拆解
本文最後乙個任務,定義下面在詞法分析器中將會使用 token 型別結構,token 的名稱和相關的型別。首先是 token 的結構,這個結構將會貫穿我們的整個**,它將會通過 channel 傳遞給解析器。
我們先來看下專案目錄結構,可以檢視 github 倉庫, 在我的 mac 上,目錄結構是 ~/code/go/src/github.com/adampresley/sample-ini-parser,lexer 詞法分析元件在 service/lexer 目錄下。token 結構的定義位於 ~/code/go/src/github.com/adampresley/sample-ini-parser/services/lexer/lexertoken 目錄下。
package lexertoken
import (
"fmt"
type token struct {
type tokentype
value string
該結構清晰的表示乙個 token 由型別和值組成的結構。你可以已經注意到這裡引用了乙個還未定義的型別 tokentype。現在,我們來定義一下:
package lexertoken
type tokentype int
const (
token_error tokentype = itoa
token_eof
token_left_bracket
token_right_bracket
token_equal_sign
token_newline
token_section
token_key
token_value
我們新定義了乙個名為 tokentype 的型別(源自整型),並建立了所有可能的 token 型別常量,它們都是從 ini 檔案基礎上拆解而來。
我們需要一種方式實現錯誤追蹤,定義 token_error 表示錯誤型別;
當到達文字結尾,我們用 token_eof 表示;
段由左括號、文字、右括號三部分組成;
token_left_bracket
token_section
token_right_bracket
key/value,以等於號進行分隔;
token_key
token_equal_sign
token_value
section 和 key/value 必須以換行符結尾,常量 token_newline;
最後,我們還需要了解上面部分的 token 型別的文字表示。比如,詞法器在分析 key/value 是,會在它們之間尋找等於號,此時,我們需要知道它的文字表示,以確認當前位置是否存在等於號。用常量表示這類 token 文字是個不錯主意。
package lexertoken
const eof rune = 0
const left_bracket string = "["
const right_bracket string = "]"
const equal_sign string = "="
const newline string = "\n"
接下來在 part 2,我們將會對詞法分析部分進行更加深入的介紹,我們在前面定義的 token 結構也將會被用到。
編譯器之詞法分析
最近我們在做乙個有關snl語言的編譯器,下面寫了一下大概流程 詞法分析器是編譯過程的第一階段,功能是 1.對以字串形式輸入的源程式 這裡是把源程式從檔案讀出,也可以在控制台輸入 按順序進行掃瞄,根據snl語言的詞法規則識別具有獨立意義的單詞 符號 序列,如保留字 由語言系統自身定義的,通常是由字母組...
編譯原理之詞法分析器(C C)
從乙個檔案中讀取原始碼,執行後可以直接看到結果,也可在指定檔案中檢視結果。這個詞法分析器只實現了部分關鍵字 字元等的識別,可在key陣列自行新增,種別碼的判別與新增用case更加方便,可寫下試試。include include include include include using namesp...
手工打造編譯器之詞法分析器3
逆波蘭表示式,可以去除括號 建立適合計算機處理的表示式,該表示式有正確的運算優先順序。正常的表示式 逆波蘭表示式 a b a,b,a b c a,b,c,a b c d a,b,c,d,a d b c a,d,b,c,a 1 3 a 1,3 運算的時候,遇到可以歸併的就歸併計算。如 5 4 3 2 ...