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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}