散歩に行く
46652 ワード
This article is originally posted here.
あなたが囲碁に興味があるとき、あなたは何を参照しますか?書類?ソースコード?
ドキュメントを読むと、抽象的に理解できますが、APIが互いに関連しているかどうかはわかりません.
言うまでもなく、全体のソースコードを読んでください、そして、あなたはそれを完全に見ます、しかし、あなたは全部のものを読むことを試みることから疲れていなければなりません.
したがって、この記事はそれらの中間にあることを目指します.それを簡単にして、我々が通常書く試みコードが内部で表される方法を理解するために、ASTを通して散歩してみましょう.
ソースコードを解析する方法を詳しく調べ、ASTが構築された後に説明を開始します.
コードをASTに変換する方法について興味があるならDigging deeper into the analysis of Go-code .
始めましょう!
インターフェース
まず、ASTノードを表すインターフェースを簡単に説明します.
すべてのASTノードは
ast.Node
AST内の位置を返すインターフェイス.追加では、実装する3つのメインインタフェースがあります
ast.Node
:ast.Expr — 式と型ノードの表現
ast.Stmt — 表明文ノード
ast.Decl — 宣言ノードの表現
すべてのノードが満足する定義から見ることができます
ast.Node
インタフェースast/ast.go
// All node types implement the Node interface.
type Node interface {
Pos() token.Pos // position of first character belonging to the node
End() token.Pos // position of first character immediately after the node
}
// All expression nodes implement the Expr interface.
type Expr interface {
Node
exprNode()
}
// All statement nodes implement the Stmt interface.
type Stmt interface {
Node
stmtNode()
}
// All declaration nodes implement the Decl interface.
type Decl interface {
Node
declNode()
}
ウォーキングから始める
歩き始めましょう!ASTに変換するファイルを見てください.
package hello
import "fmt"
func greet() {
fmt.Println("Hello, World")
}
何もファンシー-過度にシンプルなハロー、世界プログラム.この上に構築されたASTは次のとおりです.ASTファイル
*ast.File {
. Package: dummy.go:1:1
. Name: *ast.Ident {
. . NamePos: dummy.go:1:9
. . Name: "hello"
. }
. Decls: []ast.Decl (len = 2) {
. . 0: *ast.GenDecl {
. . . TokPos: dummy.go:3:1
. . . Tok: import
. . . Lparen: -
. . . Specs: []ast.Spec (len = 1) {
. . . . 0: *ast.ImportSpec {
. . . . . Path: *ast.BasicLit {
. . . . . . ValuePos: dummy.go:3:8
. . . . . . Kind: STRING
. . . . . . Value: "\"fmt\""
. . . . . }
. . . . . EndPos: -
. . . . }
. . . }
. . . Rparen: -
. . }
. . 1: *ast.FuncDecl {
. . . Name: *ast.Ident {
. . . . NamePos: dummy.go:5:6
. . . . Name: "greet"
. . . . Obj: *ast.Object {
. . . . . Kind: func
. . . . . Name: "greet"
. . . . . Decl: *(obj @ 23)
. . . . }
. . . }
. . . Type: *ast.FuncType {
. . . . Func: dummy.go:5:1
. . . . Params: *ast.FieldList {
. . . . . Opening: dummy.go:5:11
. . . . . Closing: dummy.go:5:12
. . . . }
. . . }
. . . Body: *ast.BlockStmt {
. . . . Lbrace: dummy.go:5:14
. . . . List: []ast.Stmt (len = 1) {
. . . . . 0: *ast.ExprStmt {
. . . . . . X: *ast.CallExpr {
. . . . . . . Fun: *ast.SelectorExpr {
. . . . . . . . X: *ast.Ident {
. . . . . . . . . NamePos: dummy.go:6:2
. . . . . . . . . Name: "fmt"
. . . . . . . . }
. . . . . . . . Sel: *ast.Ident {
. . . . . . . . . NamePos: dummy.go:6:6
. . . . . . . . . Name: "Println"
. . . . . . . . }
. . . . . . . }
. . . . . . . Lparen: dummy.go:6:13
. . . . . . . Args: []ast.Expr (len = 1) {
. . . . . . . . 0: *ast.BasicLit {
. . . . . . . . . ValuePos: dummy.go:6:14
. . . . . . . . . Kind: STRING
. . . . . . . . . Value: "\"Hello, World\""
. . . . . . . . }
. . . . . . . }
. . . . . . . Ellipsis: -
. . . . . . . Rparen: dummy.go:6:28
. . . . . . }
. . . . . }
. . . . }
. . . . Rbrace: dummy.go:7:1
. . . }
. . }
. }
. Scope: *ast.Scope {
. . Objects: map[string]*ast.Object (len = 1) {
. . . "greet": *(obj @ 27)
. . }
. }
. Imports: []*ast.ImportSpec (len = 1) {
. . 0: *(obj @ 12)
. }
. Unresolved: []*ast.Ident (len = 1) {
. . 0: *(obj @ 46)
. }
}
ハウツー歩き方
我々がしなければならないすべては、このASTノードを深さの最初の順序で横断することです.
呼び出しによって各ノードを一つずつ印刷しましょう
ast.Inspect()
再帰的に.また、直接ASTを印刷して、一般的に読みにくいものを見ます.
それを防ぐために、我々は使用するつもりです
ast.Print
, ASTの人間読書のための強力なAPIpackage main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "dummy.go", src, parser.ParseComments)
ast.Inspect(f, func(n ast.Node) bool {
// Called recursively.
ast.Print(fset, n)
return true
})
}
var src = `package hello
import "fmt"
func greet() {
fmt.Println("Hello, World")
}
`
ASTファイル
訪問する最初のノードは
*ast.File
, これはすべてのASTノードのルートです.それは
ast.Node
インターフェイス.ast.File
パッケージ名、インポート宣言、および子ノードとしての関数宣言を参照します.正確に言えば
Comments
といっても、シンプルのために省略しましょう.パッケージ名から始めましょう.
( NIL値のフィールドは省略されます.the document 各ノード型のフィールドの完全なリストについては
パッケージ名
ASTident
*ast.Ident {
. NamePos: dummy.go:1:9
. Name: "hello"
}
パッケージ名はASTノードタイプ *ast.Ident
, を実装するast.Expr
インターフェイス.すべての識別子はこの構造で表されます.これは主にファイル名内の名前とソース位置が含まれます.
上のコードから、パッケージ名が
hello
の値はdummy.go
.我々はこのノードに深く潜ることはできません
*ast.File
.インポート宣言
ASTgendecl
*ast.GenDecl {
. TokPos: dummy.go:3:1
. Tok: import
. Lparen: -
. Specs: []ast.Spec (len = 1) {
. . 0: *ast.ImportSpec {/* Omission */}
. }
. Rparen: -
}
インポート宣言はASTノードタイプで表されます *ast.GenDecl
, を実装するast.Decl
インターフェイス.ast.GenDecl
関数以外のすべての宣言を表しますつまり、import、const、var、typeです.Tok
字句トークンを表します.これは、宣言の内容を指定します(importまたはconstまたはtypeまたはvar).このASTノードは、インポート宣言が2007年に3行目にあることを伝えます
dummy.go
.会いましょう
ast.GenDecl
深さ優先順位.見る*ast.ImportSpec
, 次のノード.AST輸入
*ast.ImportSpec {
. Path: *ast.BasicLit {/* Omission */}
. EndPos: -
}
安 ast.ImportSpec
ノードは単一のインポート宣言に対応します.これは
ast.Spec
インターフェイス.訪問
Path
インポートパスについてより理解することができます.さあ行きましょう.ASTバスライト
*ast.BasicLit {
. ValuePos: dummy.go:3:8
. Kind: STRING
. Value: "\"fmt\""
}
安 ast.BasicLit
ノードは基本型のリテラルを表します.これは
ast.Expr
インターフェイス.これはトークンの種類と
token.INT
, token.FLOAT
, token.IMAG
, token.CHAR
, or token.STRING
を使用できます.から
ast.ImportSpec
and ast.BasicLit
, 「FMT」と呼ばれるパッケージを輸入しているのがわかります.我々は、より深くダイビングすることはできません、再びトップレベルに戻りましょう.
関数宣言
ASTファンクション
*ast.FuncDecl {
. Name: *ast.Ident {/* Omission */}
. Type: *ast.FuncType {/* Omission */}
. Body: *ast.BlockStmt {/* Omission */}
}
安 ast.FuncDecl
ノードは関数宣言を表します.それは
ast.Node
インターフェイス.それらを順に見てみましょう
Name
, 関数名を表す.ASTident
*ast.Ident {
. NamePos: dummy.go:5:6
. Name: "greet"
. Obj: *ast.Object {
. . Kind: func
. . Name: "greet"
. . Decl: *(obj @ 0)
. }
}
これが2回目になったので、基本的な説明を省略しましょう.注目すべきは
*ast.Object
.識別子が参照するオブジェクトを表しますが、なぜこれが必要ですか?
ご存知のように、Goはスコープの概念を持ちます.これは、識別子が指定された定数、型、変数、関数、ラベル、またはパッケージを表すソーステキストの範囲です.
The
Decl
フィールドは識別子の範囲を識別するために識別子が宣言された場所を示します.同一のオブジェクトを指す識別子は、同じものを共有する
*ast.Object
.AST機能型
*ast.FuncType {
. Func: dummy.go:5:1
. Params: *ast.FieldList {/* Omission */}
}
戻って親の世代は、古い世代になる ast.FuncType
「func」キーワードのパラメータ、結果、位置を含む関数シグネチャを含んでいます.ASTフィールドリスト
*ast.FieldList {
. Opening: dummy.go:5:11
. List: nil
. Closing: dummy.go:5:12
}
安 ast.FieldList
ノードは、括弧または括弧で囲まれたフィールドのリストを表します.関数パラメータは、定義されている場合に表示されますが、今回はNoneです.
List
フィールドは *ast.Field
これは、識別子と型のペアを含みます.これは非常に汎用性であり、様々なノードに使用されます
*ast.StructType
, *ast.InterfaceType
, そしてここ.つまり、次のように識別子を識別子にマッピングするときに必要です.
foo int
bar string
ループバックしましょう*ast.FuncDecl
再び、ビットに飛び込むBody
, 最後のフィールド.ASTブロックSTMT
*ast.BlockStmt {
. Lbrace: dummy.go:5:14
. List: []ast.Stmt (len = 1) {
. . 0: *ast.ExprStmt {/* Omission */}
. }
. Rbrace: dummy.go:7:1
}
安 ast.BlockStmt
ノードはブレース付きのリストを表します.これは
ast.Stmt
インターフェイス.これは、ステートメントのリストがあります.どのような想像できるノード!
AST東北大理
*ast.ExprStmt {
. X: *ast.CallExpr {/* Omission */}
}
ast.ExprStmt
ステートメント一覧の式を表します.これは
ast.Stmt
インターフェースとシングルast.Expr
.ASTcallexpr
*ast.CallExpr {
. Fun: *ast.SelectorExpr {/* Omission */}
. Lparen: dummy.go:6:13
. Args: []ast.Expr (len = 1) {
. . 0: *ast.BasicLit {/* Omission */}
. }
. Ellipsis: -
. Rparen: dummy.go:6:28
}
ast.CallExpr
関数を呼び出す式を表します.見るフィールドは
Fun
, 呼び出す関数Args
, 渡す引数のリスト.ASTselectorexpr
*ast.SelectorExpr {
. X: *ast.Ident {
. . NamePos: dummy.go:6:2
. . Name: "fmt"
. }
. Sel: *ast.Ident {
. . NamePos: dummy.go:6:6
. . Name: "Println"
. }
}
ast.SelectorExpr
式に続いてセレクタを表します.簡単に言えば
fmt.Println
.ASTバスライト
*ast.BasicLit {
. ValuePos: dummy.go:6:14
. Kind: STRING
. Value: "\"Hello, World\""
}
もはや説明、こんにちは、世界が必要!ボトムライン
私は、私が導入したノードタイプでいくつかのフィールドを除外しました、そして、まだ多くの他のノードタイプがあります.
それにもかかわらず、私は、それが少しラフであっても、実際に散歩を歩くことが重要であると言います.そして何よりも、それはかなりの楽しみです.
コピーして貼り付けのコードは、“歩いてどのように”セクションで示され、あなたのPC上を歩くしようとしている.
Reference
この問題について(散歩に行く), 我々は、より多くの情報をここで見つけました https://dev.to/nakabonne/take-a-walk-the-go-ast-e02テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol