gophp直譯器 Go 譯文之詞法分析與解析

2021-10-12 15:24:57 字數 3698 閱讀 2466

譯者前言

一直對詞法分析與解析的話題比較感興趣,最近發現了好幾篇相關的優秀文章,準備好好翻譯和研究下。我的理解,詞法分析與解析的應用還是比較廣泛的,無論簡單的配置檔案、各種模板語言、還是我們每天在寫程式語言都離不開它。

本篇文章乙個系列文章的第一篇,主要介紹的是詞法分析與解析的一些基礎概念,包括什麼是詞法分析,什麼是解析,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 ...