以Golang為例詳解AST抽象語法樹的原理與實(shí)現(xiàn)
前言
各位同行有沒有想過一件事,一個程序文件,比如 hello.go
是如何被編譯器理解的,平常在編寫程序時,IDE 又是如何提供代碼提示的。在這奧妙無窮的背后, AST(Abstract Syntax Tree)
抽象語法樹功不可沒,他站在每一行程序的身后,默默無聞的工作,為繁榮的互聯(lián)網(wǎng)世界立下了汗馬功勞。
AST 抽象語法樹
AST 使用樹狀結(jié)構(gòu)來表達(dá)編程語言的結(jié)構(gòu),樹中的每一個節(jié)點(diǎn)都表示源碼中的一個結(jié)構(gòu)。聽到這或許你的心里會咯噔一下,其實(shí)說通俗一點(diǎn),在源代碼解析后會得到一串?dāng)?shù)據(jù),這個數(shù)據(jù)自然的呈現(xiàn)樹狀結(jié)構(gòu),它被稱之為 CST(Concrete Syntax Tree)
具體語法樹,在 CST 的基礎(chǔ)上保留核心結(jié)構(gòu)。忽略一些不重要的結(jié)構(gòu),比如標(biāo)點(diǎn)符號,空白符,括號等,就得到了 AST。
如何生成 AST
生成 AST 大概需要兩個步驟,詞法分析lexical analysis
和語法分析syntactic analysis
。
詞法分析 lexical analysis
lexical analysis 簡稱 lexer ,它表示字符串序列,也就是我們的源代碼轉(zhuǎn)化為 token 的過程,進(jìn)行詞法分析的工具叫做詞法分析器(lexical analyzer,簡稱lexer),也叫掃描器(scanner)。Go 語言的 go/scanner
包提供詞法分析。
func ScannerDemo() { // 源代碼 src := []byte(` func demo() { fmt.Println("When you are old and gray and full of sleep") } `) // 初始化標(biāo)記 var s scanner.Scanner fset := token.NewFileSet() file := fset.AddFile("", fset.Base(), len(src)) s.Init(file, src, nil, scanner.ScanComments) // Scan 進(jìn)行掃碼并打印出結(jié)果 for { pos, tok, lit := s.Scan() if tok == token.EOF { break } fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit) } }
打印的結(jié)果我們接著往下看。
標(biāo)記 token
標(biāo)記(token) 是詞法分析后留下的產(chǎn)物,是構(gòu)成源代碼的最小單位,但是這些 token 之間沒有任何邏輯關(guān)系。以上述代碼為例:
func demo() { fmt.Println("When you are old and gray and full of sleep") }
經(jīng)過詞法分析后,會得到:
token | literal(字面量,以string表示) |
func | "func" |
IDENT | "demo" |
( | "" |
) | "" |
{ | "" |
IDENT | "fmt" |
. | "" |
IDENT | "Println" |
( | "" |
STRING | "\"When you are old and gray and full of sleep\"" |
) | "" |
; | "\n" |
} | "" |
; | "\n" |
在 Go 語言中,如果 token 類型就是一個字面量,例如整型,字符串類型等,那么它的值就是相對應(yīng)的值,比如上表的 STRING
;如果 token 是 Go 的關(guān)鍵詞,那么它的值就是關(guān)鍵詞,比如上表的 fun
;對于分號,它的值則是換行符;其他 token 類要么是不合法的,如果是合法的,則值為空字符串,比如上表的 {
。
語法分析 syntactic analysis
不具備邏輯關(guān)系的 token 經(jīng)過語法分析(syntactic analysis,也叫 parsing)就可以得到具有邏輯關(guān)系的 CST 具體語法樹,然后對 CST 進(jìn)行分析提煉即可得到 AST 抽象語法樹。完成語法分析的工具叫做語法分析器(parser)。Go 語言的 go/parser
提供語法分析。
func ParserDemo() { src := ` package main ` fset := token.NewFileSet() // 如果 src 為 nil,則使用第二個參數(shù),它可以是一個 .go 文件地址 f, err := parser.ParseFile(fset, "", src, 0) if err != nil { panic(err) } ast.Print(fset, f) }
打印出來的 AST:
0 *ast.File {
1 . Package: 2:1
2 . Name: *ast.Ident {
3 . . NamePos: 2:9
4 . . Name: "main"
5 . }
6 . FileStart: 1:1
7 . FileEnd: 2:14
8 . Scope: *ast.Scope {
9 . . Objects: map[string]*ast.Object (len = 0) {}
10 . }
11 }
它包含了源代碼的結(jié)構(gòu)信息,看起來像一個 JSON。
總結(jié)
源代碼經(jīng)過詞法分析后得到 token(標(biāo)記),token 經(jīng)過語法分析得到 CST 具體語法樹,在 CST 上創(chuàng)建 AST 抽象語法樹。 來個圖圖或許更直觀:
Go 的抽象語法樹
這里我們以一個具體的例子來看:從 go 代碼中提取所有結(jié)構(gòu)體的名稱。
// 源碼 type A struct{} type B struct{} type C struct{}
func ExampleGetStructName() { fileSet := token.NewFileSet() node, err := parser.ParseFile(fileSet, "demo.go", nil, parser.ParseComments) if err != nil { return } ast.Inspect(node, func(n ast.Node) bool { if v, ok := n.(*ast.TypeSpec); ok { fmt.Println(v.Name.Name) } return true }) // Output: // A // B // C }
到此這篇關(guān)于以Golang為例詳解AST抽象語法樹的原理與實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go AST抽象語法樹內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言中init函數(shù)和defer延遲調(diào)用關(guān)鍵詞詳解
這篇文章主要介紹了Go語言中init函數(shù)和defer延遲調(diào)用關(guān)鍵詞,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03詳解golang中?work與?module?的區(qū)別與聯(lián)系
Go?模塊通常由一個項(xiàng)目或庫組成,并包含一組隨后一起發(fā)布的?Go?包,Go?模塊通過允許用戶將項(xiàng)目代碼放在他們選擇的目錄中并為每個模塊指定依賴項(xiàng)的版本,解決了原始系統(tǒng)的許多問題,本文將給大家介紹一下golang中?work與?module?的區(qū)別與聯(lián)系,需要的朋友可以參考下2023-09-09Golang內(nèi)存泄露場景與定位方式的實(shí)現(xiàn)
Golang有自動垃圾回收機(jī)制,但是仍然可能會出現(xiàn)內(nèi)存泄漏的情況,本文主要介紹了Golang內(nèi)存泄露場景與定位方式的實(shí)現(xiàn),具有一定的參考價值,感興趣的可以了解一下2024-04-04解決Golang中g(shù)oroutine執(zhí)行速度的問題
這篇文章主要介紹了解決Golang中g(shù)oroutine執(zhí)行速度的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-05-05提升編程技能:學(xué)習(xí)如何在Go語言中正確格式化時間
想知道如何在Go語言中輕松地格式化時間嗎?別再浪費(fèi)時間了!本文將帶你快速入門,讓你的代碼更加優(yōu)雅高效,快來學(xué)習(xí)吧!2024-01-01淺談Go連接池的設(shè)計(jì)與實(shí)現(xiàn)
本文主要介紹了淺談Go連接池的設(shè)計(jì)與實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Go實(shí)現(xiàn)基于RSA加密算法的接口鑒權(quán)
這篇文章主要介紹了Go實(shí)現(xiàn)基于RSA加密算法的接口鑒權(quán),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06