如何打印手写的抽象语法树(AST)?

huangapple go评论73阅读模式
英文:

How to print hand-written AST?

问题

我有一组描述网络协议的XML文件,我想要从中生成Go代码,因此我没有任何现有的Go代码可供使用。所有使用go/ast(例如go fmt)的示例都是基于现有代码进行一些AST转换,然后再将其写回。由于我只有XML文件,所以我必须手动编写AST。问题是,我无法让我手写的AST输出。

期望输出:

package foo

type Bar uint32

实际输出:没有任何输出

我该如何让AST输出呢?

英文:

I have a set of XML description of network protocol that I'm trying to generate Go code from, therefore I don't have any existing Go code to work with. All of the examples that make use of go/ast (such as go fmt) take existing code, do some transformation on the AST, then write them back out. Since all I have are XML files, the AST that I'm working with have to be written by hand. The problem is that I cannot get my hand-written AST to output.

Example

package main

import (
	"go/ast"
	"go/printer"
	"go/token"
	"os"
)

func main() {
	f := ast.File{
		Name: ast.NewIdent("foo"),
		Decls: []ast.Decl{
			&ast.GenDecl{
				Tok: token.TYPE,
				Specs: []ast.Spec{
					&ast.TypeSpec{
						Name: ast.NewIdent("Bar"),
						Type: ast.NewIdent("uint32"),
					},
				},
			},
		},
	}
	fset := token.NewFileSet()
	printer.Fprint(os.Stdout, fset, f)
}

Expected output:

package foo

type Bar uint32

Actual output: nothing

How do I get the AST to print?

答案1

得分: 6

不要忽视错误!

添加:

err := printer.Fprint(os.Stdout, fset, f)
if err != nil {
	log.Fatal(err)
}

会得到:"go/printer: 不支持的节点类型 ast.File",这应该足够指引你朝正确的方向前进了。

printer.Fprint 的最后一个参数是 interface{},所以编译器接受任何类型。然而,就像 parser.ParseFile 返回的是 *ast.File(而不是 ast.File),它期望一个节点的指针。

传递一个指针会得到你想要的输出(注意 &ast.File):

package main

import (
	"go/ast"
	"go/printer"
	"go/token"
	"log"
	"os"
)

func main() {
	f := &ast.File{
		Name: ast.NewIdent("foo"),
		Decls: []ast.Decl{
			&ast.GenDecl{
				Tok: token.TYPE,
				Specs: []ast.Spec{
					&ast.TypeSpec{
						Name: ast.NewIdent("Bar"),
						Type: ast.NewIdent("uint32"),
					},
				},
			},
		},
	}
	fset := token.NewFileSet()
	err := printer.Fprint(os.Stdout, fset, f)
	if err != nil {
		log.Fatal(err)
	}
}

<kbd>playground</kbd>

英文:

Don't ignore errors!

Adding:

err := printer.Fprint(os.Stdout, fset, f)
if err != nil {
	log.Fatal(err)
}

Gives: "go/printer: unsupported node type ast.File" which should have been enough to point you in the right direction.

printer.Fprint's last argument is interface{} so the compiler accepts anything. However, just as parser.ParseFile returns an *ast.File (rather than ast.File) it expects a pointer to a node.

Passing a pointer gives the output you want (note the &amp;ast.File):

package main

import (
	&quot;go/ast&quot;
	&quot;go/printer&quot;
	&quot;go/token&quot;
	&quot;log&quot;
	&quot;os&quot;
)

func main() {
	f := &amp;ast.File{
		Name: ast.NewIdent(&quot;foo&quot;),
		Decls: []ast.Decl{
			&amp;ast.GenDecl{
				Tok: token.TYPE,
				Specs: []ast.Spec{
					&amp;ast.TypeSpec{
						Name: ast.NewIdent(&quot;Bar&quot;),
						Type: ast.NewIdent(&quot;uint32&quot;),
					},
				},
			},
		},
	}
	fset := token.NewFileSet()
	err := printer.Fprint(os.Stdout, fset, f)
	if err != nil {
		log.Fatal(err)
	}
}

<kbd>playground</kbd>

huangapple
  • 本文由 发表于 2015年2月24日 08:04:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/28685976.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定