のシリーズを、Go で。
Go で AST を触るのは初めて。こちらもまあそれらしいツリーが出力できたからいいかなと思っている
ソースコード。
go1.11.4
// go build goast.go && ./goast sample.go | dot -Tpng -ogoast.png
package main
import (
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"strings"
)
func nodeToString(node ast.Node) string {
switch x := node.(type) {
case *ast.Ident:
return "Ident(" + x.Name + ")"
case *ast.BasicLit:
return "BasicLit(" + x.Value + ")"
case *ast.BinaryExpr:
return "BinaryExpr(" + x.Op.String() + ")"
case *ast.UnaryExpr:
return "UnaryExpr(" + x.Op.String() + ")"
case *ast.IncDecStmt:
return "IncDecStmt(" + x.Tok.String() + ")"
case *ast.AssignStmt:
return "AssignStmt(" + x.Tok.String() + ")"
default:
return strings.Replace(fmt.Sprintf("%T", x), "*ast.", "", -1)
}
}
func main() {
fmt.Println(`digraph{graph [dpi=288];`)
fset := token.NewFileSet()
flag.Parse()
f, err := parser.ParseFile(fset, flag.Arg(0), nil, 0)
if err != nil {
fmt.Printf("Failed to parse file\n")
return
}
parents := []*ast.Node{}
fmt.Println(`n0x0[label="ROOT"]`)
ast.Inspect(f, func(node ast.Node) bool {
if node != nil {
var p *ast.Node
if len(parents) != 0 {
p = parents[len(parents)-1]
}
fmt.Printf("n%p->n%p\n", p, &node)
fmt.Printf(`n%p[ label=%q ]`+"\n", &node, nodeToString(node))
}
if node == nil {
parents = parents[:len(parents)-1]
} else {
parents = append(parents, &node)
}
return true
})
fmt.Println(`}`)
}
nodeToString
が不十分かもしれない。
食べさせたコードはこんな感じ:
go
package main
import (
"fmt"
)
func main() {
for x := 0; x < 10; x++ {
if x == 0 {
fmt.Println(123, -456)
} else {
fmt.Println("foo", []string{"bar"})
}
}
}
複雑過ぎたかな。
出てくる絵はこんな感じ:
なかなかおもしろい。