golang实现AST与JSON互转的插件库asty的使用

Golang实现AST与JSON互转的插件库asty的使用

asty是一个将Golang抽象语法树(AST)与JSON互相转换的工具库。它可以将Golang代码转换为JSON格式的AST表示,也可以将JSON格式的AST转换回Golang代码。

示例

输入Golang源代码

package main

import "fmt"

func main() {
    fmt.Println("hello world")
}

输出AST的JSON表示

{
  "NodeType": "File",
  "Name": {
    "NodeType": "Ident",
    "Name": "main"
  },
  "Decls": [
    {
      "NodeType": "GenDecl",
      "Tok": "import",
      "Specs": [
        {
          "NodeType": "ImportSpec",
          "Name": null,
          "Path": {
            "NodeType": "BasicLit",
            "Kind": "STRING",
            "Value": "\"fmt\""
          }
        }
      ]
    },
    {
      "NodeType": "FuncDecl",
      "Recv": null,
      "Name": {
        "NodeType": "Ident",
        "Name": "main"
      },
      "Type": {
        "NodeType": "FuncType",
        "TypeParams": null,
        "Params": {
          "NodeType": "FieldList",
          "List": null
        },
        "Results": null
      },
      "Body": {
        "NodeType": "BlockStmt",
        "List": [
          {
            "NodeType": "ExprStmt",
            "X": {
              "NodeType": "CallExpr",
              "Fun": {
                "NodeType": "SelectorExpr",
                "X": {
                  "NodeType": "Ident",
                  "Name": "fmt"
                },
                "Sel": {
                  "NodeType": "Ident",
                  "Name": "Println"
                }
              },
              "Args": [
                {
                  "NodeType": "BasicLit",
                  "Kind": "STRING",
                  "Value": "\"hello world\""
                }
              ]
            }
          }
        ]
      }
    }
  ]
}

安装

将asty安装到$GOPATH/bin目录:

go install github.com/asty-org/asty

asty -h

使用

将AST转换为JSON

asty go2json -input <input.go> -output <output.json>

将JSON转换为AST

asty json2go -input <input.json> -output <output.go>

使用asty help获取更多帮助信息。

使用Docker运行

docker run astyorg/asty go2json -input <input.go> -output <output.json>

开发原则

  1. 保持JSON输出尽可能接近真实的Golang结构,不引入额外逻辑,不做规范化或重新解释
  2. 保持高度明确性,不使用反射或字段列表,便于未来维护
  3. 在JSON结构中保持多态性,通过对象类型名称字段区分具体类型

完整示例

package main

import (
	"fmt"
	"github.com/asty-org/asty"
	"go/ast"
	"go/parser"
	"go/token"
)

func main() {
	// 示例1: 将Go代码转换为AST JSON
	fset := token.NewFileSet()
	node, err := parser.ParseFile(fset, "example.go", `
	package main

	import "fmt"

	func main() {
		fmt.Println("hello world")
	}
	`, parser.ParseComments)
	if err != nil {
		fmt.Printf("Error parsing file: %v\n", err)
		return
	}

	// 将AST转换为JSON
	jsonData, err := asty.Marshal(node)
	if err != nil {
		fmt.Printf("Error marshaling AST to JSON: %v\n", err)
		return
	}
	fmt.Printf("AST JSON:\n%s\n", jsonData)

	// 示例2: 将JSON转换回AST
	var astFile ast.File
	err = asty.Unmarshal(jsonData, &astFile)
	if err != nil {
		fmt.Printf("Error unmarshaling JSON to AST: %v\n", err)
		return
	}
	fmt.Printf("Successfully converted JSON back to AST\n")

	// 示例3: 使用asty命令行工具的功能
	fmt.Println("To convert a Go file to JSON AST:")
	fmt.Println("  asty go2json -input input.go -output output.json")
	fmt.Println("To convert a JSON AST back to Go:")
	fmt.Println("  asty json2go -input input.json -output output.go")
}

这个示例展示了如何使用asty库在代码中将Go源文件解析为AST,然后将其转换为JSON格式,以及如何将JSON格式的AST转换回Go的AST结构。同时提供了使用命令行工具的示例。


更多关于golang实现AST与JSON互转的插件库asty的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现AST与JSON互转的插件库asty的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用asty库实现Golang AST与JSON互转

asty是一个用于在Golang中实现抽象语法树(AST)与JSON之间相互转换的库。它可以帮助开发者分析和操作Go代码的语法结构。

安装asty

go get github.com/asty-org/asty

基本用法

1. 将Go代码转换为AST JSON

package main

import (
	"fmt"
	"go/parser"
	"go/token"
	"github.com/asty-org/asty"
)

func main() {
	// 示例Go代码
	src := `
package main

import "fmt"

func main() {
	fmt.Println("Hello, World!")
}
`

	// 创建token.FileSet
	fset := token.NewFileSet()

	// 解析源代码为AST
	file, err := parser.ParseFile(fset, "", src, parser.ParseComments)
	if err != nil {
		panic(err)
	}

	// 使用asty将AST转换为JSON
	jsonData, err := asty.Encode(file)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(jsonData))
}

2. 将JSON转换回AST

package main

import (
	"fmt"
	"go/ast"
	"github.com/asty-org/asty"
)

func main() {
	// 假设这是之前生成的JSON数据
	jsonStr := `{"NodeType":"File","Name":{"NodeType":"Ident","Name":"main"},"Decls":[/* 其他节点 */]}`

	// 将JSON解码为AST
	var file ast.File
	err := asty.Decode([]byte(jsonStr), &file)
	if err != nil {
		panic(err)
	}

	// 现在可以操作AST了
	fmt.Printf("Package name: %s\n", file.Name.Name)
}

高级用法

自定义编码选项

// 使用自定义选项编码AST
options := &asty.Options{
	IncludeComments: true,
	IncludePositions: false,
}

jsonData, err := asty.EncodeWithOptions(file, options)
if err != nil {
	panic(err)
}

处理特定节点类型

// 注册自定义节点处理器
asty.RegisterNodeHandler("FuncDecl", func(n ast.Node) (asty.Node, error) {
	fn := n.(*ast.FuncDecl)
	return map[string]interface{}{
		"name":    fn.Name.Name,
		"params":  fn.Type.Params,
		"results": fn.Type.Results,
	}, nil
})

// 现在编码时会使用自定义处理器
jsonData, err := asty.Encode(file)

实际应用示例

分析函数调用

package main

import (
	"encoding/json"
	"fmt"
	"go/parser"
	"go/token"
	"github.com/asty-org/asty"
)

func main() {
	src := `
package main

import "fmt"

func greet(name string) {
	fmt.Println("Hello,", name)
}
`

	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "", src, 0)
	if err != nil {
		panic(err)
	}

	// 转换为JSON
	jsonData, err := asty.Encode(file)
	if err != nil {
		panic(err)
	}

	// 解析JSON以查找函数调用
	var astMap map[string]interface{}
	if err := json.Unmarshal(jsonData, &astMap); err != nil {
		panic(err)
	}

	// 查找所有函数调用
	findFunctionCalls(astMap)
}

func findFunctionCalls(node map[string]interface{}) {
	if node["NodeType"] == "CallExpr" {
		fun := node["Fun"].(map[string]interface{})
		if fun["NodeType"] == "SelectorExpr" {
			x := fun["X"].(map[string]interface{})
			sel := fun["Sel"].(map[string]interface{})
			fmt.Printf("Found function call: %s.%s\n", x["Name"], sel["Name"])
		}
	}

	// 递归查找子节点
	for _, v := range node {
		if child, ok := v.(map[string]interface{}); ok {
			findFunctionCalls(child)
		} else if children, ok := v.([]interface{}); ok {
			for _, c := range children {
				if child, ok := c.(map[string]interface{}); ok {
					findFunctionCalls(child)
				}
			}
		}
	}
}

注意事项

  1. asty在转换过程中会丢失一些位置信息,除非显式配置保留
  2. 大型代码库的AST转换为JSON可能会产生大量数据
  3. 不是所有AST节点类型都默认支持,可能需要自定义处理器
  4. 转换后的JSON结构可能随着asty版本变化而变化

asty库为Go代码的静态分析提供了便利,特别适合需要序列化AST或在不同系统间传输AST数据的场景。

回到顶部