diff options
-rw-r--r-- | lib/pure/strformat.nim | 39 | ||||
-rw-r--r-- | tests/stdlib/tstrformat.nim | 32 |
2 files changed, 57 insertions, 14 deletions
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 257751088..5dbdf8282 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -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) = diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 732283674..a8dad6084 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -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 + """ |