<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「GoCN酷Go推薦」抽象語(yǔ)法樹(shù)go/ast庫(kù)使用

          共 6588字,需瀏覽 14分鐘

           ·

          2021-10-13 02:52

          推薦背景

          Go語(yǔ)言在編譯過(guò)程中經(jīng)過(guò)詞法分析和語(yǔ)法分析之后,就到了抽象語(yǔ)法樹(shù)的構(gòu)建階段,經(jīng)歷這一階段之后,語(yǔ)句就真正組織成了程序代碼。利用抽象語(yǔ)法樹(shù)解析庫(kù),我們可以完成代碼的自動(dòng)化分析和自動(dòng)化生成,因此通常用于做一些自動(dòng)化的工具,例如wire。

          使用案例

          package?main
          ?
          import?(
          ????"go/ast"
          ????"go/parser"
          ????"go/token"
          )
          var??src?=?`
          ?package?main
          ?import?"fmt"
          ?func?main()?{
          ?????fmt.Println("Hello,?World!")
          ?}
          `
          ?
          func?main()?{
          ????fset?:=?token.NewFileSet()?//?positions?are?relative?to?fset
          ????f,?err?:=?parser.ParseFile(fset,?"",?src,?0)
          ????if?err?!=?nil?{
          ????????panic(err)
          ????}
          ?
          ????//?Print?the?AST.
          ????ast.Print(fset,?f)
          }
          • fset是文件字符集用于定位ast.Node的文件位置
          • parser.ParserFile的第二參數(shù)為文件名,第三參數(shù)為字符串,前者是待解析的文件路徑,后者為待解析的字符串。前者優(yōu)先級(jí)高于后者。第四參數(shù)為解析模式(可以使用"|"來(lái)結(jié)合多種解析模式)

          運(yùn)行結(jié)果如下

               0  *ast.File {
          1 . Package: 2:1
          2 . Name: *ast.Ident {
          3 . . NamePos: 2:9
          4 . . Name: "main"
          5 . }
          6 . Decls: []ast.Decl (len = 2) {
          7 . . 0: *ast.GenDecl {
          8 . . . TokPos: 4:1
          9 . . . Tok: import
          10 . . . Lparen: -
          11 . . . Specs: []ast.Spec (len = 1) {
          12 . . . . 0: *ast.ImportSpec {
          13 . . . . . Path: *ast.BasicLit {
          14 . . . . . . ValuePos: 4:8
          15 . . . . . . Kind: STRING
          16 . . . . . . Value: "\"fmt\""
          17 . . . . . }
          18 . . . . . EndPos: -
          19 . . . . }
          20 . . . }
          21 . . . Rparen: -
          22 . . }
          23 . . 1: *ast.FuncDecl {
          24 . . . Name: *ast.Ident {
          25 . . . . NamePos: 6:6
          26 . . . . Name: "main"
          27 . . . . Obj: *ast.Object {
          28 . . . . . Kind: func
          29 . . . . . Name: "main"
          30 . . . . . Decl: *(obj @ 23)
          31 . . . . }
          32 . . . }
          33 . . . Type: *ast.FuncType {
          34 . . . . Func: 6:1
          35 . . . . Params: *ast.FieldList {
          36 . . . . . Opening: 6:10
          37 . . . . . Closing: 6:11
          38 . . . . }
          39 . . . }
          40 . . . Body: *ast.BlockStmt {
          41 . . . . Lbrace: 6:13
          42 . . . . List: []ast.Stmt (len = 1) {
          43 . . . . . 0: *ast.ExprStmt {
          44 . . . . . . X: *ast.CallExpr {
          45 . . . . . . . Fun: *ast.SelectorExpr {
          46 . . . . . . . . X: *ast.Ident {
          47 . . . . . . . . . NamePos: 7:5
          48 . . . . . . . . . Name: "fmt"
          49 . . . . . . . . }
          50 . . . . . . . . Sel: *ast.Ident {
          51 . . . . . . . . . NamePos: 7:9
          52 . . . . . . . . . Name: "Println"
          53 . . . . . . . . }
          54 . . . . . . . }
          55 . . . . . . . Lparen: 7:16
          56 . . . . . . . Args: []ast.Expr (len = 1) {
          57 . . . . . . . . 0: *ast.BasicLit {
          58 . . . . . . . . . ValuePos: 7:17
          59 . . . . . . . . . Kind: STRING
          60 . . . . . . . . . Value: "\"Hello World!\""
          61 . . . . . . . . }
          62 . . . . . . . }
          63 . . . . . . . Ellipsis: -
          64 . . . . . . . Rparen: 7:31
          65 . . . . . . }
          66 . . . . . }
          67 . . . . }
          68 . . . . Rbrace: 8:1
          69 . . . }
          70 . . }
          71 . }
          72 . Scope: *ast.Scope {
          74 . . Objects: map[string]*ast.Object (len = 1) {
          74 . . . "main": *(obj @ 27)
          75 . . }
          76 . }
          77 . Imports: []*ast.ImportSpec (len = 1) {
          78 . . 0: *(obj @ 12)
          79 . }
          80 . Unresolved: []*ast.Ident (len = 1) {
          81 . . 0: *(obj @ 46)
          82 . }
          83 }

          ast.File內(nèi)容

          type?File?struct?{
          ?Doc????????*CommentGroup???//?associated?documentation;?or?nil
          ?Package????token.Pos???????//?position?of?"package"?keyword
          ?Name???????*Ident??????????//?package?name
          ?Decls??????[]Decl??????????//?top-level?declarations;?or?nil
          ?Scope??????*Scope??????????//?package?scope?(this?file?only)
          ?Imports????[]*ImportSpec???//?imports?in?this?file
          ?Unresolved?[]*Ident????????//?unresolved?identifiers?in?this?file
          ?Comments???[]*CommentGroup?//?list?of?all?comments?in?the?source?file
          }

          其中Decls成員表示的就是文件中的頂級(jí)聲明。接下來(lái)我們主要是關(guān)注它的內(nèi)容。

          包聲明

          ??Package:?2:1
          ??Name:?*ast.Ident?{
          ??.??NamePos:?2:9
          ??.??Name:?"main"
          ??}

          對(duì)應(yīng)文件中的 "package main"語(yǔ)句,記錄了語(yǔ)句的位置以及包名(main)字符串的位置信息。

          引入聲明

          ?0:?*ast.GenDecl?{
          ?.??TokPos:?4:1
          ?.??Tok:?import
          ?.??Lparen:?-
          ?.??Specs:?[]ast.Spec?(len?=?1)?{
          ?.??.??0:?*ast.ImportSpec?{
          ?.??.??.??Path:?*ast.BasicLit?{
          ?.??.??.??.??ValuePos:?4:8
          ?.??.??.??.??Kind:?STRING
          ?.??.??.??.??Value:?"\"fmt\""
          ?.??.??.??}
          ?.??.??.??EndPos:?-
          ?.??.??}
          ?.??}
          ?.??Rparen:?-
          ?}

          在ast.GenDecl中記錄了import語(yǔ)句的位置信息。Specs為一個(gè)ast.Spec的數(shù)組,記錄了每一個(gè)import的包名及位置信息。

          函數(shù)聲明

          ?*ast.FuncDecl{
          ??Name:*ast.Index{...}?
          ??Type:*ast.FuncType{
          ???Params:*ast.FieldList{...}
          ???Results:?*ast.FieldList{...}
          ??},
          ??Body:*ast.BlockStmt{...}
          ?}
          • ast.FuncDecl標(biāo)明定義的是一個(gè)函數(shù)。
          • Name記錄函數(shù)名
          • Type是函數(shù)類(lèi)型,其中Params表示參數(shù)信息,這里為空;Results表示返回值信息,這里也為空。
          • Body則為函數(shù)體信息。

          表達(dá)式

          List:?[]ast.Stmt?(len?=?1)?{
          .??0:?*ast.ExprStmt?{
          .??.??X:?*ast.CallExpr?{
          .??.??.??Fun:?*ast.SelectorExpr?{
          .??.??.??.??X:?*ast.Ident?{
          .??.??.??.??.??NamePos:?7:5
          .??.??.??.??.??Name:?"fmt"
          .??.??.??.??}
          .??.??.??.??Sel:?*ast.Ident?{
          .??.??.??.??.??NamePos:?7:9
          .??.??.??.??.??Name:?"Println"
          .??.??.??.??}
          .??.??.??}
          .??.??.??Lparen:?7:16
          .??.??.??Args:?[]ast.Expr?(len?=?1
          .??.??.??.??0:?*ast.BasicLit?{
          .??.??.??.??.??ValuePos:?7:17
          .??.??.??.??.??Kind:?STRING
          .??.??.??.??.??Value:?"\"Hello?Wor
          .??.??.??.??}
          .??.??.??}
          .??.??.??Ellipsis:?-
          .??.??.??Rparen:?7:31
          .??.??}
          .??}
          }
          Rbrace:?8:1

          函數(shù)體中的表達(dá)式描述了函數(shù)的內(nèi)容,例子中的fmt.Println("hello world")。

          ast.CallExpr表示函數(shù)調(diào)用,其中SelectorExpr描述了調(diào)用函數(shù)的包名及函數(shù)名,Args則描述了參數(shù)信息。

          遍歷AST樹(shù)

          ast庫(kù)提供了可以深度優(yōu)先遍歷AST的方法:func Inspect(node Node, f func(Node) bool)。其中node為根節(jié)點(diǎn),f為處理節(jié)點(diǎn)的方法。

          ast.Inspect(f,?func(n?ast.Node)?bool?{
          ??var?s?string
          ??switch?x?:=?n.(type)?{
          ??case?*ast.BasicLit:
          ???s?=?x.Value
          ??case?*ast.Ident:
          ???s?=?x.Name
          ??}
          ??if?s?!=?""?{
          ???fmt.Printf("%s:\t%s\n",?fset.Position(n.Pos()),?s)
          ??}
          ??return?true
          ?})

          此函數(shù)遍歷f(ast.File)節(jié)點(diǎn)打印所有的標(biāo)識(shí)符和文字。

          更多相關(guān)類(lèi)型,可以通過(guò)命令 go doc ast |grep "^type .* struct"查看。

          進(jìn)階使用

          利用AST對(duì)go文件進(jìn)行分析,我們可以實(shí)現(xiàn)代碼的自動(dòng)生成,其中包括以下幾個(gè)常見(jiàn)使用領(lǐng)域:

          1. 代碼注入: wire使用AST實(shí)現(xiàn)構(gòu)造函數(shù)代碼生成。
          2. DeepCopy: 結(jié)合AST生成結(jié)構(gòu)體的深拷貝函數(shù)代碼
          3. 對(duì)象賦值: 在領(lǐng)域編程中,常常需要在不同的領(lǐng)域?qū)ο笾羞M(jìn)行數(shù)據(jù)轉(zhuǎn)換,利用AST的解析結(jié)果,可以自動(dòng)生成指定領(lǐng)域?qū)ο箝g的轉(zhuǎn)換函數(shù)文件。

          小結(jié)

          抽象語(yǔ)法樹(shù)的生成屬于程序編譯流程中的一員,利用AST及其相關(guān)庫(kù)提供到方法,我們可以很方便的解析一個(gè)go文件,把文件內(nèi)容結(jié)構(gòu)化,以便做進(jìn)一步的分析和使用。AST廣泛應(yīng)用于代碼自動(dòng)生成的功能中,例如go generate命令,wire工具等等。其中不少企業(yè)也會(huì)在開(kāi)源庫(kù)中,使用Comment的特殊格式,來(lái)自定義框架代碼的自動(dòng)生成命令。

          參考資料

          https://www.cnblogs.com/double12gzh/p/13632267.html https://cloud.tencent.com/developer/section/1142075

          實(shí)驗(yàn)工具

          https://astexplorer.net

          https://greyireland.gitee.io/goast-viewer


          《酷Go推薦》招募:


          各位Gopher同學(xué),最近我們社區(qū)打算推出一個(gè)類(lèi)似GoCN每日新聞的新欄目《酷Go推薦》,主要是每周推薦一個(gè)庫(kù)或者好的項(xiàng)目,然后寫(xiě)一點(diǎn)這個(gè)庫(kù)使用方法或者優(yōu)點(diǎn)之類(lèi)的,這樣可以真正的幫助到大家能夠?qū)W習(xí)到

          新的庫(kù),并且知道怎么用。


          大概規(guī)則和每日新聞?lì)愃疲绻麍?bào)名人多的話每個(gè)人一個(gè)月輪到一次,歡迎大家報(bào)名!戳「閱讀原文」,即可報(bào)名


          掃碼也可以加入 GoCN 的大家族喲~




          瀏覽 82
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91黑人大屌啪啪 | 欧美黄色三级视频 | 中文字幕在线不卡视频 | 性爱小说视频 | 国产精品久久久成人 |