2018-11-07 09:11:08 +03:00

164 lines
3.1 KiB
Go

// +build ignore
package main
import (
"bytes"
"go/format"
"io"
"log"
"os"
"text/template"
)
func main() {
typeList := []string{
// Expressions:
"ArrayType",
"BadExpr",
"BasicLit",
"BinaryExpr",
"CallExpr",
"ChanType",
"CompositeLit",
"Ellipsis",
"FuncLit",
"FuncType",
"Ident",
"IndexExpr",
"InterfaceType",
"KeyValueExpr",
"MapType",
"ParenExpr",
"SelectorExpr",
"SliceExpr",
"StarExpr",
"StructType",
"TypeAssertExpr",
"UnaryExpr",
// Statements:
"AssignStmt",
"BadStmt",
"BlockStmt",
"BranchStmt",
"CaseClause",
"CommClause",
"DeclStmt",
"DeferStmt",
"EmptyStmt",
"ExprStmt",
"ForStmt",
"GoStmt",
"IfStmt",
"IncDecStmt",
"LabeledStmt",
"RangeStmt",
"ReturnStmt",
"SelectStmt",
"SendStmt",
"SwitchStmt",
"TypeSwitchStmt",
// Others:
"Comment",
"CommentGroup",
"FieldList",
"File",
"Package",
}
astcastFile, err := os.Create("astcast.go")
if err != nil {
log.Fatal(err)
}
writeCode(astcastFile, typeList)
astcastTestFile, err := os.Create("astcast_test.go")
if err != nil {
log.Fatal(err)
}
writeTests(astcastTestFile, typeList)
}
func generateCode(tmplText string, typeList []string) []byte {
tmpl := template.Must(template.New("code").Parse(tmplText))
var code bytes.Buffer
tmpl.Execute(&code, typeList)
prettyCode, err := format.Source(code.Bytes())
if err != nil {
panic(err)
}
return prettyCode
}
func writeCode(output io.Writer, typeList []string) {
code := generateCode(`// Code generated by astcast_generate.go; DO NOT EDIT
// Package astcast wraps type assertion operations in such way that you don't have
// to worry about nil pointer results anymore.
package astcast
import (
"go/ast"
)
// A set of sentinel nil-like values that are returned
// by all "casting" functions in case of failed type assertion.
var (
{{ range . }}
Nil{{.}} = &ast.{{.}}{}
{{- end }}
)
{{ range . }}
// To{{.}} returns x as a non-nil *ast.{{.}}.
// If ast.Node actually has such dynamic type, the result is
// identical to normal type assertion. In case if it has
// different type, the returned value is Nil{{.}}.
func To{{.}}(x ast.Node) *ast.{{.}} {
if x, ok := x.(*ast.{{.}}); ok {
return x
}
return Nil{{.}}
}
{{ end }}
`, typeList)
output.Write(code)
}
func writeTests(output io.Writer, typeList []string) {
code := generateCode(`// Code generated by astcast_generate.go; DO NOT EDIT
package astcast
import (
"go/ast"
"testing"
)
{{ range . }}
func TestTo{{.}}(t *testing.T) {
// Test successfull cast.
if x := To{{.}}(&ast.{{.}}{}); x == Nil{{.}} || x == nil {
t.Error("expected successfull cast, got nil")
}
// Test nil cast.
if x := To{{.}}(nil); x != Nil{{.}} {
t.Error("nil node didn't resulted in a sentinel value return")
}
// Test unsuccessfull cast.
{{- if (eq . "Ident") }}
if x := To{{.}}(&ast.CallExpr{}); x != Nil{{.}} || x == nil {
t.Errorf("expected unsuccessfull cast to return nil sentinel")
}
{{- else }}
if x := To{{.}}(&ast.Ident{}); x != Nil{{.}} || x == nil {
t.Errorf("expected unsuccessfull cast to return nil sentinel")
}
{{- end }}
}
{{ end }}
`, typeList)
output.Write(code)
}