亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

源碼分析Go語(yǔ)言中g(shù)ofmt實(shí)現(xiàn)原理

 更新時(shí)間:2024年03月10日 08:56:04   作者:偷天神貓  
gofmt?是?Go?語(yǔ)言官方提供的一個(gè)工具,用于自動(dòng)格式化?Go?源代碼,使其符合?Go?語(yǔ)言的官方編碼風(fēng)格,本文給大家源碼詳細(xì)分析了Go語(yǔ)言中g(shù)ofmt實(shí)現(xiàn)原理,并通過(guò)圖文和代碼講解的非常詳細(xì),需要的朋友可以參考下

前言

gofmt 是 Go 語(yǔ)言官方提供的一個(gè)工具,用于自動(dòng)格式化 Go 源代碼,使其符合 Go 語(yǔ)言的官方編碼風(fēng)格。其實(shí)現(xiàn)原理大致可以分為以下幾個(gè)步驟:

  • 讀取和解析源代碼gofmt 首先讀取指定的 Go 源代碼文件或目錄。對(duì)于目錄,它會(huì)遞歸地查找所有的 .go 文件。然后,它使用 Go 語(yǔ)言的解析庫(kù)(如 go/parsergo/token)來(lái)解析源代碼,并構(gòu)建出一個(gè)抽象語(yǔ)法樹(AST)。

  • 遍歷抽象語(yǔ)法樹(AST):一旦源代碼被解析成 AST,gofmt 會(huì)遍歷這棵樹。這個(gè)過(guò)程中,它會(huì)識(shí)別和修改樹中的節(jié)點(diǎn),以便按照 Go 語(yǔ)言的格式化規(guī)則調(diào)整代碼的格式。這包括調(diào)整縮進(jìn)、空格、換行符等。

  • 格式化規(guī)則應(yīng)用gofmt 通過(guò)內(nèi)置的一系列格式化規(guī)則來(lái)調(diào)整和優(yōu)化代碼的布局。這些規(guī)則涵蓋了從基本的縮進(jìn)和行長(zhǎng)度控制到更復(fù)雜的代碼結(jié)構(gòu)對(duì)齊等方面。gofmt 的目標(biāo)是保持代碼風(fēng)格的一致性,以提高代碼的可讀性和維護(hù)性。

  • 生成和輸出格式化后的代碼:遍歷和修改 AST 后,gofmt 將根據(jù)修改后的 AST 生成格式化后的源代碼。這一步是通過(guò) AST 的序列化完成的,即將 AST 轉(zhuǎn)換回 Go 語(yǔ)言源代碼的文本表示。

  • 覆蓋原始文件或輸出到標(biāo)準(zhǔn)輸出:默認(rèn)情況下,gofmt 會(huì)將格式化后的代碼輸出到標(biāo)準(zhǔn)輸出(屏幕)。如果指定了 -w(write)選項(xiàng),gofmt 會(huì)將格式化后的代碼寫回原始文件,替換掉原來(lái)的內(nèi)容。還有其他選項(xiàng)可以用來(lái)控制輸出,例如 -d 選項(xiàng)會(huì)顯示格式化前后的差異。

gofmt 的設(shè)計(jì)哲學(xué)是“無(wú)需配置”,旨在為 Go 社區(qū)提供一個(gè)統(tǒng)一的代碼格式標(biāo)準(zhǔn)。通過(guò)自動(dòng)格式化代碼,gofmt 減少了代碼審查中關(guān)于風(fēng)格的討論,使開發(fā)者可以更專注于代碼的邏輯和功能上。

源碼分析

以目前最新的go穩(wěn)定版1.22.1為例,gofmt庫(kù)的源碼文件如下所示,共計(jì)8個(gè)文件和一個(gè)測(cè)試數(shù)據(jù)文件夾:

調(diào)用流程

我們先來(lái)預(yù)覽下gofmt庫(kù)的函數(shù)調(diào)用流程圖

源碼處窺

下面我們通過(guò)閱讀源碼直觀的感受下gofmt究竟是怎么工作的。

gofmt.go則是gofmt庫(kù)的核心,也是它的入口,所以我們從這個(gè)文件開始看起。 在該文件開頭可以看到gofmtflag定義:

入口函數(shù)在358行,在370行又調(diào)用了真正負(fù)責(zé)格式化處理流程的gofmtMain

gofmtMain中的關(guān)鍵函數(shù)是processFile,不過(guò)它的運(yùn)行需要依賴當(dāng)前文件中的initParserMode()以及rewrite.go中的initRewrite()

這兩個(gè)init函數(shù)我們按下不表,因?yàn)樗麄儾皇俏覀儽疚牡闹攸c(diǎn),有興趣的同學(xué)可以按圖索驥自行查看其實(shí)現(xiàn)。我們回到processFile函數(shù),繼續(xù)查看其核心執(zhí)行邏輯。

processFile讀取文件后,調(diào)用internal.go中的parse函數(shù)進(jìn)行語(yǔ)法解析,然后依賴simplify.go中的simplify生成抽象語(yǔ)法樹。在這兩個(gè)文件中我們終于看到【大致流程】中提到的go/parser 和 go/token兩個(gè)關(guān)鍵庫(kù),其實(shí)另外一個(gè)go/ast也很重要,它主要負(fù)責(zé)抽象語(yǔ)法樹相關(guān)的工作。

gofmt.go重點(diǎn)依賴的庫(kù)

go/token

功能作用go/token 包定義了 Go 語(yǔ)言的詞法標(biāo)記(tokens)及其位置信息(文件位置)。它為語(yǔ)法分析和 AST 構(gòu)建提供基礎(chǔ)設(shè)施,包括標(biāo)識(shí)符、關(guān)鍵字、文本位置等的表示。

應(yīng)用場(chǎng)景:這個(gè)包通常與 go/parsergo/ast 結(jié)合使用,用于實(shí)現(xiàn)對(duì) Go 代碼的詞法分析和位置追蹤。應(yīng)用場(chǎng)景包括編譯器開發(fā)、代碼分析工具、代碼格式化工具等,任何需要精確控制和了解代碼結(jié)構(gòu)及元素位置的場(chǎng)合。

go/ast

功能作用go/ast 包提供了構(gòu)造和操作 Go 語(yǔ)言源代碼的抽象語(yǔ)法樹(AST)的能力。AST 是源代碼的樹狀結(jié)構(gòu)表示,每個(gè)節(jié)點(diǎn)代表代碼的一部分,比如表達(dá)式、語(yǔ)句或聲明。

應(yīng)用場(chǎng)景:它主要用于開發(fā)需要分析、檢查、修改或生成 Go 代碼的工具,如靜態(tài)分析工具、代碼格式化程序、重構(gòu)工具等。

go/parser

功能作用go/parser 包負(fù)責(zé)解析 Go 源代碼文件,將其轉(zhuǎn)化為 AST(抽象語(yǔ)法樹)。它提供了解析 Go 代碼的接口和功能,可以從文件、字符串等輸入源解析 Go 代碼。

應(yīng)用場(chǎng)景:用于需要讀取和解析 Go 源代碼的場(chǎng)合,比如編寫代碼編輯器的語(yǔ)法高亮功能、靜態(tài)代碼分析、文檔生成工具等。

format函數(shù)

通過(guò)閱讀processFile源碼我們了解到,當(dāng)源碼被解析為AST后,gofmt執(zhí)行了一個(gè)format函數(shù)來(lái)生成格式化后的結(jié)果。

res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
if err != nil {
    return err
}

internal.go中放置了fotmat函數(shù)的實(shí)現(xiàn)。

其實(shí)format函數(shù)才是gofmt真正發(fā)揮威力的地方,畢竟讀文件、解析文件、生成語(yǔ)法樹、寫文件這些工作都是調(diào)用的別的庫(kù),而format才是它自己實(shí)打?qū)崒?shí)現(xiàn)的,我添加了詳細(xì)的中文注釋。

// format函數(shù)用于格式化給定的包文件,該文件最初從src獲得,
// 并基于原始源代碼通過(guò)sourceAdj和indentAdj進(jìn)行結(jié)果調(diào)整。
func format(
    fset *token.FileSet, // 文件集,用于存儲(chǔ)和處理文件的位置信息
    file *ast.File, // 抽象語(yǔ)法樹表示的文件
    sourceAdj func(src []byte, indent int) []byte, // 調(diào)整源代碼的函數(shù)
    indentAdj int, // 縮進(jìn)調(diào)整量
    src []byte, // 原始源代碼
    cfg printer.Config, // 打印配置,控制輸出格式
) ([]byte, error) {
    if sourceAdj == nil {
        // 如果沒(méi)有提供sourceAdj函數(shù),則處理完整的源文件
        var buf bytes.Buffer // 創(chuàng)建一個(gè)緩沖區(qū)來(lái)存儲(chǔ)格式化后的代碼
        err := cfg.Fprint(&buf, fset, file) // 將格式化后的文件輸出到緩沖區(qū)
        if err != nil {
            return nil, err // 如果發(fā)生錯(cuò)誤,返回錯(cuò)誤
        }
        return buf.Bytes(), nil // 返回緩沖區(qū)中的字節(jié)切片
    }

    // 處理部分源文件的情況
    // 確定并添加前導(dǎo)空格
    i, j := 0, 0
    for j < len(src) && isSpace(src[j]) { // 遍歷源代碼前面的空白字符
        if src[j] == '\n' {
            i = j + 1 // 更新最后一行前導(dǎo)空間的字節(jié)偏移量
        }
        j++
    }
    var res []byte // 用于存儲(chǔ)最終結(jié)果的切片
    res = append(res, src[:i]...) // 將前導(dǎo)空格添加到結(jié)果中

    // 確定并添加第一行代碼的縮進(jìn)
    // 除非沒(méi)有制表符,否則忽略空格,空格視為一個(gè)制表符
    indent := 0
    hasSpace := false
    for _, b := range src[i:j] {
        switch b {
        case ' ':
            hasSpace = true
        case '\t':
            indent++ // 統(tǒng)計(jì)制表符數(shù)量作為縮進(jìn)量
        }
    }
    if indent == 0 && hasSpace {
        indent = 1 // 如果沒(méi)有制表符但有空格,則將縮進(jìn)設(shè)置為1
    }
    for i := 0; i < indent; i++ {
        res = append(res, '\t') // 將縮進(jìn)添加到結(jié)果中
    }

    // 格式化源代碼
    cfg.Indent = indent + indentAdj // 設(shè)置縮進(jìn)配置
    var buf bytes.Buffer
    err := cfg.Fprint(&buf, fset, file) // 將格式化的代碼寫入緩沖區(qū),不包括前導(dǎo)和尾隨空格
    if err != nil {
        return nil, err
    }
    out := sourceAdj(buf.Bytes(), cfg.Indent) // 調(diào)整格式化后的代碼

    // 如果調(diào)整后的輸出為空,則源代碼除了空白字符外是空的
    // 結(jié)果是傳入的源代碼
    if len(out) == 0 {
        return src, nil
    }

    // 否則,將輸出添加到前導(dǎo)空間后面
    res = append(res, out...)

    // 確定并添加尾隨空格
    i = len(src)
    for i > 0 && isSpace(src[i-1]) {
        i-- // 回溯以確定尾隨空格的開始位置
    }
    return append(res, src[i:]...), nil // 將尾隨空格添加到結(jié)果中并返回
}

結(jié)語(yǔ)

至此我們得已窺到gofmt的廬山真面目。

對(duì)于想要了解gofmt大體實(shí)現(xiàn)流程的同學(xué)來(lái)說(shuō),走到這里實(shí)屬不易,您已經(jīng)超越了只是知道怎么使用gofmt的段位。

不過(guò)對(duì)于想要了解更多細(xì)節(jié)的同學(xué)來(lái)說(shuō),越往下挖疑問(wèn)反而越多,比如:

  • 用于調(diào)整源代碼的函數(shù)sourceAdj究竟是如何實(shí)現(xiàn)的?
  • parser、ast、token這些包內(nèi)部又是如何實(shí)現(xiàn)的?他們應(yīng)用了哪些基礎(chǔ)算法知識(shí)?

限于篇幅,我就不在本文作答以上問(wèn)題了,我覺得能給大家?guī)?lái)思考和啟發(fā)就是最好的分享。后續(xù)如果大家對(duì)于源碼解析很感興趣,我再作進(jìn)一步的細(xì)節(jié)展開。

以上就是源碼分析Go語(yǔ)言中g(shù)ofmt實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于Go gofmt實(shí)現(xiàn)原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang?JSON技巧小結(jié)

    golang?JSON技巧小結(jié)

    本文介紹了在Go語(yǔ)言中使用JSON時(shí)的一些小技巧,包括如何將字符串轉(zhuǎn)換為數(shù)字、臨時(shí)忽略空字段、臨時(shí)添加額外字段、粘合兩個(gè)struct、改名struct字段、支持不同類型的數(shù)字,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Go語(yǔ)言安裝和GoLand2021最全超詳細(xì)安裝教程

    Go語(yǔ)言安裝和GoLand2021最全超詳細(xì)安裝教程

    Go語(yǔ)言和GoLand的關(guān)系好比于java和idea、python和pycharm,因此我們需要先安裝好Go語(yǔ)言后才能安裝GoLand。它的安裝和java,python的安裝大同小異,好了,下面給大家?guī)?lái)了GoLand2021安裝教程,需要的朋友參考下吧
    2021-08-08
  • Go語(yǔ)言學(xué)習(xí)之函數(shù)的定義與使用詳解

    Go語(yǔ)言學(xué)習(xí)之函數(shù)的定義與使用詳解

    這篇文章主要為大家詳細(xì)介紹Go語(yǔ)言中函數(shù)的定義與使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定幫助,需要的可以參考一下
    2022-04-04
  • Go?Java?算法之字符串解碼示例詳解

    Go?Java?算法之字符串解碼示例詳解

    這篇文章主要為大家介紹了Go?Java?算法之字符串解碼示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go語(yǔ)言中內(nèi)存泄漏的常見案例與解決方法

    Go語(yǔ)言中內(nèi)存泄漏的常見案例與解決方法

    Go雖然是自動(dòng)GC類型的語(yǔ)言,但在編碼過(guò)程中如果不注意,很容易造成內(nèi)存泄漏的問(wèn)題,本文為大家整理了一些內(nèi)存泄漏的常見Case與解決方法,希望對(duì)大家有所幫助
    2024-03-03
  • gRPC的發(fā)布訂閱模式及REST接口和超時(shí)控制

    gRPC的發(fā)布訂閱模式及REST接口和超時(shí)控制

    這篇文章主要為大家介紹了gRPC的發(fā)布訂閱模式及REST接口和超時(shí)控制,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • golang組件swagger生成接口文檔實(shí)踐示例

    golang組件swagger生成接口文檔實(shí)踐示例

    這篇文章主要為大家介紹了golang組件swagger生成接口文檔實(shí)踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • Go 類型轉(zhuǎn)換工具包strconv包的用法

    Go 類型轉(zhuǎn)換工具包strconv包的用法

    Go 語(yǔ)言的?strconv?包提供了用于基本數(shù)據(jù)類型之間轉(zhuǎn)換的函數(shù),本文主要介紹了Go 類型轉(zhuǎn)換工具包strconv包的用法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-05-05
  • 深入了解Go的interface{}底層原理實(shí)現(xiàn)

    深入了解Go的interface{}底層原理實(shí)現(xiàn)

    本文主要介紹了Go的interface{}底層原理實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 使用Go進(jìn)行單元測(cè)試的實(shí)現(xiàn)

    使用Go進(jìn)行單元測(cè)試的實(shí)現(xiàn)

    這篇文章主要介紹了使用Go進(jìn)行單元測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評(píng)論