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 }