mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-26 01:04:00 +00:00
[feature]strformat: add 2 'fmt' macros that use specified characters instead of '{}' (#11748)
* [feature]strformat: add 2 'fmt' macros that use specified chars instead of '{}'
* strformat: revert documentation comments of `&` and 'fmt'
* strformat: removed single open/close char variant of fmt
This commit is contained in:
@@ -523,10 +523,11 @@ template formatValue(result: var string; value: char; specifier: string) =
|
||||
template formatValue(result: var string; value: cstring; specifier: string) =
|
||||
result.add value
|
||||
|
||||
macro `&`*(pattern: string): untyped =
|
||||
## For a specification of the ``&`` macro, see the module level documentation.
|
||||
proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
||||
if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
|
||||
error "string formatting (fmt(), &) only works with string literals", pattern
|
||||
if openChar == ':' or closeChar == ':':
|
||||
error "openChar and closeChar must not be ':'"
|
||||
let f = pattern.strVal
|
||||
var i = 0
|
||||
let res = genSym(nskVar, "fmtRes")
|
||||
@@ -539,18 +540,18 @@ macro `&`*(pattern: string): untyped =
|
||||
newLit(f.len + expectedGrowth)))
|
||||
var strlit = ""
|
||||
while i < f.len:
|
||||
if f[i] == '{':
|
||||
if f[i] == openChar:
|
||||
inc i
|
||||
if f[i] == '{':
|
||||
if f[i] == openChar:
|
||||
inc i
|
||||
strlit.add '{'
|
||||
strlit.add openChar
|
||||
else:
|
||||
if strlit.len > 0:
|
||||
result.add newCall(bindSym"add", res, newLit(strlit))
|
||||
strlit = ""
|
||||
|
||||
var subexpr = ""
|
||||
while i < f.len and f[i] != '}' and f[i] != ':':
|
||||
while i < f.len and f[i] != closeChar and f[i] != ':':
|
||||
subexpr.add f[i]
|
||||
inc i
|
||||
var x: NimNode
|
||||
@@ -566,17 +567,17 @@ macro `&`*(pattern: string): untyped =
|
||||
var options = ""
|
||||
if f[i] == ':':
|
||||
inc i
|
||||
while i < f.len and f[i] != '}':
|
||||
while i < f.len and f[i] != closeChar:
|
||||
options.add f[i]
|
||||
inc i
|
||||
if f[i] == '}':
|
||||
if f[i] == closeChar:
|
||||
inc i
|
||||
else:
|
||||
doAssert false, "invalid format string: missing '}'"
|
||||
result.add newCall(formatSym, res, x, newLit(options))
|
||||
elif f[i] == '}':
|
||||
if f[i+1] == '}':
|
||||
strlit.add '}'
|
||||
elif f[i] == closeChar:
|
||||
if f[i+1] == closeChar:
|
||||
strlit.add closeChar
|
||||
inc i, 2
|
||||
else:
|
||||
doAssert false, "invalid format string: '}' instead of '}}'"
|
||||
@@ -590,10 +591,20 @@ macro `&`*(pattern: string): untyped =
|
||||
when defined(debugFmtDsl):
|
||||
echo repr result
|
||||
|
||||
template fmt*(pattern: string): untyped =
|
||||
macro `&`*(pattern: string): untyped = strformatImpl(pattern, '{', '}')
|
||||
## For a specification of the ``&`` macro, see the module level documentation.
|
||||
|
||||
macro fmt*(pattern: string): untyped = strformatImpl(pattern, '{', '}')
|
||||
## An alias for ``&``.
|
||||
bind `&`
|
||||
&pattern
|
||||
|
||||
macro fmt*(pattern: string; openChar, closeChar: char): untyped =
|
||||
## Use ``openChar`` instead of '{' and ``closeChar`` instead of '}'
|
||||
runnableExamples:
|
||||
let testInt = 123
|
||||
doAssert "<testInt>".fmt('<', '>') == "123"
|
||||
doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)"
|
||||
doAssert """ ""{"123+123"}"" """.fmt('"', '"') == " \"{246}\" "
|
||||
strformatImpl(pattern, openChar.intVal.char, closeChar.intVal.char)
|
||||
|
||||
when isMainModule:
|
||||
template check(actual, expected: string) =
|
||||
|
||||
@@ -133,3 +133,35 @@ doAssert fmt"{nat:3d}" == " 64"
|
||||
doAssert fmt"{nat:3o}" == "100"
|
||||
doAssert fmt"{nat:3x}" == " 40"
|
||||
doAssert fmt"{nat:3X}" == " 40"
|
||||
|
||||
block:
|
||||
template fmt(pattern: string; openCloseChar: char): untyped =
|
||||
fmt(pattern, openCloseChar, openCloseChar)
|
||||
|
||||
let
|
||||
testInt = 123
|
||||
testStr = "foobar"
|
||||
testFlt = 3.141592
|
||||
doAssert ">><<".fmt('<', '>') == "><"
|
||||
doAssert " >> << ".fmt('<', '>') == " > < "
|
||||
doAssert "<<>>".fmt('<', '>') == "<>"
|
||||
doAssert " << >> ".fmt('<', '>') == " < > "
|
||||
doAssert "''".fmt('\'') == "'"
|
||||
doAssert "''''".fmt('\'') == "''"
|
||||
doAssert "'' ''".fmt('\'') == "' '"
|
||||
doAssert "<testInt>".fmt('<', '>') == "123"
|
||||
doAssert "<testInt>".fmt('<', '>') == "123"
|
||||
doAssert "'testFlt:1.2f'".fmt('\'') == "3.14"
|
||||
doAssert "<testInt><testStr>".fmt('<', '>') == "123foobar"
|
||||
doAssert """ ""{"123+123"}"" """.fmt('"') == " \"{246}\" "
|
||||
doAssert "(((testFlt:1.2f)))((111))".fmt('(', ')') == "(3.14)(111)"
|
||||
doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)"
|
||||
doAssert "{}abc`testStr' `testFlt:1.2f' `1+1' ``".fmt('`', '\'') == "{}abcfoobar 3.14 2 `"
|
||||
doAssert """x = '"foo" & "bar"'
|
||||
y = '123 + 111'
|
||||
z = '3 in {2..7}'
|
||||
""".fmt('\'') ==
|
||||
"""x = foobar
|
||||
y = 234
|
||||
z = true
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user