Skip to content

Commit

Permalink
feat: Add handling for float types, and also for %v (#3263)
Browse files Browse the repository at this point in the history
This PR includes the changes to `ufmt.Sprintf()` from #2868 as well as
another branch where I had added support for `%v`.

These changes add support for a variety of float formatting options to
Sprintf, as well as support for the %v flag to automatically chose a
default representation for a given type.

Tests were added for these additions. This PR is needed for the PRNG PR
(#2868) to be fully functional, as the generators include some built in
statistical examples/tests which will not function without
`ufmt.Sprintf()` support for floats.

<details><summary>Contributors' checklist...</summary>

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
</details>

---------

Co-authored-by: Nathan Toups <[email protected]>
  • Loading branch information
wyhaines and n2p5 authored Dec 4, 2024
1 parent c1b928f commit ebb4948
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 15 deletions.
104 changes: 89 additions & 15 deletions examples/gno.land/p/demo/ufmt/ufmt.gno
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func Println(args ...interface{}) {
strs = append(strs, v.String())
case error:
strs = append(strs, v.Error())
case float64:
strs = append(strs, Sprintf("%f", v))
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
strs = append(strs, Sprintf("%d", v))
case bool:
Expand Down Expand Up @@ -49,21 +51,28 @@ func Println(args ...interface{}) {
//
// The currently formatted verbs are the following:
//
// %s: places a string value directly.
// If the value implements the interface interface{ String() string },
// the String() method is called to retrieve the value. Same about Error()
// string.
// %c: formats the character represented by Unicode code point
// %d: formats an integer value using package "strconv".
// Currently supports only uint, uint64, int, int64.
// %t: formats a boolean value to "true" or "false".
// %x: formats an integer value as a hexadecimal string.
// Currently supports only uint8, []uint8, [32]uint8.
// %c: formats a rune value as a string.
// Currently supports only rune, int.
// %q: formats a string value as a quoted string.
// %T: formats the type of the value.
// %%: outputs a literal %. Does not consume an argument.
// %s: places a string value directly.
// If the value implements the interface interface{ String() string },
// the String() method is called to retrieve the value. Same about Error()
// string.
// %c: formats the character represented by Unicode code point
// %d: formats an integer value using package "strconv".
// Currently supports only uint, uint64, int, int64.
// %f: formats a float value, with a default precision of 6.
// %e: formats a float with scientific notation; 1.23456e+78
// %E: formats a float with scientific notation; 1.23456E+78
// %F: The same as %f
// %g: formats a float value with %e for large exponents, and %f with full precision for smaller numbers
// %G: formats a float value with %G for large exponents, and %F with full precision for smaller numbers
// %t: formats a boolean value to "true" or "false".
// %x: formats an integer value as a hexadecimal string.
// Currently supports only uint8, []uint8, [32]uint8.
// %c: formats a rune value as a string.
// Currently supports only rune, int.
// %q: formats a string value as a quoted string.
// %T: formats the type of the value.
// %v: formats the value with a default representation appropriate for the value's type
// %%: outputs a literal %. Does not consume an argument.
func Sprintf(format string, args ...interface{}) string {
// we use runes to handle multi-byte characters
sTor := []rune(format)
Expand Down Expand Up @@ -97,6 +106,51 @@ func Sprintf(format string, args ...interface{}) string {
argNum++

switch verb {
case "v":
switch v := arg.(type) {
case nil:
buf += "<nil>"
case bool:
if v {
buf += "true"
} else {
buf += "false"
}
case int:
buf += strconv.Itoa(v)
case int8:
buf += strconv.Itoa(int(v))
case int16:
buf += strconv.Itoa(int(v))
case int32:
buf += strconv.Itoa(int(v))
case int64:
buf += strconv.Itoa(int(v))
case uint:
buf += strconv.FormatUint(uint64(v), 10)
case uint8:
buf += strconv.FormatUint(uint64(v), 10)
case uint16:
buf += strconv.FormatUint(uint64(v), 10)
case uint32:
buf += strconv.FormatUint(uint64(v), 10)
case uint64:
buf += strconv.FormatUint(v, 10)
case float64:
buf += strconv.FormatFloat(v, 'g', -1, 64)
case string:
buf += v
case []byte:
buf += string(v)
case []rune:
buf += string(v)
case interface{ String() string }:
buf += v.String()
case error:
buf += v.Error()
default:
buf += fallback(verb, v)
}
case "s":
switch v := arg.(type) {
case interface{ String() string }:
Expand Down Expand Up @@ -153,6 +207,24 @@ func Sprintf(format string, args ...interface{}) string {
default:
buf += fallback(verb, v)
}
case "e", "E", "f", "F", "g", "G":
switch v := arg.(type) {
case float64:
switch verb {
case "e":
buf += strconv.FormatFloat(v, byte('e'), -1, 64)
case "E":
buf += strconv.FormatFloat(v, byte('E'), -1, 64)
case "f", "F":
buf += strconv.FormatFloat(v, byte('f'), 6, 64)
case "g":
buf += strconv.FormatFloat(v, byte('g'), -1, 64)
case "G":
buf += strconv.FormatFloat(v, byte('G'), -1, 64)
}
default:
buf += fallback(verb, v)
}
case "t":
switch v := arg.(type) {
case bool:
Expand Down Expand Up @@ -244,6 +316,8 @@ func fallback(verb string, arg interface{}) string {
case error:
// note: also "string=" in Go fmt
s = "string=" + v.Error()
case float64:
s = "float64=" + Sprintf("%f", v)
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
// note: rune, byte would be dups, being aliases
if typename, e := typeToString(v); e != nil {
Expand Down
13 changes: 13 additions & 0 deletions examples/gno.land/p/demo/ufmt/ufmt_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,34 @@ func TestSprintf(t *testing.T) {
expectedOutput string
}{
{"hello %s!", []interface{}{"planet"}, "hello planet!"},
{"hello %v!", []interface{}{"planet"}, "hello planet!"},
{"hi %%%s!", []interface{}{"worl%d"}, "hi %worl%d!"},
{"%s %c %d %t", []interface{}{"foo", 'α', 421, true}, "foo α 421 true"},
{"string [%s]", []interface{}{"foo"}, "string [foo]"},
{"int [%d]", []interface{}{int(42)}, "int [42]"},
{"int [%v]", []interface{}{int(42)}, "int [42]"},
{"int8 [%d]", []interface{}{int8(8)}, "int8 [8]"},
{"int8 [%v]", []interface{}{int8(8)}, "int8 [8]"},
{"int16 [%d]", []interface{}{int16(16)}, "int16 [16]"},
{"int16 [%v]", []interface{}{int16(16)}, "int16 [16]"},
{"int32 [%d]", []interface{}{int32(32)}, "int32 [32]"},
{"int32 [%v]", []interface{}{int32(32)}, "int32 [32]"},
{"int64 [%d]", []interface{}{int64(64)}, "int64 [64]"},
{"int64 [%v]", []interface{}{int64(64)}, "int64 [64]"},
{"uint [%d]", []interface{}{uint(42)}, "uint [42]"},
{"uint [%v]", []interface{}{uint(42)}, "uint [42]"},
{"uint8 [%d]", []interface{}{uint8(8)}, "uint8 [8]"},
{"uint8 [%v]", []interface{}{uint8(8)}, "uint8 [8]"},
{"uint16 [%d]", []interface{}{uint16(16)}, "uint16 [16]"},
{"uint16 [%v]", []interface{}{uint16(16)}, "uint16 [16]"},
{"uint32 [%d]", []interface{}{uint32(32)}, "uint32 [32]"},
{"uint32 [%v]", []interface{}{uint32(32)}, "uint32 [32]"},
{"uint64 [%d]", []interface{}{uint64(64)}, "uint64 [64]"},
{"uint64 [%v]", []interface{}{uint64(64)}, "uint64 [64]"},
{"bool [%t]", []interface{}{true}, "bool [true]"},
{"bool [%v]", []interface{}{true}, "bool [true]"},
{"bool [%t]", []interface{}{false}, "bool [false]"},
{"bool [%v]", []interface{}{false}, "bool [false]"},
{"no args", nil, "no args"},
{"finish with %", nil, "finish with %"},
{"stringer [%s]", []interface{}{stringer{}}, "stringer [I'm a stringer]"},
Expand Down

0 comments on commit ebb4948

Please sign in to comment.