mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
strformat.fmt now supports non-literal const strings (#18274)
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
@@ -93,7 +93,9 @@
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
- Added support for parenthesized expressions in `strformat`
|
||||
- `strformat`:
|
||||
added support for parenthesized expressions.
|
||||
added support for const string's instead of just string literals
|
||||
|
||||
- Fixed buffer overflow bugs in `net`
|
||||
|
||||
|
||||
@@ -476,8 +476,9 @@ proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
|
||||
## needs to occur in a declaration context.
|
||||
|
||||
proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated:
|
||||
"Deprecated since v0.18.1; use varargs[untyped] in the macro prototype instead".}
|
||||
"Deprecated since v0.18.1; use `varargs[untyped]` in the macro prototype instead".}
|
||||
## Returns the AST of the invocation expression that invoked this macro.
|
||||
# see https://github.com/nim-lang/RFCs/issues/387 as candidate replacement.
|
||||
|
||||
proc toStrLit*(n: NimNode): NimNode =
|
||||
## Converts the AST `n` to the concrete Nim code and wraps that
|
||||
|
||||
@@ -573,15 +573,12 @@ template formatValue(result: var string; value: char; specifier: string) =
|
||||
template formatValue(result: var string; value: cstring; specifier: string) =
|
||||
result.add value
|
||||
|
||||
proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
||||
if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
|
||||
error "string formatting (fmt(), &) only works with string literals", pattern
|
||||
proc strformatImpl(f: string; openChar, closeChar: char): NimNode =
|
||||
if openChar == ':' or closeChar == ':':
|
||||
error "openChar and closeChar must not be ':'"
|
||||
let f = pattern.strVal
|
||||
var i = 0
|
||||
let res = genSym(nskVar, "fmtRes")
|
||||
result = newNimNode(nnkStmtListExpr, lineInfoFrom = pattern)
|
||||
result = newNimNode(nnkStmtListExpr)
|
||||
# XXX: https://github.com/nim-lang/Nim/issues/8405
|
||||
# When compiling with -d:useNimRtl, certain procs such as `count` from the strutils
|
||||
# module are not accessible at compile-time:
|
||||
@@ -633,12 +630,8 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
||||
var x: NimNode
|
||||
try:
|
||||
x = parseExpr(subexpr)
|
||||
except ValueError:
|
||||
when declared(getCurrentExceptionMsg):
|
||||
let msg = getCurrentExceptionMsg()
|
||||
error("could not parse `" & subexpr & "`.\n" & msg, pattern)
|
||||
else:
|
||||
error("could not parse `" & subexpr & "`.\n", pattern)
|
||||
except ValueError as e:
|
||||
error("could not parse `$#` in `$#`.\n$#" % [subexpr, f, e.msg])
|
||||
let formatSym = bindSym("formatValue", brOpen)
|
||||
var options = ""
|
||||
if f[i] == ':':
|
||||
@@ -667,19 +660,39 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
||||
when defined(debugFmtDsl):
|
||||
echo repr result
|
||||
|
||||
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 `& <#&.m,string>`_.
|
||||
|
||||
macro fmt*(pattern: string; openChar, closeChar: char): untyped =
|
||||
## The same as `fmt <#fmt.m,string>`_, but uses `openChar` instead of `'{'`
|
||||
## and `closeChar` instead of `'}'`.
|
||||
macro fmt*(pattern: static string; openChar: static char, closeChar: static char): string =
|
||||
## Interpolates `pattern` using symbols in scope.
|
||||
runnableExamples:
|
||||
let testInt = 123
|
||||
assert "<testInt>".fmt('<', '>') == "123"
|
||||
assert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)"
|
||||
assert """ ""{"123+123"}"" """.fmt('"', '"') == " \"{246}\" "
|
||||
let x = 7
|
||||
assert "var is {x * 2}".fmt == "var is 14"
|
||||
assert "var is {{x}}".fmt == "var is {x}" # escape via doubling
|
||||
const s = "foo: {x}"
|
||||
assert s.fmt == "foo: 7" # also works with const strings
|
||||
|
||||
strformatImpl(pattern, openChar.intVal.char, closeChar.intVal.char)
|
||||
assert fmt"\n" == r"\n" # raw string literal
|
||||
assert "\n".fmt == "\n" # regular literal (likewise with `fmt("\n")` or `fmt "\n"`)
|
||||
runnableExamples:
|
||||
# custom `openChar`, `closeChar`
|
||||
let x = 7
|
||||
assert "<x>".fmt('<', '>') == "7"
|
||||
assert "<<<x>>>".fmt('<', '>') == "<7>"
|
||||
assert "`x`".fmt('`', '`') == "7"
|
||||
strformatImpl(pattern, openChar, closeChar)
|
||||
|
||||
template fmt*(pattern: static string): untyped =
|
||||
## Alias for `fmt(pattern, '{', '}')`.
|
||||
fmt(pattern, '{', '}')
|
||||
|
||||
macro `&`*(pattern: string{lit}): string =
|
||||
## `&pattern` is the same as `pattern.fmt`.
|
||||
## For a specification of the `&` macro, see the module level documentation.
|
||||
# pending bug #18275, bug #18278, use `pattern: static string`
|
||||
# consider deprecating this, it's redundant with `fmt` and `fmt` is strictly
|
||||
# more flexible, readable (no confusion with the binary `&`), self-documenting,
|
||||
# not to mention #18275, bug #18278.
|
||||
runnableExamples:
|
||||
let x = 7
|
||||
assert &"{x}\n" == "7\n" # regular string literal
|
||||
assert &"{x}\n" == "7\n".fmt # `fmt` can be used instead
|
||||
assert &"{x}\n" != fmt"7\n" # see `fmt` docs, this would use a raw string literal
|
||||
strformatImpl(pattern.strVal, '{', '}')
|
||||
|
||||
Reference in New Issue
Block a user