-
Notifications
You must be signed in to change notification settings - Fork 0
/
format.go
250 lines (244 loc) · 6.52 KB
/
format.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ivyshims
import (
"bytes"
"fmt"
"io"
"math/big"
"strings"
"unicode/utf8"
)
// fmtText returns a vector of Chars holding the string representation
// of the value v. The lhs u defines the format:
// 1 item: number of decimals, or if textual, the complete format.
// 2 items: width of field, number of decimals.
// 3 items: width of field, number of decimals, format char.
// For the 1-item variant, the format is as in Go, except that
// unlike in Go conversions can occur to coerce the value,
// for instance to print a floating point number as a decimal
// integer with '%d'.
func fmtText(c Context, u, v Value) Value {
config := c.Config()
format, verb := formatString(config, u)
if format == "" {
Errorf("illegal format %q", u.Sprint(config))
}
var b bytes.Buffer
switch val := v.(type) {
case Int, BigInt, BigRat, BigFloat, Char:
formatOne(c, &b, format, verb, val)
case Complex:
formatOne(c, &b, format, verb, val.real)
b.WriteByte('j')
formatOne(c, &b, format, verb, val.imag)
case Vector:
if val.AllChars() && strings.ContainsRune("boOqsvxX", rune(verb)) {
// Print the string as a unit.
fmt.Fprintf(&b, format, val.Sprint(debugConf))
} else {
for i, v := range val {
if i > 0 {
b.WriteByte(' ')
}
formatOne(c, &b, format, verb, v)
}
}
case *Matrix:
val.fprintf(c, &b, format)
default:
Errorf("cannot format '%s'", val.Sprint(config))
}
str := b.String()
elem := make([]Value, utf8.RuneCountInString(str))
for i, r := range str {
elem[i] = Char(r)
}
return NewVector(elem)
}
// formatString returns the format string given u, the lhs of a binary text invocation.
func formatString(c *Config, u Value) (string, byte) {
switch val := u.(type) {
case Int:
return fmt.Sprintf("%%.%df", val), 'f'
case Char:
s := fmt.Sprintf("%%%c", val)
return s, verbOf(s) // Error check is in there.
case Vector:
if val.AllChars() {
s := val.Sprint(c)
if !strings.ContainsRune(s, '%') {
s = "%" + s
}
verb := verbOf(s)
return s, verb
}
char := Char('f')
switch len(val) {
case 1:
// Decimal count only.
dec, ok := val[0].(Int)
if ok {
return fmt.Sprintf("%%.%df", dec), 'f'
}
case 3:
// Width count, and char.
var ok bool
char, ok = val[2].(Char)
if !ok {
break
}
char |= ' '
if char != 'e' && char != 'f' && char != 'g' {
break
}
fallthrough
case 2:
// Width and decimal count.
wid, ok1 := val[0].(Int)
dec, ok2 := val[1].(Int)
if ok1 && ok2 {
return fmt.Sprintf("%%%d.%d%c", wid, dec, char), byte(char)
}
}
}
return "", 'f'
}
// verbOf returns the first formatting verb, after an obligatory percent, in the string,
// skipping %% of course. It returns 0 if no verb is found. It does some rudimentary
// validation.
func verbOf(format string) byte {
percent := strings.IndexByte(format, '%')
if percent < 0 {
Errorf("invalid format %q", format)
}
s := format[percent+1:]
Loop:
for i, c := range s {
if c == '%' {
}
switch c {
// Flags etc.
case '+', '-', '#', ' ', '0':
continue
// Digits etc.
case '.', '1', '2', '3', '4', '5', '6', '7', '8', '9':
continue
// Special case for %%: go on to next verb.
case '%':
return verbOf(s[i+1:])
case 'b', 'c', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'o', 'O', 'q', 's', 't', 'U', 'v', 'x', 'X':
return byte(c)
default:
break Loop
}
}
Errorf("invalid format %q", format)
panic("not reached")
}
// formatOne prints a scalar value into b with the specified format.
// How it does this depends on the format, permitting us to use %d on
// floats and rationals, for example.
func formatOne(c Context, w io.Writer, format string, verb byte, v Value) {
switch verb {
case 't': // Boolean. TODO: Should be 0 or 1, but that's messy. Odd case anyway.
fmt.Fprintf(w, format, toBool(v))
case 'v':
fmt.Fprintf(w, format, v.Sprint(debugConf)) // Cleanest output.
case 'c', 'U':
// Dig inside the values to find or form a char.
switch val := v.(type) {
case Int:
fmt.Fprintf(w, format, int32(val))
case Char:
fmt.Fprintf(w, format, uint32(val))
case BigInt:
Errorf("value too large for %%%c: %v", verb, v)
case BigRat:
i, _ := val.Float64()
fmt.Fprintf(w, format, int64(i))
case BigFloat:
i, _ := val.Int64()
fmt.Fprintf(w, format, i)
case Complex:
Errorf("%%%c not implemented for complex: %v", verb, val)
}
return
case 's', 'q':
// Chars become strings.
switch val := v.(type) {
case Int:
fmt.Fprintf(w, format, string(int32(val)))
case Char:
fmt.Fprintf(w, format, string(int32(val)))
case BigInt:
Errorf("value too large for %%%c: %v", verb, v)
case BigRat:
i, _ := val.Float64()
fmt.Fprintf(w, format, string(int32(i)))
case BigFloat:
i, _ := val.Int64()
fmt.Fprintf(w, format, string(int32(i)))
case Complex:
Errorf("%%%c not implemented for complex: %v", verb, val)
}
return
case 'b', 'd', 'o', 'O', 'x', 'X':
// Dig inside the values to find or form an int. Avoid default String method.
switch val := v.(type) {
case Int:
fmt.Fprintf(w, format, int64(val))
case Char:
fmt.Fprintf(w, format, uint32(val))
case BigInt:
fmt.Fprintf(w, format, val.Int)
case BigRat:
// This formats numerator and denomator separately,
// but that's like applying the format to a vector.
fmt.Fprintf(w, format, val.Num())
fmt.Fprint(w, "/")
fmt.Fprintf(w, format, val.Denom())
case BigFloat:
// Hex float format is special, but big.Float does not implement 'X'.
switch verb {
case 'x':
fmt.Fprintf(w, format, val.Float)
return
case 'X':
Errorf("%%X not implemented for float: %v", val)
}
i, _ := val.Int(big.NewInt(0)) // TODO: Truncates towards zero. Do rounding?
fmt.Fprintf(w, format, i)
case Complex:
formatOne(c, w, format, verb, val.real)
fmt.Fprint(w, "j")
formatOne(c, w, format, verb, val.imag)
}
return
case 'e', 'E', 'f', 'F', 'g', 'G':
f := newFloat(c)
switch val := v.(type) {
case Int:
f.SetInt64(int64(val))
fmt.Fprintf(w, format, f)
case Char:
f.SetInt64(int64(val))
fmt.Fprintf(w, format, f)
case BigInt:
f.SetInt(val.Int)
fmt.Fprintf(w, format, f)
case BigRat:
f.SetRat(val.Rat)
fmt.Fprintf(w, format, f)
case BigFloat:
fmt.Fprintf(w, format, val.Float)
case Complex:
formatOne(c, w, format, verb, val.real)
fmt.Fprint(w, "j")
formatOne(c, w, format, verb, val.imag)
}
default:
fmt.Fprintf(w, format, v)
}
}