strformat: support 'sign' as Python does
This commit is contained in:
parent
5c7493f833
commit
2e61e6edf9
1 changed files with 36 additions and 25 deletions
|
|
@ -92,7 +92,7 @@ Standard format specifier
|
||||||
|
|
||||||
The general form of a standard format specifier is::
|
The general form of a standard format specifier is::
|
||||||
|
|
||||||
[[fill]align][#][0][minimumwidth][.precision][type]
|
[[fill]align][sign][#][0][minimumwidth][.precision][type]
|
||||||
|
|
||||||
The brackets ([]) indicate an optional element.
|
The brackets ([]) indicate an optional element.
|
||||||
|
|
||||||
|
|
@ -116,6 +116,18 @@ The optional 'fill' character defines the character to be used to pad
|
||||||
the field to the minimum width. The fill character, if present, must be
|
the field to the minimum width. The fill character, if present, must be
|
||||||
followed by an alignment flag.
|
followed by an alignment flag.
|
||||||
|
|
||||||
|
The 'sign' option is only valid for numeric types, and can be one of the following:
|
||||||
|
|
||||||
|
================= ====================================================
|
||||||
|
Sign Meaning
|
||||||
|
================= ====================================================
|
||||||
|
``+`` Indicates that a sign should be used for both
|
||||||
|
positive as well as negative numbers.
|
||||||
|
``-`` Indicates that a sign should be used only for
|
||||||
|
negative numbers (this is the default behavior).
|
||||||
|
`` `` (space) Indicates that a leading space should be used on
|
||||||
|
positive numbers.
|
||||||
|
|
||||||
If the '#' character is present, integers use the 'alternate form' for formatting.
|
If the '#' character is present, integers use the 'alternate form' for formatting.
|
||||||
This means that binary, octal, and hexadecimal output will be prefixed
|
This means that binary, octal, and hexadecimal output will be prefixed
|
||||||
with '0b', '0o', and '0x', respectively.
|
with '0b', '0o', and '0x', respectively.
|
||||||
|
|
@ -273,6 +285,7 @@ macro fmt*(pattern: string): untyped =
|
||||||
check fmt"{10}", "10"
|
check fmt"{10}", "10"
|
||||||
check fmt"{16:#X}", "0x10"
|
check fmt"{16:#X}", "0x10"
|
||||||
check fmt"{16:^#7X}", " 0x10 "
|
check fmt"{16:^#7X}", " 0x10 "
|
||||||
|
check fmt"{16:^+#7X}", " +0x10 "
|
||||||
|
|
||||||
# Hex tests
|
# Hex tests
|
||||||
check fmt"{0:x}", "0"
|
check fmt"{0:x}", "0"
|
||||||
|
|
@ -292,6 +305,8 @@ macro fmt*(pattern: string): untyped =
|
||||||
check fmt"{123.456}", "123.456"
|
check fmt"{123.456}", "123.456"
|
||||||
check fmt"{-123.456}", "-123.456"
|
check fmt"{-123.456}", "-123.456"
|
||||||
check fmt"{123.456:.3f}", "123.456"
|
check fmt"{123.456:.3f}", "123.456"
|
||||||
|
check fmt"{123.456:+.3f}", "+123.456"
|
||||||
|
check fmt"{-123.456:+.3f}", "-123.456"
|
||||||
check fmt"{-123.456:.3f}", "-123.456"
|
check fmt"{-123.456:.3f}", "-123.456"
|
||||||
check fmt"{123.456:1g}", "123.456"
|
check fmt"{123.456:1g}", "123.456"
|
||||||
check fmt"{123.456:.1f}", "123.5"
|
check fmt"{123.456:.1f}", "123.5"
|
||||||
|
|
@ -417,8 +432,7 @@ proc alignString*(s: string, minimumWidth: int; align = '<'; fill = ' '): string
|
||||||
type
|
type
|
||||||
StandardFormatSpecifier* = object ## Type that describes "standard format specifiers".
|
StandardFormatSpecifier* = object ## Type that describes "standard format specifiers".
|
||||||
fill*, align*: char ## Desired fill and alignment.
|
fill*, align*: char ## Desired fill and alignment.
|
||||||
when false:
|
sign*: char ## Desired sign.
|
||||||
sign: char ## Desired sign.
|
|
||||||
alternateForm*: bool ## Whether to prefix binary, octal and hex numbers
|
alternateForm*: bool ## Whether to prefix binary, octal and hex numbers
|
||||||
## with ``0b``, ``0o``, ``0x``.
|
## with ``0b``, ``0o``, ``0x``.
|
||||||
padWithZero*: bool ## Whether to pad with zeros rather than spaces.
|
padWithZero*: bool ## Whether to pad with zeros rather than spaces.
|
||||||
|
|
@ -461,24 +475,23 @@ proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string
|
||||||
result.add(mkDigit(d.int, spec.typ))
|
result.add(mkDigit(d.int, spec.typ))
|
||||||
for idx in 0..<(result.len div 2):
|
for idx in 0..<(result.len div 2):
|
||||||
swap result[idx], result[result.len - idx - 1]
|
swap result[idx], result[result.len - idx - 1]
|
||||||
let adjustedWid = if negative: spec.minimumWidth - 1 else: spec.minimumWidth
|
|
||||||
if spec.padWithZero:
|
if spec.padWithZero:
|
||||||
let toFill = spec.minimumWidth - result.len - xx.len - ord(negative)
|
let sign = negative or spec.sign != '-'
|
||||||
|
let toFill = spec.minimumWidth - result.len - xx.len - ord(sign)
|
||||||
if toFill > 0:
|
if toFill > 0:
|
||||||
result = repeat('0', toFill) & result
|
result = repeat('0', toFill) & result
|
||||||
|
|
||||||
|
if negative:
|
||||||
|
result = "-" & xx & result
|
||||||
|
elif spec.sign != '-':
|
||||||
|
result = spec.sign & xx & result
|
||||||
|
else:
|
||||||
|
result = xx & result
|
||||||
|
|
||||||
if spec.align == '<':
|
if spec.align == '<':
|
||||||
if negative:
|
|
||||||
result = "-" & xx & result
|
|
||||||
else:
|
|
||||||
result = xx & result
|
|
||||||
for i in result.len..<spec.minimumWidth:
|
for i in result.len..<spec.minimumWidth:
|
||||||
result.add(spec.fill)
|
result.add(spec.fill)
|
||||||
else:
|
else:
|
||||||
if negative:
|
|
||||||
result = "-" & xx & result
|
|
||||||
else:
|
|
||||||
result = xx & result
|
|
||||||
let toFill = spec.minimumWidth - result.len
|
let toFill = spec.minimumWidth - result.len
|
||||||
if spec.align == '^':
|
if spec.align == '^':
|
||||||
let half = toFill div 2
|
let half = toFill div 2
|
||||||
|
|
@ -492,7 +505,7 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
|
||||||
## An exported helper proc that parses the "standard format specifiers",
|
## An exported helper proc that parses the "standard format specifiers",
|
||||||
## as specified by the grammar::
|
## as specified by the grammar::
|
||||||
##
|
##
|
||||||
## [[fill]align][#][0][minimumwidth][.precision][type]
|
## [[fill]align][sign][#][0][minimumwidth][.precision][type]
|
||||||
##
|
##
|
||||||
## This is only of interest if you want to write a custom ``format`` proc that
|
## This is only of interest if you want to write a custom ``format`` proc that
|
||||||
## should support the standard format specifiers. If ``ignoreUnknownSuffix`` is true,
|
## should support the standard format specifiers. If ``ignoreUnknownSuffix`` is true,
|
||||||
|
|
@ -500,6 +513,7 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
|
||||||
const alignChars = {'<', '>', '^'}
|
const alignChars = {'<', '>', '^'}
|
||||||
result.fill = ' '
|
result.fill = ' '
|
||||||
result.align = '<'
|
result.align = '<'
|
||||||
|
result.sign = '-'
|
||||||
var i = start
|
var i = start
|
||||||
if i + 1 < s.len and s[i+1] in alignChars:
|
if i + 1 < s.len and s[i+1] in alignChars:
|
||||||
result.fill = s[i]
|
result.fill = s[i]
|
||||||
|
|
@ -509,11 +523,9 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
|
||||||
result.align = s[i]
|
result.align = s[i]
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
when false:
|
if i < s.len and s[i] in {'-', '+', ' '}:
|
||||||
# XXX Python inspired 'sign' not yet supported!
|
result.sign = s[i]
|
||||||
if i < s.len and s[i] in {'-', '+', ' '}:
|
inc i
|
||||||
result.sign = s[i]
|
|
||||||
inc i
|
|
||||||
|
|
||||||
if i < s.len and s[i] == '#':
|
if i < s.len and s[i] == '#':
|
||||||
result.alternateForm = true
|
result.alternateForm = true
|
||||||
|
|
@ -578,12 +590,11 @@ proc format*(value: SomeReal; specifier: string; res: var string) =
|
||||||
"invalid type in format string for number, expected one " &
|
"invalid type in format string for number, expected one " &
|
||||||
" of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & spec.typ)
|
" of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & spec.typ)
|
||||||
|
|
||||||
#let result = if spec.minimumWidth > 0 and spec.align == '<' and value < 0 and spec.padWithZero:
|
var f = formatBiggestFloat(value, fmode, spec.precision)
|
||||||
# "-" & alignString(formatBiggestFloat(-value, fmode, spec.precision), spec.minimumWidth-1,
|
if value >= 0.0 and spec.sign != '-':
|
||||||
# spec.align, '0')
|
f = spec.sign & f
|
||||||
#else:
|
let result = alignString(f, spec.minimumWidth,
|
||||||
let result = alignString(formatBiggestFloat(value, fmode, spec.precision), spec.minimumWidth,
|
spec.align, spec.fill)
|
||||||
spec.align, spec.fill)
|
|
||||||
if spec.typ in {'A'..'Z'}:
|
if spec.typ in {'A'..'Z'}:
|
||||||
res.add toUpperAscii(result)
|
res.add toUpperAscii(result)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue