/
main.go
141 lines (130 loc) · 2.7 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Play with implementing goon dumper using go/ast and go/printer.
package main
import (
"fmt"
"go/ast"
"go/printer"
"go/token"
"os"
"reflect"
"strconv"
"github.com/shurcooL/go-goon"
)
func main() {
for _, Dump := range []func(interface{}){
GoonDump,
Dump,
} {
Dump(123)
Dump("Hello.")
type Inner struct {
Field1 string
Field2 int
}
type Lang struct {
Name string
Year int32
URL string
Inner *Inner
Pointer *string
}
x := Lang{
Name: "Go",
Year: 2009,
URL: "http",
Inner: &Inner{
Field1: "Secret!",
},
}
Dump(x)
}
}
func GoonDump(v interface{}) {
goon.Dump(v)
}
// Consistent with the default gofmt behavior.
var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
func Dump(v interface{}) {
expr := dump(reflect.ValueOf(v))
const size = 1000
fset := token.NewFileSet()
f := fset.AddFile("", -1, size)
for i := 0; i < size; i++ {
f.AddLine(i)
}
config.Fprint(os.Stdout, fset, expr)
os.Stdout.WriteString("\n")
}
// typeValue creates an expression of the form:
//
// (type)(value)
func typeValue(typ, value ast.Expr) ast.Expr {
return &ast.CallExpr{
Fun: &ast.ParenExpr{
X: typ,
},
Args: []ast.Expr{
value,
},
}
}
func dump(v reflect.Value) ast.Expr {
vt := v.Type()
switch v.Kind() {
case reflect.Bool:
return typeValue(
&ast.Ident{Name: vt.String()},
&ast.BasicLit{Value: strconv.FormatBool(v.Bool())},
)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return typeValue(
&ast.Ident{Name: vt.String()},
&ast.BasicLit{Value: strconv.FormatInt(v.Int(), 10)},
)
case reflect.String:
return typeValue(
&ast.Ident{Name: vt.String()},
&ast.BasicLit{Value: strconv.Quote(v.String())},
)
case reflect.Struct:
var fields []ast.Expr
for i := 0; i < vt.NumField(); i++ {
fields = append(fields, &ast.KeyValueExpr{
Key: &ast.Ident{
Name: vt.Field(i).Name,
//NamePos: token.Pos(1 + i + 1),
},
Value: dump(v.Field(i)),
})
}
typ := &ast.Ident{Name: vt.Name()}
return typeValue(
typ,
&ast.CompositeLit{
Type: typ,
Elts: fields,
//Lbrace: token.Pos(1),
//Rbrace: token.Pos(1 + len(fields) + 1),
},
)
case reflect.Ptr:
// TODO: Keep track of visited pointers to avoid infinite loop.
if v.IsNil() {
return typeValue(
&ast.StarExpr{X: &ast.Ident{Name: vt.Elem().Name()}},
&ast.BasicLit{Value: "nil"},
)
}
tv := dump(v.Elem()).(*ast.CallExpr)
typ, value := tv.Fun.(*ast.ParenExpr).X, tv.Args[0]
return typeValue(
&ast.StarExpr{X: typ},
&ast.UnaryExpr{
Op: token.AND,
X: value,
},
)
default:
panic(fmt.Errorf("unsupported kind: %v", v.Kind()))
}
}