stringer.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
  5. // interface. Given the name of a (signed or unsigned) integer type T that has constants
  6. // defined, stringer will create a new self-contained Go source file implementing
  7. //
  8. // func (t T) String() string
  9. //
  10. // The file is created in the same package and directory as the package that defines T.
  11. // It has helpful defaults designed for use with go generate.
  12. //
  13. // Stringer works best with constants that are consecutive values such as created using iota,
  14. // but creates good code regardless. In the future it might also provide custom support for
  15. // constant sets that are bit patterns.
  16. //
  17. // For example, given this snippet,
  18. //
  19. // package painkiller
  20. //
  21. // type Pill int
  22. //
  23. // const (
  24. // Placebo Pill = iota
  25. // Aspirin
  26. // Ibuprofen
  27. // Paracetamol
  28. // Acetaminophen = Paracetamol
  29. // )
  30. //
  31. // running this command
  32. //
  33. // stringer -type=Pill
  34. //
  35. // in the same directory will create the file pill_string.go, in package painkiller,
  36. // containing a definition of
  37. //
  38. // func (Pill) String() string
  39. //
  40. // That method will translate the value of a Pill constant to the string representation
  41. // of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
  42. // print the string "Aspirin".
  43. //
  44. // Typically this process would be run using go generate, like this:
  45. //
  46. // //go:generate stringer -type=Pill
  47. //
  48. // If multiple constants have the same value, the lexically first matching name will
  49. // be used (in the example, Acetaminophen will print as "Paracetamol").
  50. //
  51. // With no arguments, it processes the package in the current directory.
  52. // Otherwise, the arguments must name a single directory holding a Go package
  53. // or a set of Go source files that represent a single Go package.
  54. //
  55. // The -type flag accepts a comma-separated list of types so a single run can
  56. // generate methods for multiple types. The default output file is t_string.go,
  57. // where t is the lower-cased name of the first type listed. It can be overridden
  58. // with the -output flag.
  59. //
  60. // The -linecomment flag tells stringer to generate the text of any line comment, trimmed
  61. // of leading spaces, instead of the constant name. For instance, if the constants above had a
  62. // Pill prefix, one could write
  63. //
  64. // PillAspirin // Aspirin
  65. //
  66. // to suppress it in the output.
  67. package main // import "golang.org/x/tools/cmd/stringer"
  68. import (
  69. "bytes"
  70. "flag"
  71. "fmt"
  72. "go/ast"
  73. "go/constant"
  74. "go/format"
  75. "go/token"
  76. "go/types"
  77. "log"
  78. "os"
  79. "path/filepath"
  80. "sort"
  81. "strings"
  82. "golang.org/x/tools/go/packages"
  83. )
  84. var (
  85. typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
  86. output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
  87. trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
  88. linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
  89. buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
  90. )
  91. // Usage is a replacement usage function for the flags package.
  92. func Usage() {
  93. fmt.Fprintf(os.Stderr, "Usage of stringer:\n")
  94. fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
  95. fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n")
  96. fmt.Fprintf(os.Stderr, "For more information, see:\n")
  97. fmt.Fprintf(os.Stderr, "\thttps://pkg.go.dev/golang.org/x/tools/cmd/stringer\n")
  98. fmt.Fprintf(os.Stderr, "Flags:\n")
  99. flag.PrintDefaults()
  100. }
  101. func main() {
  102. log.SetFlags(0)
  103. log.SetPrefix("stringer: ")
  104. flag.Usage = Usage
  105. flag.Parse()
  106. if len(*typeNames) == 0 {
  107. flag.Usage()
  108. os.Exit(2)
  109. }
  110. types := strings.Split(*typeNames, ",")
  111. var tags []string
  112. if len(*buildTags) > 0 {
  113. tags = strings.Split(*buildTags, ",")
  114. }
  115. // We accept either one directory or a list of files. Which do we have?
  116. args := flag.Args()
  117. if len(args) == 0 {
  118. // Default: process whole package in current directory.
  119. args = []string{"."}
  120. }
  121. // Parse the package once.
  122. var dir string
  123. g := Generator{
  124. trimPrefix: *trimprefix,
  125. lineComment: *linecomment,
  126. }
  127. // TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc).
  128. if len(args) == 1 && isDirectory(args[0]) {
  129. dir = args[0]
  130. } else {
  131. if len(tags) != 0 {
  132. log.Fatal("-tags option applies only to directories, not when files are specified")
  133. }
  134. dir = filepath.Dir(args[0])
  135. }
  136. g.parsePackage(args, tags)
  137. // Print the header and package clause.
  138. g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
  139. g.Printf("\n")
  140. g.Printf("package %s", g.pkg.name)
  141. g.Printf("\n")
  142. g.Printf("import \"strconv\"\n") // Used by all methods.
  143. // Run generate for each type.
  144. for _, typeName := range types {
  145. g.generate(typeName)
  146. }
  147. // Format the output.
  148. src := g.format()
  149. // Write to file.
  150. outputName := *output
  151. if outputName == "" {
  152. baseName := fmt.Sprintf("%s_string.go", types[0])
  153. outputName = filepath.Join(dir, strings.ToLower(baseName))
  154. }
  155. err := os.WriteFile(outputName, src, 0644)
  156. if err != nil {
  157. log.Fatalf("writing output: %s", err)
  158. }
  159. }
  160. // isDirectory reports whether the named file is a directory.
  161. func isDirectory(name string) bool {
  162. info, err := os.Stat(name)
  163. if err != nil {
  164. log.Fatal(err)
  165. }
  166. return info.IsDir()
  167. }
  168. // Generator holds the state of the analysis. Primarily used to buffer
  169. // the output for format.Source.
  170. type Generator struct {
  171. buf bytes.Buffer // Accumulated output.
  172. pkg *Package // Package we are scanning.
  173. trimPrefix string
  174. lineComment bool
  175. }
  176. func (g *Generator) Printf(format string, args ...interface{}) {
  177. fmt.Fprintf(&g.buf, format, args...)
  178. }
  179. // File holds a single parsed file and associated data.
  180. type File struct {
  181. pkg *Package // Package to which this file belongs.
  182. file *ast.File // Parsed AST.
  183. // These fields are reset for each type being generated.
  184. typeName string // Name of the constant type.
  185. values []Value // Accumulator for constant values of that type.
  186. trimPrefix string
  187. lineComment bool
  188. }
  189. type Package struct {
  190. name string
  191. defs map[*ast.Ident]types.Object
  192. files []*File
  193. }
  194. // parsePackage analyzes the single package constructed from the patterns and tags.
  195. // parsePackage exits if there is an error.
  196. func (g *Generator) parsePackage(patterns []string, tags []string) {
  197. cfg := &packages.Config{
  198. Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
  199. // TODO: Need to think about constants in test files. Maybe write type_string_test.go
  200. // in a separate pass? For later.
  201. Tests: false,
  202. BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
  203. }
  204. pkgs, err := packages.Load(cfg, patterns...)
  205. if err != nil {
  206. log.Fatal(err)
  207. }
  208. if len(pkgs) != 1 {
  209. log.Fatalf("error: %d packages found", len(pkgs))
  210. }
  211. g.addPackage(pkgs[0])
  212. }
  213. // addPackage adds a type checked Package and its syntax files to the generator.
  214. func (g *Generator) addPackage(pkg *packages.Package) {
  215. g.pkg = &Package{
  216. name: pkg.Name,
  217. defs: pkg.TypesInfo.Defs,
  218. files: make([]*File, len(pkg.Syntax)),
  219. }
  220. for i, file := range pkg.Syntax {
  221. g.pkg.files[i] = &File{
  222. file: file,
  223. pkg: g.pkg,
  224. trimPrefix: g.trimPrefix,
  225. lineComment: g.lineComment,
  226. }
  227. }
  228. }
  229. // generate produces the String method for the named type.
  230. func (g *Generator) generate(typeName string) {
  231. values := make([]Value, 0, 100)
  232. for _, file := range g.pkg.files {
  233. // Set the state for this run of the walker.
  234. file.typeName = typeName
  235. file.values = nil
  236. if file.file != nil {
  237. ast.Inspect(file.file, file.genDecl)
  238. values = append(values, file.values...)
  239. }
  240. }
  241. if len(values) == 0 {
  242. log.Fatalf("no values defined for type %s", typeName)
  243. }
  244. // Generate code that will fail if the constants change value.
  245. g.Printf("func _() {\n")
  246. g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n")
  247. g.Printf("\t// Re-run the stringer command to generate them again.\n")
  248. g.Printf("\tvar x [1]struct{}\n")
  249. for _, v := range values {
  250. g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str)
  251. }
  252. g.Printf("}\n")
  253. runs := splitIntoRuns(values)
  254. // The decision of which pattern to use depends on the number of
  255. // runs in the numbers. If there's only one, it's easy. For more than
  256. // one, there's a tradeoff between complexity and size of the data
  257. // and code vs. the simplicity of a map. A map takes more space,
  258. // but so does the code. The decision here (crossover at 10) is
  259. // arbitrary, but considers that for large numbers of runs the cost
  260. // of the linear scan in the switch might become important, and
  261. // rather than use yet another algorithm such as binary search,
  262. // we punt and use a map. In any case, the likelihood of a map
  263. // being necessary for any realistic example other than bitmasks
  264. // is very low. And bitmasks probably deserve their own analysis,
  265. // to be done some other day.
  266. switch {
  267. case len(runs) == 1:
  268. g.buildOneRun(runs, typeName)
  269. case len(runs) <= 10:
  270. g.buildMultipleRuns(runs, typeName)
  271. default:
  272. g.buildMap(runs, typeName)
  273. }
  274. }
  275. // splitIntoRuns breaks the values into runs of contiguous sequences.
  276. // For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
  277. // The input slice is known to be non-empty.
  278. func splitIntoRuns(values []Value) [][]Value {
  279. // We use stable sort so the lexically first name is chosen for equal elements.
  280. sort.Stable(byValue(values))
  281. // Remove duplicates. Stable sort has put the one we want to print first,
  282. // so use that one. The String method won't care about which named constant
  283. // was the argument, so the first name for the given value is the only one to keep.
  284. // We need to do this because identical values would cause the switch or map
  285. // to fail to compile.
  286. j := 1
  287. for i := 1; i < len(values); i++ {
  288. if values[i].value != values[i-1].value {
  289. values[j] = values[i]
  290. j++
  291. }
  292. }
  293. values = values[:j]
  294. runs := make([][]Value, 0, 10)
  295. for len(values) > 0 {
  296. // One contiguous sequence per outer loop.
  297. i := 1
  298. for i < len(values) && values[i].value == values[i-1].value+1 {
  299. i++
  300. }
  301. runs = append(runs, values[:i])
  302. values = values[i:]
  303. }
  304. return runs
  305. }
  306. // format returns the gofmt-ed contents of the Generator's buffer.
  307. func (g *Generator) format() []byte {
  308. src, err := format.Source(g.buf.Bytes())
  309. if err != nil {
  310. // Should never happen, but can arise when developing this code.
  311. // The user can compile the output to see the error.
  312. log.Printf("warning: internal error: invalid Go generated: %s", err)
  313. log.Printf("warning: compile the package to analyze the error")
  314. return g.buf.Bytes()
  315. }
  316. return src
  317. }
  318. // Value represents a declared constant.
  319. type Value struct {
  320. originalName string // The name of the constant.
  321. name string // The name with trimmed prefix.
  322. // The value is stored as a bit pattern alone. The boolean tells us
  323. // whether to interpret it as an int64 or a uint64; the only place
  324. // this matters is when sorting.
  325. // Much of the time the str field is all we need; it is printed
  326. // by Value.String.
  327. value uint64 // Will be converted to int64 when needed.
  328. signed bool // Whether the constant is a signed type.
  329. str string // The string representation given by the "go/constant" package.
  330. }
  331. func (v *Value) String() string {
  332. return v.str
  333. }
  334. // byValue lets us sort the constants into increasing order.
  335. // We take care in the Less method to sort in signed or unsigned order,
  336. // as appropriate.
  337. type byValue []Value
  338. func (b byValue) Len() int { return len(b) }
  339. func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  340. func (b byValue) Less(i, j int) bool {
  341. if b[i].signed {
  342. return int64(b[i].value) < int64(b[j].value)
  343. }
  344. return b[i].value < b[j].value
  345. }
  346. // genDecl processes one declaration clause.
  347. func (f *File) genDecl(node ast.Node) bool {
  348. decl, ok := node.(*ast.GenDecl)
  349. if !ok || decl.Tok != token.CONST {
  350. // We only care about const declarations.
  351. return true
  352. }
  353. // The name of the type of the constants we are declaring.
  354. // Can change if this is a multi-element declaration.
  355. typ := ""
  356. // Loop over the elements of the declaration. Each element is a ValueSpec:
  357. // a list of names possibly followed by a type, possibly followed by values.
  358. // If the type and value are both missing, we carry down the type (and value,
  359. // but the "go/types" package takes care of that).
  360. for _, spec := range decl.Specs {
  361. vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
  362. if vspec.Type == nil && len(vspec.Values) > 0 {
  363. // "X = 1". With no type but a value. If the constant is untyped,
  364. // skip this vspec and reset the remembered type.
  365. typ = ""
  366. // If this is a simple type conversion, remember the type.
  367. // We don't mind if this is actually a call; a qualified call won't
  368. // be matched (that will be SelectorExpr, not Ident), and only unusual
  369. // situations will result in a function call that appears to be
  370. // a type conversion.
  371. ce, ok := vspec.Values[0].(*ast.CallExpr)
  372. if !ok {
  373. continue
  374. }
  375. id, ok := ce.Fun.(*ast.Ident)
  376. if !ok {
  377. continue
  378. }
  379. typ = id.Name
  380. }
  381. if vspec.Type != nil {
  382. // "X T". We have a type. Remember it.
  383. ident, ok := vspec.Type.(*ast.Ident)
  384. if !ok {
  385. continue
  386. }
  387. typ = ident.Name
  388. }
  389. if typ != f.typeName {
  390. // This is not the type we're looking for.
  391. continue
  392. }
  393. // We now have a list of names (from one line of source code) all being
  394. // declared with the desired type.
  395. // Grab their names and actual values and store them in f.values.
  396. for _, name := range vspec.Names {
  397. if name.Name == "_" {
  398. continue
  399. }
  400. // This dance lets the type checker find the values for us. It's a
  401. // bit tricky: look up the object declared by the name, find its
  402. // types.Const, and extract its value.
  403. obj, ok := f.pkg.defs[name]
  404. if !ok {
  405. log.Fatalf("no value for constant %s", name)
  406. }
  407. info := obj.Type().Underlying().(*types.Basic).Info()
  408. if info&types.IsInteger == 0 {
  409. log.Fatalf("can't handle non-integer constant type %s", typ)
  410. }
  411. value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
  412. if value.Kind() != constant.Int {
  413. log.Fatalf("can't happen: constant is not an integer %s", name)
  414. }
  415. i64, isInt := constant.Int64Val(value)
  416. u64, isUint := constant.Uint64Val(value)
  417. if !isInt && !isUint {
  418. log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
  419. }
  420. if !isInt {
  421. u64 = uint64(i64)
  422. }
  423. v := Value{
  424. originalName: name.Name,
  425. value: u64,
  426. signed: info&types.IsUnsigned == 0,
  427. str: value.String(),
  428. }
  429. if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
  430. v.name = strings.TrimSpace(c.Text())
  431. } else {
  432. v.name = strings.TrimPrefix(v.originalName, f.trimPrefix)
  433. }
  434. f.values = append(f.values, v)
  435. }
  436. }
  437. return false
  438. }
  439. // Helpers
  440. // usize returns the number of bits of the smallest unsigned integer
  441. // type that will hold n. Used to create the smallest possible slice of
  442. // integers to use as indexes into the concatenated strings.
  443. func usize(n int) int {
  444. switch {
  445. case n < 1<<8:
  446. return 8
  447. case n < 1<<16:
  448. return 16
  449. default:
  450. // 2^32 is enough constants for anyone.
  451. return 32
  452. }
  453. }
  454. // declareIndexAndNameVars declares the index slices and concatenated names
  455. // strings representing the runs of values.
  456. func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
  457. var indexes, names []string
  458. for i, run := range runs {
  459. index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
  460. if len(run) != 1 {
  461. indexes = append(indexes, index)
  462. }
  463. names = append(names, name)
  464. }
  465. g.Printf("const (\n")
  466. for _, name := range names {
  467. g.Printf("\t%s\n", name)
  468. }
  469. g.Printf(")\n\n")
  470. if len(indexes) > 0 {
  471. g.Printf("var (")
  472. for _, index := range indexes {
  473. g.Printf("\t%s\n", index)
  474. }
  475. g.Printf(")\n\n")
  476. }
  477. }
  478. // declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
  479. func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
  480. index, name := g.createIndexAndNameDecl(run, typeName, "")
  481. g.Printf("const %s\n", name)
  482. g.Printf("var %s\n", index)
  483. }
  484. // createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
  485. func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) {
  486. b := new(bytes.Buffer)
  487. indexes := make([]int, len(run))
  488. for i := range run {
  489. b.WriteString(run[i].name)
  490. indexes[i] = b.Len()
  491. }
  492. nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String())
  493. nameLen := b.Len()
  494. b.Reset()
  495. fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen))
  496. for i, v := range indexes {
  497. if i > 0 {
  498. fmt.Fprintf(b, ", ")
  499. }
  500. fmt.Fprintf(b, "%d", v)
  501. }
  502. fmt.Fprintf(b, "}")
  503. return b.String(), nameConst
  504. }
  505. // declareNameVars declares the concatenated names string representing all the values in the runs.
  506. func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
  507. g.Printf("const _%s_name%s = \"", typeName, suffix)
  508. for _, run := range runs {
  509. for i := range run {
  510. g.Printf("%s", run[i].name)
  511. }
  512. }
  513. g.Printf("\"\n")
  514. }
  515. // buildOneRun generates the variables and String method for a single run of contiguous values.
  516. func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
  517. values := runs[0]
  518. g.Printf("\n")
  519. g.declareIndexAndNameVar(values, typeName)
  520. // The generated code is simple enough to write as a Printf format.
  521. lessThanZero := ""
  522. if values[0].signed {
  523. lessThanZero = "i < 0 || "
  524. }
  525. if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
  526. g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero)
  527. } else {
  528. g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero)
  529. }
  530. }
  531. // Arguments to format are:
  532. //
  533. // [1]: type name
  534. // [2]: size of index element (8 for uint8 etc.)
  535. // [3]: less than zero check (for signed types)
  536. const stringOneRun = `func (i %[1]s) String() string {
  537. if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
  538. return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
  539. }
  540. return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
  541. }
  542. `
  543. // Arguments to format are:
  544. // [1]: type name
  545. // [2]: lowest defined value for type, as a string
  546. // [3]: size of index element (8 for uint8 etc.)
  547. // [4]: less than zero check (for signed types)
  548. /*
  549. */
  550. const stringOneRunWithOffset = `func (i %[1]s) String() string {
  551. i -= %[2]s
  552. if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
  553. return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")"
  554. }
  555. return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
  556. }
  557. `
  558. // buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
  559. // For this pattern, a single Printf format won't do.
  560. func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
  561. g.Printf("\n")
  562. g.declareIndexAndNameVars(runs, typeName)
  563. g.Printf("func (i %s) String() string {\n", typeName)
  564. g.Printf("\tswitch {\n")
  565. for i, values := range runs {
  566. if len(values) == 1 {
  567. g.Printf("\tcase i == %s:\n", &values[0])
  568. g.Printf("\t\treturn _%s_name_%d\n", typeName, i)
  569. continue
  570. }
  571. if values[0].value == 0 && !values[0].signed {
  572. // For an unsigned lower bound of 0, "0 <= i" would be redundant.
  573. g.Printf("\tcase i <= %s:\n", &values[len(values)-1])
  574. } else {
  575. g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1])
  576. }
  577. if values[0].value != 0 {
  578. g.Printf("\t\ti -= %s\n", &values[0])
  579. }
  580. g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n",
  581. typeName, i, typeName, i, typeName, i)
  582. }
  583. g.Printf("\tdefault:\n")
  584. g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName)
  585. g.Printf("\t}\n")
  586. g.Printf("}\n")
  587. }
  588. // buildMap handles the case where the space is so sparse a map is a reasonable fallback.
  589. // It's a rare situation but has simple code.
  590. func (g *Generator) buildMap(runs [][]Value, typeName string) {
  591. g.Printf("\n")
  592. g.declareNameVars(runs, typeName, "")
  593. g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName)
  594. n := 0
  595. for _, values := range runs {
  596. for _, value := range values {
  597. g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name))
  598. n += len(value.name)
  599. }
  600. }
  601. g.Printf("}\n\n")
  602. g.Printf(stringMap, typeName)
  603. }
  604. // Argument to format is the type name.
  605. const stringMap = `func (i %[1]s) String() string {
  606. if str, ok := _%[1]s_map[i]; ok {
  607. return str
  608. }
  609. return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
  610. }
  611. `