bou.ke/statictemplate@v0.0.0-20180821122055-510411a5e7dd/statictemplate/translate.go (about) 1 package statictemplate 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/format" 7 "go/types" 8 "io" 9 "path" 10 "text/template/parse" 11 12 "bou.ke/statictemplate/internal" 13 "golang.org/x/tools/go/types/typeutil" 14 ) 15 16 var builtinFuncs map[string]*types.Func 17 18 func init() { 19 var err error 20 _, _, builtinFuncs, err = internal.ImportFuncMap("bou.ke/statictemplate/funcs.Funcs") 21 if err != nil { 22 panic(err) 23 } 24 } 25 26 const varPrefix = "_Var" 27 28 type scope map[string]types.Type 29 30 // TranslateInstruction specifies a single function to be generated from a template 31 type TranslateInstruction struct { 32 FunctionName string 33 TemplateName string 34 Dot types.Type 35 } 36 37 // Translate is a convenience method for New(template).Translate(pkg, instructions) 38 func Translate(template interface{}, pkg string, instructions []TranslateInstruction) ([]byte, error) { 39 translator := New(template) 40 return translator.Translate(pkg, instructions) 41 } 42 43 // Translator converts a template with a set of instructions to Go code 44 type Translator struct { 45 Funcs map[string]*types.Func 46 47 scopes []scope 48 template wrappedTemplate 49 id int 50 specializedFunctions map[wrappedTemplate]*typeutil.Map 51 errorFunctions *typeutil.Map 52 generatedFunctions []string 53 imports map[string]string 54 } 55 56 // New creates a new instance of Translator 57 func New(template interface{}) *Translator { 58 wrapped := wrap(template) 59 return &Translator{ 60 Funcs: map[string]*types.Func{}, 61 62 scopes: []scope{ 63 make(scope), 64 }, 65 specializedFunctions: make(map[wrappedTemplate]*typeutil.Map), 66 errorFunctions: &typeutil.Map{}, 67 imports: make(map[string]string), 68 template: wrapped, 69 } 70 } 71 72 // Translate converts a template with a set of instructions to Go code 73 func (t *Translator) Translate(pkg string, instructions []TranslateInstruction) ([]byte, error) { 74 var result []resultEntry 75 76 for _, instruction := range instructions { 77 temp, err := t.template.Lookup(instruction.TemplateName) 78 if err != nil { 79 return nil, err 80 } 81 functionName, err := t.generateTemplate(temp, instruction.Dot) 82 if err != nil { 83 return nil, err 84 } 85 result = append(result, resultEntry{ 86 name: instruction.FunctionName, 87 typeName: t.typeName(instruction.Dot), 88 functionName: functionName, 89 }) 90 } 91 92 t.importPackage("io") 93 94 var buf bytes.Buffer 95 96 fmt.Fprintf(&buf, `package %s 97 import ( 98 `, pkg) 99 for pkgPath, alias := range t.imports { 100 if path.Base(pkgPath) == alias { 101 fmt.Fprintf(&buf, "%q\n", pkgPath) 102 } else { 103 fmt.Fprintf(&buf, "%s %q\n", alias, pkgPath) 104 } 105 } 106 io.WriteString(&buf, ")") 107 108 for _, entry := range result { 109 fmt.Fprintf(&buf, ` 110 func %s(w io.Writer, dot %s) (err error) { 111 defer func() { 112 if recovered := recover(); recovered != nil { 113 var ok bool 114 if err, ok = recovered.(error); !ok { 115 panic(recovered) 116 } 117 } 118 }() 119 return %s(w, dot) 120 } 121 `, entry.name, entry.typeName, entry.functionName) 122 } 123 124 for _, code := range t.generatedFunctions { 125 io.WriteString(&buf, "\n") 126 io.WriteString(&buf, code) 127 } 128 129 formatted, err := format.Source(buf.Bytes()) 130 if err != nil { 131 return nil, fmt.Errorf("%s: %v", buf.String(), err) 132 } 133 return formatted, nil 134 } 135 136 func (t *Translator) importPackage(name string) string { 137 if pkg, ok := t.imports[name]; ok { 138 return pkg 139 } 140 141 var pkg string 142 switch name { 143 case "fmt", "io": 144 pkg = name 145 case "text/template": 146 pkg = "template" 147 case "bou.ke/statictemplate/funcs": 148 pkg = "funcs" 149 default: 150 pkg = fmt.Sprintf("pkg%d", t.id) 151 t.id++ 152 } 153 154 t.imports[name] = pkg 155 return pkg 156 } 157 158 func (t *Translator) generateFunctionName() string { 159 name := fmt.Sprintf("fun%d", t.id) 160 t.id++ 161 return name 162 } 163 164 func (t *Translator) pushScope() { 165 t.scopes = append(t.scopes, make(scope)) 166 } 167 168 func (t *Translator) popScope() { 169 t.scopes = t.scopes[:len(t.scopes)-1] 170 } 171 172 // Checks whether identifier is in scope 173 func (t *Translator) inScope(name string) bool { 174 _, ok := t.scopes[len(t.scopes)-1][name] 175 return ok 176 } 177 178 // Checks whether identifier is in scope, or add it otherwise 179 func (t *Translator) addToScope(name string, typ types.Type) { 180 t.scopes[len(t.scopes)-1][name] = typ 181 } 182 183 func (t *Translator) findVariable(name string) (types.Type, error) { 184 for i := len(t.scopes) - 1; i >= 0; i-- { 185 if typ, ok := t.scopes[i][name]; ok { 186 return typ, nil 187 } 188 } 189 return nil, fmt.Errorf("can't find variable %s in scope", name) 190 } 191 192 type sortedTypes []types.Type 193 194 func (a sortedTypes) Len() int { return len(a) } 195 func (a sortedTypes) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 196 func (a sortedTypes) Less(i, j int) bool { 197 if a[i] == nil { 198 return true 199 } else if a[j] == nil { 200 return false 201 } else { 202 return a[i].String() < a[j].String() 203 } 204 } 205 206 type resultEntry struct { 207 name, typeName, functionName string 208 } 209 210 func (t *Translator) translateNode(w io.Writer, node parse.Node, dot types.Type) error { 211 switch node := node.(type) { 212 case *parse.ActionNode: 213 pipe := node.Pipe 214 writer := w 215 if len(pipe.Decl) == 0 { 216 writer = new(bytes.Buffer) 217 } else if len(pipe.Decl) == 1 { 218 ident := pipe.Decl[0].Ident[0][1:] 219 if t.inScope(ident) { 220 fmt.Fprintf(writer, "%s%s = ", varPrefix, ident) 221 } else { 222 fmt.Fprintf(writer, "%s%s := ", varPrefix, ident) 223 } 224 } else { 225 return fmt.Errorf("only support single variable for assignment") 226 } 227 228 typ, err := t.translatePipe(writer, dot, pipe) 229 if err != nil { 230 return err 231 } 232 if len(pipe.Decl) == 1 { 233 ident := pipe.Decl[0].Ident[0][1:] 234 if !t.inScope(ident) { 235 fmt.Fprintf(writer, "\n_ = %s%s", varPrefix, ident) 236 } 237 t.addToScope(ident, typ) 238 } 239 240 if len(node.Pipe.Decl) == 0 { 241 basic, ok := typ.(*types.Basic) 242 if ok && basic.Kind() == types.String { 243 t.importPackage("io") 244 io.WriteString(w, "_, _ = io.WriteString(w, ") 245 } else { 246 t.importPackage("fmt") 247 io.WriteString(w, "_, _ = fmt.Fprint(w, ") 248 } 249 writer.(*bytes.Buffer).WriteTo(w) 250 io.WriteString(w, ")") 251 } 252 _, err = io.WriteString(w, "\n") 253 254 return err 255 case *parse.IfNode: 256 return t.translateScoped(w, dot, node.Type(), node.Pipe, node.List, node.ElseList) 257 case *parse.ListNode: 258 for _, item := range node.Nodes { 259 if err := t.translateNode(w, item, dot); err != nil { 260 return err 261 } 262 } 263 return nil 264 case *parse.RangeNode: 265 return t.translateScoped(w, dot, node.Type(), node.Pipe, node.List, node.ElseList) 266 case *parse.TemplateNode: 267 return t.translateTemplate(w, dot, node) 268 case *parse.TextNode: 269 t.importPackage("io") 270 _, err := fmt.Fprintf(w, "_, _ = io.WriteString(w, %q)\n", node.Text) 271 return err 272 case *parse.WithNode: 273 return t.translateScoped(w, dot, node.Type(), node.Pipe, node.List, node.ElseList) 274 default: 275 return fmt.Errorf("unknown Node %v", node.Type()) 276 } 277 } 278 279 func typeIsNil(typ types.Type) bool { 280 return typ == nil || types.Identical(typ, types.Typ[types.UntypedNil]) 281 } 282 283 func writeTruthiness(w io.Writer, typ types.Type) error { 284 if typeIsNil(typ) { 285 _, err := io.WriteString(w, "eval != nil") 286 return err 287 } 288 switch typ := typ.(type) { 289 case *types.Array, *types.Map, *types.Slice: 290 _, err := io.WriteString(w, "len(eval) != 0") 291 return err 292 case *types.Basic: 293 info := typ.Info() 294 if info&types.IsNumeric != 0 { 295 _, err := io.WriteString(w, "eval != 0") 296 return err 297 } else if info&types.IsString != 0 { 298 _, err := io.WriteString(w, "len(eval) != 0") 299 return err 300 } else if info&types.IsBoolean != 0 { 301 _, err := io.WriteString(w, "eval") 302 return err 303 } 304 return fmt.Errorf("don't know how to evaluate %s", typ) 305 case *types.Pointer, *types.Chan: 306 _, err := io.WriteString(w, "eval != nil") 307 return err 308 case *types.Struct: 309 _, err := io.WriteString(w, "true") 310 return err 311 default: 312 return fmt.Errorf("don't know how to evaluate %s", typ) 313 } 314 } 315 316 func (t *Translator) generateTemplate(temp wrappedTemplate, typ types.Type) (string, error) { 317 funcs, ok := t.specializedFunctions[temp] 318 if !ok { 319 funcs = &typeutil.Map{} 320 t.specializedFunctions[temp] = funcs 321 } 322 functionName, ok := funcs.At(typ).(string) 323 if !ok { 324 functionName = t.generateFunctionName() 325 funcs.Set(typ, functionName) 326 327 var buf bytes.Buffer 328 typeName := "interface{}" 329 if !typeIsNil(typ) { 330 typeName = t.typeName(typ) 331 } 332 333 fmt.Fprintf(&buf, "// %s(", temp.Name()) 334 if typeIsNil(typ) { 335 buf.WriteString("nil") 336 } else { 337 buf.WriteString(typeName) 338 } 339 t.importPackage("io") 340 fmt.Fprintf(&buf, ")\nfunc %s(w io.Writer, dot %s) error {\n", functionName, typeName) 341 oldScopes := t.scopes 342 t.scopes = []scope{make(scope)} 343 if err := t.translateNode(&buf, temp.Tree().Root, typ); err != nil { 344 return "", err 345 } 346 t.scopes = oldScopes 347 buf.WriteString("return nil\n}\n") 348 349 t.generatedFunctions = append(t.generatedFunctions, buf.String()) 350 } 351 352 return functionName, nil 353 } 354 355 func (t *Translator) translateTemplate(w io.Writer, dot types.Type, node *parse.TemplateNode) error { 356 var buf bytes.Buffer 357 typ, err := t.translatePipe(&buf, dot, node.Pipe) 358 if err != nil { 359 return err 360 } 361 temp, err := t.template.Lookup(node.Name) 362 if err != nil { 363 return err 364 } 365 name, err := t.generateTemplate(temp, typ) 366 if err != nil { 367 return err 368 } 369 370 fmt.Fprintf(w, "if err := %s(w, ", name) 371 buf.WriteTo(w) 372 _, err = io.WriteString(w, "); err != nil {\nreturn err\n}\n") 373 return err 374 } 375 376 func (t *Translator) translateScoped(w io.Writer, dot types.Type, nodeType parse.NodeType, pipe *parse.PipeNode, list, elseList *parse.ListNode) error { 377 io.WriteString(w, "if eval := ") 378 typ, err := t.translatePipe(w, dot, pipe) 379 if err != nil { 380 return err 381 } 382 io.WriteString(w, "; ") 383 if err := writeTruthiness(w, typ); err != nil { 384 return err 385 } 386 io.WriteString(w, "{\n") 387 t.pushScope() 388 389 if nodeType == parse.NodeWith { 390 io.WriteString(w, "dot := eval\n_ = dot\n") 391 } 392 393 if nodeType == parse.NodeRange { 394 var elem types.Type 395 switch typ := typ.(type) { 396 case *types.Chan: 397 elem = typ.Elem() 398 case *types.Slice: 399 elem = typ.Elem() 400 case *types.Array: 401 elem = typ.Elem() 402 default: 403 return fmt.Errorf("range over non-iterable: %v", pipe.Pos) 404 } 405 406 switch len(pipe.Decl) { 407 case 0: 408 io.WriteString(w, "for _, dot := range eval {\n_ = dot\n") 409 case 1: 410 ident := pipe.Decl[0].Ident[0][1:] 411 fmt.Fprintf(w, "for _, %s%s := range eval {\ndot := %s%s\n_ = dot\n", varPrefix, ident, varPrefix, ident) 412 t.addToScope(ident, elem) 413 case 2: 414 index := pipe.Decl[0].Ident[0][1:] 415 ident := pipe.Decl[1].Ident[0][1:] 416 t.addToScope(index, types.Typ[types.Int64]) 417 t.addToScope(ident, elem) 418 fmt.Fprintf(w, "for %s%s, %s%s := range eval {\n_ = %s%s\ndot := %s%s\n_ = dot\n", varPrefix, index, varPrefix, ident, varPrefix, index, varPrefix, ident) 419 default: 420 return fmt.Errorf("too many declarations for range") 421 } 422 423 if err := t.translateNode(w, list, elem); err != nil { 424 return err 425 } 426 427 io.WriteString(w, "}\n") 428 } else { 429 switch len(pipe.Decl) { 430 case 0: 431 case 1: 432 ident := pipe.Decl[0].Ident[0][1:] 433 fmt.Fprintf(w, "%s%s := eval\n_ = %s%s\n", varPrefix, ident, varPrefix, ident) 434 t.addToScope(ident, typ) 435 default: 436 return fmt.Errorf("too many declarations") 437 } 438 439 if err := t.translateNode(w, list, dot); err != nil { 440 return err 441 } 442 } 443 444 t.popScope() 445 io.WriteString(w, "}") 446 if elseList != nil { 447 io.WriteString(w, " else {\n") 448 if err := t.translateNode(w, elseList, dot); err != nil { 449 return err 450 } 451 io.WriteString(w, "}") 452 } 453 io.WriteString(w, "\n") 454 return nil 455 } 456 457 func (t *Translator) translatePipe(w io.Writer, dot types.Type, pipe *parse.PipeNode) (types.Type, error) { 458 if pipe == nil { 459 io.WriteString(w, "nil") 460 return types.Typ[types.UntypedNil], nil 461 } else { 462 return t.translateCommand(w, dot, pipe.Cmds[len(pipe.Cmds)-1], pipe.Cmds[:len(pipe.Cmds)-1]) 463 } 464 } 465 466 func (t *Translator) translateCall(w io.Writer, dot types.Type, args []parse.Node, nextCommands []*parse.CommandNode) error { 467 io.WriteString(w, "(") 468 for i, arg := range args { 469 if i != 0 { 470 io.WriteString(w, ", ") 471 } 472 if _, err := t.translateArg(w, dot, arg); err != nil { 473 return err 474 } 475 } 476 if len(nextCommands) != 0 { 477 if len(args) != 0 { 478 io.WriteString(w, ", ") 479 } 480 if _, err := t.translateCommand(w, dot, nextCommands[len(nextCommands)-1], nextCommands[:len(nextCommands)-1]); err != nil { 481 return err 482 } 483 } 484 io.WriteString(w, ")") 485 return nil 486 } 487 488 func (t *Translator) translateCommand(w io.Writer, dot types.Type, cmd *parse.CommandNode, nextCommands []*parse.CommandNode) (types.Type, error) { 489 action := cmd.Args[0] 490 args := cmd.Args[1:] 491 492 switch action := action.(type) { 493 case *parse.ChainNode: 494 return t.translateChain(w, dot, action, args, nextCommands) 495 case *parse.FieldNode: 496 return t.translateField(w, dot, action, args, nextCommands) 497 case *parse.IdentifierNode: 498 return t.translateFunction(w, dot, action, args, nextCommands) 499 case *parse.PipeNode: 500 // We ignore args, nextCommands in pipes 501 return t.translatePipe(w, dot, action) 502 case *parse.VariableNode: 503 return t.translateVariable(w, dot, action, args, nextCommands) 504 } 505 506 if len(args) > 0 || len(nextCommands) > 0 { 507 return nil, fmt.Errorf("dunno what to do with args %v %v %v", cmd.Args, action.Type(), nextCommands) 508 } 509 510 switch action := action.(type) { 511 case *parse.BoolNode: 512 _, err := fmt.Fprint(w, action.True) 513 return types.Typ[types.Bool], err 514 case *parse.DotNode: 515 _, err := io.WriteString(w, "dot") 516 return dot, err 517 case *parse.NilNode: 518 return nil, fmt.Errorf("nil is not a command") 519 case *parse.NumberNode: 520 if action.IsInt { 521 _, err := fmt.Fprint(w, action.Int64) 522 return types.Typ[types.Int64], err 523 } else if action.IsUint { 524 _, err := fmt.Fprint(w, action.Uint64) 525 return types.Typ[types.Uint64], err 526 } else if action.IsFloat { 527 _, err := fmt.Fprint(w, action.Float64) 528 return types.Typ[types.Float64], err 529 } else if action.IsComplex { 530 _, err := fmt.Fprint(w, action.Complex128) 531 return types.Typ[types.Complex128], err 532 } else { 533 return nil, fmt.Errorf("unknown number node %v", action) 534 } 535 case *parse.StringNode: 536 _, err := fmt.Fprintf(w, "%q", action.Text) 537 return types.Typ[types.String], err 538 default: 539 return nil, fmt.Errorf("unknown pipe node %s, %v", action.String(), action.Type()) 540 } 541 } 542 543 func (t *Translator) translateArg(w io.Writer, dot types.Type, arg parse.Node) (types.Type, error) { 544 switch arg := arg.(type) { 545 case *parse.BoolNode: 546 _, err := fmt.Fprint(w, arg.True) 547 return types.Typ[types.Bool], err 548 case *parse.ChainNode: 549 return t.translateChain(w, dot, arg, nil, nil) 550 case *parse.DotNode: 551 _, err := io.WriteString(w, "dot") 552 return dot, err 553 case *parse.FieldNode: 554 return t.translateField(w, dot, arg, nil, nil) 555 case *parse.IdentifierNode: 556 return t.translateFunction(w, dot, arg, nil, nil) 557 case *parse.NilNode: 558 _, err := io.WriteString(w, "nil") 559 return types.Typ[types.UntypedNil], err 560 case *parse.NumberNode: 561 if arg.IsInt { 562 _, err := fmt.Fprint(w, arg.Int64) 563 return types.Typ[types.Int64], err 564 } else { 565 return nil, fmt.Errorf("unknown number node %v", arg) 566 } 567 case *parse.PipeNode: 568 if len(arg.Decl) > 0 { 569 // TODO(bouk): do (is it even possible?) 570 return nil, fmt.Errorf("can't process inline variable assignment right now") 571 } 572 return t.translatePipe(w, dot, arg) 573 case *parse.StringNode: 574 _, err := fmt.Fprintf(w, "%q", arg.Text) 575 return types.Typ[types.String], err 576 case *parse.VariableNode: 577 return t.translateVariable(w, dot, arg, nil, nil) 578 default: 579 return nil, fmt.Errorf("unknown arg %s, %v", arg.String(), arg.Type()) 580 } 581 } 582 583 func (t *Translator) translateChain(w io.Writer, dot types.Type, node *parse.ChainNode, args []parse.Node, nextCommands []*parse.CommandNode) (types.Type, error) { 584 var buf bytes.Buffer 585 typ, err := t.translateArg(&buf, dot, node.Node) 586 if err != nil { 587 return nil, err 588 } 589 return t.translateFieldChain(w, dot, &buf, typ, node.Field, args, nextCommands) 590 } 591 592 func (t *Translator) translateVariable(w io.Writer, dot types.Type, node *parse.VariableNode, args []parse.Node, nextCommands []*parse.CommandNode) (types.Type, error) { 593 ident := node.Ident[0][1:] 594 if len(node.Ident) > 1 && (len(args) != 0 || len(nextCommands) != 0) { 595 return nil, fmt.Errorf("can't call variable %s", node.Ident[0]) 596 } 597 typ, err := t.findVariable(ident) 598 if err != nil { 599 return nil, err 600 } 601 602 return t.translateFieldChain(w, dot, constantWriterTo(varPrefix+ident), typ, node.Ident[1:], args, nextCommands) 603 } 604 605 func (t *Translator) generateErrorFunction(typ types.Type) string { 606 name, ok := t.errorFunctions.At(typ).(string) 607 if !ok { 608 name = t.generateFunctionName() 609 typeName := t.typeName(typ) 610 611 t.generatedFunctions = append(t.generatedFunctions, fmt.Sprintf(` 612 func %s(value %s, err error) %s { 613 if err != nil { 614 panic(err) 615 } 616 return value 617 }`, name, typeName, typeName)) 618 t.errorFunctions.Set(typ, name) 619 } 620 return name 621 } 622 623 func (t *Translator) getFunction(ident string) (*types.Signature, string, error) { 624 if f, ok := t.Funcs[ident]; ok { 625 pkgName := t.importPackage(f.Pkg().Path()) 626 return f.Type().(*types.Signature), fmt.Sprintf("%s.%s", pkgName, f.Name()), nil 627 } else if f, ok := builtinFuncs[ident]; ok { 628 pkgName := t.importPackage(f.Pkg().Path()) 629 return f.Type().(*types.Signature), fmt.Sprintf("%s.%s", pkgName, f.Name()), nil 630 } else { 631 return nil, "", fmt.Errorf("unknown function %s", ident) 632 } 633 } 634 635 func (t *Translator) translateFunction(w io.Writer, dot types.Type, ident *parse.IdentifierNode, args []parse.Node, nextCommands []*parse.CommandNode) (types.Type, error) { 636 typ, fName, err := t.getFunction(ident.Ident) 637 if err != nil { 638 return nil, err 639 } 640 641 numOut := typ.Results().Len() 642 643 if numOut == 2 { 644 fmt.Fprintf(w, "%s(", t.generateErrorFunction(typ)) 645 } else if numOut != 1 { 646 return nil, fmt.Errorf("only support 1, 2 output variable %s", ident.Ident) 647 } 648 649 io.WriteString(w, fName) 650 651 if err := t.translateCall(w, dot, args, nextCommands); err != nil { 652 return nil, err 653 } 654 655 if numOut == 2 { 656 io.WriteString(w, ")") 657 } 658 659 return typ.Results().At(0).Type(), nil 660 } 661 662 func (t *Translator) translateField(w io.Writer, dot types.Type, field *parse.FieldNode, args []parse.Node, nextCommands []*parse.CommandNode) (types.Type, error) { 663 return t.translateFieldChain(w, dot, constantWriterTo("dot"), dot, field.Ident, args, nextCommands) 664 } 665 666 func (t *Translator) translateFieldChain(w io.Writer, dot types.Type, dotCode io.WriterTo, typ types.Type, fields []string, args []parse.Node, nextCommands []*parse.CommandNode) (types.Type, error) { 667 var buf bytes.Buffer 668 guards := []string{} 669 for i, name := range fields { 670 obj, _, _ := types.LookupFieldOrMethod(typ, true, nil, name) 671 672 switch obj := obj.(type) { 673 case *types.Func: 674 sig := obj.Type().(*types.Signature) 675 out := sig.Results() 676 numOut := out.Len() 677 returnTyp := out.At(0).Type() 678 if numOut == 2 { 679 guards = append(guards, fmt.Sprintf("%s(", t.generateErrorFunction(returnTyp))) 680 } else if numOut != 1 { 681 return nil, fmt.Errorf("only support 1, 2 output variable %s.%s", t.typeName(typ), obj.Name()) 682 } 683 fmt.Fprintf(&buf, ".%s", name) 684 685 var err error 686 if i == len(fields)-1 { 687 err = t.translateCall(&buf, dot, args, nextCommands) 688 } else { 689 err = t.translateCall(&buf, dot, nil, nil) 690 } 691 if err != nil { 692 return nil, err 693 } 694 if numOut == 2 { 695 io.WriteString(&buf, ")") 696 } 697 typ = returnTyp 698 case *types.Var: 699 fmt.Fprintf(&buf, ".%s", name) 700 typ = obj.Type() 701 default: 702 return nil, fmt.Errorf("unknown field %s for type %s", name, typ.String()) 703 } 704 } 705 for i := len(guards) - 1; i >= 0; i-- { 706 io.WriteString(w, guards[i]) 707 } 708 _, err := dotCode.WriteTo(w) 709 if err != nil { 710 return nil, err 711 } 712 _, err = buf.WriteTo(w) 713 return typ, err 714 } 715 716 func (t *Translator) typeName(typ types.Type) string { 717 switch obj := typ.(type) { 718 case *types.Array: 719 return fmt.Sprintf("[%d]%s", obj.Len(), t.typeName(obj.Elem())) 720 case *types.Chan: 721 return fmt.Sprintf("chan %s", t.typeName(obj.Elem())) 722 case *types.Map: 723 return fmt.Sprintf("map[%s]%s", t.typeName(obj.Key()), t.typeName(obj.Elem())) 724 case *types.Named: 725 name := obj.Obj() 726 return t.importPackage(name.Pkg().Path()) + "." + name.Name() 727 case *types.Pointer: 728 return fmt.Sprintf("*%s", t.typeName(obj.Elem())) 729 case *types.Slice: 730 return fmt.Sprintf("[]%s", t.typeName(obj.Elem())) 731 default: 732 return typ.String() 733 } 734 }