| // Copyright 2010 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package try contains the executable part of the gotry command. | |
| // It is not intended for general use. | |
| package try | |
| import ( | |
| "fmt" | |
| "io" | |
| "os" | |
| "reflect" | |
| "unicode" | |
| ) | |
| var output io.Writer = os.Stdout // redirected when testing | |
| // Main is called directly from the gotry-generated Go source file to perform | |
| // the evaluations. | |
| func Main(pkg, firstArg string, functions map[string]interface{}, args []interface{}) { | |
| switch len(args) { | |
| case 0: | |
| // Nothing to do. | |
| case 1: | |
| // Compiler has already evaluated the expression; just print the result. | |
| printSlice(firstArg, args) | |
| default: | |
| // See if methods satisfy the expressions. | |
| tryMethods(pkg, firstArg, args) | |
| // See if functions satisfy the expressions. | |
| for name, fn := range functions { | |
| tryFunction(pkg, name, fn, args) | |
| } | |
| } | |
| } | |
| // printSlice prints the zeroth element of the args slice, which should (by construction) | |
| // itself be a slice of interface{}. | |
| func printSlice(firstArg string, args []interface{}) { | |
| // Args should be length 1 and a slice. | |
| if len(args) != 1 { | |
| return | |
| } | |
| arg, ok := args[0].([]interface{}) | |
| if !ok { | |
| return | |
| } | |
| fmt.Fprintf(output, "%s = ", firstArg) | |
| if len(arg) > 1 { | |
| fmt.Fprint(output, "(") | |
| } | |
| for i, a := range arg { | |
| if i > 0 { | |
| fmt.Fprint(output, ", ") | |
| } | |
| fmt.Fprintf(output, "%#v", a) | |
| } | |
| if len(arg) > 1 { | |
| fmt.Fprint(output, ")") | |
| } | |
| fmt.Fprint(output, "\n") | |
| } | |
| // tryMethods sees if the zeroth arg has methods, and if so treats them as potential | |
| // functions to satisfy the remaining arguments. | |
| func tryMethods(pkg, firstArg string, args []interface{}) { | |
| defer func() { recover() }() | |
| // Is the first argument something with methods? | |
| v := reflect.ValueOf(args[0]) | |
| typ := v.Type() | |
| if typ.NumMethod() == 0 { | |
| return | |
| } | |
| for i := 0; i < typ.NumMethod(); i++ { | |
| if unicode.IsUpper(int(typ.Method(i).Name[0])) { | |
| tryMethod(pkg, firstArg, typ.Method(i), args) | |
| } | |
| } | |
| } | |
| // tryMethod converts a method to a function for tryOneFunction. | |
| func tryMethod(pkg, firstArg string, method reflect.Method, args []interface{}) { | |
| rfn := method.Func | |
| typ := method.Type | |
| name := method.Name | |
| tryOneFunction(pkg, firstArg, name, typ, rfn, args) | |
| } | |
| // tryFunction sees if fn satisfies the arguments. | |
| func tryFunction(pkg, name string, fn interface{}, args []interface{}) { | |
| defer func() { recover() }() | |
| rfn := reflect.ValueOf(fn) | |
| typ := rfn.Type() | |
| tryOneFunction(pkg, "", name, typ, rfn, args) | |
| } | |
| // tryOneFunction is the common code for tryMethod and tryFunction. | |
| func tryOneFunction(pkg, firstArg, name string, typ reflect.Type, rfn reflect.Value, args []interface{}) { | |
| // Any results? | |
| if typ.NumOut() == 0 { | |
| return // Nothing to do. | |
| } | |
| // Right number of arguments + results? | |
| if typ.NumIn()+typ.NumOut() != len(args) { | |
| return | |
| } | |
| // Right argument and result types? | |
| for i, a := range args { | |
| if i < typ.NumIn() { | |
| if !compatible(a, typ.In(i)) { | |
| return | |
| } | |
| } else { | |
| if !compatible(a, typ.Out(i-typ.NumIn())) { | |
| return | |
| } | |
| } | |
| } | |
| // Build the call args. | |
| argsVal := make([]reflect.Value, typ.NumIn()+typ.NumOut()) | |
| for i, a := range args { | |
| argsVal[i] = reflect.ValueOf(a) | |
| } | |
| // Call the function and see if the results are as expected. | |
| resultVal := rfn.Call(argsVal[:typ.NumIn()]) | |
| for i, v := range resultVal { | |
| if !reflect.DeepEqual(v.Interface(), args[i+typ.NumIn()]) { | |
| return | |
| } | |
| } | |
| // Present the result including a godoc command to get more information. | |
| firstIndex := 0 | |
| if firstArg != "" { | |
| fmt.Fprintf(output, "%s.%s(", firstArg, name) | |
| firstIndex = 1 | |
| } else { | |
| fmt.Fprintf(output, "%s.%s(", pkg, name) | |
| } | |
| for i := firstIndex; i < typ.NumIn(); i++ { | |
| if i > firstIndex { | |
| fmt.Fprint(output, ", ") | |
| } | |
| fmt.Fprintf(output, "%#v", args[i]) | |
| } | |
| fmt.Fprint(output, ") = ") | |
| if typ.NumOut() > 1 { | |
| fmt.Fprint(output, "(") | |
| } | |
| for i := 0; i < typ.NumOut(); i++ { | |
| if i > 0 { | |
| fmt.Fprint(output, ", ") | |
| } | |
| fmt.Fprintf(output, "%#v", resultVal[i].Interface()) | |
| } | |
| if typ.NumOut() > 1 { | |
| fmt.Fprint(output, ")") | |
| } | |
| fmt.Fprintf(output, " // godoc %s %s\n", pkg, name) | |
| } | |
| // compatible reports whether the argument is compatible with the type. | |
| func compatible(arg interface{}, typ reflect.Type) bool { | |
| if reflect.TypeOf(arg) == typ { | |
| return true | |
| } | |
| if arg == nil { | |
| // nil is OK if the type is an interface. | |
| if typ.Kind() == reflect.Interface { | |
| return true | |
| } | |
| } | |
| return false | |
| } |