strformat.fmt now supports non-literal const strings (#18274)
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
parent
87cd9b24a3
commit
5600a62229
3 changed files with 43 additions and 27 deletions
|
|
@ -93,7 +93,9 @@
|
||||||
|
|
||||||
## Standard library additions and changes
|
## 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`
|
- Fixed buffer overflow bugs in `net`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -476,8 +476,9 @@ proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
|
||||||
## needs to occur in a declaration context.
|
## needs to occur in a declaration context.
|
||||||
|
|
||||||
proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated:
|
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.
|
## 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 =
|
proc toStrLit*(n: NimNode): NimNode =
|
||||||
## Converts the AST `n` to the concrete Nim code and wraps that
|
## 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) =
|
template formatValue(result: var string; value: cstring; specifier: string) =
|
||||||
result.add value
|
result.add value
|
||||||
|
|
||||||
proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
proc strformatImpl(f: string; openChar, closeChar: char): NimNode =
|
||||||
if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
|
|
||||||
error "string formatting (fmt(), &) only works with string literals", pattern
|
|
||||||
if openChar == ':' or closeChar == ':':
|
if openChar == ':' or closeChar == ':':
|
||||||
error "openChar and closeChar must not be ':'"
|
error "openChar and closeChar must not be ':'"
|
||||||
let f = pattern.strVal
|
|
||||||
var i = 0
|
var i = 0
|
||||||
let res = genSym(nskVar, "fmtRes")
|
let res = genSym(nskVar, "fmtRes")
|
||||||
result = newNimNode(nnkStmtListExpr, lineInfoFrom = pattern)
|
result = newNimNode(nnkStmtListExpr)
|
||||||
# XXX: https://github.com/nim-lang/Nim/issues/8405
|
# XXX: https://github.com/nim-lang/Nim/issues/8405
|
||||||
# When compiling with -d:useNimRtl, certain procs such as `count` from the strutils
|
# When compiling with -d:useNimRtl, certain procs such as `count` from the strutils
|
||||||
# module are not accessible at compile-time:
|
# module are not accessible at compile-time:
|
||||||
|
|
@ -633,12 +630,8 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
||||||
var x: NimNode
|
var x: NimNode
|
||||||
try:
|
try:
|
||||||
x = parseExpr(subexpr)
|
x = parseExpr(subexpr)
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
when declared(getCurrentExceptionMsg):
|
error("could not parse `$#` in `$#`.\n$#" % [subexpr, f, e.msg])
|
||||||
let msg = getCurrentExceptionMsg()
|
|
||||||
error("could not parse `" & subexpr & "`.\n" & msg, pattern)
|
|
||||||
else:
|
|
||||||
error("could not parse `" & subexpr & "`.\n", pattern)
|
|
||||||
let formatSym = bindSym("formatValue", brOpen)
|
let formatSym = bindSym("formatValue", brOpen)
|
||||||
var options = ""
|
var options = ""
|
||||||
if f[i] == ':':
|
if f[i] == ':':
|
||||||
|
|
@ -667,19 +660,39 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
||||||
when defined(debugFmtDsl):
|
when defined(debugFmtDsl):
|
||||||
echo repr result
|
echo repr result
|
||||||
|
|
||||||
macro `&`*(pattern: string): untyped = strformatImpl(pattern, '{', '}')
|
macro fmt*(pattern: static string; openChar: static char, closeChar: static char): string =
|
||||||
## For a specification of the `&` macro, see the module level documentation.
|
## Interpolates `pattern` using symbols in scope.
|
||||||
|
|
||||||
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 `'}'`.
|
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
let testInt = 123
|
let x = 7
|
||||||
assert "<testInt>".fmt('<', '>') == "123"
|
assert "var is {x * 2}".fmt == "var is 14"
|
||||||
assert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)"
|
assert "var is {{x}}".fmt == "var is {x}" # escape via doubling
|
||||||
assert """ ""{"123+123"}"" """.fmt('"', '"') == " \"{246}\" "
|
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, '{', '}')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue