Skip to content

Commit 833d4da

Browse files
committed
py: string - slightly better implementation of % operator
1 parent 440aa7c commit 833d4da

File tree

1 file changed

+133
-1
lines changed

1 file changed

+133
-1
lines changed

py/string.go

+133-1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,131 @@ func (a String) M__ge__(other Object) (Object, error) {
150150

151151
// % operator
152152

153+
/*
154+
155+
4.7.2. printf-style String Formatting
156+
157+
Note The formatting operations described here exhibit a variety of
158+
quirks that lead to a number of common errors (such as failing to
159+
display tuples and dictionaries correctly). Using the newer
160+
str.format() interface helps avoid these errors, and also provides a
161+
generally more powerful, flexible and extensible approach to
162+
formatting text.
163+
164+
String objects have one unique built-in operation: the % operator
165+
(modulo). This is also known as the string formatting or interpolation
166+
operator. Given format % values (where format is a string), %
167+
conversion specifications in format are replaced with zero or more
168+
elements of values. The effect is similar to using the sprintf() in
169+
the C language.
170+
171+
If format requires a single argument, values may be a single non-tuple
172+
object. [5] Otherwise, values must be a tuple with exactly the number
173+
of items specified by the format string, or a single mapping object
174+
(for example, a dictionary).
175+
176+
A conversion specifier contains two or more characters and has the
177+
following components, which must occur in this order:
178+
179+
The '%' character, which marks the start of the specifier.
180+
181+
Mapping key (optional), consisting of a parenthesised sequence of
182+
characters (for example, (somename)).
183+
184+
Conversion flags (optional), which affect the result of some
185+
conversion types.
186+
187+
Minimum field width (optional). If specified as an '*' (asterisk), the
188+
actual width is read from the next element of the tuple in values, and
189+
the object to convert comes after the minimum field width and optional
190+
precision.
191+
192+
Precision (optional), given as a '.' (dot) followed by the
193+
precision. If specified as '*' (an asterisk), the actual precision is
194+
read from the next element of the tuple in values, and the value to
195+
convert comes after the precision.
196+
197+
Length modifier (optional).
198+
199+
Conversion type.
200+
201+
When the right argument is a dictionary (or other mapping type), then
202+
the formats in the string must include a parenthesised mapping key
203+
into that dictionary inserted immediately after the '%' character. The
204+
mapping key selects the value to be formatted from the mapping. For
205+
example:
206+
207+
>>>
208+
>>> print('%(language)s has %(number)03d quote types.' %
209+
... {'language': "Python", "number": 2})
210+
Python has 002 quote types.
211+
212+
In this case no * specifiers may occur in a format (since they require
213+
a sequential parameter list).
214+
215+
The conversion flag characters are:
216+
217+
Flag Meaning
218+
'#' The value conversion will use the “alternate form” (where defined below).
219+
'0' The conversion will be zero padded for numeric values.
220+
'-' The converted value is left adjusted (overrides the '0' conversion if both are given).
221+
' ' (a space) A blank should be left before a positive number (or empty string) produced by a signed conversion.
222+
'+' A sign character ('+' or '-') will precede the conversion (overrides a “space” flag).
223+
224+
A length modifier (h, l, or L) may be present, but is ignored as it is
225+
not necessary for Python – so e.g. %ld is identical to %d.
226+
227+
The conversion types are:
228+
229+
Conversion Meaning Notes
230+
'd' Signed integer decimal.
231+
'i' Signed integer decimal.
232+
'o' Signed octal value. (1)
233+
'u' Obsolete type – it is identical to 'd'. (7)
234+
'x' Signed hexadecimal (lowercase). (2)
235+
'X' Signed hexadecimal (uppercase). (2)
236+
'e' Floating point exponential format (lowercase). (3)
237+
'E' Floating point exponential format (uppercase). (3)
238+
'f' Floating point decimal format. (3)
239+
'F' Floating point decimal format. (3)
240+
'g' Floating point format. Uses lowercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise. (4)
241+
'G' Floating point format. Uses uppercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise. (4)
242+
'c' Single character (accepts integer or single character string).
243+
'r' String (converts any Python object using repr()). (5)
244+
's' String (converts any Python object using str()). (5)
245+
'a' String (converts any Python object using ascii()). (5)
246+
'%' No argument is converted, results in a '%' character in the result.
247+
Notes:
248+
249+
The alternate form causes a leading zero ('0') to be inserted between
250+
left-hand padding and the formatting of the number if the leading
251+
character of the result is not already a zero.
252+
253+
The alternate form causes a leading '0x' or '0X' (depending on whether
254+
the 'x' or 'X' format was used) to be inserted between left-hand
255+
padding and the formatting of the number if the leading character of
256+
the result is not already a zero.
257+
258+
The alternate form causes the result to always contain a decimal
259+
point, even if no digits follow it.
260+
261+
The precision determines the number of digits after the decimal point
262+
and defaults to 6.
263+
264+
The alternate form causes the result to always contain a decimal
265+
point, and trailing zeroes are not removed as they would otherwise be.
266+
267+
The precision determines the number of significant digits before and
268+
after the decimal point and defaults to 6.
269+
270+
If precision is N, the output is truncated to N characters.
271+
272+
See PEP 237. Since Python strings have an explicit length, %s
273+
conversions do not assume that '\0' is the end of the string.
274+
275+
Changed in version 3.1: %f conversions for numbers whose absolute
276+
value is over 1e50 are no longer replaced by %g conversions.
277+
*/
153278
func (a String) M__mod__(other Object) (Object, error) {
154279
var values Tuple
155280
switch b := other.(type) {
@@ -159,7 +284,14 @@ func (a String) M__mod__(other Object) (Object, error) {
159284
values = Tuple{other}
160285
}
161286
// FIXME not a full implementation ;-)
162-
return String(fmt.Sprintf("%s %#v", a, values)), nil
287+
params := make([]interface{}, len(values))
288+
for i := range values {
289+
params[i] = values[i]
290+
}
291+
s := string(a)
292+
s = strings.Replace(s, "%s", "%v", -1)
293+
s = strings.Replace(s, "%r", "%#v", -1)
294+
return String(fmt.Sprintf(s, params...)), nil
163295
}
164296

165297
func (a String) M__rmod__(other Object) (Object, error) {

0 commit comments

Comments
 (0)