github.com/lheiskan/zebrapack@v4.1.1-0.20181107023619-e955d028f9bf+incompatible/cmd/addzid/bam.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/parser"
     8  	"go/token"
     9  	"io"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"regexp"
    14  	"sort"
    15  	"strconv"
    16  	"strings"
    17  	"unicode"
    18  	//"github.com/shurcooL/go-goon"
    19  )
    20  
    21  type Extractor struct {
    22  	fieldCount  int
    23  	out         bytes.Buffer
    24  	pkgName     string
    25  	importDecl  string
    26  	fieldPrefix string
    27  	fieldSuffix string
    28  
    29  	curStruct      *Struct
    30  	heldComment    string
    31  	extractPrivate bool
    32  
    33  	// map structs' goName <-> capName
    34  	goType2capTypeCache map[string]string
    35  	capType2goType      map[string]string
    36  
    37  	// key is goName
    38  	srs        map[string]*Struct
    39  	ToGoCode   map[string][]byte
    40  	ToCapnCode map[string][]byte
    41  	SaveCode   map[string][]byte
    42  	LoadCode   map[string][]byte
    43  
    44  	// key is CanonGoType(goTypeSeq)
    45  	SliceToListCode map[string][]byte
    46  	ListToSliceCode map[string][]byte
    47  
    48  	compileDir *TempDir
    49  	outDir     string
    50  	srcFiles   []*SrcFile
    51  	overwrite  bool
    52  
    53  	// fields for testing zid tagging
    54  	PubABC int `zid:"1"`
    55  	PubYXZ int `zid:"0"`
    56  	PubDEF int
    57  }
    58  
    59  func NewExtractor() *Extractor {
    60  	return &Extractor{
    61  		pkgName:             "testpkg",
    62  		importDecl:          "testpkg",
    63  		goType2capTypeCache: make(map[string]string),
    64  		capType2goType:      make(map[string]string),
    65  
    66  		// key is goTypeName
    67  		ToGoCode:        make(map[string][]byte),
    68  		ToCapnCode:      make(map[string][]byte),
    69  		SaveCode:        make(map[string][]byte),
    70  		LoadCode:        make(map[string][]byte),
    71  		srs:             make(map[string]*Struct),
    72  		compileDir:      NewTempDir(),
    73  		srcFiles:        make([]*SrcFile, 0),
    74  		SliceToListCode: make(map[string][]byte),
    75  		ListToSliceCode: make(map[string][]byte),
    76  	}
    77  }
    78  
    79  func (x *Extractor) Cleanup() {
    80  	if x.compileDir != nil {
    81  		x.compileDir.Cleanup()
    82  	}
    83  }
    84  
    85  type Field struct {
    86  	capname      string
    87  	goCapGoName  string // Uppercased-first-letter of capname, as generated in go bindings.
    88  	goCapGoType  string // int64 when goType is int, because capType is Int64.
    89  	capType      string
    90  	goName       string
    91  	goType       string
    92  	goTypePrefix string
    93  	goToCapFunc  string
    94  
    95  	goTypeSeq      []string
    96  	capTypeSeq     []string
    97  	goCapGoTypeSeq []string
    98  
    99  	tagValue                   string
   100  	isList                     bool
   101  	zidFromTag                 int
   102  	orderOfAppearance          int
   103  	finalOrder                 int
   104  	embedded                   bool
   105  	astField                   *ast.Field
   106  	canonGoType                string // key into SliceToListCode and ListToSliceCode
   107  	canonGoTypeListToSliceFunc string
   108  	canonGoTypeSliceToListFunc string
   109  	singleCapListType          string
   110  	baseIsIntrinsic            bool
   111  	newListExpression          string
   112  }
   113  
   114  type Struct struct {
   115  	capName              string
   116  	goName               string
   117  	fld                  []*Field
   118  	longestField         int
   119  	comment              string
   120  	zidMap               map[int]*Field
   121  	firstNonTextListSeen bool
   122  	listNum              int
   123  }
   124  
   125  type SrcFile struct {
   126  	filename string
   127  	fset     *token.FileSet
   128  	astFile  *ast.File
   129  }
   130  
   131  func (s *Struct) computeFinalOrder() {
   132  
   133  	// assign Field.finalOrder to all values in s.fld, from 0..(len(s.fld)-1)
   134  	max := len(s.fld) - 1
   135  	// check for out of bounds requests
   136  	for _, f := range s.fld {
   137  		if f.zidFromTag > max {
   138  			err := fmt.Errorf(`problem in zid tag '%s' on field '%s' in struct '%s': number '%d' is beyond the count of fields we have, largest available is %d`, f.tagValue, f.goName, s.goName, f.zidFromTag, max)
   139  			panic(err)
   140  		}
   141  	}
   142  
   143  	// only one field? already done
   144  	if len(s.fld) == 1 {
   145  		s.fld[0].finalOrder = 0
   146  		return
   147  	}
   148  	// INVAR: no duplicate requests, and all are in bounds.
   149  
   150  	// wipe slate clean
   151  	for _, f := range s.fld {
   152  		f.finalOrder = -1
   153  	}
   154  
   155  	final := make([]*Field, len(s.fld))
   156  
   157  	// assign from map
   158  	for _, v := range s.zidMap {
   159  		v.finalOrder = v.zidFromTag
   160  		final[v.zidFromTag] = v
   161  	}
   162  
   163  	appear := make([]*Field, len(s.fld))
   164  	copy(appear, s.fld)
   165  
   166  	sort.Sort(ByOrderOfAppearance(appear))
   167  
   168  	//find next available slot, and fill in, in order of appearance
   169  	write := 0
   170  
   171  	for read := 0; read <= max; read++ {
   172  		if appear[read].finalOrder != -1 {
   173  			continue
   174  		}
   175  		// appear[read] needs an assignment
   176  
   177  		done := advanceWrite(&write, final)
   178  		if !done {
   179  			// final[write] has a slot for an assignment
   180  			final[write] = appear[read]
   181  			final[write].finalOrder = write
   182  		}
   183  
   184  	}
   185  }
   186  
   187  // returns true if done. if false, then final[*pw] is available for writing.
   188  func advanceWrite(pw *int, final []*Field) bool {
   189  	for n := len(final); *pw < n; (*pw)++ {
   190  		if final[*pw] == nil { // .finalOrder == -1 {
   191  			return false
   192  		}
   193  	}
   194  	return true
   195  }
   196  
   197  func NewStruct(capName, goName string) *Struct {
   198  	return &Struct{
   199  		capName: capName,
   200  		goName:  goName,
   201  		fld:     []*Field{},
   202  		zidMap:  map[int]*Field{},
   203  	}
   204  }
   205  
   206  func (x *Extractor) GenerateTranslators() {
   207  
   208  	for _, s := range x.srs {
   209  
   210  		x.SaveCode[s.goName] = []byte(fmt.Sprintf(`
   211  func (s *%s) Save(w io.Writer) error {
   212    	seg := capn.NewBuffer(nil)
   213    	%sGoToCapn(seg, s)
   214      _, err := seg.WriteTo(w)
   215      return err
   216  }
   217   `, s.goName, s.goName))
   218  
   219  		x.LoadCode[s.goName] = []byte(fmt.Sprintf(`
   220  func (s *%s) Load(r io.Reader) error {
   221    	capMsg, err := capn.ReadFromStream(r, nil)
   222    	if err != nil {
   223    		//panic(fmt.Errorf("capn.ReadFromStream error: %%s", err))
   224          return err
   225    	}
   226    	z := ReadRoot%s(capMsg)
   227        %sToGo(z, s)
   228     return nil
   229  }
   230  `, s.goName, s.capName, s.capName))
   231  
   232  		x.ToGoCode[s.goName] = []byte(fmt.Sprintf(`
   233  func %sToGo(src %s, dest *%s) *%s {
   234    if dest == nil {
   235      dest = &%s{}
   236    }
   237  %s
   238    return dest
   239  }
   240  `, s.capName, s.capName, s.goName, s.goName, s.goName, x.SettersToGo(s.goName)))
   241  
   242  		x.ToCapnCode[s.goName] = []byte(fmt.Sprintf(`
   243  func %sGoToCapn(seg *capn.Segment, src *%s) %s {
   244    dest := AutoNew%s(seg)
   245  %s
   246    return dest
   247  }
   248  `, s.goName, s.goName, s.capName, s.capName, x.SettersToCapn(s.goName)))
   249  
   250  	}
   251  }
   252  
   253  func (x *Extractor) packageDot() string {
   254  	if x.pkgName == "" || x.pkgName == "main" {
   255  		return ""
   256  	}
   257  	return x.pkgName + "."
   258  }
   259  
   260  func (x *Extractor) SettersToGo(goName string) string {
   261  	var buf bytes.Buffer
   262  	myStruct := x.srs[goName]
   263  	if myStruct == nil {
   264  		panic(fmt.Sprintf("bad goName '%s'", goName))
   265  	}
   266  	VPrintf("\n\n SettersToGo running on myStruct = %#v\n", myStruct)
   267  	i := 0
   268  	for _, f := range myStruct.fld {
   269  		VPrintf("\n\n SettersToGo running on myStruct.fld[%d] = %#v\n", i, f)
   270  
   271  		n := len(f.goTypeSeq)
   272  
   273  		if n >= 2 && f.goTypeSeq[0] == "[]" {
   274  			x.SettersToGoListHelper(&buf, myStruct, f)
   275  		} else {
   276  
   277  			var isCapType bool = false
   278  			if _, ok := x.goType2capTypeCache[f.goTypeSeq[0]]; !ok {
   279  				if len(f.goTypeSeq) > 1 {
   280  					if _, ok := x.goType2capTypeCache[f.goTypeSeq[1]]; ok {
   281  						isCapType = true
   282  					}
   283  				}
   284  			} else {
   285  				isCapType = true
   286  			}
   287  
   288  			if isCapType {
   289  				if f.goTypeSeq[0] == "*" {
   290  					fmt.Fprintf(&buf, "  dest.%s = %sCapnToGo(src.%s(), nil)\n",
   291  						f.goName, f.goType, f.goCapGoName)
   292  				} else {
   293  					fmt.Fprintf(&buf, "  dest.%s = *%sCapnToGo(src.%s(), nil)\n",
   294  						f.goName, f.goType, f.goCapGoName)
   295  				}
   296  
   297  				continue
   298  			}
   299  
   300  			switch f.goType {
   301  			case "int":
   302  				fmt.Fprintf(&buf, "  dest.%s = int(src.%s())\n", f.goName, f.goCapGoName)
   303  
   304  			default:
   305  				fmt.Fprintf(&buf, "  dest.%s = src.%s()\n", f.goName, f.goCapGoName)
   306  			}
   307  		}
   308  		i++
   309  	}
   310  	return string(buf.Bytes())
   311  }
   312  
   313  func (x *Extractor) SettersToGoListHelper(buf io.Writer, myStruct *Struct, f *Field) {
   314  
   315  	VPrintf("\n in SettersToGoListHelper(): debug: field f = %#v\n", f)
   316  	VPrintf("\n in SettersToGoListHelper(): debug: myStruct = %#v\n", myStruct)
   317  
   318  	// special case Text / string slices
   319  	if f.capType == "List(Text)" {
   320  		fmt.Fprintf(buf, "  dest.%s = src.%s().ToArray()\n", f.goName, f.goCapGoName)
   321  		return
   322  	}
   323  	if !myStruct.firstNonTextListSeen {
   324  		fmt.Fprintf(buf, "\n  var n int\n")
   325  		myStruct.firstNonTextListSeen = true
   326  	}
   327  	// add a dereference (*) in from of the ToGo() invocation for go types that aren't pointers.
   328  	addStar := "*"
   329  	if isPointerType(f.goTypePrefix) {
   330  		addStar = ""
   331  	}
   332  
   333  	fmt.Fprintf(buf, `
   334      // %s
   335  	n = src.%s().Len()
   336  	dest.%s = make(%s%s, n)
   337  	for i := 0; i < n; i++ {
   338          dest.%s[i] = %s
   339      }
   340  
   341  `, f.goName, f.goCapGoName, f.goName, f.goTypePrefix, f.goType, f.goName, x.ElemStarCapToGo(addStar, f))
   342  
   343  }
   344  
   345  func (x *Extractor) ElemStarCapToGo(addStar string, f *Field) string {
   346  	f.goToCapFunc = x.goToCapTypeFunction(f.capTypeSeq)
   347  
   348  	VPrintf("\n\n f = %#v   addStar = '%v'    f.goToCapFunc = '%s'\n", f, addStar, f.goToCapFunc)
   349  
   350  	// list of list special handling, try to generalize it, as it needs
   351  	// to work for intrinsics and structs
   352  	if f.goTypePrefix == "[][]" {
   353  		VPrintf("\n slice of slice / ListList detected.\n")
   354  		return fmt.Sprintf("%s(%s(src.%s().At(i)))", f.canonGoTypeListToSliceFunc, f.singleCapListType, f.goName)
   355  	}
   356  
   357  	if IsIntrinsicGoType(f.goType) {
   358  		VPrintf("\n intrinsic detected.\n")
   359  		return fmt.Sprintf("%s(src.%s().At(i))", f.goType, f.goName)
   360  	} else {
   361  		VPrintf("\n non-intrinsic detected. f.goType = '%v'\n", f.goType)
   362  		return fmt.Sprintf("%s%sToGo(src.%s().At(i), nil)", addStar, f.goToCapFunc, f.goName)
   363  	}
   364  }
   365  
   366  func (x *Extractor) goToCapTypeFunction(capTypeSeq []string) string {
   367  	var r string
   368  	for _, s := range capTypeSeq {
   369  		if s != "*" && s != "List" {
   370  			r = r + s
   371  		}
   372  	}
   373  	return r
   374  }
   375  
   376  func isPointerType(goTypePrefix string) bool {
   377  	if len(goTypePrefix) == 0 {
   378  		return false
   379  	}
   380  	prefix := []rune(goTypePrefix)
   381  	if prefix[len(prefix)-1] == '*' {
   382  		return true
   383  	}
   384  	return false
   385  }
   386  
   387  func last(slc []string) string {
   388  	n := len(slc)
   389  	if n == 0 {
   390  		panic("last of empty slice undefined")
   391  	}
   392  	return slc[n-1]
   393  }
   394  
   395  func IsDoubleList(f *Field) bool {
   396  	if len(f.capTypeSeq) > 2 {
   397  		if f.capTypeSeq[0] == f.capTypeSeq[1] && f.capTypeSeq[1] == "List" {
   398  			return true
   399  		}
   400  	}
   401  	return false
   402  }
   403  
   404  func (x *Extractor) SettersToCapn(goName string) string {
   405  	var buf bytes.Buffer
   406  	t := x.srs[goName]
   407  	if t == nil {
   408  		panic(fmt.Sprintf("bad goName '%s'", goName))
   409  	}
   410  	VPrintf("\n\n SettersToCapn running on myStruct = %#v\n", t)
   411  	for i, f := range t.fld {
   412  		VPrintf("\n\n SettersToCapn running on t.fld[%d] = %#v\n", i, f)
   413  
   414  		if f.isList {
   415  			t.listNum++
   416  			if IsIntrinsicGoType(f.goType) {
   417  				VPrintf("\n intrinsic detected in SettersToCapn.\n")
   418  
   419  				if IsDoubleList(f) {
   420  					VPrintf("\n yes IsDoubleList(f: '%s') in SettersToCapn.\n", f.goName)
   421  
   422  					fmt.Fprintf(&buf, `
   423  
   424    mylist%d := seg.NewPointerList(len(src.%s))
   425    for i := range src.%s {
   426       mylist%d.Set(i, capn.Object(%s(seg, src.%s[i])))
   427    }
   428    dest.Set%s(mylist%d)
   429  `, t.listNum, f.goName, f.goName, t.listNum, f.canonGoTypeSliceToListFunc, f.goName, f.goCapGoName, t.listNum)
   430  
   431  				} else {
   432  					VPrintf("\n\n  at non-List(List()), yes intrinsic list in SettersToCap(): f = %#v\n", f)
   433  					fmt.Fprintf(&buf, `
   434  
   435    mylist%d := seg.New%sList(len(src.%s))
   436    for i := range src.%s {
   437       mylist%d.Set(i, %s(src.%s[i]))
   438    }
   439    dest.Set%s(mylist%d)
   440  `, t.listNum, last(f.capTypeSeq), f.goName, f.goName, t.listNum, last(f.goCapGoTypeSeq), f.goName, f.goCapGoName, t.listNum)
   441  				}
   442  			} else {
   443  				// handle list of struct
   444  				VPrintf("\n\n  at struct list in SettersToCap(): f = %#v\n", f)
   445  				addAmpersand := "&"
   446  				if isPointerType(f.goTypePrefix) {
   447  					addAmpersand = ""
   448  				}
   449  
   450  				// list of list needs special casing
   451  				if isListList(f.goTypePrefix) {
   452  					fmt.Fprintf(&buf, `
   453    // %s -> %s (go slice to capn list)
   454    if len(src.%s) > 0 {
   455  		plist := seg.NewPointerList(len(src.%s))
   456  		i := 0
   457  		for _, ele := range src.%s {
   458  			plist.Set(i, capn.Object(%s(seg, ele)))
   459  			i++
   460  		}
   461  		dest.Set%s(plist)
   462  	}
   463  `, f.goName, f.goToCapFunc, f.goName, f.goName, f.goName, f.canonGoTypeSliceToListFunc, f.goCapGoName)
   464  				} else {
   465  					fmt.Fprintf(&buf, `
   466    // %s -> %s (go slice to capn list)
   467    if len(src.%s) > 0 {
   468  		typedList := New%sList(seg, len(src.%s))
   469  		plist := capn.PointerList(typedList)
   470  		i := 0
   471  		for _, ele := range src.%s {
   472  			plist.Set(i, capn.Object(%sGoToCapn(seg, %sele)))
   473  			i++
   474  		}
   475  		dest.Set%s(typedList)
   476  	}
   477  `, f.goName, f.goToCapFunc, f.goName, f.goToCapFunc, f.goName, f.goName, f.goType, addAmpersand, f.goCapGoName)
   478  				}
   479  			} // end switch f.goType
   480  
   481  		} else {
   482  
   483  			var isCapType bool = false
   484  			if _, ok := x.goType2capTypeCache[f.goTypeSeq[0]]; !ok {
   485  				if len(f.goTypeSeq) > 1 {
   486  					if _, ok := x.goType2capTypeCache[f.goTypeSeq[1]]; ok {
   487  						isCapType = true
   488  					}
   489  				}
   490  			} else {
   491  				isCapType = true
   492  			}
   493  
   494  			if isCapType {
   495  				if f.goTypeSeq[0] == "*" {
   496  					fmt.Fprintf(&buf, "  dest.Set%s(%sGoToCapn(seg, src.%s))\n",
   497  						f.goName, f.goType, f.goName)
   498  				} else {
   499  					fmt.Fprintf(&buf, "  dest.Set%s(%sGoToCapn(seg, &src.%s))\n",
   500  						f.goName, f.goType, f.goName)
   501  				}
   502  
   503  				continue
   504  			}
   505  
   506  			switch f.goType {
   507  			case "int":
   508  				fmt.Fprintf(&buf, "  dest.Set%s(int64(src.%s))\n", f.goCapGoName, f.goName)
   509  			default:
   510  				fmt.Fprintf(&buf, "  dest.Set%s(src.%s)\n", f.goCapGoName, f.goName)
   511  			}
   512  		}
   513  	}
   514  	return string(buf.Bytes())
   515  }
   516  
   517  func isListList(goTypePrefix string) bool {
   518  	return strings.HasPrefix(goTypePrefix, "[][]")
   519  }
   520  
   521  func (x *Extractor) ToGoCodeFor(goName string) []byte {
   522  	return x.ToGoCode[goName]
   523  }
   524  
   525  func (x *Extractor) ToCapnCodeFor(goStructName string) []byte {
   526  	return x.ToCapnCode[goStructName]
   527  }
   528  
   529  func (x *Extractor) WriteToSchema(w io.Writer) (n int64, err error) {
   530  
   531  	var m int
   532  	var spaces string
   533  
   534  	// sort structs alphabetically to get a stable (testable) ordering.
   535  	sortedStructs := ByGoName(make([]*Struct, 0, len(x.srs)))
   536  	for _, strct := range x.srs {
   537  		sortedStructs = append(sortedStructs, strct)
   538  	}
   539  	sort.Sort(ByGoName(sortedStructs))
   540  
   541  	for _, s := range sortedStructs {
   542  
   543  		if w != nil {
   544  			m, err = fmt.Fprintf(w, "%sstruct %s { %s", x.fieldSuffix, s.capName, x.fieldSuffix)
   545  			n += int64(m)
   546  			if err != nil {
   547  				return
   548  			}
   549  		}
   550  
   551  		s.computeFinalOrder()
   552  
   553  		sort.Sort(ByFinalOrder(s.fld))
   554  
   555  		for i, fld := range s.fld {
   556  
   557  			VPrintf("\n\n debug in WriteToSchema(), fld = %#v\n", fld)
   558  
   559  			SetSpaces(&spaces, s.longestField, len(fld.capname))
   560  
   561  			capType, already := x.goType2capTypeCache[fld.goType]
   562  			if !already {
   563  				VPrintf("\n\n debug: setting capType = '%s', instead of '%s', already = false\n", fld.capType, capType)
   564  				capType = fld.capType
   565  			} else {
   566  				VPrintf("\n\n debug: already = true, capType = '%s'   fld.capType = %v\n", capType, fld.capType)
   567  			}
   568  
   569  			if w != nil {
   570  				m, err = fmt.Fprintf(w, "%s%s  %s@%d: %s%s; %s", x.fieldPrefix, fld.capname, spaces, fld.finalOrder, ExtraSpaces(i), fld.capType, x.fieldSuffix)
   571  				//m, err = fmt.Fprintf(w, "%s%s  %s@%d: %s%s; %s", x.fieldPrefix, fld.capname, spaces, fld.finalOrder, ExtraSpaces(i), capType, x.fieldSuffix)
   572  				n += int64(m)
   573  				if err != nil {
   574  					return
   575  				}
   576  			}
   577  		} // end field loop
   578  
   579  		if w != nil {
   580  			m, err = fmt.Fprintf(w, "} %s", x.fieldSuffix)
   581  			n += int64(m)
   582  			if err != nil {
   583  				return
   584  			}
   585  		}
   586  
   587  	} // end loop over structs
   588  
   589  	return
   590  }
   591  
   592  func (x *Extractor) GenZidTag(f *Field) string {
   593  	if f.astField == nil {
   594  		f.astField = &ast.Field{}
   595  	}
   596  	if f.astField.Tag == nil {
   597  		f.astField.Tag = &ast.BasicLit{}
   598  	}
   599  
   600  	curTag := f.astField.Tag.Value
   601  
   602  	if hasZidTag(curTag) {
   603  		//p("has current zid tag, returning early")
   604  		return curTag
   605  	}
   606  
   607  	if hasMsgDashTag(curTag) {
   608  		//p("has current msg:\"-\" tag, returning early")
   609  		return curTag
   610  	}
   611  
   612  	//p("no `zid` tag found, adding a `zid` tag... for f = %#v\n", f)
   613  	// else add one
   614  	addme := fmt.Sprintf(`zid:"%d"`, f.finalOrder)
   615  	if curTag == "" || curTag == "``" {
   616  		return fmt.Sprintf("`%s`", addme)
   617  	}
   618  	return fmt.Sprintf("`%s %s`", stripBackticks(curTag), addme)
   619  }
   620  
   621  func hasZidTag(s string) bool {
   622  	return strings.Contains(s, "zid")
   623  }
   624  
   625  // does it have msp:"-" as a tag?
   626  var reUnserz = regexp.MustCompile("`\\s*msg:\\s*\"-\"\\s*`")
   627  
   628  // does it have msp:"-" as a tag?
   629  func hasMsgDashTag(s string) bool {
   630  	return reUnserz.MatchString(s)
   631  }
   632  
   633  func stripBackticks(s string) string {
   634  	if len(s) == 0 {
   635  		return s
   636  	}
   637  
   638  	r := []rune(s)
   639  	if r[0] == '`' {
   640  		r = r[1:]
   641  	}
   642  	if len(r) > 0 && r[len(r)-1] == '`' {
   643  		r = r[:len(r)-1]
   644  	}
   645  	return string(r)
   646  }
   647  
   648  func (x *Extractor) CopySourceFilesAddZidTag(w io.Writer) error {
   649  
   650  	// run through struct fields, adding tags
   651  	for _, s := range x.srs {
   652  		for _, f := range s.fld {
   653  
   654  			VPrintf("\n\n\n ********** before  f.astField.Tag = %#v\n", f.astField.Tag)
   655  			f.astField.Tag.Value = x.GenZidTag(f)
   656  			VPrintf("\n\n\n ********** AFTER:  f.astField.Tag = %#v\n", f.astField.Tag)
   657  		}
   658  	}
   659  
   660  	if w != nil {
   661  		//p("w was not nil... len(x.srcFiles)=%v", len(x.srcFiles))
   662  		// run through files, printing to w
   663  		for _, s := range x.srcFiles {
   664  			//p("calling x.PrettyPrint...")
   665  			err := x.PrettyPrint(w, s.fset, s.astFile)
   666  			if err != nil {
   667  				return err
   668  			}
   669  		}
   670  	} else {
   671  		//p("w was nil... printing to odir, len(x.srcFiles)=%v", len(x.srcFiles))
   672  
   673  		// run through files, printing to odir
   674  		for _, s := range x.srcFiles {
   675  			if s.filename != "" {
   676  
   677  				f, err := os.Create(x.compileDir.DirPath + string(os.PathSeparator) + s.filename)
   678  				if err != nil {
   679  					panic(err)
   680  				}
   681  				err = x.PrettyPrint(f, s.fset, s.astFile)
   682  				if err != nil {
   683  					return err
   684  				}
   685  			}
   686  		}
   687  	}
   688  
   689  	if x.overwrite {
   690  		bk := fmt.Sprintf("%s%cbk%c", x.compileDir.DirPath, os.PathSeparator, os.PathSeparator)
   691  		err := os.MkdirAll(bk, 0755)
   692  		if err != nil {
   693  			panic(err)
   694  		}
   695  		for _, s := range x.srcFiles {
   696  			if s.filename != "" {
   697  				// make a backup
   698  				err := Cp(s.filename, bk+s.filename)
   699  				if err != nil {
   700  					panic(err)
   701  				}
   702  				// overwrite
   703  				err = Cp(x.compileDir.DirPath+string(os.PathSeparator)+s.filename, s.filename)
   704  				if err != nil {
   705  					panic(err)
   706  				}
   707  			}
   708  		}
   709  
   710  	}
   711  
   712  	return nil
   713  }
   714  
   715  func (x *Extractor) WriteToTranslators(w io.Writer) (n int64, err error) {
   716  
   717  	var m int
   718  
   719  	x.GenerateTranslators()
   720  
   721  	// sort structs alphabetically to get a stable (testable) ordering.
   722  	sortedStructs := ByGoName(make([]*Struct, 0, len(x.srs)))
   723  	for _, strct := range x.srs {
   724  		sortedStructs = append(sortedStructs, strct)
   725  	}
   726  	sort.Sort(ByGoName(sortedStructs))
   727  
   728  	// now print the translating methods, in a second pass over structures, to accomodate
   729  	// our test structure
   730  	for _, s := range sortedStructs {
   731  
   732  		m, err = fmt.Fprintf(w, "\n\n")
   733  		n += int64(m)
   734  		if err != nil {
   735  			return
   736  		}
   737  
   738  		m, err = w.Write(x.SaveCode[s.goName])
   739  		n += int64(m)
   740  		if err != nil {
   741  			return
   742  		}
   743  
   744  		m, err = fmt.Fprintf(w, "\n\n")
   745  		n += int64(m)
   746  		if err != nil {
   747  			return
   748  		}
   749  
   750  		m, err = w.Write(x.LoadCode[s.goName])
   751  		n += int64(m)
   752  		if err != nil {
   753  			return
   754  		}
   755  
   756  		m, err = fmt.Fprintf(w, "\n\n")
   757  		n += int64(m)
   758  		if err != nil {
   759  			return
   760  		}
   761  
   762  		m, err = w.Write(x.ToGoCodeFor(s.goName))
   763  		n += int64(m)
   764  		if err != nil {
   765  			return
   766  		}
   767  
   768  		m, err = fmt.Fprintf(w, "\n\n")
   769  		n += int64(m)
   770  		if err != nil {
   771  			return
   772  		}
   773  
   774  		m, err = w.Write(x.ToCapnCodeFor(s.goName))
   775  		n += int64(m)
   776  		if err != nil {
   777  			return
   778  		}
   779  
   780  	} // end second loop over structs for translating methods.
   781  
   782  	// print the helpers made from x.GenerateListHelpers(capListTypeSeq, goTypeSeq)
   783  	// sort helper functions to get consistent (testable) order.
   784  	a := make([]AlphaHelper, len(x.SliceToListCode)+len(x.ListToSliceCode))
   785  	i := 0
   786  	for k, v := range x.SliceToListCode {
   787  		a[i].Name = k
   788  		a[i].Code = v
   789  		i++
   790  	}
   791  	for k, v := range x.ListToSliceCode {
   792  		a[i].Name = k
   793  		a[i].Code = v
   794  		i++
   795  	}
   796  
   797  	sort.Sort(AlphaHelperSlice(a))
   798  
   799  	for _, help := range a {
   800  
   801  		m, err = fmt.Fprintf(w, "\n\n")
   802  		n += int64(m)
   803  		if err != nil {
   804  			return
   805  		}
   806  
   807  		m, err = w.Write(help.Code)
   808  		n += int64(m)
   809  		if err != nil {
   810  			return
   811  		}
   812  
   813  	}
   814  
   815  	return
   816  }
   817  
   818  func ExtractFromString(src string) ([]byte, error) {
   819  	return ExtractStructs("", "package main; "+src, nil)
   820  }
   821  
   822  func ExtractString2String(src string) string {
   823  
   824  	x := NewExtractor()
   825  	defer x.Cleanup()
   826  	_, err := ExtractStructs("", "package main; "+src, x)
   827  	if err != nil {
   828  		panic(err)
   829  	}
   830  
   831  	//goon.Dump(x.srs)
   832  
   833  	// final write, this time accounting for zid tag ordering
   834  	var buf bytes.Buffer
   835  	_, err = x.WriteToSchema(&buf)
   836  	if err != nil {
   837  		panic(err)
   838  	}
   839  	_, err = x.WriteToTranslators(&buf)
   840  	if err != nil {
   841  		panic(err)
   842  	}
   843  	err = x.CopySourceFilesAddZidTag(&buf)
   844  	if err != nil {
   845  		panic(err)
   846  	}
   847  
   848  	return string(buf.Bytes())
   849  }
   850  
   851  func ExtractCapnToGoCode(src string, goName string) string {
   852  
   853  	x := NewExtractor()
   854  	defer x.Cleanup()
   855  	_, err := ExtractStructs("", "package main; "+src, x)
   856  	if err != nil {
   857  		panic(err)
   858  	}
   859  	x.GenerateTranslators()
   860  	return string(x.ToGoCodeFor(goName))
   861  }
   862  
   863  func ExtractGoToCapnCode(src string, goName string) string {
   864  
   865  	x := NewExtractor()
   866  	defer x.Cleanup()
   867  	_, err := ExtractStructs("", "package main; "+src, x)
   868  	if err != nil {
   869  		panic(err)
   870  	}
   871  	x.GenerateTranslators()
   872  	return string(x.ToCapnCodeFor(goName))
   873  }
   874  
   875  // ExtractStructs pulls out the struct definitions from a golang source file.
   876  //
   877  // src has to be string, []byte, or io.Reader, as in parser.ParseFile(). src
   878  // can be nil if fname is provided. See http://golang.org/pkg/go/parser/#ParseFile
   879  //
   880  func ExtractStructs(fname string, src interface{}, x *Extractor) ([]byte, error) {
   881  	if x == nil {
   882  		x = NewExtractor()
   883  		defer x.Cleanup()
   884  	}
   885  
   886  	return x.ExtractStructsFromOneFile(src, fname)
   887  }
   888  
   889  func (x *Extractor) ExtractStructsFromOneFile(src interface{}, fname string) ([]byte, error) {
   890  	//p("starting ExtractStructsFromOneFile(fname='%s')", fname)
   891  	fset := token.NewFileSet() // positions are relative to fset
   892  
   893  	f, err := parser.ParseFile(fset, fname, src, parser.ParseComments)
   894  	if err != nil {
   895  		panic(err)
   896  	}
   897  
   898  	if fname != "" {
   899  		//p("adding fname='%s' to x.srcFiles...", fname)
   900  		x.srcFiles = append(x.srcFiles, &SrcFile{filename: fname, fset: fset, astFile: f})
   901  	}
   902  
   903  	VPrintf("parsed output f.Decls is:\n")
   904  	VPrintf("len(f.Decls) = %d\n", len(f.Decls))
   905  
   906  	for _, v := range f.Decls {
   907  		switch v.(type) {
   908  		case *ast.GenDecl:
   909  			d := v.(*ast.GenDecl)
   910  			//VPrintf("dump of d, an %#v = \n", ty)
   911  			//goon.Dump(d)
   912  
   913  			VPrintf("\n\n\n  detail dump of d.Specs\n")
   914  			for _, spe := range d.Specs {
   915  				switch spe.(type) {
   916  				case (*ast.TypeSpec):
   917  
   918  					// go back and print the comments
   919  					if d.Doc != nil && d.Doc.List != nil && len(d.Doc.List) > 0 {
   920  						for _, com := range d.Doc.List {
   921  							x.GenerateComment(com.Text)
   922  						}
   923  					}
   924  
   925  					typeSpec := spe.(*ast.TypeSpec)
   926  					VPrintf("\n\n *ast.TypeSpec spe = \n")
   927  
   928  					if typeSpec.Name.Obj.Kind == ast.Typ {
   929  
   930  						switch typeSpec.Name.Obj.Decl.(type) {
   931  						case (*ast.TypeSpec):
   932  							//curStructName := typeSpec.Name.String()
   933  							curStructName := typeSpec.Name.Name
   934  							ts2 := typeSpec.Name.Obj.Decl.(*ast.TypeSpec)
   935  
   936  							VPrintf("\n\n  in ts2 = %#v\n", ts2)
   937  							//goon.Dump(ts2)
   938  
   939  							switch ty := ts2.Type.(type) {
   940  							default:
   941  								// *ast.InterfaceType and *ast.Ident end up here.
   942  								VPrintf("\n\n unrecog type ty = %#v\n", ty)
   943  							case (*ast.Ident):
   944  								goNewTypeName := ts2.Name.Obj.Name
   945  								goTargetTypeName := ty.Name
   946  								x.NoteTypedef(goNewTypeName, goTargetTypeName)
   947  
   948  							case (*ast.StructType):
   949  								stru := ts2.Type.(*ast.StructType)
   950  
   951  								err = x.StartStruct(curStructName)
   952  								if err != nil {
   953  									return []byte{}, err
   954  								}
   955  								VPrintf("\n\n stru = %#v\n", stru)
   956  								//goon.Dump(stru)
   957  
   958  								if stru.Fields != nil {
   959  									for _, fld := range stru.Fields.List {
   960  										if fld != nil {
   961  											VPrintf("\n\n    fld.Names = %#v\n", fld.Names) // looking for
   962  											//goon.Dump(fld.Names)
   963  
   964  											if len(fld.Names) == 0 {
   965  												// field without name: embedded/anonymous struct
   966  												var typeName string
   967  												switch nmmm := fld.Type.(type) {
   968  												case *ast.StarExpr:
   969  													// ???
   970  												case *ast.Ident:
   971  													typeName = nmmm.Name
   972  													err = x.GenerateStructField(typeName, "", typeName, fld, NotList, fld.Tag, YesEmbedded, []string{typeName})
   973  													if err != nil {
   974  														return []byte{}, err
   975  													}
   976  												}
   977  
   978  											} else {
   979  												// field with name
   980  												for _, ident := range fld.Names {
   981  
   982  													switch ident.Obj.Decl.(type) {
   983  													case (*ast.Field):
   984  														// named field
   985  														fld2 := ident.Obj.Decl.(*ast.Field)
   986  
   987  														VPrintf("\n\n    fld2 = %#v\n", fld2)
   988  														//goon.Dump(fld2)
   989  
   990  														typeNamePrefix, ident4, gotypeseq := GetTypeAsString(fld2.Type, "", []string{})
   991  														VPrintf("\n\n tnas = %#v, ident4 = %s\n", typeNamePrefix, ident4)
   992  
   993  														err = x.GenerateStructField(ident.Name, typeNamePrefix, ident4, fld2, IsSlice(typeNamePrefix), fld2.Tag, NotEmbedded, gotypeseq)
   994  														if err != nil {
   995  															return []byte{}, err
   996  														}
   997  													}
   998  												}
   999  											}
  1000  
  1001  										}
  1002  									}
  1003  								}
  1004  
  1005  								VPrintf("} // end of %s \n\n", typeSpec.Name) // prod
  1006  								x.EndStruct()
  1007  
  1008  								//goon.Dump(stru)
  1009  								VPrintf("\n =========== end stru =======\n\n\n")
  1010  							}
  1011  						}
  1012  					}
  1013  
  1014  				}
  1015  			}
  1016  		}
  1017  	}
  1018  
  1019  	return x.out.Bytes(), err
  1020  }
  1021  
  1022  func IsSlice(tnas string) bool {
  1023  	return strings.HasPrefix(tnas, "[]")
  1024  }
  1025  
  1026  func (x *Extractor) NoteTypedef(goNewTypeName string, goTargetTypeName string) {
  1027  	// we just want to preserve the mapping, without adding Capn suffix
  1028  	VPrintf("\n\n noting typedef: goNewTypeName: '%s', goTargetTypeName: '%s'\n", goNewTypeName, goTargetTypeName)
  1029  	var capTypeSeq []string
  1030  	capTypeSeq, x.goType2capTypeCache[goNewTypeName] = x.GoTypeToCapnpType(nil, []string{goTargetTypeName})
  1031  	VPrintf("\n\n 888 noting typedef: goNewTypeName: '%s', goTargetTypeName: '%s'   x.goType2capTypeCache[goNewTypeName]: '%v'  capTypeSeq: '%v'\n", goNewTypeName, goTargetTypeName, x.goType2capTypeCache[goNewTypeName], capTypeSeq)
  1032  }
  1033  
  1034  var regexCapname = regexp.MustCompile(`capname:[ \t]*\"([^\"]+)\"`)
  1035  
  1036  var regexZid = regexp.MustCompile(`zid:[ \t]*\"([^\"]+)\"`)
  1037  
  1038  func GoType2CapnType(gotypeName string) string {
  1039  	return UppercaseFirstLetter(gotypeName) + "Capn"
  1040  }
  1041  
  1042  func (x *Extractor) StartStruct(goName string) error {
  1043  	x.fieldCount = 0
  1044  
  1045  	capname := GoType2CapnType(goName)
  1046  	x.goType2capTypeCache[goName] = capname
  1047  
  1048  	VPrintf("\n\n debug 777 setting x.goType2capTypeCache['%s'] = '%s'\n", goName, capname)
  1049  
  1050  	x.capType2goType[capname] = goName
  1051  
  1052  	// check for rename comment, capname:"newCapName"
  1053  	if x.heldComment != "" {
  1054  
  1055  		match := regexCapname.FindStringSubmatch(x.heldComment)
  1056  		if match != nil {
  1057  			if len(match) == 2 {
  1058  				capname = match[1]
  1059  			}
  1060  		}
  1061  	}
  1062  
  1063  	if isCapnpKeyword(capname) {
  1064  		err := fmt.Errorf(`after uppercasing the first letter, struct '%s' becomes '%s' but this is a reserved capnp word, so please write a comment annotation just before the struct definition in go (e.g. // capname:"capName") to rename it.`, goName, capname)
  1065  		panic(err)
  1066  		return err
  1067  	}
  1068  
  1069  	fmt.Fprintf(&x.out, "struct %s { %s", capname, x.fieldSuffix)
  1070  
  1071  	x.curStruct = NewStruct(capname, goName)
  1072  	x.curStruct.comment = x.heldComment
  1073  	x.heldComment = ""
  1074  	x.srs[goName] = x.curStruct
  1075  
  1076  	return nil
  1077  }
  1078  func (x *Extractor) EndStruct() {
  1079  	fmt.Fprintf(&x.out, "} %s", x.fieldSuffix)
  1080  }
  1081  
  1082  func (x *Extractor) GenerateComment(c string) {
  1083  	x.heldComment = x.heldComment + c + "\n"
  1084  }
  1085  
  1086  func UppercaseFirstLetter(name string) string {
  1087  	if len(name) == 0 {
  1088  		return name
  1089  	}
  1090  
  1091  	// gotta upercase the first letter of type (struct) names
  1092  	runes := []rune(name)
  1093  	runes[0] = unicode.ToUpper(runes[0])
  1094  	return string(runes)
  1095  
  1096  }
  1097  
  1098  func LowercaseCapnpFieldName(name string) string {
  1099  	if len(name) == 0 {
  1100  		return name
  1101  	}
  1102  
  1103  	// gotta lowercase the first letter of field names
  1104  	runes := []rune(name)
  1105  	runes[0] = unicode.ToLower(runes[0])
  1106  	return string(runes)
  1107  }
  1108  
  1109  const YesIsList = true
  1110  const NotList = false
  1111  
  1112  const NotEmbedded = false
  1113  const YesEmbedded = true
  1114  
  1115  func (x *Extractor) GenerateStructField(goFieldName string, goFieldTypePrefix string, goFieldTypeName string, astfld *ast.Field, isList bool, tag *ast.BasicLit, IsEmbedded bool, goTypeSeq []string) error {
  1116  
  1117  	//p("in GenerateStructField(): goFieldName = '%s'.  goFieldTypeName='%s'.",goFieldName, goFieldTypeName)
  1118  
  1119  	if goFieldTypeName == "" {
  1120  		return nil
  1121  	}
  1122  
  1123  	// skip protobuf side effects
  1124  	if goFieldTypeName == "XXX_unrecognized" {
  1125  		fmt.Printf("detected XXX_unrecognized\n")
  1126  		return nil
  1127  	}
  1128  
  1129  	VPrintf("\n\n\n GenerateStructField called with goFieldName = '%s', goFieldTypeName = '%s', astfld = %#v, tag = %#v\n\n", goFieldName, goFieldTypeName, astfld, tag)
  1130  
  1131  	// if we are ignoring private (lowercase first letter) fields, then stop here.
  1132  	if !IsEmbedded {
  1133  		if len(goFieldName) > 0 && unicode.IsLower([]rune(goFieldName)[0]) && !x.extractPrivate {
  1134  			return nil
  1135  		}
  1136  	}
  1137  
  1138  	curField := &Field{orderOfAppearance: x.fieldCount, embedded: IsEmbedded, astField: astfld, goTypeSeq: goTypeSeq, capTypeSeq: []string{}, zidFromTag: -1}
  1139  
  1140  	var tagValue string
  1141  	loweredName := underToCamelCase(LowercaseCapnpFieldName(goFieldName))
  1142  
  1143  	if tag != nil {
  1144  		VPrintf("tag = %#v\n", tag)
  1145  
  1146  		if tag.Value != "" {
  1147  
  1148  			// capname tag
  1149  			match := regexCapname.FindStringSubmatch(tag.Value)
  1150  			if match != nil {
  1151  				if len(match) == 2 {
  1152  					VPrintf("matched, using '%s' instead of '%s'\n", match[1], goFieldName)
  1153  					loweredName = match[1]
  1154  					tagValue = tag.Value
  1155  
  1156  					if isCapnpKeyword(loweredName) {
  1157  						err := fmt.Errorf(`problem detected after applying the capname tag '%s' found on field '%s': '%s' is a reserved capnp word, so please use a *different* struct field tag (e.g. capname:"capnpName") to rename it`, tag.Value, goFieldName, loweredName)
  1158  						return err
  1159  					}
  1160  
  1161  				}
  1162  			}
  1163  
  1164  			// zid tag
  1165  			match2 := regexZid.FindStringSubmatch(tag.Value)
  1166  			if match2 != nil {
  1167  				if len(match2) == 2 {
  1168  					if match2[1] == "skip" || match2[1] == "-" {
  1169  						VPrintf("skipping field '%s' marked with zid:\"%s\"", loweredName, match2[1])
  1170  						return nil
  1171  					}
  1172  					VPrintf("matched, applying zid tag '%s' for field '%s'\n", match2[1], loweredName)
  1173  					n, err := strconv.Atoi(match2[1])
  1174  					if err != nil {
  1175  						err := fmt.Errorf(`problem in zid tag '%s' on field '%s' in struct '%s': could not convert to number, error: '%s'`, match2[1], goFieldName, x.curStruct.goName, err)
  1176  						panic(err)
  1177  						return err
  1178  					}
  1179  					if n < 0 {
  1180  						VPrintf("skipping field '%s' marked with negative zid:\"%d\"", loweredName, n)
  1181  						return nil
  1182  					}
  1183  					fld, already := x.curStruct.zidMap[n]
  1184  					if already {
  1185  						err := fmt.Errorf(`problem in zid tag '%s' on field '%s' in struct '%s': number '%d' is already taken by field '%s'`, match2[1], goFieldName, x.curStruct.goName, n, fld.goName)
  1186  						panic(err)
  1187  						return err
  1188  
  1189  					} else {
  1190  						x.curStruct.zidMap[n] = curField
  1191  						curField.zidFromTag = n
  1192  					}
  1193  				}
  1194  			}
  1195  		}
  1196  
  1197  	}
  1198  
  1199  	VPrintf("\n\n\n GenerateStructField: goFieldName:'%s' -> loweredName:'%s'\n\n", goFieldName, loweredName)
  1200  
  1201  	if isCapnpKeyword(loweredName) {
  1202  		err := fmt.Errorf(`after lowercasing the first letter, field '%s' becomes '%s' but this is a reserved capnp word, so please use a struct field tag (e.g. capname:"capnpName") to rename it`, goFieldName, loweredName)
  1203  		return err
  1204  	}
  1205  	/*
  1206  			var capnTypeDisplayed string
  1207  			curField.capTypeSeq, capnTypeDisplayed = x.GoTypeToCapnpType(curField, goTypeSeq)
  1208  
  1209  			VPrintf("\n\n\n DEBUG:  '%s' '%s' @%d: %s; %s\n\n", x.fieldPrefix, loweredName, x.fieldCount, capnTypeDisplayed, x.fieldSuffix)
  1210  
  1211  			sz := len(loweredName)
  1212  			if sz > x.curStruct.longestField {
  1213  				x.curStruct.longestField = sz
  1214  			}
  1215  
  1216  			curField.capname = loweredName
  1217  
  1218  			curField.goCapGoName = UppercaseFirstLetter(loweredName)
  1219  
  1220  			curField.goCapGoTypeSeq, curField.goCapGoType = x.CapnTypeToGoType(curField.capTypeSeq)
  1221  		curField.capType = capnTypeDisplayed
  1222  	*/
  1223  	curField.goName = goFieldName
  1224  	curField.goType = goFieldTypeName
  1225  	if len(curField.capTypeSeq) > 0 && curField.capTypeSeq[0] == "List" {
  1226  		curField.isList = true
  1227  	}
  1228  	curField.tagValue = tagValue
  1229  	curField.goTypePrefix = goFieldTypePrefix
  1230  
  1231  	x.curStruct.fld = append(x.curStruct.fld, curField)
  1232  	x.fieldCount++
  1233  
  1234  	VPrintf("\n\n curField = %#v\n", curField)
  1235  
  1236  	return nil
  1237  }
  1238  
  1239  func (x *Extractor) CapnTypeToGoType(capTypeSeq []string) (goTypeSeq []string, displayGoCapGoType string) {
  1240  
  1241  	for _, c := range capTypeSeq {
  1242  		goType, special := x.c2g(c)
  1243  
  1244  		if special {
  1245  			if goType == "Data" {
  1246  				goTypeSeq = append(goTypeSeq, "[]", "byte")
  1247  				continue
  1248  			}
  1249  
  1250  			if x.capType2goType[c] != "" {
  1251  				goType = x.capType2goType[c]
  1252  			}
  1253  		}
  1254  		goTypeSeq = append(goTypeSeq, goType)
  1255  	}
  1256  	return goTypeSeq, x.assembleGoType(goTypeSeq)
  1257  }
  1258  
  1259  func (x *Extractor) assembleGoType(goTypeSeq []string) string {
  1260  	// make a legitimate go type
  1261  	return strings.Join(goTypeSeq, "")
  1262  }
  1263  
  1264  // special flags Data <-> []byte, and types we couldn't convert
  1265  func (x *Extractor) c2g(capType string) (goType string, special bool) {
  1266  
  1267  	switch capType {
  1268  	default:
  1269  		return capType, true
  1270  	case "Data":
  1271  		return "Data", true
  1272  	case "List":
  1273  		return "[]", false
  1274  	case "Text":
  1275  		return "string", false
  1276  	case "Bool":
  1277  		return "bool", false
  1278  	case "Int8":
  1279  		return "int8", false
  1280  	case "Int16":
  1281  		return "int16", false
  1282  	case "Int32":
  1283  		return "int32", false
  1284  	case "Int64":
  1285  		return "int64", false
  1286  	case "UInt8":
  1287  		return "uint8", false
  1288  	case "UInt16":
  1289  		return "uint16", false
  1290  	case "UInt32":
  1291  		return "uint32", false
  1292  	case "UInt64":
  1293  		return "uint64", false
  1294  	case "Float32":
  1295  		return "float32", false
  1296  	case "Float64":
  1297  		return "float64", false
  1298  	}
  1299  }
  1300  
  1301  func (x *Extractor) GoTypeToCapnpType(curField *Field, goTypeSeq []string) (capTypeSeq []string, capnTypeDisplayed string) {
  1302  
  1303  	VPrintf("\n\n In GoTypeToCapnpType() : goTypeSeq=%#v)\n", goTypeSeq)
  1304  
  1305  	capTypeSeq = make([]string, len(goTypeSeq))
  1306  	for i, t := range goTypeSeq {
  1307  		capTypeSeq[i] = x.g2c(t)
  1308  	}
  1309  
  1310  	// now that the capTypeSeq is completely generated, check for lists
  1311  	// currently only do List(primitive or struct type); no List(List(prim)) or List(List(struct))
  1312  	n := len(capTypeSeq)
  1313  	for i, ty := range capTypeSeq {
  1314  		if ty == "List" && i == n-2 {
  1315  			VPrintf("\n\n generating List helpers at i=%d, capTypeSeq = '%#v\n", i, capTypeSeq)
  1316  			x.GenerateListHelpers(curField, capTypeSeq[i:], goTypeSeq[i:])
  1317  		}
  1318  	}
  1319  
  1320  	return capTypeSeq, x.assembleCapType(capTypeSeq)
  1321  }
  1322  
  1323  func (x *Extractor) assembleCapType(capTypeSeq []string) string {
  1324  	// make a legitimate capnp type
  1325  	switch capTypeSeq[0] {
  1326  	case "List":
  1327  		return "List(" + x.assembleCapType(capTypeSeq[1:]) + ")"
  1328  	case "*":
  1329  		return x.assembleCapType(capTypeSeq[1:])
  1330  	default:
  1331  		return capTypeSeq[0]
  1332  	}
  1333  }
  1334  
  1335  func (x *Extractor) g2c(goFieldTypeName string) string {
  1336  
  1337  	switch goFieldTypeName {
  1338  	case "[]":
  1339  		return "List"
  1340  	case "*":
  1341  		return "*"
  1342  	case "string":
  1343  		return "Text"
  1344  	case "int":
  1345  		return "Int64"
  1346  	case "bool":
  1347  		return "Bool"
  1348  	case "int8":
  1349  		return "Int8"
  1350  	case "int16":
  1351  		return "Int16"
  1352  	case "int32":
  1353  		return "Int32"
  1354  	case "int64":
  1355  		return "Int64"
  1356  	case "uint8":
  1357  		return "UInt8"
  1358  	case "uint16":
  1359  		return "UInt16"
  1360  	case "uint32":
  1361  		return "UInt32"
  1362  	case "uint64":
  1363  		return "UInt64"
  1364  	case "float32":
  1365  		return "Float32"
  1366  	case "float64":
  1367  		return "Float64"
  1368  	case "byte":
  1369  		return "UInt8"
  1370  	}
  1371  
  1372  	var capnTypeDisplayed string
  1373  	alreadyKnownCapnType := x.goType2capTypeCache[goFieldTypeName]
  1374  	if alreadyKnownCapnType != "" {
  1375  		VPrintf("\n\n debug: x.goType2capTypeCache[goFieldTypeName='%s'] -> '%s'\n", goFieldTypeName, alreadyKnownCapnType)
  1376  		capnTypeDisplayed = alreadyKnownCapnType
  1377  	} else {
  1378  		capnTypeDisplayed = GoType2CapnType(goFieldTypeName)
  1379  		VPrintf("\n\n 999 debug: adding to  x.goType2capTypeCache[goFieldTypeName='%s'] = '%s'\n", goFieldTypeName, capnTypeDisplayed)
  1380  		x.goType2capTypeCache[goFieldTypeName] = capnTypeDisplayed
  1381  	}
  1382  
  1383  	return capnTypeDisplayed
  1384  }
  1385  
  1386  func (x *Extractor) GenerateEmbedded(typeName string) {
  1387  	fmt.Fprintf(&x.out, "%s; ", typeName) // prod
  1388  }
  1389  
  1390  func getNewCapnpId() string {
  1391  	id, err := exec.Command("capnp", "id").CombinedOutput()
  1392  	if err != nil {
  1393  		panic(err)
  1394  	}
  1395  	n := len(id)
  1396  	if n > 0 {
  1397  		id = id[:n-1]
  1398  	}
  1399  
  1400  	return string(id)
  1401  }
  1402  
  1403  func (x *Extractor) GenCapnpHeader() *bytes.Buffer {
  1404  	var by bytes.Buffer
  1405  
  1406  	id := getNewCapnpId()
  1407  
  1408  	fmt.Fprintf(&by, `%s;
  1409  using Go = import "go.capnp";
  1410  $Go.package("%s");
  1411  $Go.import("%s");
  1412  %s`, id, x.pkgName, x.importDecl, x.fieldSuffix)
  1413  
  1414  	return &by
  1415  }
  1416  
  1417  func (x *Extractor) AssembleCapnpFile(in []byte) *bytes.Buffer {
  1418  	by := x.GenCapnpHeader()
  1419  
  1420  	by.Write(in)
  1421  	fmt.Fprintf(by, "\n")
  1422  
  1423  	return by
  1424  }
  1425  
  1426  func CapnpCompileFragment(in []byte) ([]byte, error, *Extractor) {
  1427  	x := NewExtractor()
  1428  	out, err := x.CapnpCompileFragment(in)
  1429  	return out, err, x
  1430  }
  1431  
  1432  func (x *Extractor) CapnpCompileFragment(in []byte) ([]byte, error) {
  1433  
  1434  	if x.compileDir != nil {
  1435  		x.compileDir.Cleanup()
  1436  	}
  1437  	x.compileDir = NewTempDir()
  1438  
  1439  	f := x.compileDir.TempFile()
  1440  
  1441  	by := x.AssembleCapnpFile(in)
  1442  	debug := string(by.Bytes())
  1443  
  1444  	f.Write(by.Bytes())
  1445  	f.Close()
  1446  
  1447  	compiled, combinedOut, err := CapnpCompilePath(f.Name())
  1448  	if err != nil {
  1449  		errmsg := fmt.Sprintf("error compiling the generated capnp code: '%s'; error: '%s'\n", debug, err) + string(combinedOut)
  1450  		return []byte(errmsg), fmt.Errorf(errmsg)
  1451  	}
  1452  
  1453  	return compiled, nil
  1454  }
  1455  
  1456  func CapnpCompilePath(fname string) (generatedGoFile []byte, comboOut []byte, err error) {
  1457  	goOutFn := fname + ".go"
  1458  
  1459  	by, err := exec.Command("capnp", "compile", "-ogo", fname).CombinedOutput()
  1460  	if err != nil {
  1461  		return []byte{}, by, err
  1462  	}
  1463  
  1464  	generatedGoFile, err = ioutil.ReadFile(goOutFn)
  1465  
  1466  	return generatedGoFile, by, err
  1467  }
  1468  
  1469  func SetSpaces(spaces *string, Max int, Len int) {
  1470  	if Len >= Max {
  1471  		*spaces = ""
  1472  		return
  1473  	}
  1474  	*spaces = strings.Repeat(" ", Max-Len)
  1475  }
  1476  
  1477  func ExtraSpaces(fieldNum int) string {
  1478  	if fieldNum < 10 {
  1479  		return "  "
  1480  	}
  1481  	if fieldNum < 100 {
  1482  		return " "
  1483  	}
  1484  	return ""
  1485  }
  1486  
  1487  func IsIntrinsicGoType(goFieldTypeName string) bool {
  1488  	VPrintf("\n IsIntrinsic called with '%s'\n", goFieldTypeName)
  1489  
  1490  	switch goFieldTypeName {
  1491  	case "string":
  1492  		return true
  1493  	case "int":
  1494  		return true
  1495  	case "bool":
  1496  		return true
  1497  	case "int8":
  1498  		return true
  1499  	case "int16":
  1500  		return true
  1501  	case "int32":
  1502  		return true
  1503  	case "int64":
  1504  		return true
  1505  	case "uint8":
  1506  		return true
  1507  	case "uint16":
  1508  		return true
  1509  	case "uint32":
  1510  		return true
  1511  	case "uint64":
  1512  		return true
  1513  	case "float32":
  1514  		return true
  1515  	case "float64":
  1516  		return true
  1517  	case "byte":
  1518  		return true
  1519  	default:
  1520  		return false
  1521  	}
  1522  	return false
  1523  }
  1524  
  1525  func CanonGoType(goTypeSeq []string) string {
  1526  	var r string
  1527  	for _, s := range goTypeSeq {
  1528  		if s == "[]" {
  1529  			r += "Slice"
  1530  		} else {
  1531  			r += UppercaseFirstLetter(s)
  1532  		}
  1533  	}
  1534  	return r
  1535  }
  1536  
  1537  func CanonCapType(capTypeSeq []string) string {
  1538  	var r string
  1539  	for _, s := range capTypeSeq {
  1540  		r += s
  1541  	}
  1542  	return r
  1543  }
  1544  
  1545  func (x *Extractor) GenerateListHelpers(f *Field, capListTypeSeq []string, goTypeSeq []string) {
  1546  
  1547  	canonGoType := CanonGoType(goTypeSeq)
  1548  	//canonCapType := CanonCapType(capListTypeSeq)
  1549  
  1550  	// already done before? but maybe f was nil and so needs re-doing!
  1551  	// so don't return early even if partially set before!
  1552  
  1553  	VPrintf("\n\n debug GenerateListHelper: called with capListTypeSeq = '%#v'\n", capListTypeSeq)
  1554  
  1555  	n := len(capListTypeSeq)
  1556  	capBaseType := capListTypeSeq[n-1]
  1557  	capTypeThenList := strings.Join(capListTypeSeq, "")
  1558  	if n > 1 {
  1559  		capTypeThenList = capBaseType + strings.Join(capListTypeSeq[:n-1], "")
  1560  	}
  1561  
  1562  	if IsIntrinsicGoType(last(goTypeSeq)) {
  1563  		f.baseIsIntrinsic = true
  1564  		capTypeThenList = capBaseType + "List"
  1565  		f.singleCapListType = fmt.Sprintf("capn.%sList", capBaseType)
  1566  		f.newListExpression = fmt.Sprintf("seg.New%sList(len(m))", capBaseType)
  1567  	} else {
  1568  		capTypeThenList = capBaseType + strings.Join(capListTypeSeq[:n-1], "")
  1569  		f.singleCapListType = fmt.Sprintf("%s_List", capBaseType)
  1570  		f.newListExpression = fmt.Sprintf("New%s(seg, len(m))", capTypeThenList)
  1571  	}
  1572  	VPrintf("\n capTypeThenList is set to : '%s'\n\n", capTypeThenList)
  1573  
  1574  	collapGoType := strings.Join(goTypeSeq, "")
  1575  	m := len(goTypeSeq)
  1576  	goBaseType := goTypeSeq[m-1]
  1577  
  1578  	c2g, _ := x.c2g(capBaseType)
  1579  
  1580  	f.canonGoType = canonGoType
  1581  	f.canonGoTypeListToSliceFunc = fmt.Sprintf("%sTo%s", capTypeThenList, canonGoType)
  1582  	f.canonGoTypeSliceToListFunc = fmt.Sprintf("%sTo%s", canonGoType, capTypeThenList)
  1583  
  1584  	x.SliceToListCode[canonGoType] = []byte(fmt.Sprintf(`
  1585  func %sTo%s(seg *capn.Segment, m %s) %s {
  1586  	lst := %s
  1587  	for i := range m {
  1588  		lst.Set(i, %s)
  1589  	}
  1590  	return lst
  1591  }
  1592  `, canonGoType, capTypeThenList, collapGoType, f.singleCapListType, f.newListExpression, x.SliceToListSetRHS(f.baseIsIntrinsic, goBaseType, c2g)))
  1593  
  1594  	x.ListToSliceCode[canonGoType] = []byte(fmt.Sprintf(`
  1595  func %sTo%s(p %s) %s {
  1596  	v := make(%s, p.Len())
  1597  	for i := range v {
  1598          %s
  1599  	}
  1600  	return v
  1601  }
  1602  `, capTypeThenList, canonGoType, f.singleCapListType, collapGoType, collapGoType, x.ListToSliceSetLHS_RHS(f.baseIsIntrinsic, capBaseType, goBaseType)))
  1603  
  1604  	VPrintf("\n\n GenerateListHelpers done for field '%#v'\n\n", f)
  1605  }
  1606  
  1607  func (x *Extractor) SliceToListSetRHS(baseIsIntrinsic bool, goName string, c2g string) string {
  1608  	if baseIsIntrinsic {
  1609  		return fmt.Sprintf("%s(m[i])", c2g)
  1610  	} else {
  1611  		return fmt.Sprintf("%sGoToCapn(seg, &m[i])", goName)
  1612  	}
  1613  }
  1614  
  1615  func (x *Extractor) ListToSliceSetLHS_RHS(baseIsIntrinsic bool, capName string, goBaseType string) string {
  1616  	if baseIsIntrinsic {
  1617  		return fmt.Sprintf("v[i] = %s(p.At(i))", goBaseType) // e.g. int64
  1618  	} else {
  1619  		return fmt.Sprintf("%sToGo(p.At(i), &v[i])", capName)
  1620  	}
  1621  }
  1622  
  1623  func (x *Extractor) SetFinalFieldOrder() {
  1624  
  1625  	// sort structs alphabetically to get a stable (testable) ordering.
  1626  	sortedStructs := ByGoName(make([]*Struct, 0, len(x.srs)))
  1627  	for _, strct := range x.srs {
  1628  		sortedStructs = append(sortedStructs, strct)
  1629  	}
  1630  	sort.Sort(ByGoName(sortedStructs))
  1631  
  1632  	for _, s := range sortedStructs {
  1633  		s.computeFinalOrder()
  1634  		sort.Sort(ByFinalOrder(s.fld))
  1635  	} // end loop over structs
  1636  
  1637  	return
  1638  }
  1639  
  1640  func ExtractStringAddZid(src string) string {
  1641  
  1642  	x := NewExtractor()
  1643  	defer x.Cleanup()
  1644  	_, err := ExtractStructs("ExtractStringAddZid", "package main; "+src, x)
  1645  	if err != nil {
  1646  		panic(err)
  1647  	}
  1648  
  1649  	//goon.Dump(x.srs)
  1650  
  1651  	x.SetFinalFieldOrder()
  1652  
  1653  	// final write, this time accounting for zid tag ordering
  1654  	var buf bytes.Buffer
  1655  	err = x.CopySourceFilesAddZidTag(&buf)
  1656  	if err != nil {
  1657  		panic(err)
  1658  	}
  1659  
  1660  	ans := string(buf.Bytes())
  1661  	//p("returning ans='%s'", ans)
  1662  	return ans
  1663  }