上篇文章 Go 語言的詞法分析和語法分析(1) 作者闡述了漸漸式詞法分析的過程;通過這一步,原始檔已被轉化為 Syntax.File 結構體,其屬性情況如下:
File 結構體中的 PkgName 屬性已被初始化為 syntax.Name{Value: “main”}
。Go 語言掃描器 Scanner 再處理完包名 PkgName 後,下一次解析將掃描出 import token;此時 *scanner 的 b, r, e 屬性如下圖所示:
同樣,通過 buf[b:r]
計算出當前掃描的 token 為 import 字串,對比 Go 關鍵字列表後將 tok 設定為內建 _Import,此時 *scanner 內部各屬性如下圖所示:
然後掃描器將開始迴圈解析原始檔的 import 宣告,直至掃描到的 token 不等於 _Import,並把 import 的 path 部分(如 net/http, main)等儲存到掃描器的 DeclList 陣列(slice) 中。
for p.got(_Import) {
f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
}
func (p *parser) got(tok token) bool {
if p.tok == tok {
p.next()
return true
}
return false
}
Import 有多種不同格式的宣告,常見文法有:
import . path/package
import alias path/package
import path/package
import _ path/package
// by group
import (
. path/package
alias path/package
path/package
_ path/package
)
Go 語言內部用 ImportDecl 結構體來描述 import 宣告,其結構如下:
ImportDecl struct {
Group *Group // 分組,同一括號下的為同一組
LocalPkgName *Name // 別名
Path *BasicLit // 路徑,如 net/http
}
在進行 import 詞法解析時, Go 語言會嘗試初始化 ImportDecl 結構體併為其各屬性賦值;下面作者針對不同情況來檢視賦值後的 ImportDecl 結構體內容。
- 假設申明語句為
import "net/http"
,解析完該語句後結構體內容如下:
結構體中的 PkgName 屬性(包別名)被設定為 syntax.Name{Value: ""}
,Group 被設定為 nil,Path 部分被設定為 syntax.BasicLit{Value: "net/http"}
。
- 假設申明語句為
import alias "net/http"
,解析完該語句後結構體內容如下:
- 假設申明語句為
import . "net/http"
,解析完該語句後結構體內容如下:
- 假設申明語句為下面分組
import (
"fmt"
"net/http"
)
解析完該語句後結構體內容如下(多個 *ImportDecl 指向同一 Group):
至此,Import 語法申明已基本解析完畢,接下來 Go 語言將解析 const, val, func, type
等 TopLevelDecl 級別的宣告;畢竟 Go 語言的頂級申明就只有這六個關鍵字,全部解析完畢後,Go 語言的詞法語法分析就完成了。
package
import
const
var
type
func
針對上篇文章 Go 語言的詞法分析和語法分析(1) 中的 main.go 檔案,進過這一步解析後,syntax.File 結構體資訊如下:
下面是 Go 語言處理完 import 宣告後開始處理其他 TopLevelDecl 的函式程式碼,程式碼所在位置 src/cmd/compile/internal/syntax/parser.go:399。
// { ImportDecl ";" }
for p.got(_Import) {
f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
}
// { TopLevelDecl ";" }
for p.tok != _EOF {
switch p.tok {
case _Const:
// 處理常量
case _Type:
// 處理型別定義
case _Var:
// 處理變數
case _Func:
// 處理函式
default:
p.syntaxError("non-declaration statement outside function body")
}
}
從這裡也能看出,import 宣告只能放在 package 宣告後面,其他頂級宣告前面;因為在 import 宣告解析完成後,掃描器將進入下一個 for 迴圈繼續解析其他 TopLevelDecl,若這時再遇到 _Import token,程式將走到 switch 的 default 分支,從而發生編譯錯誤。
./prog.go:11:1: syntax error: non-declaration statement outside function body
文章釋出於作者部落格二愣的閒談雜魚,歡迎大家來觀摩 Go 語言編譯原理 — Import宣告的解析。
接下來閱讀
下篇文章將探討 Go 語言對常量的解析,這部分作者將在後面的文章中繼續輸出。
本作品採用《CC 協議》,轉載必須註明作者和本文連結