code: func lookup(contextChain []interface{}, name string) reflect.Value {
      // dot notation
      if name != "." && strings.Contains(name, ".") {
          parts := strings.SplitN(name, ".", 2)
          
          v := lookup(contextChain, parts[0])
          return lookup([]interface{}{v}, parts[1])
      }
  
      defer func() {
          if r := recover(); r != nil {
              fmt.Printf("Panic while looking up %q: %s\n", name, r)
          }
      }()
  
  Outer:
      for _, ctx := range contextChain { //i := len(contextChain) - 1; i >= 0; i-- {
          v := ctx.(reflect.Value)
          for v.IsValid() {
              typ := v.Type()
              if n := v.Type().NumMethod(); n > 0 {
                  for i := 0; i < n; i++ {
                      m := typ.Method(i)
                      mtyp := m.Type
                      if m.Name == name && mtyp.NumIn() == 1 {
                          return v.Method(i).Call(nil)[0]
                      }
                  }
              }
              if name == "." {
                  return v
              }
              switch av := v; av.Kind() {
              case reflect.Ptr:
                  v = av.Elem()
              case reflect.Interface:
                  v = av.Elem()
              case reflect.Struct:
                  ret := av.FieldByName(name)
                  if ret.IsValid() {
                      return ret
                  } else {
                      continue Outer
                  }
              case reflect.Map:
                  ret := av.MapIndex(reflect.ValueOf(name))
                  if ret.IsValid() {
                      return ret
                  } else {
                      continue Outer
                  }
              default:
                  continue Outer
              }
          }
      }
      return reflect.Value{}
  }
docs: // Evaluate interfaces and pointers looking for a value that can look up the name, via a
  // struct field, method, or map key, and return the result of the lookup.