This commit is contained in:
Araq
2019-04-14 18:28:55 +02:00
committed by Andreas Rumpf
parent 499fa3f3dc
commit 59ccaa43c7
3 changed files with 28 additions and 50 deletions

View File

@@ -102,7 +102,9 @@ of ``formatValue`` procs. The required signature for a type ``T`` that supports
formatting is usually ``proc formatValue(result: var string; x: T; specifier: string)``.
The subexpression after the colon
(``arg`` in ``&"{key} is {value:arg} {{z}}"``) is optional. It will be passed as the last argument to ``formatValue``. When the colon with the subexpression it is left out, an empty string will be taken instead.
(``arg`` in ``&"{key} is {value:arg} {{z}}"``) is optional. It will be passed as
the last argument to ``formatValue``. When the colon with the subexpression it is
left out, an empty string will be taken instead.
For strings and numeric types the optional argument is a so-called
"standard format specifier".
@@ -215,8 +217,7 @@ Limitations
===========
Because of the well defined order how templates and macros are
expanded, strformat cannot expand template arguments.
expanded, strformat cannot expand template arguments:
.. code-block:: nim
template myTemplate(arg: untyped): untyped =
@@ -233,27 +234,6 @@ quoted string literal. It is not an identifier yet. Then the strformat
macro creates the ``arg`` identifier from the string literal. An
identifier that cannot be resolved anymore.
.. code-block:: nim
let x = "abc"
myTemplate(x)
# expansion of myTemplate
let x = "abc"
echo "arg is: ", x
echo &"--- {arg} ---"
# expansion of `&`
let x = "abc"
echo "arg is: ", x
echo:
var temp = newStringOfCap(20)
temp.add "--- "
temp.formatValue arg, "" # arg cannot be resolved anymore
temp.add " ---"
temp
The workaround for this is to bind template argument to a new local variable.
.. code-block:: nim
@@ -265,11 +245,12 @@ The workaround for this is to bind template argument to a new local variable.
echo &"--- {arg1} ---"
The use of ``{.inject.}`` here is necessary again because of template
expansion order and hygienic templates. But since we generelly want to
expansion order and hygienic templates. But since we generally want to
keep the hygienicness of ``myTemplate``, and we do not want ``arg1``
to be injected into the context where ``myTemplate`` is expanded,
everything is wrapped in a ``block``.
Future directions
=================
@@ -522,8 +503,9 @@ proc formatValue*(result: var string; value: string; specifier: string) =
setLen(value, runeOffset(value, spec.precision))
result.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
template formatValue[T: enum](result: var string; value: T; specifier: string) =
result.add $value
proc formatValue[T](result: var string; value: T; specifier: string) =
mixin `$`
formatValue(result, $value, specifier)
template formatValue(result: var string; value: char; specifier: string) =
result.add value
@@ -531,7 +513,7 @@ template formatValue(result: var string; value: char; specifier: string) =
template formatValue(result: var string; value: cstring; specifier: string) =
result.add value
proc formatValue[T](result: var string; value: openarray[T]; specifier: string) =
proc formatValue(result: var string; value: (array|seq|openArray); specifier: string) =
result.add "["
for i, it in value:
if i != 0:

View File

@@ -1,5 +1,6 @@
discard """
action: "run"
output: '''Received (name: "Foo", species: "Bar")'''
"""
import strformat
@@ -64,16 +65,14 @@ doAssert fmt"{0.0: g}" == " 0"
let data1 = [1'i64, 10000'i64, 10000000'i64]
let data2 = [10000000'i64, 100'i64, 1'i64]
doAssert fmt"data1: {data1:8} " == "data1: [ 1, 10000, 10000000] "
doAssert fmt"data2: {data2:8} " == "data2: [10000000, 100, 1] "
doAssert fmt"data1: {data1:8} #" == "data1: [ 1, 10000, 10000000] #"
doAssert fmt"data2: {data2:8} =" == "data2: [10000000, 100, 1] ="
# custom format Value
type
Vec2[T] = object
x,y: T
Vec2f = Vec2[float32]
Vec2i = Vec2[int32]
proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) =
result.add '['
@@ -82,8 +81,8 @@ proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) =
result.formatValue value.y, specifier
result.add "]"
let v1 = Vec2f(x:1.0, y: 2.0)
let v2 = Vec2i(x:1, y: 1337)
let v1 = Vec2[float32](x:1.0, y: 2.0)
let v2 = Vec2[int32](x:1, y: 1337)
doAssert fmt"v1: {v1:+08} v2: {v2:>4}" == "v1: [+0000001, +0000002] v2: [ 1, 1337]"
# issue #7632
@@ -94,3 +93,16 @@ doAssert works(5) == "formatted 5"
doAssert fails0(6) == "formatted 6"
doAssert fails(7) == "formatted 7"
doAssert fails2[0](8) == "formatted 8"
# bug #11012
type
Animal = object
name, species: string
AnimalRef = ref Animal
proc print_object(animalAddr: AnimalRef) =
echo fmt"Received {animalAddr[]}"
print_object(AnimalRef(name: "Foo", species: "Bar"))

View File

@@ -1,16 +0,0 @@
discard """
errormsg: '''type mismatch: got <string, Obj, string>'''
nimout: '''proc formatValue'''
"""
# This test is here to make sure that there is a clean error that
# that indicates ``formatValue`` needs to be overloaded with the custom type.
import strformat
type Obj = object
proc `$`(o: Obj): string = "foobar"
var o: Obj
doAssert fmt"{o}" == "foobar"