@@ -76,7 +76,6 @@ import (
7676 "go/format"
7777 "go/token"
7878 "go/types"
79- "io/ioutil"
8079 "log"
8180 "os"
8281 "path/filepath"
@@ -94,6 +93,8 @@ type CodeMsg struct {
9493 BuildTags string `clop:"--tags" usage:"comma-separated list of build tags to apply"`
9594 CodeMsg bool `clop:"long" usage:"turn on the ability to generate codemsg code"`
9695 Grpc bool `clop:"long" usage:"Generate grpc error type"`
96+ StringMethod string `clop:"long" usage:"String function name" default:"String"`
97+ String bool `clop:"long" usage:"Generate string function"`
9798 CodeMsgStructName string `clop:"long" usage:"turn on the ability to generate codemsg code" default:"CodeMsg"` //TODO
9899
99100 CodeName string `clop:"long" usage:"set new code name" default:"Code"`
@@ -142,21 +143,13 @@ func genString(c *CodeMsg) {
142143
143144 // Run generate for each type.
144145 for _ , typeName := range types {
145- g .generate (typeName )
146+ g .generate (typeName , c )
146147 }
147148
148149 // Format the output.
149- src := g .format ()
150150
151- // Write to file.
152- outputName := c .Output
153- if outputName == "" {
154- baseName := fmt .Sprintf ("%s_string.go" , types [0 ])
155- outputName = filepath .Join (dir , strings .ToLower (baseName ))
156- }
157- err := ioutil .WriteFile (outputName , src , 0644 )
158- if err != nil {
159- log .Fatalf ("writing output: %s" , err )
151+ if c .String {
152+ saveFile (c , dir , g .format (), "_string.go" , types [0 ])
160153 }
161154}
162155
@@ -245,7 +238,7 @@ func (g *Generator) addPackage(pkg *packages.Package) {
245238}
246239
247240// generate produces the String method for the named type.
248- func (g * Generator ) generate (typeName string ) {
241+ func (g * Generator ) generate (typeName string , c * CodeMsg ) {
249242
250243 values := make ([]Value , 0 , 100 )
251244
@@ -273,26 +266,7 @@ func (g *Generator) generate(typeName string) {
273266 }
274267 g .Printf ("}\n " )
275268 runs := splitIntoRuns (values )
276- // The decision of which pattern to use depends on the number of
277- // runs in the numbers. If there's only one, it's easy. For more than
278- // one, there's a tradeoff between complexity and size of the data
279- // and code vs. the simplicity of a map. A map takes more space,
280- // but so does the code. The decision here (crossover at 10) is
281- // arbitrary, but considers that for large numbers of runs the cost
282- // of the linear scan in the switch might become important, and
283- // rather than use yet another algorithm such as binary search,
284- // we punt and use a map. In any case, the likelihood of a map
285- // being necessary for any realistic example other than bitmasks
286- // is very low. And bitmasks probably deserve their own analysis,
287- // to be done some other day.
288- switch {
289- case len (runs ) == 1 :
290- g .buildOneRun (runs , typeName )
291- case len (runs ) <= 10 :
292- g .buildMultipleRuns (runs , typeName )
293- default :
294- g .buildMap (runs , typeName )
295- }
269+ g .buildMap (runs , typeName , c )
296270}
297271
298272// splitIntoRuns breaks the values into runs of contiguous sequences.
@@ -565,86 +539,9 @@ func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix stri
565539 g .Printf ("\" \n " )
566540}
567541
568- // buildOneRun generates the variables and String method for a single run of contiguous values.
569- func (g * Generator ) buildOneRun (runs [][]Value , typeName string ) {
570- values := runs [0 ]
571- g .Printf ("\n " )
572- g .declareIndexAndNameVar (values , typeName )
573- // The generated code is simple enough to write as a Printf format.
574- lessThanZero := ""
575- if values [0 ].signed {
576- lessThanZero = "i < 0 || "
577- }
578- if values [0 ].value == 0 { // Signed or unsigned, 0 is still 0.
579- g .Printf (stringOneRun , typeName , usize (len (values )), lessThanZero )
580- } else {
581- g .Printf (stringOneRunWithOffset , typeName , values [0 ].String (), usize (len (values )), lessThanZero )
582- }
583- }
584-
585- // Arguments to format are:
586- //
587- // [1]: type name
588- // [2]: size of index element (8 for uint8 etc.)
589- // [3]: less than zero check (for signed types)
590- const stringOneRun = `func (i %[1]s) String() string {
591- if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
592- return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
593- }
594- return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
595- }
596- `
597-
598- // Arguments to format are:
599- // [1]: type name
600- // [2]: lowest defined value for type, as a string
601- // [3]: size of index element (8 for uint8 etc.)
602- // [4]: less than zero check (for signed types)
603- /*
604- */
605- const stringOneRunWithOffset = `func (i %[1]s) String() string {
606- i -= %[2]s
607- if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
608- return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")"
609- }
610- return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
611- }
612- `
613-
614- // buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
615- // For this pattern, a single Printf format won't do.
616- func (g * Generator ) buildMultipleRuns (runs [][]Value , typeName string ) {
617- g .Printf ("\n " )
618- g .declareIndexAndNameVars (runs , typeName )
619- g .Printf ("func (i %s) String() string {\n " , typeName )
620- g .Printf ("\t switch {\n " )
621- for i , values := range runs {
622- if len (values ) == 1 {
623- g .Printf ("\t case i == %s:\n " , & values [0 ])
624- g .Printf ("\t \t return _%s_name_%d\n " , typeName , i )
625- continue
626- }
627- if values [0 ].value == 0 && ! values [0 ].signed {
628- // For an unsigned lower bound of 0, "0 <= i" would be redundant.
629- g .Printf ("\t case i <= %s:\n " , & values [len (values )- 1 ])
630- } else {
631- g .Printf ("\t case %s <= i && i <= %s:\n " , & values [0 ], & values [len (values )- 1 ])
632- }
633- if values [0 ].value != 0 {
634- g .Printf ("\t \t i -= %s\n " , & values [0 ])
635- }
636- g .Printf ("\t \t return _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n " ,
637- typeName , i , typeName , i , typeName , i )
638- }
639- g .Printf ("\t default:\n " )
640- g .Printf ("\t \t return \" %s(\" + strconv.FormatInt(int64(i), 10) + \" )\" \n " , typeName )
641- g .Printf ("\t }\n " )
642- g .Printf ("}\n " )
643- }
644-
645542// buildMap handles the case where the space is so sparse a map is a reasonable fallback.
646543// It's a rare situation but has simple code.
647- func (g * Generator ) buildMap (runs [][]Value , typeName string ) {
544+ func (g * Generator ) buildMap (runs [][]Value , typeName string , c * CodeMsg ) {
648545 g .Printf ("\n " )
649546 g .declareNameVars (runs , typeName , "" )
650547 g .Printf ("\n var _%s_map = map[%s]string{\n " , typeName , typeName )
@@ -656,11 +553,11 @@ func (g *Generator) buildMap(runs [][]Value, typeName string) {
656553 }
657554 }
658555 g .Printf ("}\n \n " )
659- g .Printf (stringMap , typeName )
556+ g .Printf (stringMap , typeName , c . StringMethod )
660557}
661558
662559// Argument to format is the type name.
663- const stringMap = `func (i %[1]s) String () string {
560+ const stringMap = `func (i %[1]s) %[2]s () string {
664561 if str, ok := _%[1]s_map[i]; ok {
665562 return str
666563 }
0 commit comments