| // 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.NewValue(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.NewValue(fn).(*reflect.FuncValue) |
| typ := rfn.Type().(*reflect.FuncType) |
| tryOneFunction(pkg, "", name, typ, rfn, args) |
| } |
| |
| // tryOneFunction is the common code for tryMethod and tryFunction. |
| func tryOneFunction(pkg, firstArg, name string, typ *reflect.FuncType, rfn *reflect.FuncValue, 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.NewValue(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 _, ok := typ.(*reflect.InterfaceType); ok { |
| return true |
| } |
| } |
| return false |
| } |