func stringify(e ast.Expr) string {
	switch e := e.(type) {
	case *ast.Ident:
		return e.Name
	case *ast.StarExpr:
		return "*" + stringify(e.X)
	case *ast.SelectorExpr:
		return stringify(e.X) + "." + e.Sel.Name
	case *ast.ArrayType:
		if e.Len == nil {
			return "[]" + stringify(e.Elt)
		}
		return fmt.Sprintf("[%s]%s", stringify(e.Len), stringify(e.Elt))
	case *ast.InterfaceType:
		if e.Methods == nil || e.Methods.NumFields() == 0 {
			return "interface{}"
		}
	}
	return "<BAD>"
}
stringify a field type name.
package gen
import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"reflect"
	"sort"
	"strings"
)
type source struct {
	pkg        string               
	specs      map[string]ast.Expr  
	identities map[string]Elem      
	directives []string             
	imports    []*ast.ImportSpec    
}
func newSource(srcPath string, unexported bool) (*source, error) {
	pushState(srcPath)
	defer popState()
	s := &source{
		specs:      make(map[string]ast.Expr),
		identities: make(map[string]Elem),
	}
	stat, err := os.Stat(srcPath)
	if err != nil {
		return nil, err
	}
	fset := token.NewFileSet()
	if stat.IsDir() {
		pkgs, err := parser.ParseDir(fset, srcPath, nil, parser.ParseComments)
		if err != nil {
			return nil, err
		}
		if len(pkgs) != 1 {
			return nil, fmt.Errorf("multiple packages in directory %s", srcPath)
		}
		var pkg *ast.Package
		for n := range pkgs {
			s.pkg = n
			pkg = pkgs[n]
			break
		}
		for _, fl := range pkg.Files {
			pushState(fl.Name.Name)
			s.directives = append(s.directives, getComments(fl.Comments)...)
			if !unexported {
				ast.FileExports(fl)
			}
			s.getTypeSpecs(fl)
			popState()
		}
	} else {
		f, err := parser.ParseFile(fset, srcPath, nil, parser.ParseComments)
		if err != nil {
			return nil, err
		}
		s.pkg = f.Name.Name
		s.directives = getComments(f.Comments)
		if !unexported {
			ast.FileExports(f)
		}
		s.getTypeSpecs(f)
	}
	if len(s.specs) == 0 {
		return nil, fmt.Errorf("no definitions in %s", srcPath)
	}
	s.process()
	s.applyDirectives()
	s.propInline()
	return s, nil
}
func (s *source) printTo(gs generatorSet) error {
	s.applyDirs(gs)
	names := make([]string, 0, len(s.identities))
	for name := range s.identities {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		el := s.identities[name]
		el.SetVarname("z")
		pushState(el.TypeName())
		err := gs.Print(el)
		popState()
		if err != nil {
			return err
		}
	}
	return nil
}
func (s *source) applyDirectives() {
	newdirs := make([]string, 0, len(s.directives))
	for _, d := range s.directives {
		chunks := strings.Split(d, " ")
		if len(chunks) > 0 {
			if fn, ok := directives[chunks[0]]; ok {
				pushState(chunks[0])
				if err := fn(chunks, s); err != nil {
					warnln(err.Error())
				}
				popState()
			} else {
				newdirs = append(newdirs, d)
			}
		}
	}
	s.directives = newdirs
}
type linkset map[string]*BaseElem
func (s *source) resolve(ls linkset) {
	progress := true
	for progress && len(ls) > 0 {
		progress = false
		for name, elem := range ls {
			n, ok := s.identities[elem.TypeName()]
			if ok {
				progress = true
				nt := n.Copy()
				nt.Alias(name)
				s.identities[name] = nt
				delete(ls, name)
			}
		}
	}
	for name, elem := range ls {
		warnf("couldn't resolve type %s (%s)\n", name, elem.TypeName())
	}
}
func (s *source) process() {
	deferred := make(linkset)
	for name, def := range s.specs {
		pushState(name)
		el := s.parseExpr(def)
		if el == nil {
			warnln("failed to parse")
			popState()
			continue
		}
		if be, ok := el.(*BaseElem); ok && be.Value == IDENT {
			deferred[name] = be
			popState()
			continue
		}
		el.Alias(name)
		s.identities[name] = el
		popState()
	}
	if len(deferred) > 0 {
		s.resolve(deferred)
	}
}
func strToMethod(s string) Method {
	switch s {
	case "encode":
		return Encode
	case "decode":
		return Decode
	case "test":
		return Test
	case "size":
		return Size
	case "marshal":
		return Marshal
	case "unmarshal":
		return Unmarshal
	default:
		return 0
	}
}
func (s *source) applyDirs(p generatorSet) {
	for _, d := range s.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
			}
			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 (s *source) getTypeSpecs(f *ast.File) {
	s.imports = append(s.imports, f.Imports...)
	for i := range f.Decls {
		if g, ok := f.Decls[i].(*ast.GenDecl); ok {
			for _, spec := range g.Specs {
				if ts, ok := spec.(*ast.TypeSpec); ok {
					switch ts.Type.(type) {  
					case *ast.StructType,
						*ast.ArrayType,
						*ast.StarExpr,
						*ast.MapType,
						*ast.Ident:
						s.specs[ts.Name.Name] = ts.Type
					}
				}
			}
		}
	}
}
func fieldName(f *ast.Field) string {
	l := len(f.Names)
	if l == 0 {
		return stringify(f.Type)
	} else if l == 1 {
		return f.Names[0].Name
	}
	return f.Names[0].Name + " (and others)"
}
func (s *source) parseFieldList(fl *ast.FieldList) []structField {
	if fl == nil || fl.NumFields() == 0 {
		return nil
	}
	out := make([]structField, 0, fl.NumFields())
	for _, field := range fl.List {
		pushState(fieldName(field))
		fds := s.getField(field)
		if len(fds) > 0 {
			out = append(out, fds...)
		} else {
			warnln("ignored.")
		}
		popState()
	}
	return out
}
func (s *source) getField(f *ast.Field) []structField {
	fields := make([]structField, 1)
	var extension bool
	if f.Tag != nil {
		body := reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("msgp")
		tags := strings.Split(body, ",")
		if len(tags) == 2 && tags[1] == "extension" {
			extension = true
		}
		if tags[0] == "-" {
			return nil
		}
		fields[0].fieldTag = tags[0]
		fields[0].rawTag = f.Tag.Value
	}
	ex := s.parseExpr(f.Type)
	if ex == nil {
		return nil
	}
	switch len(f.Names) {
	case 0:
		fields[0].fieldName = embedded(f.Type)
	case 1:
		fields[0].fieldName = f.Names[0].Name
	default:
		fields = fields[0:0]
		for _, nm := range f.Names {
			fields = append(fields, structField{
				fieldTag:  nm.Name,
				fieldName: nm.Name,
				fieldElem: ex.Copy(),
			})
		}
		return fields
	}
	fields[0].fieldElem = ex
	if fields[0].fieldTag == "" {
		fields[0].fieldTag = fields[0].fieldName
	}
	if extension {
		switch ex := ex.(type) {
		case *Ptr:
			if b, ok := ex.Value.(*BaseElem); ok {
				b.Value = Ext
			} else {
				warnln("Couldn't cast to extension.")
				return nil
			}
		case *BaseElem:
			ex.Value = Ext
		default:
			warnln("Couldn't cast to extension.")
			return nil
		}
	}
	return fields
}
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 ""
	}
}