github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/text/internal/gen/code.go (about) 1 // Copyright 2015 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 5 package gen 6 7 import ( 8 "bytes" 9 "encoding/gob" 10 "fmt" 11 "hash" 12 "hash/fnv" 13 "io" 14 "log" 15 "os" 16 "reflect" 17 "strings" 18 "unicode" 19 "unicode/utf8" 20 ) 21 22 // This file contains utilities for generating code. 23 24 // TODO: other write methods like: 25 // - slices, maps, types, etc. 26 27 // CodeWriter is a utility for writing structured code. It computes the content 28 // hash and size of written content. It ensures there are newlines between 29 // written code blocks. 30 type CodeWriter struct { 31 buf bytes.Buffer 32 Size int 33 Hash hash.Hash32 // content hash 34 gob *gob.Encoder 35 // For comments we skip the usual one-line separator if they are followed by 36 // a code block. 37 skipSep bool 38 } 39 40 func (w *CodeWriter) Write(p []byte) (n int, err error) { 41 return w.buf.Write(p) 42 } 43 44 // NewCodeWriter returns a new CodeWriter. 45 func NewCodeWriter() *CodeWriter { 46 h := fnv.New32() 47 return &CodeWriter{Hash: h, gob: gob.NewEncoder(h)} 48 } 49 50 // WriteGoFile appends the buffer with the total size of all created structures 51 // and writes it as a Go file to the the given file with the given package name. 52 func (w *CodeWriter) WriteGoFile(filename, pkg string) { 53 f, err := os.Create(filename) 54 if err != nil { 55 log.Fatalf("Could not create file %s: %v", filename, err) 56 } 57 defer f.Close() 58 if _, err = w.WriteGo(f, pkg); err != nil { 59 log.Fatalf("Error writing file %s: %v", filename, err) 60 } 61 } 62 63 // WriteGo appends the buffer with the total size of all created structures and 64 // writes it as a Go file to the the given writer with the given package name. 65 func (w *CodeWriter) WriteGo(out io.Writer, pkg string) (n int, err error) { 66 sz := w.Size 67 w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32()) 68 defer w.buf.Reset() 69 return WriteGo(out, pkg, w.buf.Bytes()) 70 } 71 72 func (w *CodeWriter) printf(f string, x ...interface{}) { 73 fmt.Fprintf(w, f, x...) 74 } 75 76 func (w *CodeWriter) insertSep() { 77 if w.skipSep { 78 w.skipSep = false 79 return 80 } 81 // Use at least two newlines to ensure a blank space between the previous 82 // block. WriteGoFile will remove extraneous newlines. 83 w.printf("\n\n") 84 } 85 86 // WriteComment writes a comment block. All line starts are prefixed with "//". 87 // Initial empty lines are gobbled. The indentation for the first line is 88 // stripped from consecutive lines. 89 func (w *CodeWriter) WriteComment(comment string, args ...interface{}) { 90 s := fmt.Sprintf(comment, args...) 91 s = strings.Trim(s, "\n") 92 93 // Use at least two newlines to ensure a blank space between the previous 94 // block. WriteGoFile will remove extraneous newlines. 95 w.printf("\n\n// ") 96 w.skipSep = true 97 98 // strip first indent level. 99 sep := "\n" 100 for ; len(s) > 0 && (s[0] == '\t' || s[0] == ' '); s = s[1:] { 101 sep += s[:1] 102 } 103 104 strings.NewReplacer(sep, "\n// ", "\n", "\n// ").WriteString(w, s) 105 106 w.printf("\n") 107 } 108 109 func (w *CodeWriter) writeSizeInfo(size int) { 110 w.printf("// Size: %d bytes\n", size) 111 } 112 113 // WriteConst writes a constant of the given name and value. 114 func (w *CodeWriter) WriteConst(name string, x interface{}) { 115 w.insertSep() 116 v := reflect.ValueOf(x) 117 118 switch v.Type().Kind() { 119 case reflect.String: 120 // See yougam/libraries/issue/13145. 121 const arbitraryCutoff = 16 122 if v.Len() > arbitraryCutoff { 123 w.printf("var %s %s = ", name, typeName(x)) 124 } else { 125 w.printf("const %s %s = ", name, typeName(x)) 126 } 127 w.WriteString(v.String()) 128 w.printf("\n") 129 default: 130 w.printf("const %s = %#v\n", name, x) 131 } 132 } 133 134 // WriteVar writes a variable of the given name and value. 135 func (w *CodeWriter) WriteVar(name string, x interface{}) { 136 w.insertSep() 137 v := reflect.ValueOf(x) 138 oldSize := w.Size 139 sz := int(v.Type().Size()) 140 w.Size += sz 141 142 switch v.Type().Kind() { 143 case reflect.String: 144 w.printf("var %s %s = ", name, typeName(x)) 145 w.WriteString(v.String()) 146 case reflect.Struct: 147 w.gob.Encode(x) 148 fallthrough 149 case reflect.Slice, reflect.Array: 150 w.printf("var %s = ", name) 151 w.writeValue(v) 152 w.writeSizeInfo(w.Size - oldSize) 153 default: 154 w.printf("var %s %s = ", name, typeName(x)) 155 w.gob.Encode(x) 156 w.writeValue(v) 157 w.writeSizeInfo(w.Size - oldSize) 158 } 159 w.printf("\n") 160 } 161 162 func (w *CodeWriter) writeValue(v reflect.Value) { 163 x := v.Interface() 164 switch v.Kind() { 165 case reflect.String: 166 w.WriteString(v.String()) 167 case reflect.Array: 168 // Don't double count: callers of WriteArray count on the size being 169 // added, so we need to discount it here. 170 w.Size -= int(v.Type().Size()) 171 w.writeSlice(x, true) 172 case reflect.Slice: 173 w.writeSlice(x, false) 174 case reflect.Struct: 175 w.printf("%s{\n", typeName(v.Interface())) 176 t := v.Type() 177 for i := 0; i < v.NumField(); i++ { 178 w.printf("%s: ", t.Field(i).Name) 179 w.writeValue(v.Field(i)) 180 w.printf(",\n") 181 } 182 w.printf("}") 183 default: 184 w.printf("%#v", x) 185 } 186 } 187 188 // WriteString writes a string literal. 189 func (w *CodeWriter) WriteString(s string) { 190 io.WriteString(w.Hash, s) // content hash 191 w.Size += len(s) 192 193 const maxInline = 40 194 if len(s) <= maxInline { 195 w.printf("%q", s) 196 return 197 } 198 199 // We will render the string as a multi-line string. 200 const maxWidth = 80 - 4 - len(`"`) - len(`" +`) 201 202 // When starting on its own line, go fmt indents line 2+ an extra level. 203 n, max := maxWidth, maxWidth-4 204 205 // Print "" +\n, if a string does not start on its own line. 206 b := w.buf.Bytes() 207 if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' { 208 w.printf("\"\" + // Size: %d bytes\n", len(s)) 209 n, max = maxWidth, maxWidth 210 } 211 212 w.printf(`"`) 213 214 for sz, p := 0, 0; p < len(s); { 215 var r rune 216 r, sz = utf8.DecodeRuneInString(s[p:]) 217 out := s[p : p+sz] 218 chars := 1 219 if !unicode.IsPrint(r) || r == utf8.RuneError { 220 switch sz { 221 case 1: 222 out = fmt.Sprintf("\\x%02x", s[p]) 223 case 2, 3: 224 out = fmt.Sprintf("\\u%04x", r) 225 case 4: 226 out = fmt.Sprintf("\\U%08x", r) 227 } 228 chars = len(out) 229 } 230 if n -= chars; n < 0 { 231 w.printf("\" +\n\"") 232 n = max - len(out) 233 } 234 w.printf("%s", out) 235 p += sz 236 } 237 w.printf(`"`) 238 } 239 240 // WriteSlice writes a slice value. 241 func (w *CodeWriter) WriteSlice(x interface{}) { 242 w.writeSlice(x, false) 243 } 244 245 // WriteArray writes an array value. 246 func (w *CodeWriter) WriteArray(x interface{}) { 247 w.writeSlice(x, true) 248 } 249 250 func (w *CodeWriter) writeSlice(x interface{}, isArray bool) { 251 v := reflect.ValueOf(x) 252 w.gob.Encode(v.Len()) 253 w.Size += v.Len() * int(v.Type().Elem().Size()) 254 name := typeName(x) 255 if isArray { 256 name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:]) 257 } 258 if isArray { 259 w.printf("%s{\n", name) 260 } else { 261 w.printf("%s{ // %d elements\n", name, v.Len()) 262 } 263 264 switch kind := v.Type().Elem().Kind(); kind { 265 case reflect.String: 266 for _, s := range x.([]string) { 267 w.WriteString(s) 268 w.printf(",\n") 269 } 270 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 271 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 272 // nLine and nBlock are the number of elements per line and block. 273 nLine, nBlock, format := 8, 64, "%d," 274 switch kind { 275 case reflect.Uint8: 276 format = "%#02x," 277 case reflect.Uint16: 278 format = "%#04x," 279 case reflect.Uint32: 280 nLine, nBlock, format = 4, 32, "%#08x," 281 case reflect.Uint, reflect.Uint64: 282 nLine, nBlock, format = 4, 32, "%#016x," 283 case reflect.Int8: 284 nLine = 16 285 } 286 n := nLine 287 for i := 0; i < v.Len(); i++ { 288 if i%nBlock == 0 && v.Len() > nBlock { 289 w.printf("// Entry %X - %X\n", i, i+nBlock-1) 290 } 291 x := v.Index(i).Interface() 292 w.gob.Encode(x) 293 w.printf(format, x) 294 if n--; n == 0 { 295 n = nLine 296 w.printf("\n") 297 } 298 } 299 w.printf("\n") 300 case reflect.Struct: 301 zero := reflect.Zero(v.Type().Elem()).Interface() 302 for i := 0; i < v.Len(); i++ { 303 x := v.Index(i).Interface() 304 w.gob.EncodeValue(v) 305 if !reflect.DeepEqual(zero, x) { 306 line := fmt.Sprintf("%#v,\n", x) 307 line = line[strings.IndexByte(line, '{'):] 308 w.printf("%d: ", i) 309 w.printf(line) 310 } 311 } 312 case reflect.Array: 313 for i := 0; i < v.Len(); i++ { 314 w.printf("%d: %#v,\n", i, v.Index(i).Interface()) 315 } 316 default: 317 panic("gen: slice elem type not supported") 318 } 319 w.printf("}") 320 } 321 322 // WriteType writes a definition of the type of the given value and returns the 323 // type name. 324 func (w *CodeWriter) WriteType(x interface{}) string { 325 t := reflect.TypeOf(x) 326 w.printf("type %s struct {\n", t.Name()) 327 for i := 0; i < t.NumField(); i++ { 328 w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type) 329 } 330 w.printf("}\n") 331 return t.Name() 332 } 333 334 // typeName returns the name of the go type of x. 335 func typeName(x interface{}) string { 336 t := reflect.ValueOf(x).Type() 337 return strings.Replace(fmt.Sprint(t), "main.", "", 1) 338 }