以 Golang 為例詳解 AST 抽象語法樹
前言
各位同行有沒有想過一件事,一個程式檔案,比如
hello.go
是如何被編譯器理解的,平常在編寫程式時,IDE 又是如何提供程式碼提示的。在這奧妙無窮的背後,
AST(Abstract Syntax Tree)
抽象語法樹功不可沒,他站在每一行程式的身後,默默無聞的工作,為繁榮的網際網路世界立下了汗馬功勞。
AST 抽象語法樹
AST 使用樹狀結構來表達程式語言的結構,樹中的每一個節點都表示原始碼中的一個結構。聽到這或許你的心裡會咯噔一下,其實說通俗一點,在原始碼解析後會得到一串資料,這個資料自然的呈現樹狀結構,它被稱之為
CST(Concrete Syntax Tree)
具體語法樹,在 CST 的基礎上保留核心結構。忽略一些不重要的結構,比如標點符號,空白符,括號等,就得到了 AST。
如何生成 AST
生成 AST 大概需要兩個步驟,詞法分析
lexical analysis
和語法分析
syntactic analysis
。
詞法分析 lexical analysis
lexical analysis 簡稱 lexer ,它表示字串序列,也就是我們的原始碼轉化為 token 的過程,進行詞法分析的工具叫做詞法分析器(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") } `) // 初始化標記 var s scanner.Scanner fset := token.NewFileSet() file := fset.AddFile("", fset.Base(), len(src)) s.Init(file, src, nil, scanner.ScanComments) // Scan 進行掃碼並列印出結果 for { pos, tok, lit := s.Scan() if tok == token.EOF { break } fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit) } }
列印的結果我們接著往下看。
標記 token
標記(token) 是詞法分析後留下的產物,是構成原始碼的最小單位,但是這些 token 之間沒有任何邏輯關係。以上述程式碼為例:
func demo() { fmt.Println("When you are old and gray and full of sleep") }
經過詞法分析後,會得到:
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 型別就是一個字面量,例如整型,字串型別等,那麼它的值就是相對應的值,比如上表的
STRING
;如果 token 是 Go 的關鍵詞,那麼它的值就是關鍵詞,比如上表的
fun
;對於分號,它的值則是換行符;其他 token 類要麼是不合法的,如果是合法的,則值為空字串,比如上表的
{
。
語法分析 syntactic analysis
不具備邏輯關係的 token 經過語法分析(syntactic analysis,也叫 parsing)就可以得到具有邏輯關係的 CST 具體語法樹,然後對 CST 進行分析提煉即可得到 AST 抽象語法樹。完成語法分析的工具叫做語法分析器(parser)。Go 語言的
go/parser
提供語法分析。
func ParserDemo() { src := ` package main ` fset := token.NewFileSet() // 如果 src 為 nil,則使用第二個引數,它可以是一個 .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 }
它包含了原始碼的結構資訊,看起來像一個 JSON。
總結
原始碼經過 詞法分析後得到 token(標記),token 經過 語法分析得到 CST 具體語法樹,在 CST 上建立 AST 抽象語法樹。 來個圖圖或許更直觀:
Go 的抽象語法樹
這裡我們以一個具體的例子來看:從 go 程式碼中提取所有結構體的名稱。
// 原始碼 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 }
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70036623/viewspace-3004214/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- AST抽象語法樹AST抽象語法樹
- Javascrip—AST抽象語法樹(8)JavaAST抽象語法樹
- babel外掛入門-AST(抽象語法樹)BabelAST抽象語法樹
- 高階前端基礎-JavaScript抽象語法樹AST前端JavaScript抽象語法樹AST
- 前端碼農之蛻變 — AST(抽象語法樹)前端AST抽象語法樹
- 從Babel開始認識AST抽象語法樹BabelAST抽象語法樹
- [原始碼-webpack01-前置知識] AST抽象語法樹原始碼WebAST抽象語法樹
- 《8分鐘學會 Vue.js 原理》:一、template 字串編譯為抽象語法樹 ASTVue.js字串編譯抽象語法樹AST
- Go 抽象語法樹Go抽象語法樹
- 通過開發 Babel 外掛來理解什麼是抽象語法樹(AST)Babel抽象語法樹AST
- JavaScript的工作原理:解析、抽象語法樹(AST)+ 提升編譯速度5個技巧JavaScript抽象語法樹AST編譯
- javascript編寫一個簡單的編譯器(理解抽象語法樹AST)JavaScript編譯抽象語法樹AST
- 理解Babel是如何編譯JS程式碼的及理解抽象語法樹(AST)Babel編譯JS抽象語法樹AST
- Javascript與抽象語法樹JavaScript抽象語法樹
- 抽象語法樹 Abstract syntax tree抽象語法樹
- AST語法結構樹初學者完整教程AST
- 「譯」什麼是抽象語法樹抽象語法樹
- 前端進階之 JS 抽象語法樹前端JS抽象語法樹
- 前端進階之 Javascript 抽象語法樹前端JavaScript抽象語法樹
- Abstract Syntax Tree 抽象語法樹簡介抽象語法樹
- 抽象語法樹在 JavaScript 中的應用抽象語法樹JavaScript
- 13 個示例快速入門 JS 抽象語法樹JS抽象語法樹
- babel 修改抽象語法樹——入門與實踐Babel抽象語法樹
- 一看就懂的JS抽象語法樹JS抽象語法樹
- 以美顏sdk為例,詳解sdk接入流程
- 【詳解】以 ASP.NET Core 為例的CI/CDASP.NET
- Go編譯原理系列5(抽象語法樹構建)Go編譯原理抽象語法樹
- SQL抽象語法樹及改寫場景應用SQL抽象語法樹
- SQL 抽象語法樹及改寫場景應用SQL抽象語法樹
- golang設計模式-以kubernetes原始碼為例Golang設計模式原始碼
- smali語法詳解
- Markdown語法詳解
- 語法樹
- 詳解Dockerfile基本語法Docker
- Hive sql語法詳解HiveSQL
- Java語法糖詳解Java
- jQuery 的語法詳解jQuery
- sed命令語法詳解