this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

pkg/strconv: allow string argument for format

Fixes #1436.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I4a4e6f50696e5a34f894b3eaeb918f99c538e199
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/537569
Unity-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>

+129 -50
+118 -1
pkg/strconv/manual.go
··· 14 14 15 15 package strconv 16 16 17 - import "cuelang.org/go/cue/literal" 17 + import ( 18 + "fmt" 19 + "strconv" 20 + 21 + "cuelang.org/go/cue" 22 + "cuelang.org/go/cue/literal" 23 + "cuelang.org/go/internal/core/adt" 24 + "cuelang.org/go/pkg/internal" 25 + ) 18 26 19 27 // Unquote interprets s as a single-quoted, double-quoted, 20 28 // or backquoted CUE string literal, returning the string value ··· 23 31 return literal.Unquote(s) 24 32 } 25 33 34 + // FormatFloat converts the floating-point number f to a string, 35 + // according to the format fmt and precision prec. It rounds the 36 + // result assuming that the original was obtained from a floating-point 37 + // value of bitSize bits (32 for float32, 64 for float64). 38 + // 39 + // The format fmt a string: one of 40 + // "b" (-ddddp±ddd, a binary exponent), 41 + // "e" (-d.dddde±dd, a decimal exponent), 42 + // "E" (-d.ddddE±dd, a decimal exponent), 43 + // "f" (-ddd.dddd, no exponent), 44 + // "g" ("e" for large exponents, "f" otherwise), 45 + // "G" ("E" for large exponents, "f" otherwise), 46 + // "x" (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or 47 + // "X" (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent). 48 + // 49 + // The precision prec controls the number of digits (excluding the exponent) 50 + // printed by the "e", "E", "f", "g", "G", "x", and "X" formats. 51 + // For "e", "E", "f", "x", and "X", it is the number of digits after the decimal point. 52 + // For "g" and "G" it is the maximum number of significant digits (trailing 53 + // zeros are removed). 54 + // The special precision -1 uses the smallest number of digits 55 + // necessary such that ParseFloat will return f exactly. 56 + // 57 + // For historical reasons, an integer (the ASCII code point of the 58 + // a format character) is also accepted for the format. 59 + func FormatFloat(f float64, fmtVal cue.Value, prec, bitSize int) (string, error) { 60 + // Note: none of the error cases below should ever happen 61 + // because the argument disjunction type fully enumerates all 62 + // the allowed values. 63 + var fmtByte byte 64 + switch k := fmtVal.Kind(); k { 65 + case cue.StringKind: 66 + s, err := fmtVal.String() 67 + if err != nil { 68 + return "", err 69 + } 70 + if len(s) != 1 { 71 + return "", fmt.Errorf("expected single character string") 72 + } 73 + fmtByte = s[0] 74 + case cue.IntKind: 75 + n, err := fmtVal.Int64() 76 + if err != nil { 77 + return "", err 78 + } 79 + // It might look like converts any arbitrary int mod 256, but 80 + // the disjunction allows only valid values, so it's OK. 81 + fmtByte = byte(n) 82 + default: 83 + return "", fmt.Errorf("unexpected kind %v", k) 84 + } 85 + return strconv.FormatFloat(f, fmtByte, prec, bitSize), nil 86 + } 87 + 26 88 // TODO: replace parsing functions with parsing to apd 89 + 90 + var formatCodes = &adt.Disjunction{ 91 + Values: []*adt.Vertex{}, 92 + } 93 + 94 + const formatFloatChars = "beEfgGxX" 95 + 96 + // Use a var here rather than an init function so it's guaranteed 97 + // to run before the call to internal.Register inside pkg.go. 98 + var _ = func() (_ struct{}) { 99 + formatArg := &adt.Disjunction{ 100 + Values: make([]*adt.Vertex, 0, len(formatFloatChars)*2), 101 + } 102 + // Create the disjunction in two loops rather than one so it's 103 + // formatted a little nicer ("g" | "e" | ... | 98 | 101 | ...) 104 + // rather than alternating strings and numbers. 105 + for i := range formatFloatChars { 106 + formatArg.Values = append(formatArg.Values, newStr(formatFloatChars[i:i+1])) 107 + } 108 + for i := range formatFloatChars { 109 + formatArg.Values = append(formatArg.Values, newInt(int(formatFloatChars[i]))) 110 + } 111 + 112 + pkg.Native = append(pkg.Native, &internal.Builtin{ 113 + Name: "FormatFloat", 114 + Params: []internal.Param{ 115 + {Kind: adt.NumKind}, 116 + {Kind: adt.IntKind | adt.StringKind, Value: formatArg}, 117 + {Kind: adt.IntKind}, 118 + {Kind: adt.IntKind}, 119 + }, 120 + Result: adt.StringKind, 121 + Func: func(c *internal.CallCtxt) { 122 + f, fmt, prec, bitSize := c.Float64(0), c.Value(1), c.Int(2), c.Int(3) 123 + if c.Do() { 124 + c.Ret, c.Err = FormatFloat(f, fmt, prec, bitSize) 125 + } 126 + }, 127 + }) 128 + return 129 + }() 130 + 131 + func newStr(s string) *adt.Vertex { 132 + v := &adt.Vertex{} 133 + v.SetValue(nil, adt.Finalized, &adt.String{Str: s}) 134 + return v 135 + } 136 + 137 + func newInt(i int) *adt.Vertex { 138 + n := &adt.Num{K: adt.IntKind} 139 + n.X.SetInt64(int64(i)) 140 + v := &adt.Vertex{} 141 + v.SetValue(nil, adt.Finalized, n) 142 + return v 143 + }
-15
pkg/strconv/pkg.go
··· 123 123 } 124 124 }, 125 125 }, { 126 - Name: "FormatFloat", 127 - Params: []internal.Param{ 128 - {Kind: adt.NumKind}, 129 - {Kind: adt.IntKind}, 130 - {Kind: adt.IntKind}, 131 - {Kind: adt.IntKind}, 132 - }, 133 - Result: adt.StringKind, 134 - Func: func(c *internal.CallCtxt) { 135 - f, fmt, prec, bitSize := c.Float64(0), c.Byte(1), c.Int(2), c.Int(3) 136 - if c.Do() { 137 - c.Ret = FormatFloat(f, fmt, prec, bitSize) 138 - } 139 - }, 140 - }, { 141 126 Name: "FormatUint", 142 127 Params: []internal.Param{ 143 128 {Kind: adt.IntKind},
-26
pkg/strconv/strconv.go
··· 123 123 return strconv.Atoi(s) 124 124 } 125 125 126 - // FormatFloat converts the floating-point number f to a string, 127 - // according to the format fmt and precision prec. It rounds the 128 - // result assuming that the original was obtained from a floating-point 129 - // value of bitSize bits (32 for float32, 64 for float64). 130 - // 131 - // The format fmt is one of 132 - // 'b' (-ddddp±ddd, a binary exponent), 133 - // 'e' (-d.dddde±dd, a decimal exponent), 134 - // 'E' (-d.ddddE±dd, a decimal exponent), 135 - // 'f' (-ddd.dddd, no exponent), 136 - // 'g' ('e' for large exponents, 'f' otherwise), 137 - // 'G' ('E' for large exponents, 'f' otherwise), 138 - // 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or 139 - // 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent). 140 - // 141 - // The precision prec controls the number of digits (excluding the exponent) 142 - // printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats. 143 - // For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point. 144 - // For 'g' and 'G' it is the maximum number of significant digits (trailing 145 - // zeros are removed). 146 - // The special precision -1 uses the smallest number of digits 147 - // necessary such that ParseFloat will return f exactly. 148 - func FormatFloat(f float64, fmt byte, prec, bitSize int) string { 149 - return strconv.FormatFloat(f, fmt, prec, bitSize) 150 - } 151 - 152 126 // FormatUint returns the string representation of i in the given base, 153 127 // for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z' 154 128 // for digit values >= 10.
+11 -8
pkg/strconv/testdata/gen.txtar
··· 8 8 t3: strconv.FormatFloat(3.02, -1, 4, 64) 9 9 t4: strconv.FormatFloat(3.02, 1.0, 4, 64) 10 10 t5: strconv.FormatBool(true) 11 + t6: strconv.FormatFloat(3.5, "g", -1, 64) 12 + t7: strconv.FormatFloat(3.5, 102, 3, 64) 11 13 -- out/strconv -- 12 14 Errors: 13 - t2: int 300 overflows byte in argument 1 in call to strconv.FormatFloat: 14 - ./in.cue:4:5 15 - t3: cannot use -1 (type int) as byte in argument 1 to strconv.FormatFloat: 16 - ./in.cue:5:5 15 + t2: cannot use 300 as "b" | "e" | "E" | "f" | "g" | "G" | "x" | "X" | 98 | 101 | 69 | 102 | 103 | 71 | 120 | 88 in argument 2 to strconv.FormatFloat: 16 + ./in.cue:4:31 17 + t3: cannot use -1 as "b" | "e" | "E" | "f" | "g" | "G" | "x" | "X" | 98 | 101 | 69 | 102 | 103 | 71 | 120 | 88 in argument 2 to strconv.FormatFloat: 17 18 ./in.cue:5:31 18 - t4: cannot use 1.0 (type float) as int in argument 2 to strconv.FormatFloat: 19 + t4: cannot use 1.0 (type float) as (int|string) in argument 2 to strconv.FormatFloat: 19 20 ./in.cue:6:31 20 21 21 22 Result: 22 23 t1: "40" 23 - t2: _|_ // t2: int 300 overflows byte in argument 1 in call to strconv.FormatFloat 24 - t3: _|_ // t3: cannot use -1 (type int) as byte in argument 1 to strconv.FormatFloat 25 - t4: _|_ // t4: cannot use 1.0 (type float) as int in argument 2 to strconv.FormatFloat 24 + t2: _|_ // t2: cannot use 300 as "b" | "e" | "E" | "f" | "g" | "G" | "x" | "X" | 98 | 101 | 69 | 102 | 103 | 71 | 120 | 88 in argument 2 to strconv.FormatFloat 25 + t3: _|_ // t3: cannot use -1 as "b" | "e" | "E" | "f" | "g" | "G" | "x" | "X" | 98 | 101 | 69 | 102 | 103 | 71 | 120 | 88 in argument 2 to strconv.FormatFloat 26 + t4: _|_ // t4: cannot use 1.0 (type float) as (int|string) in argument 2 to strconv.FormatFloat 26 27 t5: "true" 28 + t6: "3.5" 29 + t7: "3.500" 27 30