summary refs log tree commit diff stats
path: root/lib/pure/strformat.nim
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2017-12-17 17:58:04 +0100
committerAraq <rumpf_a@web.de>2017-12-17 17:58:04 +0100
commit2e61e6edf99fef44dcfaf23177714a064dbc05fd (patch)
treea5e73f23a97ec70b9ea336ad70dc82f1fe1fd419 /lib/pure/strformat.nim
parent5c7493f833443f34f2fa3df36aa4648bc818678d (diff)
downloadNim-2e61e6edf99fef44dcfaf23177714a064dbc05fd.tar.gz
strformat: support 'sign' as Python does
Diffstat (limited to 'lib/pure/strformat.nim')
-rw-r--r--lib/pure/strformat.nim61
1 files changed, 36 insertions, 25 deletions
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
index 97dff630e..3feb046b7 100644
--- a/lib/pure/strformat.nim
+++ b/lib/pure/strformat.nim
@@ -92,7 +92,7 @@ Standard format specifier
 
 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.
 
@@ -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
 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.
 This means that binary, octal, and hexadecimal output will be prefixed
 with '0b', '0o', and '0x', respectively.
@@ -273,6 +285,7 @@ macro fmt*(pattern: string): untyped =
     check fmt"{10}", "10"
     check fmt"{16:#X}", "0x10"
     check fmt"{16:^#7X}", " 0x10  "
+    check fmt"{16:^+#7X}", " +0x10 "
 
     # Hex tests
     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:.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:.1f}", "123.5"
@@ -417,8 +432,7 @@ proc alignString*(s: string, minimumWidth: int; align = '<'; fill = ' '): string
 type
   StandardFormatSpecifier* = object ## Type that describes "standard format specifiers".
     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
                                     ## with ``0b``, ``0o``, ``0x``.
     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))
     for idx in 0..<(result.len div 2):
       swap result[idx], result[result.len - idx - 1]
-  let adjustedWid = if negative: spec.minimumWidth - 1 else: spec.minimumWidth
   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:
       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 negative:
-      result = "-" & xx & result
-    else:
-      result = xx & result
     for i in result.len..<spec.minimumWidth:
       result.add(spec.fill)
   else:
-    if negative:
-      result = "-" & xx & result
-    else:
-      result = xx & result
     let toFill = spec.minimumWidth - result.len
     if spec.align == '^':
       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",
   ## 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
   ## should support the standard format specifiers. If ``ignoreUnknownSuffix`` is true,
@@ -500,6 +513,7 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
   const alignChars = {'<', '>', '^'}
   result.fill = ' '
   result.align = '<'
+  result.sign = '-'
   var i = start
   if i + 1 < s.len and s[i+1] in alignChars:
     result.fill = s[i]
@@ -509,11 +523,9 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
     result.align = s[i]
     inc i
 
-  when false:
-    # XXX Python inspired 'sign' not yet supported!
-    if i < s.len and s[i] in {'-', '+', ' '}:
-      result.sign = s[i]
-      inc i
+  if i < s.len and s[i] in {'-', '+', ' '}:
+    result.sign = s[i]
+    inc i
 
   if i < s.len and s[i] == '#':
     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 " &
       " 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:
-  #  "-" & alignString(formatBiggestFloat(-value, fmode, spec.precision), spec.minimumWidth-1,
-  #                     spec.align, '0')
-  #else:
-  let result = alignString(formatBiggestFloat(value, fmode, spec.precision), spec.minimumWidth,
-                            spec.align, spec.fill)
+  var f = formatBiggestFloat(value, fmode, spec.precision)
+  if value >= 0.0 and spec.sign != '-':
+    f = spec.sign & f
+  let result = alignString(f, spec.minimumWidth,
+                           spec.align, spec.fill)
   if spec.typ in {'A'..'Z'}:
     res.add toUpperAscii(result)
   else: