OLD | NEW |
| (Empty) |
1 // Copyright 2010 The Go Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style | |
3 // license that can be found in the LICENSE file. | |
4 | |
5 // Package try contains the executable part of the gotry command. | |
6 // It is not intended for general use. | |
7 package try | |
8 | |
9 import ( | |
10 "fmt" | |
11 "io" | |
12 "os" | |
13 "reflect" | |
14 "unicode" | |
15 ) | |
16 | |
17 var output io.Writer = os.Stdout // redirected when testing | |
18 | |
19 // Main is called directly from the gotry-generated Go source file to perform | |
20 // the evaluations. | |
21 func Main(pkg, firstArg string, functions map[string]interface{}, args []interfa
ce{}) { | |
22 switch len(args) { | |
23 case 0: | |
24 // Nothing to do. | |
25 case 1: | |
26 // Compiler has already evaluated the expression; just print the
result. | |
27 printSlice(firstArg, args) | |
28 default: | |
29 // See if methods satisfy the expressions. | |
30 tryMethods(pkg, firstArg, args) | |
31 // See if functions satisfy the expressions. | |
32 for name, fn := range functions { | |
33 tryFunction(pkg, name, fn, args) | |
34 } | |
35 } | |
36 } | |
37 | |
38 // printSlice prints the zeroth element of the args slice, which should (by cons
truction) | |
39 // itself be a slice of interface{}. | |
40 func printSlice(firstArg string, args []interface{}) { | |
41 // Args should be length 1 and a slice. | |
42 if len(args) != 1 { | |
43 return | |
44 } | |
45 arg, ok := args[0].([]interface{}) | |
46 if !ok { | |
47 return | |
48 } | |
49 fmt.Fprintf(output, "%s = ", firstArg) | |
50 if len(arg) > 1 { | |
51 fmt.Fprint(output, "(") | |
52 } | |
53 for i, a := range arg { | |
54 if i > 0 { | |
55 fmt.Fprint(output, ", ") | |
56 } | |
57 fmt.Fprintf(output, "%#v", a) | |
58 } | |
59 if len(arg) > 1 { | |
60 fmt.Fprint(output, ")") | |
61 } | |
62 fmt.Fprint(output, "\n") | |
63 } | |
64 | |
65 // tryMethods sees if the zeroth arg has methods, and if so treats them as poten
tial | |
66 // functions to satisfy the remaining arguments. | |
67 func tryMethods(pkg, firstArg string, args []interface{}) { | |
68 defer func() { recover() }() | |
69 // Is the first argument something with methods? | |
70 v := reflect.ValueOf(args[0]) | |
71 typ := v.Type() | |
72 if typ.NumMethod() == 0 { | |
73 return | |
74 } | |
75 for i := 0; i < typ.NumMethod(); i++ { | |
76 if unicode.IsUpper(int(typ.Method(i).Name[0])) { | |
77 tryMethod(pkg, firstArg, typ.Method(i), args) | |
78 } | |
79 } | |
80 } | |
81 | |
82 // tryMethod converts a method to a function for tryOneFunction. | |
83 func tryMethod(pkg, firstArg string, method reflect.Method, args []interface{})
{ | |
84 rfn := method.Func | |
85 typ := method.Type | |
86 name := method.Name | |
87 tryOneFunction(pkg, firstArg, name, typ, rfn, args) | |
88 } | |
89 | |
90 // tryFunction sees if fn satisfies the arguments. | |
91 func tryFunction(pkg, name string, fn interface{}, args []interface{}) { | |
92 defer func() { recover() }() | |
93 rfn := reflect.ValueOf(fn) | |
94 typ := rfn.Type() | |
95 tryOneFunction(pkg, "", name, typ, rfn, args) | |
96 } | |
97 | |
98 // tryOneFunction is the common code for tryMethod and tryFunction. | |
99 func tryOneFunction(pkg, firstArg, name string, typ reflect.Type, rfn reflect.Va
lue, args []interface{}) { | |
100 // Any results? | |
101 if typ.NumOut() == 0 { | |
102 return // Nothing to do. | |
103 } | |
104 // Right number of arguments + results? | |
105 if typ.NumIn()+typ.NumOut() != len(args) { | |
106 return | |
107 } | |
108 // Right argument and result types? | |
109 for i, a := range args { | |
110 if i < typ.NumIn() { | |
111 if !compatible(a, typ.In(i)) { | |
112 return | |
113 } | |
114 } else { | |
115 if !compatible(a, typ.Out(i-typ.NumIn())) { | |
116 return | |
117 } | |
118 } | |
119 } | |
120 // Build the call args. | |
121 argsVal := make([]reflect.Value, typ.NumIn()+typ.NumOut()) | |
122 for i, a := range args { | |
123 argsVal[i] = reflect.ValueOf(a) | |
124 } | |
125 // Call the function and see if the results are as expected. | |
126 resultVal := rfn.Call(argsVal[:typ.NumIn()]) | |
127 for i, v := range resultVal { | |
128 if !reflect.DeepEqual(v.Interface(), args[i+typ.NumIn()]) { | |
129 return | |
130 } | |
131 } | |
132 // Present the result including a godoc command to get more information. | |
133 firstIndex := 0 | |
134 if firstArg != "" { | |
135 fmt.Fprintf(output, "%s.%s(", firstArg, name) | |
136 firstIndex = 1 | |
137 } else { | |
138 fmt.Fprintf(output, "%s.%s(", pkg, name) | |
139 } | |
140 for i := firstIndex; i < typ.NumIn(); i++ { | |
141 if i > firstIndex { | |
142 fmt.Fprint(output, ", ") | |
143 } | |
144 fmt.Fprintf(output, "%#v", args[i]) | |
145 } | |
146 fmt.Fprint(output, ") = ") | |
147 if typ.NumOut() > 1 { | |
148 fmt.Fprint(output, "(") | |
149 } | |
150 for i := 0; i < typ.NumOut(); i++ { | |
151 if i > 0 { | |
152 fmt.Fprint(output, ", ") | |
153 } | |
154 fmt.Fprintf(output, "%#v", resultVal[i].Interface()) | |
155 } | |
156 if typ.NumOut() > 1 { | |
157 fmt.Fprint(output, ")") | |
158 } | |
159 fmt.Fprintf(output, " // godoc %s %s\n", pkg, name) | |
160 } | |
161 | |
162 // compatible reports whether the argument is compatible with the type. | |
163 func compatible(arg interface{}, typ reflect.Type) bool { | |
164 if reflect.TypeOf(arg) == typ { | |
165 return true | |
166 } | |
167 if arg == nil { | |
168 // nil is OK if the type is an interface. | |
169 if typ.Kind() == reflect.Interface { | |
170 return true | |
171 } | |
172 } | |
173 return false | |
174 } | |
OLD | NEW |