Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

print.gno

19.39 Kb · 894 lines
  1package ufmt
  2
  3import (
  4	"sort"
  5	"unicode/utf8"
  6)
  7
  8// This file is the high level: parsing the format, and choosing how each
  9// operand prints. It is a port of print.go in Go's fmt, minus reflect.
 10
 11const (
 12	commaSpace  = ", "
 13	nilAngle    = "<nil>"
 14	nilParen    = "(nil)"
 15	unhandled   = "(unhandled)"
 16	percentBang = "%!"
 17	missing     = "(MISSING)"
 18	badIndex    = "(BADINDEX)"
 19	extra       = "%!(EXTRA "
 20	badWidth    = "%!(BADWIDTH)"
 21	badPrec     = "%!(BADPREC)"
 22	noVerb      = "%!(NOVERB)"
 23)
 24
 25// printer holds the output and the state of the verb being printed.
 26type printer struct {
 27	buf []byte
 28
 29	// The flags, width and precision of the current verb.
 30	plus, minus, sharp, space, zero bool
 31	plusV, sharpV                   bool // %+v and %#v
 32	wid, prec                       int
 33	widPresent, precPresent         bool
 34
 35	arg        any  // the operand being printed, for error messages
 36	erroring   bool // printing an error message: do not call methods
 37	reordered  bool // an explicit index was used
 38	goodArgNum bool // the last index was valid
 39	wrapErrs   bool // Errorf: %w is allowed
 40	wrapped    []int
 41}
 42
 43func newPrinter() *printer {
 44	return &printer{}
 45}
 46
 47// Write implements State, so a Formatter can write.
 48func (p *printer) Write(b []byte) (int, error) {
 49	p.buf = append(p.buf, b...)
 50	return len(b), nil
 51}
 52
 53// WriteString lets a Formatter write a string without a conversion.
 54func (p *printer) WriteString(s string) (int, error) {
 55	p.buf = append(p.buf, s...)
 56	return len(s), nil
 57}
 58
 59// Width implements State.
 60func (p *printer) Width() (int, bool) { return p.wid, p.widPresent }
 61
 62// Precision implements State.
 63func (p *printer) Precision() (int, bool) { return p.prec, p.precPresent }
 64
 65// Flag implements State.
 66func (p *printer) Flag(c int) bool {
 67	switch c {
 68	case '-':
 69		return p.minus
 70	case '+':
 71		return p.plus || p.plusV
 72	case '#':
 73		return p.sharp || p.sharpV
 74	case ' ':
 75		return p.space
 76	case '0':
 77		return p.zero
 78	}
 79	return false
 80}
 81
 82func (p *printer) writeString(s string) { p.buf = append(p.buf, s...) }
 83func (p *printer) writeByte(c byte)     { p.buf = append(p.buf, c) }
 84func (p *printer) writeRune(r rune)     { p.buf = utf8.AppendRune(p.buf, r) }
 85
 86// doPrint is Sprint: %v, with a space between two operands neither of which is
 87// a string.
 88func (p *printer) doPrint(a []any) {
 89	prevString := false
 90	for argNum, arg := range a {
 91		_, isString := arg.(string)
 92		if argNum > 0 && !isString && !prevString {
 93			p.writeByte(' ')
 94		}
 95		p.printArg(arg, 'v')
 96		prevString = isString
 97	}
 98}
 99
100// doPrintln is Sprintln: %v, always a space between operands, then a newline.
101func (p *printer) doPrintln(a []any) {
102	for argNum, arg := range a {
103		if argNum > 0 {
104			p.writeByte(' ')
105		}
106		p.printArg(arg, 'v')
107	}
108	p.writeByte('\n')
109}
110
111// parsenum reads a decimal number at s[start:end].
112func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
113	if start >= end {
114		return 0, false, end
115	}
116	for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
117		if num > 1e6 {
118			return 0, false, end // absurdly large: refuse rather than allocate
119		}
120		num = num*10 + int(s[newi]-'0')
121		isnum = true
122	}
123	return
124}
125
126// intFromArg reads a * width or precision from a[argNum].
127func intFromArg(a []any, argNum int) (num int, isInt bool, newArgNum int) {
128	newArgNum = argNum
129	if argNum >= len(a) {
130		return
131	}
132	isInt = true
133	switch v := a[argNum].(type) {
134	case int:
135		num = v
136	case int8:
137		num = int(v)
138	case int16:
139		num = int(v)
140	case int32:
141		num = int(v)
142	case int64:
143		num = int(v)
144	case uint:
145		num = int(v)
146	case uint8:
147		num = int(v)
148	case uint16:
149		num = int(v)
150	case uint32:
151		num = int(v)
152	case uint64:
153		num = int(v)
154	default:
155		isInt = false
156	}
157	newArgNum = argNum + 1
158	if num > 1e6 || num < -1e6 {
159		num = 0
160		isInt = false
161	}
162	return
163}
164
165// argNumber reads an explicit index, "[3]", at format[i:].
166func (p *printer) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {
167	if len(format) <= i || format[i] != '[' {
168		return argNum, i, false
169	}
170	p.reordered = true
171	for j := 1; j < len(format)-i; j++ {
172		if format[i+j] == ']' {
173			width, ok, newi := parsenum(format, i+1, i+j)
174			if !ok || newi != i+j {
175				p.goodArgNum = false
176				return argNum, i + j + 1, true
177			}
178			index := width - 1
179			if index >= 0 && index < numArgs {
180				return index, i + j + 1, true
181			}
182			p.goodArgNum = false
183			return argNum, i + j + 1, true
184		}
185	}
186	p.goodArgNum = false
187	return argNum, i + 1, false
188}
189
190// doPrintf is Sprintf.
191func (p *printer) doPrintf(format string, a []any) {
192	end := len(format)
193	argNum := 0
194	afterIndex := false // the previous item was an index like [3]
195	p.reordered = false
196
197	for i := 0; i < end; {
198		p.goodArgNum = true
199		lasti := i
200		for i < end && format[i] != '%' {
201			i++
202		}
203		if i > lasti {
204			p.writeString(format[lasti:i])
205		}
206		if i >= end {
207			break
208		}
209		i++ // skip '%'
210
211		p.clearFlags()
212
213		// Fast path: a bare lowercase verb, "%d" or "%s", is most of what any
214		// realm prints, and skips the specifier parsing entirely.
215		if i < end && 'a' <= format[i] && format[i] <= 'z' && argNum < len(a) {
216			verb := rune(format[i])
217			if verb == 'w' {
218				p.wrapped = append(p.wrapped, argNum)
219			}
220			p.printArg(a[argNum], verb)
221			argNum++
222			i++
223			continue
224		}
225
226	flags:
227		for ; i < end; i++ {
228			switch format[i] {
229			case '#':
230				p.sharp = true
231			case '0':
232				p.zero = !p.minus // zero padding only ever goes on the left
233			case '+':
234				p.plus = true
235			case '-':
236				p.minus = true
237				p.zero = false
238			case ' ':
239				p.space = true
240			default:
241				break flags
242			}
243		}
244
245		argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
246
247		// Width.
248		if i < end && format[i] == '*' {
249			i++
250			p.wid, p.widPresent, argNum = intFromArg(a, argNum)
251			if !p.widPresent {
252				p.writeString(badWidth)
253			}
254			if p.wid < 0 {
255				p.wid = -p.wid
256				p.minus = true
257				p.zero = false
258			}
259			afterIndex = false
260		} else {
261			p.wid, p.widPresent, i = parsenum(format, i, end)
262			if afterIndex && p.widPresent { // "%[3]2d"
263				p.goodArgNum = false
264			}
265		}
266
267		// Precision.
268		if i+1 < end && format[i] == '.' {
269			i++
270			if afterIndex { // "%[3].2d"
271				p.goodArgNum = false
272			}
273			argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
274			if i < end && format[i] == '*' {
275				i++
276				p.prec, p.precPresent, argNum = intFromArg(a, argNum)
277				if p.prec < 0 {
278					p.prec = 0
279					p.precPresent = false
280				}
281				if !p.precPresent {
282					p.writeString(badPrec)
283				}
284				afterIndex = false
285			} else {
286				p.prec, p.precPresent, i = parsenum(format, i, end)
287				if !p.precPresent {
288					p.prec = 0
289					p.precPresent = true
290				}
291			}
292		}
293
294		if !afterIndex {
295			argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
296		}
297
298		if i >= end {
299			p.writeString(noVerb)
300			break
301		}
302
303		verb, size := rune(format[i]), 1
304		if verb >= utf8.RuneSelf {
305			verb, size = utf8.DecodeRuneInString(format[i:])
306		}
307		i += size
308
309		switch {
310		case verb == '%': // takes no operand, ignores width and precision
311			p.writeByte('%')
312		case !p.goodArgNum:
313			p.badArgNum(verb)
314		case argNum >= len(a):
315			p.missingArg(verb)
316		default:
317			if verb == 'w' {
318				p.wrapped = append(p.wrapped, argNum)
319			}
320			if verb == 'v' || verb == 'w' {
321				p.sharpV, p.sharp = p.sharp, false
322				p.plusV, p.plus = p.plus, false
323			}
324			p.printArg(a[argNum], verb)
325			argNum++
326		}
327	}
328
329	// Report unused operands, unless an index made the count meaningless.
330	if !p.reordered && argNum < len(a) {
331		p.clearFlags()
332		p.writeString(extra)
333		for i, arg := range a[argNum:] {
334			if i > 0 {
335				p.writeString(commaSpace)
336			}
337			if arg == nil {
338				p.writeString(nilAngle)
339				continue
340			}
341			p.writeString(typeName(arg))
342			p.writeByte('=')
343			p.printArg(arg, 'v')
344		}
345		p.writeByte(')')
346	}
347}
348
349func (p *printer) badArgNum(verb rune) {
350	p.writeString(percentBang)
351	p.writeRune(verb)
352	p.writeString(badIndex)
353}
354
355func (p *printer) missingArg(verb rune) {
356	p.writeString(percentBang)
357	p.writeRune(verb)
358	p.writeString(missing)
359}
360
361// badVerb writes "%!d(string=hi)": the verb, the operand's type and its value.
362func (p *printer) badVerb(verb rune) {
363	p.erroring = true
364	p.writeString(percentBang)
365	p.writeRune(verb)
366	p.writeByte('(')
367	switch {
368	case p.arg == nil:
369		p.writeString(nilAngle)
370	default:
371		if t := typeName(p.arg); t != "unknown" {
372			p.writeString(t)
373			p.writeByte('=')
374			p.printArg(p.arg, 'v')
375		} else {
376			p.writeString("unhandled")
377		}
378	}
379	p.writeByte(')')
380	p.erroring = false
381}
382
383// printArg prints one operand.
384func (p *printer) printArg(arg any, verb rune) {
385	p.arg = arg
386
387	if arg == nil {
388		switch verb {
389		case 'T', 'v':
390			p.padString(nilAngle)
391		default:
392			p.badVerb(verb)
393		}
394		return
395	}
396
397	switch verb {
398	case 'T':
399		p.fmtS(typeName(arg))
400		return
401	case 'p':
402		p.badVerb(verb)
403		return
404	}
405
406	// The basic types first, exactly as Go: a named type never matches these,
407	// so its methods are still found below.
408	switch f := arg.(type) {
409	case bool:
410		p.fmtBool(f, verb)
411	case float32:
412		p.fmtFloatVerb(float64(f), 32, verb)
413	case float64:
414		p.fmtFloatVerb(f, 64, verb)
415	case int:
416		p.fmtSigned(int64(f), verb)
417	case int8:
418		p.fmtSigned(int64(f), verb)
419	case int16:
420		p.fmtSigned(int64(f), verb)
421	case int32:
422		p.fmtSigned(int64(f), verb)
423	case int64:
424		p.fmtSigned(f, verb)
425	case uint:
426		p.fmtUnsigned(uint64(f), verb)
427	case uint8:
428		p.fmtUnsigned(uint64(f), verb)
429	case uint16:
430		p.fmtUnsigned(uint64(f), verb)
431	case uint32:
432		p.fmtUnsigned(uint64(f), verb)
433	case uint64:
434		p.fmtUnsigned(f, verb)
435	case string:
436		p.fmtString(f, verb)
437	case []byte:
438		p.fmtBytes(f, verb)
439	default:
440		if !p.handleMethods(verb) {
441			p.printComposite(arg, verb)
442		}
443	}
444}
445
446// handleMethods prints arg with its own Format, GoString, Error or String
447// method, in Go's order, and reports whether it did.
448func (p *printer) handleMethods(verb rune) (handled bool) {
449	if p.erroring {
450		return false
451	}
452	if verb == 'w' {
453		// %w is for Errorf, and for an error.
454		if _, ok := p.arg.(error); !ok || !p.wrapErrs {
455			p.badVerb(verb)
456			return true
457		}
458		verb = 'v'
459	}
460
461	if f, ok := p.arg.(Formatter); ok {
462		handled = true // stays true if the method panics
463		defer p.catchPanic(p.arg, verb, "Format")
464		f.Format(p, verb)
465		return
466	}
467
468	if p.sharpV {
469		if g, ok := p.arg.(GoStringer); ok {
470			handled = true
471			defer p.catchPanic(p.arg, verb, "GoString")
472			p.fmtS(g.GoString())
473			return
474		}
475		return false
476	}
477
478	switch verb {
479	case 'v', 's', 'x', 'X', 'q':
480		switch v := p.arg.(type) {
481		case error:
482			handled = true
483			defer p.catchPanic(p.arg, verb, "Error")
484			p.fmtString(v.Error(), verb)
485			return
486		case Stringer:
487			handled = true
488			defer p.catchPanic(p.arg, verb, "String")
489			p.fmtString(v.String(), verb)
490			return
491		}
492	}
493	return false
494}
495
496// catchPanic turns a panicking method into "%!v(PANIC=String method: msg)".
497func (p *printer) catchPanic(arg any, verb rune, method string) {
498	r := recover()
499	if r == nil {
500		return
501	}
502	if p.erroring {
503		panic(r) // the method panicked again while printing its own panic
504	}
505	p.clearFlags()
506	p.writeString(percentBang)
507	p.writeRune(verb)
508	p.writeString("(PANIC=")
509	p.writeString(method)
510	p.writeString(" method: ")
511	p.erroring = true
512	p.printArg(r, 'v')
513	p.erroring = false
514	p.writeByte(')')
515}
516
517func (p *printer) fmtBool(v bool, verb rune) {
518	switch verb {
519	case 't', 'v':
520		p.fmtBoolean(v)
521	default:
522		p.badVerb(verb)
523	}
524}
525
526// fmtSigned prints a signed integer. The magnitude of math.MinInt64 does not
527// fit an int64, so it is computed without ever negating v itself.
528func (p *printer) fmtSigned(v int64, verb rune) {
529	if v >= 0 {
530		p.fmtIntegerVerb(uint64(v), false, true, verb)
531		return
532	}
533	mag := uint64(-(v + 1)) + 1
534	p.fmtIntegerVerb(mag, true, true, verb)
535}
536
537func (p *printer) fmtUnsigned(u uint64, verb rune) {
538	p.fmtIntegerVerb(u, false, false, verb)
539}
540
541func (p *printer) fmtIntegerVerb(u uint64, negative, isSigned bool, verb rune) {
542	switch verb {
543	case 'v':
544		if p.sharpV && !isSigned {
545			p.fmt0x64(u, true)
546		} else {
547			p.fmtInteger(u, negative, 10, verb, ldigits)
548		}
549	case 'd':
550		p.fmtInteger(u, negative, 10, verb, ldigits)
551	case 'b':
552		p.fmtInteger(u, negative, 2, verb, ldigits)
553	case 'o', 'O':
554		p.fmtInteger(u, negative, 8, verb, ldigits)
555	case 'x':
556		p.fmtInteger(u, negative, 16, verb, ldigits)
557	case 'X':
558		p.fmtInteger(u, negative, 16, verb, udigits)
559	case 'c', 'q', 'U':
560		if negative {
561			u = ^u + 1 // back to two's complement, as Go sees it: never a rune
562		}
563		switch verb {
564		case 'c':
565			p.fmtC(u)
566		case 'q':
567			p.fmtQc(u)
568		default:
569			p.fmtUnicode(u)
570		}
571	default:
572		p.badVerb(verb)
573	}
574}
575
576// fmt0x64 prints u in hex with a 0x prefix (leading0x) or without one.
577func (p *printer) fmt0x64(u uint64, leading0x bool) {
578	sharp := p.sharp
579	p.sharp = leading0x
580	p.fmtInteger(u, false, 16, 'v', ldigits)
581	p.sharp = sharp
582}
583
584func (p *printer) fmtFloatVerb(v float64, size int, verb rune) {
585	switch verb {
586	case 'v':
587		p.fmtFloat(v, size, 'g', -1)
588	case 'b', 'g', 'G', 'x', 'X':
589		p.fmtFloat(v, size, verb, -1)
590	case 'f', 'e', 'E':
591		p.fmtFloat(v, size, verb, 6)
592	case 'F':
593		p.fmtFloat(v, size, 'f', 6)
594	default:
595		p.badVerb(verb)
596	}
597}
598
599func (p *printer) fmtString(v string, verb rune) {
600	switch verb {
601	case 'v':
602		if p.sharpV {
603			p.fmtQ(v)
604		} else {
605			p.fmtS(v)
606		}
607	case 's':
608		p.fmtS(v)
609	case 'x':
610		p.fmtSx(v, ldigits)
611	case 'X':
612		p.fmtSx(v, udigits)
613	case 'q':
614		p.fmtQ(v)
615	default:
616		p.badVerb(verb)
617	}
618}
619
620func (p *printer) fmtBytes(v []byte, verb rune) {
621	switch verb {
622	case 'v', 'd':
623		if p.sharpV {
624			p.writeString("[]byte")
625			if v == nil {
626				p.writeString(nilParen)
627				return
628			}
629			p.writeByte('{')
630			for i, c := range v {
631				if i > 0 {
632					p.writeString(commaSpace)
633				}
634				p.fmt0x64(uint64(c), true)
635			}
636			p.writeByte('}')
637			return
638		}
639		p.writeByte('[')
640		for i, c := range v {
641			if i > 0 {
642				p.writeByte(' ')
643			}
644			p.fmtInteger(uint64(c), false, 10, verb, ldigits)
645		}
646		p.writeByte(']')
647	case 's':
648		p.fmtS(string(v))
649	case 'x':
650		p.fmtSx(string(v), ldigits)
651	case 'X':
652		p.fmtSx(string(v), udigits)
653	case 'q':
654		p.fmtQ(string(v))
655	default:
656		// Every other verb applies to each byte, as for any slice.
657		p.writeByte('[')
658		for i, c := range v {
659			if i > 0 {
660				p.writeByte(' ')
661			}
662			p.fmtUnsigned(uint64(c), verb)
663		}
664		p.writeByte(']')
665	}
666}
667
668// printComposite prints the slices and maps gno can see without reflect.
669func (p *printer) printComposite(arg any, verb rune) {
670	switch v := arg.(type) {
671	case []any:
672		p.printSlice("[]interface {}", v == nil, len(v), func(i int) any { return v[i] }, verb)
673	case []string:
674		p.printSlice("[]string", v == nil, len(v), func(i int) any { return v[i] }, verb)
675	case []error:
676		p.printSlice("[]error", v == nil, len(v), func(i int) any { return v[i] }, verb)
677	case []bool:
678		p.printSlice("[]bool", v == nil, len(v), func(i int) any { return v[i] }, verb)
679	case []int:
680		p.printSlice("[]int", v == nil, len(v), func(i int) any { return v[i] }, verb)
681	case []int8:
682		p.printSlice("[]int8", v == nil, len(v), func(i int) any { return v[i] }, verb)
683	case []int16:
684		p.printSlice("[]int16", v == nil, len(v), func(i int) any { return v[i] }, verb)
685	case []int32:
686		p.printSlice("[]int32", v == nil, len(v), func(i int) any { return v[i] }, verb)
687	case []int64:
688		p.printSlice("[]int64", v == nil, len(v), func(i int) any { return v[i] }, verb)
689	case []uint:
690		p.printSlice("[]uint", v == nil, len(v), func(i int) any { return v[i] }, verb)
691	case []uint16:
692		p.printSlice("[]uint16", v == nil, len(v), func(i int) any { return v[i] }, verb)
693	case []uint32:
694		p.printSlice("[]uint32", v == nil, len(v), func(i int) any { return v[i] }, verb)
695	case []uint64:
696		p.printSlice("[]uint64", v == nil, len(v), func(i int) any { return v[i] }, verb)
697	case []float32:
698		p.printSlice("[]float32", v == nil, len(v), func(i int) any { return v[i] }, verb)
699	case []float64:
700		p.printSlice("[]float64", v == nil, len(v), func(i int) any { return v[i] }, verb)
701	case map[string]string:
702		keys := sortedKeys(len(v), func(add func(string)) {
703			for k := range v {
704				add(k)
705			}
706		})
707		p.printMap("map[string]string", v == nil, keys, func(k string) any { return v[k] }, verb)
708	case map[string]any:
709		keys := sortedKeys(len(v), func(add func(string)) {
710			for k := range v {
711				add(k)
712			}
713		})
714		p.printMap("map[string]interface {}", v == nil, keys, func(k string) any { return v[k] }, verb)
715	case map[string]int:
716		keys := sortedKeys(len(v), func(add func(string)) {
717			for k := range v {
718				add(k)
719			}
720		})
721		p.printMap("map[string]int", v == nil, keys, func(k string) any { return v[k] }, verb)
722	case map[string]bool:
723		keys := sortedKeys(len(v), func(add func(string)) {
724			for k := range v {
725				add(k)
726			}
727		})
728		p.printMap("map[string]bool", v == nil, keys, func(k string) any { return v[k] }, verb)
729	default:
730		if verb == 'v' {
731			p.padString(unhandled)
732		} else {
733			p.badVerb(verb)
734		}
735	}
736}
737
738// printSlice prints "[a b]", or "[]T{a, b}" for %#v, each element with verb.
739func (p *printer) printSlice(typ string, isNil bool, n int, at func(int) any, verb rune) {
740	if p.sharpV {
741		p.writeString(typ)
742		if isNil {
743			p.writeString(nilParen)
744			return
745		}
746		p.writeByte('{')
747		for i := 0; i < n; i++ {
748			if i > 0 {
749				p.writeString(commaSpace)
750			}
751			p.printElem(at(i), verb)
752		}
753		p.writeByte('}')
754		return
755	}
756	p.writeByte('[')
757	for i := 0; i < n; i++ {
758		if i > 0 {
759			p.writeByte(' ')
760		}
761		p.printElem(at(i), verb)
762	}
763	p.writeByte(']')
764}
765
766// printMap prints "map[a:1 b:2]", or "map[string]int{"a":1, "b":2}" for %#v,
767// keys sorted as Go sorts them.
768func (p *printer) printMap(typ string, isNil bool, keys []string, at func(string) any, verb rune) {
769	if p.sharpV {
770		p.writeString(typ)
771		if isNil {
772			p.writeString(nilParen)
773			return
774		}
775		p.writeByte('{')
776	} else {
777		p.writeString("map[")
778	}
779	for i, k := range keys {
780		if i > 0 {
781			if p.sharpV {
782				p.writeString(commaSpace)
783			} else {
784				p.writeByte(' ')
785			}
786		}
787		p.printElem(k, verb)
788		p.writeByte(':')
789		p.printElem(at(k), verb)
790	}
791	if p.sharpV {
792		p.writeByte('}')
793	} else {
794		p.writeByte(']')
795	}
796}
797
798// printElem prints one element of a composite, keeping the outer operand for
799// error messages. A nil element prints "<nil>" whatever the verb, as in Go.
800func (p *printer) printElem(v any, verb rune) {
801	outer := p.arg
802	if v == nil {
803		if p.sharpV {
804			p.writeString("interface {}(nil)")
805		} else {
806			p.padString(nilAngle)
807		}
808	} else {
809		p.printArg(v, verb)
810	}
811	p.arg = outer
812}
813
814func sortedKeys(n int, each func(add func(string))) []string {
815	keys := make([]string, 0, n)
816	each(func(k string) { keys = append(keys, k) })
817	sort.Strings(keys)
818	return keys
819}
820
821// typeName is %T for the types gno can name without reflect.
822func typeName(v any) string {
823	switch v.(type) {
824	case bool:
825		return "bool"
826	case int:
827		return "int"
828	case int8:
829		return "int8"
830	case int16:
831		return "int16"
832	case int32:
833		return "int32"
834	case int64:
835		return "int64"
836	case uint:
837		return "uint"
838	case uint8:
839		return "uint8"
840	case uint16:
841		return "uint16"
842	case uint32:
843		return "uint32"
844	case uint64:
845		return "uint64"
846	case float32:
847		return "float32"
848	case float64:
849		return "float64"
850	case string:
851		return "string"
852	case []byte:
853		return "[]uint8"
854	case []any:
855		return "[]interface {}"
856	case []string:
857		return "[]string"
858	case []error:
859		return "[]error"
860	case []bool:
861		return "[]bool"
862	case []int:
863		return "[]int"
864	case []int8:
865		return "[]int8"
866	case []int16:
867		return "[]int16"
868	case []int32:
869		return "[]int32"
870	case []int64:
871		return "[]int64"
872	case []uint:
873		return "[]uint"
874	case []uint16:
875		return "[]uint16"
876	case []uint32:
877		return "[]uint32"
878	case []uint64:
879		return "[]uint64"
880	case []float32:
881		return "[]float32"
882	case []float64:
883		return "[]float64"
884	case map[string]string:
885		return "map[string]string"
886	case map[string]any:
887		return "map[string]interface {}"
888	case map[string]int:
889		return "map[string]int"
890	case map[string]bool:
891		return "map[string]bool"
892	}
893	return "unknown"
894}