mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-07 04:14:19 +00:00
fixes #7078
This commit is contained in:
@@ -15,27 +15,27 @@ Examples:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
doAssert %"""{"abc":>4}""" == " abc"
|
||||
doAssert %"""{"abc":<4}""" == "abc "
|
||||
doAssert &"""{"abc":>4}""" == " abc"
|
||||
doAssert &"""{"abc":<4}""" == "abc "
|
||||
|
||||
doAssert %"{-12345:08}" == "-0012345"
|
||||
doAssert %"{-1:3}" == " -1"
|
||||
doAssert %"{-1:03}" == "-01"
|
||||
doAssert %"{16:#X}" == "0x10"
|
||||
doAssert &"{-12345:08}" == "-0012345"
|
||||
doAssert &"{-1:3}" == " -1"
|
||||
doAssert &"{-1:03}" == "-01"
|
||||
doAssert &"{16:#X}" == "0x10"
|
||||
|
||||
doAssert %"{123.456}" == "123.456"
|
||||
doAssert %"{123.456:>9.3f}" == " 123.456"
|
||||
doAssert %"{123.456:9.3f}" == " 123.456"
|
||||
doAssert %"{123.456:9.4f}" == " 123.4560"
|
||||
doAssert %"{123.456:>9.0f}" == " 123."
|
||||
doAssert %"{123.456:<9.4f}" == "123.4560 "
|
||||
doAssert &"{123.456}" == "123.456"
|
||||
doAssert &"{123.456:>9.3f}" == " 123.456"
|
||||
doAssert &"{123.456:9.3f}" == " 123.456"
|
||||
doAssert &"{123.456:9.4f}" == " 123.4560"
|
||||
doAssert &"{123.456:>9.0f}" == " 123."
|
||||
doAssert &"{123.456:<9.4f}" == "123.4560 "
|
||||
|
||||
doAssert %"{123.456:e}" == "1.234560e+02"
|
||||
doAssert %"{123.456:>13e}" == " 1.234560e+02"
|
||||
doAssert %"{123.456:13e}" == " 1.234560e+02"
|
||||
doAssert &"{123.456:e}" == "1.234560e+02"
|
||||
doAssert &"{123.456:>13e}" == " 1.234560e+02"
|
||||
doAssert &"{123.456:13e}" == " 1.234560e+02"
|
||||
|
||||
|
||||
An expression like ``%"{key} is {value:arg} {{z}}"`` is transformed into:
|
||||
An expression like ``&"{key} is {value:arg} {{z}}"`` is transformed into:
|
||||
|
||||
.. code-block:: nim
|
||||
var temp = newStringOfCap(educatedCapGuess)
|
||||
@@ -48,13 +48,13 @@ An expression like ``%"{key} is {value:arg} {{z}}"`` is transformed into:
|
||||
Parts of the string that are enclosed in the curly braces are interpreted
|
||||
as Nim code, to escape an ``{`` or ``}`` double it.
|
||||
|
||||
``%`` delegates most of the work to an open overloaded set
|
||||
``&`` delegates most of the work to an open overloaded set
|
||||
of ``format`` procs. The required signature for a type ``T`` that supports
|
||||
formatting is usually ``proc format(x: T; result: var string)`` for efficiency
|
||||
but can also be ``proc format(x: T): string``. ``add`` and ``$`` procs are
|
||||
used as the fallback implementation.
|
||||
|
||||
This is the concrete lookup algorithm that ``%`` uses:
|
||||
This is the concrete lookup algorithm that ``&`` uses:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -69,7 +69,7 @@ This is the concrete lookup algorithm that ``%`` uses:
|
||||
|
||||
|
||||
The subexpression after the colon
|
||||
(``arg`` in ``%"{key} is {value:arg} {{z}}"``) is an optional argument
|
||||
(``arg`` in ``&"{key} is {value:arg} {{z}}"``) is an optional argument
|
||||
passed to ``format``.
|
||||
|
||||
If an optional argument is present the following lookup algorithm is used:
|
||||
@@ -226,8 +226,8 @@ template callFormatOption(res, arg, option) {.dirty.} =
|
||||
else:
|
||||
format($arg, option, res)
|
||||
|
||||
macro `%`*(pattern: string): untyped =
|
||||
## For a specification of the ``%`` macro, see the module level documentation.
|
||||
macro `&`*(pattern: string): untyped =
|
||||
## For a specification of the ``&`` macro, see the module level documentation.
|
||||
runnableExamples:
|
||||
template check(actual, expected: string) =
|
||||
doAssert actual == expected
|
||||
@@ -236,113 +236,113 @@ macro `%`*(pattern: string): untyped =
|
||||
|
||||
# Basic tests
|
||||
let s = "string"
|
||||
check %"{0} {s}", "0 string"
|
||||
check %"{s[0..2].toUpperAscii}", "STR"
|
||||
check %"{-10:04}", "-010"
|
||||
check %"{-10:<04}", "-010"
|
||||
check %"{-10:>04}", "-010"
|
||||
check %"0x{10:02X}", "0x0A"
|
||||
check &"{0} {s}", "0 string"
|
||||
check &"{s[0..2].toUpperAscii}", "STR"
|
||||
check &"{-10:04}", "-010"
|
||||
check &"{-10:<04}", "-010"
|
||||
check &"{-10:>04}", "-010"
|
||||
check &"0x{10:02X}", "0x0A"
|
||||
|
||||
check %"{10:#04X}", "0x0A"
|
||||
check &"{10:#04X}", "0x0A"
|
||||
|
||||
check %"""{"test":#>5}""", "#test"
|
||||
check %"""{"test":>5}""", " test"
|
||||
check &"""{"test":#>5}""", "#test"
|
||||
check &"""{"test":>5}""", " test"
|
||||
|
||||
check %"""{"test":#^7}""", "#test##"
|
||||
check &"""{"test":#^7}""", "#test##"
|
||||
|
||||
check %"""{"test": <5}""", "test "
|
||||
check %"""{"test":<5}""", "test "
|
||||
check %"{1f:.3f}", "1.000"
|
||||
check %"Hello, {s}!", "Hello, string!"
|
||||
check &"""{"test": <5}""", "test "
|
||||
check &"""{"test":<5}""", "test "
|
||||
check &"{1f:.3f}", "1.000"
|
||||
check &"Hello, {s}!", "Hello, string!"
|
||||
|
||||
# Tests for identifers without parenthesis
|
||||
check %"{s} works{s}", "string worksstring"
|
||||
check %"{s:>7}", " string"
|
||||
doAssert(not compiles(%"{s_works}")) # parsed as identifier `s_works`
|
||||
check &"{s} works{s}", "string worksstring"
|
||||
check &"{s:>7}", " string"
|
||||
doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works`
|
||||
|
||||
# Misc general tests
|
||||
check %"{{}}", "{}"
|
||||
check %"{0}%", "0%"
|
||||
check %"{0}%asdf", "0%asdf"
|
||||
check %("\n{\"\\n\"}\n"), "\n\n\n"
|
||||
check %"""{"abc"}s""", "abcs"
|
||||
check &"{{}}", "{}"
|
||||
check &"{0}%", "0%"
|
||||
check &"{0}%asdf", "0%asdf"
|
||||
check &("\n{\"\\n\"}\n"), "\n\n\n"
|
||||
check &"""{"abc"}s""", "abcs"
|
||||
|
||||
# String tests
|
||||
check %"""{"abc"}""", "abc"
|
||||
check %"""{"abc":>4}""", " abc"
|
||||
check %"""{"abc":<4}""", "abc "
|
||||
check %"""{"":>4}""", " "
|
||||
check %"""{"":<4}""", " "
|
||||
check &"""{"abc"}""", "abc"
|
||||
check &"""{"abc":>4}""", " abc"
|
||||
check &"""{"abc":<4}""", "abc "
|
||||
check &"""{"":>4}""", " "
|
||||
check &"""{"":<4}""", " "
|
||||
|
||||
# Int tests
|
||||
check %"{12345}", "12345"
|
||||
check %"{ - 12345}", "-12345"
|
||||
check %"{12345:6}", " 12345"
|
||||
check %"{12345:>6}", " 12345"
|
||||
check %"{12345:4}", "12345"
|
||||
check %"{12345:08}", "00012345"
|
||||
check %"{-12345:08}", "-0012345"
|
||||
check %"{0:0}", "0"
|
||||
check %"{0:02}", "00"
|
||||
check %"{-1:3}", " -1"
|
||||
check %"{-1:03}", "-01"
|
||||
check %"{10}", "10"
|
||||
check %"{16:#X}", "0x10"
|
||||
check %"{16:^#7X}", " 0x10 "
|
||||
check %"{16:^+#7X}", " +0x10 "
|
||||
check &"{12345}", "12345"
|
||||
check &"{ - 12345}", "-12345"
|
||||
check &"{12345:6}", " 12345"
|
||||
check &"{12345:>6}", " 12345"
|
||||
check &"{12345:4}", "12345"
|
||||
check &"{12345:08}", "00012345"
|
||||
check &"{-12345:08}", "-0012345"
|
||||
check &"{0:0}", "0"
|
||||
check &"{0:02}", "00"
|
||||
check &"{-1:3}", " -1"
|
||||
check &"{-1:03}", "-01"
|
||||
check &"{10}", "10"
|
||||
check &"{16:#X}", "0x10"
|
||||
check &"{16:^#7X}", " 0x10 "
|
||||
check &"{16:^+#7X}", " +0x10 "
|
||||
|
||||
# Hex tests
|
||||
check %"{0:x}", "0"
|
||||
check %"{-0:x}", "0"
|
||||
check %"{255:x}", "ff"
|
||||
check %"{255:X}", "FF"
|
||||
check %"{-255:x}", "-ff"
|
||||
check %"{-255:X}", "-FF"
|
||||
check %"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe"
|
||||
check %"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe"
|
||||
check %"{255:4x}", " ff"
|
||||
check %"{255:04x}", "00ff"
|
||||
check %"{-255:4x}", " -ff"
|
||||
check %"{-255:04x}", "-0ff"
|
||||
check &"{0:x}", "0"
|
||||
check &"{-0:x}", "0"
|
||||
check &"{255:x}", "ff"
|
||||
check &"{255:X}", "FF"
|
||||
check &"{-255:x}", "-ff"
|
||||
check &"{-255:X}", "-FF"
|
||||
check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe"
|
||||
check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe"
|
||||
check &"{255:4x}", " ff"
|
||||
check &"{255:04x}", "00ff"
|
||||
check &"{-255:4x}", " -ff"
|
||||
check &"{-255:04x}", "-0ff"
|
||||
|
||||
# Float tests
|
||||
check %"{123.456}", "123.456"
|
||||
check %"{-123.456}", "-123.456"
|
||||
check %"{123.456:.3f}", "123.456"
|
||||
check %"{123.456:+.3f}", "+123.456"
|
||||
check %"{-123.456:+.3f}", "-123.456"
|
||||
check %"{-123.456:.3f}", "-123.456"
|
||||
check %"{123.456:1g}", "123.456"
|
||||
check %"{123.456:.1f}", "123.5"
|
||||
check %"{123.456:.0f}", "123."
|
||||
#check %"{123.456:.0f}", "123."
|
||||
check %"{123.456:>9.3f}", " 123.456"
|
||||
check %"{123.456:9.3f}", " 123.456"
|
||||
check %"{123.456:>9.4f}", " 123.4560"
|
||||
check %"{123.456:>9.0f}", " 123."
|
||||
check %"{123.456:<9.4f}", "123.4560 "
|
||||
check &"{123.456}", "123.456"
|
||||
check &"{-123.456}", "-123.456"
|
||||
check &"{123.456:.3f}", "123.456"
|
||||
check &"{123.456:+.3f}", "+123.456"
|
||||
check &"{-123.456:+.3f}", "-123.456"
|
||||
check &"{-123.456:.3f}", "-123.456"
|
||||
check &"{123.456:1g}", "123.456"
|
||||
check &"{123.456:.1f}", "123.5"
|
||||
check &"{123.456:.0f}", "123."
|
||||
#check &"{123.456:.0f}", "123."
|
||||
check &"{123.456:>9.3f}", " 123.456"
|
||||
check &"{123.456:9.3f}", " 123.456"
|
||||
check &"{123.456:>9.4f}", " 123.4560"
|
||||
check &"{123.456:>9.0f}", " 123."
|
||||
check &"{123.456:<9.4f}", "123.4560 "
|
||||
|
||||
# Float (scientific) tests
|
||||
check %"{123.456:e}", "1.234560e+02"
|
||||
check %"{123.456:>13e}", " 1.234560e+02"
|
||||
check %"{123.456:<13e}", "1.234560e+02 "
|
||||
check %"{123.456:.1e}", "1.2e+02"
|
||||
check %"{123.456:.2e}", "1.23e+02"
|
||||
check %"{123.456:.3e}", "1.235e+02"
|
||||
check &"{123.456:e}", "1.234560e+02"
|
||||
check &"{123.456:>13e}", " 1.234560e+02"
|
||||
check &"{123.456:<13e}", "1.234560e+02 "
|
||||
check &"{123.456:.1e}", "1.2e+02"
|
||||
check &"{123.456:.2e}", "1.23e+02"
|
||||
check &"{123.456:.3e}", "1.235e+02"
|
||||
|
||||
# Note: times.format adheres to the format protocol. Test that this
|
||||
# works:
|
||||
import times
|
||||
|
||||
var nullTime: DateTime
|
||||
check %"{nullTime:yyyy-mm-dd}", "0000-00-00"
|
||||
check &"{nullTime:yyyy-mm-dd}", "0000-00-00"
|
||||
|
||||
# Unicode string tests
|
||||
check %"""{"αβγ"}""", "αβγ"
|
||||
check %"""{"αβγ":>5}""", " αβγ"
|
||||
check %"""{"αβγ":<5}""", "αβγ "
|
||||
check %"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈"
|
||||
check %"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 "
|
||||
check &"""{"αβγ"}""", "αβγ"
|
||||
check &"""{"αβγ":>5}""", " αβγ"
|
||||
check &"""{"αβγ":<5}""", "αβγ "
|
||||
check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈"
|
||||
check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 "
|
||||
# Invalid unicode sequences should be handled as plain strings.
|
||||
# Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173
|
||||
let invalidUtf8 = [
|
||||
@@ -351,10 +351,10 @@ macro `%`*(pattern: string): untyped =
|
||||
"\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28"
|
||||
]
|
||||
for s in invalidUtf8:
|
||||
check %"{s:>5}", repeat(" ", 5-s.len) & s
|
||||
check &"{s:>5}", repeat(" ", 5-s.len) & s
|
||||
|
||||
if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
|
||||
error "% only works with string literals", pattern
|
||||
error "& only works with string literals", pattern
|
||||
let f = pattern.strVal
|
||||
var i = 0
|
||||
let res = genSym(nskVar, "fmtRes")
|
||||
@@ -408,17 +408,17 @@ macro `%`*(pattern: string): untyped =
|
||||
echo repr result
|
||||
|
||||
template fmt*(pattern: string): untyped =
|
||||
## An alias for ``%``. Helps to avoid conflicts with ``json``'s ``%`` operator.
|
||||
## An alias for ``&``.
|
||||
## **Examples:**
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## import json
|
||||
## import strformat except `%`
|
||||
## import strformat except `&`
|
||||
##
|
||||
## let example = "oh, look no conflicts anymore"
|
||||
## echo fmt"{example}"
|
||||
bind `%`
|
||||
%pattern
|
||||
bind `&`
|
||||
&pattern
|
||||
|
||||
proc mkDigit(v: int, typ: char): string {.inline.} =
|
||||
assert(v < 26)
|
||||
@@ -573,7 +573,7 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
|
||||
proc format*(value: SomeInteger; specifier: string; res: var string) =
|
||||
## Standard format implementation for ``SomeInteger``. It makes little
|
||||
## sense to call this directly, but it is required to exist
|
||||
## by the ``%`` macro.
|
||||
## by the ``&`` macro.
|
||||
let spec = parseStandardFormatSpecifier(specifier)
|
||||
var radix = 10
|
||||
case spec.typ
|
||||
@@ -590,7 +590,7 @@ proc format*(value: SomeInteger; specifier: string; res: var string) =
|
||||
proc format*(value: SomeReal; specifier: string; res: var string) =
|
||||
## Standard format implementation for ``SomeReal``. It makes little
|
||||
## sense to call this directly, but it is required to exist
|
||||
## by the ``%`` macro.
|
||||
## by the ``&`` macro.
|
||||
let spec = parseStandardFormatSpecifier(specifier)
|
||||
|
||||
var fmode = ffDefault
|
||||
@@ -622,7 +622,7 @@ proc format*(value: SomeReal; specifier: string; res: var string) =
|
||||
proc format*(value: string; specifier: string; res: var string) =
|
||||
## Standard format implementation for ``string``. It makes little
|
||||
## sense to call this directly, but it is required to exist
|
||||
## by the ``%`` macro.
|
||||
## by the ``&`` macro.
|
||||
let spec = parseStandardFormatSpecifier(specifier)
|
||||
case spec.typ
|
||||
of 's', '\0': discard
|
||||
|
||||
@@ -22,7 +22,7 @@ proc frexp_test(lo, hi, step: float64) =
|
||||
doAssert(abs(rslt - x) < eps)
|
||||
|
||||
when manualTest:
|
||||
echo %("x: {x:10.3f} exp: {exp:4d} frac: {frac:24.20f} check: {$(abs(rslt - x) < eps):-5s} {rslt: 9.3f}")
|
||||
echo fmt("x: {x:10.3f} exp: {exp:4d} frac: {frac:24.20f} check: {$(abs(rslt - x) < eps):-5s} {rslt: 9.3f}")
|
||||
x += step
|
||||
|
||||
when manualTest:
|
||||
|
||||
@@ -9,5 +9,5 @@ type Obj = object
|
||||
proc `$`(o: Obj): string = "foobar"
|
||||
|
||||
var o: Obj
|
||||
doAssert %"{o}" == "foobar"
|
||||
doAssert %"{o:10}" == "foobar "
|
||||
doAssert fmt"{o}" == "foobar"
|
||||
doAssert fmt"{o:10}" == "foobar "
|
||||
Reference in New Issue
Block a user