func embedded(f ast.Expr) string {
	switch f := f.(type) {
	case *ast.Ident:
		return f.Name
	case *ast.StarExpr:
		return embedded(f.X)
	case *ast.SelectorExpr:
		return f.Sel.Name
	default:
		return ""
	}
}
extract embedded field name

so, for a struct like

type A struct {
io.Writer
}

we want "Writer"
package parse
import (
	"fmt"
	"go/ast"
	"reflect"
	"sort"
	"strings"
	"github.com/algorand/msgp/gen"
	"github.com/ttacon/chalk"
	"golang.org/x/tools/go/packages"
)
type FileSet struct {
	Package    string               
	PkgPath    string               
	Specs      map[string]ast.Expr  
	Aliases    map[string]ast.Expr  
	Interfaces map[string]ast.Expr  
	Consts     map[string]ast.Expr  
	Identities map[string]gen.Elem  
	Directives []string             
	Imports    []*ast.ImportSpec    
	ImportSet  ImportSet
	ImportName map[string]string
}
type ImportSet map[string]*FileSet
func File(name string, unexported bool, warnPkgMask string) (*FileSet, error) {
	pushstate(name)
	defer popstate()
	cfg := &packages.Config{
		Mode: packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedSyntax | packages.NeedFiles | packages.NeedExportsFile | packages.NeedTypesInfo,
	}
	pkgs, err := packages.Load(cfg, name)
	if err != nil {
		return nil, err
	}
	if len(pkgs) != 1 {
		return nil, fmt.Errorf("multiple packages in directory: %s", name)
	}
	var one *packages.Package
	for _, nm := range pkgs {
		one = nm
		break
	}
	imps := make(map[string]*FileSet)
	fs := packageToFileSet(one, imps, unexported)
	for _, ifs := range imps {
		ifs.process(warnPkgMask)
		ifs.applyDirectives()
		ifs.propInline()
	}
	fs.process(warnPkgMask)
	fs.applyDirectives()
	fs.propInline()
	return fs, nil
}
func packageToFileSet(p *packages.Package, imps map[string]*FileSet, unexported bool) *FileSet {
	fs := &FileSet{
		Package:    p.Name,
		PkgPath:    p.PkgPath,
		Specs:      make(map[string]ast.Expr),
		Aliases:    make(map[string]ast.Expr),
		Interfaces: make(map[string]ast.Expr),
		Consts:     make(map[string]ast.Expr),
		Identities: make(map[string]gen.Elem),
		ImportSet:  imps,
		ImportName: make(map[string]string),
	}
	for name, importpkg := range p.Imports {
		_, ok := imps[name]
		if ok {
			continue
		}
		imps[name] = packageToFileSet(importpkg, imps, unexported)
	}
	for _, fl := range p.Syntax {
		pushstate(fl.Name.Name)
		fs.Directives = append(fs.Directives, yieldComments(fl.Comments)...)
		if !unexported {
			ast.FileExports(fl)
		}
		for _, importspec := range fl.Imports {
			pkgpath := importspec.Path.Value[1 : len(importspec.Path.Value)-1]
			if pkgpath == "C" {
				continue
			}
			var importname string
			if importspec.Name != nil {
				importname = importspec.Name.Name
			} else {
				p, ok := imps[pkgpath]
				if !ok {
					fmt.Printf("missing import %s\n", pkgpath)
				} else {
					importname = p.Package
				}
			}
			fs.ImportName[importname] = pkgpath
		}
		fs.getTypeSpecs(fl)
		popstate()
	}
	return fs
}
func (f *FileSet) applyDirectives() {
	newdirs := make([]string, 0, len(f.Directives))
	for _, d := range f.Directives {
		chunks := strings.Split(d, " ")
		if len(chunks) > 0 {
			if fn, ok := directives[chunks[0]]; ok {
				pushstate(chunks[0])
				err := fn(chunks, f)
				if err != nil {
					warnln(err.Error())
				}
				popstate()
			} else {
				newdirs = append(newdirs, d)
			}
		}
	}
	f.Directives = newdirs
}
type linkset map[string]*gen.BaseElem
func (f *FileSet) resolve(ls linkset) {
	progress := true
	for progress && len(ls) > 0 {
		progress = false
		for name, elem := range ls {
			real, ok := f.Identities[elem.TypeName()]
			if ok {
				progress = true
				nt := real.Copy()
				nt.Alias(name)
				f.Identities[name] = nt
				delete(ls, name)
			}
		}
	}
	for name, elem := range ls {
		nt := elem.Copy()
		nt.Alias(name)
		f.Identities[name] = nt
	}
}
func (f *FileSet) process(warnPkgMask string) {
	if warnPkgMask != "" && !strings.HasPrefix(f.PkgPath, warnPkgMask) {
		increasePrintLevel()
		defer decreasePrintLevel()
	}
	deferred := make(linkset)
parse:
	for name, def := range f.Specs {
		pushstate(name)
		el := f.parseExpr("", def)
		if el == nil {
			warnln("failed to parse")
			popstate()
			continue parse
		}
		if be, ok := el.(*gen.BaseElem); ok && be.Value == gen.IDENT {
			deferred[name] = be
			popstate()
			continue parse
		}
		el.Alias(name)
		f.Identities[name] = el
		popstate()
	}
	if len(deferred) > 0 {
		f.resolve(deferred)
	}
}
func strToMethod(s string) gen.Method {
	switch s {
	case "test":
		return gen.Test
	case "size":
		return gen.Size
	case "marshal":
		return gen.Marshal
	case "unmarshal":
		return gen.Unmarshal
	default:
		return 0
	}
}
func (f *FileSet) applyDirs(p *gen.Printer) {
loop:
	for _, d := range f.Directives {
		chunks := strings.Split(d, " ")
		if len(chunks) > 1 {
			for i := range chunks {
				chunks[i] = strings.TrimSpace(chunks[i])
			}
			m := strToMethod(chunks[0])
			if m == 0 {
				warnf("unknown pass name: %q\n", chunks[0])
				continue loop
			}
			if fn, ok := passDirectives[chunks[1]]; ok {
				pushstate(chunks[1])
				err := fn(m, chunks[2:], p)
				if err != nil {
					warnf("error applying directive: %s\n", err)
				}
				popstate()
			} else {
				warnf("unrecognized directive %q\n", chunks[1])
			}
		} else {
			warnf("empty directive: %q\n", d)
		}
	}
}
func (f *FileSet) PrintTo(p *gen.Printer) error {
	var msgs []string
	f.applyDirs(p)
	names := make([]string, 0, len(f.Identities))
	for name := range f.Identities {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		el := f.Identities[name]
		el.SetVarname("z")
		pushstate(el.TypeName())
		m, err := p.Print(el)
		popstate()
		if err != nil {
			return err
		}
		msgs = append(msgs, m...)
	}
	for _, msg := range msgs {
		warnln(msg)
	}
	if len(msgs) > 0 {
		return fmt.Errorf("Errors encountered, exiting")
	}
	return nil
}
func (fs *FileSet) getTypeSpecs(f *ast.File) {
	fs.Imports = append(fs.Imports, f.Imports...)
	for i := range f.Decls {
		if g, ok := f.Decls[i].(*ast.GenDecl); ok {
			for _, s := range g.Specs {
				switch s := s.(type) {
				case *ast.TypeSpec:
					switch s.Type.(type) {
					case *ast.StructType,
						*ast.ArrayType,
						*ast.StarExpr,
						*ast.SelectorExpr,
						*ast.MapType,
						*ast.Ident:
						if strings.HasPrefix(s.Name.Name, "_Ctype_") {
							continue
						}
						if s.Assign == 0 {
							fs.Specs[s.Name.Name] = s.Type
						} else {
							fs.Aliases[s.Name.Name] = s.Type
						}
					case *ast.InterfaceType:
						fs.Interfaces[s.Name.Name] = s.Type
					}
				case *ast.ValueSpec:
					if len(s.Names) == 1 && len(s.Values) == 1 {
						fs.Consts[s.Names[0].Name] = s.Values[0]
					}
				}
			}
		}
	}
}
func fieldName(f *ast.Field) string {
	switch len(f.Names) {
	case 0:
		return stringify(f.Type)
	case 1:
		return f.Names[0].Name
	default:
		return f.Names[0].Name + " (and others)"
	}
}
func (fs *FileSet) parseFieldList(importPrefix string, fl *ast.FieldList) []gen.StructField {
	if fl == nil || fl.NumFields() == 0 {
		return nil
	}
	out := make([]gen.StructField, 0, fl.NumFields())
	for _, field := range fl.List {
		pushstate(fieldName(field))
		fds := fs.getField(importPrefix, field)
		if len(fds) > 0 {
			out = append(out, fds...)
		} else {
			warnln("ignored.")
		}
		popstate()
	}
	return out
}
func (fs *FileSet) getField(importPrefix string, f *ast.Field) []gen.StructField {
	sf := make([]gen.StructField, 1)
	var extension, flatten bool
	var allocbound string
	var allocbounds []string
	flatten = true
	if f.Tag != nil {
		var body string
		body, sf[0].HasCodecTag = reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Lookup("codec")
		tags := strings.Split(body, ",")
		for _, tag := range tags[1:] {
			if tag == "extension" {
				extension = true
			}
			if strings.HasPrefix(tag, "allocbound=") {
				allocbounds = append(allocbounds, strings.Split(tag, "=")[1])
			}
		}
		if tags[0] == "-" {
			return nil
		}
		sf[0].FieldTag = tags[0]
		sf[0].FieldTagParts = tags
		sf[0].RawTag = f.Tag.Value
	}
	allocbound = strings.Join(allocbounds, ",")
	ex := fs.parseExpr(importPrefix, f.Type)
	if ex == nil {
		return nil
	}
	switch len(f.Names) {
	case 0:
		if flatten {
			maybe := fs.getFieldsFromEmbeddedStruct(importPrefix, f.Type)
			if maybe != nil {
				for i := range maybe {
					maybe[i].FieldPath = append([]string{embedded(f.Type)}, maybe[i].FieldPath...)
				}
				return maybe
			}
		}
		sf[0].FieldName = embedded(f.Type)
	case 1:
		sf[0].FieldName = f.Names[0].Name
	default:
		sf = sf[0:0]
		for _, nm := range f.Names {
			sf = append(sf, gen.StructField{
				FieldTag:  nm.Name,
				FieldName: nm.Name,
				FieldElem: ex.Copy(),
			})
		}
		return sf
	}
	sf[0].FieldElem = ex
	if sf[0].FieldTag == "" {
		sf[0].FieldTag = sf[0].FieldName
	}
	if sf[0].FieldTagParts == nil {
		sf[0].FieldTagParts = []string{sf[0].FieldName}
	}
	sf[0].FieldElem.SetAllocBound(allocbound)
	if extension {
		switch ex := ex.(type) {
		case *gen.Ptr:
			if b, ok := ex.Value.(*gen.BaseElem); ok {
				b.Value = gen.Ext
			} else {
				warnln("couldn't cast to extension.")
				return nil
			}
		case *gen.BaseElem:
			ex.Value = gen.Ext
		default:
			warnln("couldn't cast to extension.")
			return nil
		}
	}
	return sf
}
func (fs *FileSet) getFieldsFromEmbeddedStruct(importPrefix string, f ast.Expr) []gen.StructField {
	switch f := f.(type) {
	case *ast.Ident:
		s, ok := fs.Specs[f.Name]
		if !ok {
			s = fs.Aliases[f.Name]
		}
		switch s := s.(type) {
		case *ast.StructType:
			return fs.parseFieldList(importPrefix, s.Fields)
		default:
			return nil
		}
	case *ast.SelectorExpr:
		pkg := f.X
		pkgid, ok := pkg.(*ast.Ident)
		if !ok {
			return nil
		}
		pkgname, ok := fs.ImportName[pkgid.Name]
		if !ok {
			return nil
		}
		pkgfs, ok := fs.ImportSet[pkgname]
		if !ok {
			return nil
		}
		return pkgfs.getFieldsFromEmbeddedStruct(pkgid.Name+".", f.Sel)
	default:
		return nil
	}
}