followup strformat PR. backslash escapes, tests, docs (#17700)
* Allow use of colons inside fmt
allowing colons inside fmt by replacing the format specifier delimiter lets arbitrary nim code be run within fmt expressions.
Co-authored-by: flywind <xzsflywind@gmail.com>
* formatting,documentation,backslash escapes
Adding support for evaluating expressions by special-casing parentheses causes this regression: `&"""{ "(hello)" }"""` no longer parses.
In addition, code such as &"""{(if open: '(' else: ')')}""" wouldn't work.
To enable that, as well as the use of, e.g. Table constructors inside curlies, I've added backslash escapes.
This also means that if/for/etc statements, unparenthesized, will work, if the colons are escaped, but i've left that under-documented.
It's not exactly elegant having two types of escape, but I believe it's the least bad option.
* changelog
* added json strformat test
* pulled my thumb out and wrote a parser
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: flywind <xzsflywind@gmail.com>
This commit is contained in:
parent
cae1839154
commit
0bc943ad54
3 changed files with 89 additions and 5 deletions
|
|
@ -144,6 +144,19 @@ An expression like `&"{key} is {value:arg} {{z}}"` is transformed into:
|
|||
Parts of the string that are enclosed in the curly braces are interpreted
|
||||
as Nim code, to escape a `{` or `}`, double it.
|
||||
|
||||
Within a curly expression,however, '{','}', must be escaped with a backslash.
|
||||
|
||||
To enable evaluating Nim expressions within curlies, inside parentheses
|
||||
colons do not need to be escaped.
|
||||
]##
|
||||
|
||||
runnableExamples:
|
||||
let x = "hello"
|
||||
assert fmt"""{ "\{(" & x & ")\}" }""" == "{(hello)}"
|
||||
assert fmt"""{{({ x })}}""" == "{(hello)}"
|
||||
assert fmt"""{ $(\{x:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]"""
|
||||
|
||||
##[
|
||||
`&` delegates most of the work to an open overloaded set
|
||||
of `formatValue` procs. The required signature for a type `T` that supports
|
||||
formatting is usually `proc formatValue(result: var string; x: T; specifier: string)`.
|
||||
|
|
@ -289,6 +302,7 @@ keep the hygiene 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
|
||||
|
||||
A curly expression with commas in it like `{x, argA, argB}` could be
|
||||
|
|
@ -588,10 +602,21 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
|||
|
||||
var subexpr = ""
|
||||
var inParens = 0
|
||||
while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens!=0):
|
||||
var inSingleQuotes = false
|
||||
var inDoubleQuotes = false
|
||||
template notEscaped:bool = f[i-1]!='\\'
|
||||
while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens != 0):
|
||||
case f[i]
|
||||
of '(': inc inParens
|
||||
of ')': dec inParens
|
||||
of '\\':
|
||||
if i < f.len-1 and f[i+1] in {openChar,closeChar,':'}: inc i
|
||||
of '\'':
|
||||
if not inDoubleQuotes and notEscaped: inSingleQuotes = not inSingleQuotes
|
||||
of '\"':
|
||||
if notEscaped: inDoubleQuotes = not inDoubleQuotes
|
||||
of '(':
|
||||
if not (inSingleQuotes or inDoubleQuotes): inc inParens
|
||||
of ')':
|
||||
if not (inSingleQuotes or inDoubleQuotes): dec inParens
|
||||
of '=':
|
||||
let start = i
|
||||
inc i
|
||||
|
|
@ -604,7 +629,7 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode =
|
|||
else: discard
|
||||
subexpr.add f[i]
|
||||
inc i
|
||||
|
||||
|
||||
var x: NimNode
|
||||
try:
|
||||
x = parseExpr(subexpr)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue