Added strscans.scanTuple (#16300)

* Added since and changelog
This commit is contained in:
Jason Beetham 2020-12-12 15:29:20 -07:00 • committed by GitHub
commit 5a58440ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 1 deletions

View file

@ -284,6 +284,7 @@ efficiency and perform different checks.
import macros, parseutils
import std/private/since
proc conditionsToIfChain(n, idx, res: NimNode; start: int): NimNode =
assert n.kind == nnkStmtList
@ -465,6 +466,54 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
else:
result.add res
macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[untyped]): untyped {.since: (1, 5).}=
## Works identically as scanf, but instead of predeclaring variables it returns a tuple.
## Tuple is started with a bool which indicates if the scan was successful
## followed by the requested data.
## If using a user defined matcher, provide the types in order they appear after pattern:
## `line.scanTuple("${yourMatcher()}", int)`
runnableExamples:
let (success, year, month, day, time) = scanTuple("1000-01-01 00:00:00", "$i-$i-$i$s$+")
if success:
assert year == 1000
assert month == 1
assert day == 1
assert time == "00:00:00"
var
p = 0
userMatches = 0
arguments: seq[NimNode]
result = newStmtList()
template addVar(typ: string) =
let varIdent = ident("temp" & $arguments.len)
result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, ident(typ), newEmptyNode())))
arguments.add(varIdent)
while p < pattern.len:
if pattern[p] == '$':
inc p
case pattern[p]
of 'w', '*', '+':
addVar("string")
of 'c':
addVar("char")
of 'b', 'o', 'i', 'h':
addVar("int")
of 'f':
addVar("float")
of '{':
if userMatches < matcherTypes.len:
let varIdent = ident("temp" & $arguments.len)
result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, matcherTypes[userMatches], newEmptyNode())))
arguments.add(varIdent)
inc userMatches
else: discard
inc p
result.add newPar(newCall(ident("scanf"), input, newStrLitNode(pattern)))
for arg in arguments:
result[^1][0].add arg
result[^1].add arg
result = newBlockStmt(result)
template atom*(input: string; idx: int; c: char): bool =
## Used in scanp for the matching of atoms (usually chars).
## EOF is matched as ``'\0'``.
@ -639,4 +688,4 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
result.add toIfChain(conds, idx, res, 0)
result.add res
when defined(debugScanp):
echo repr result
echo repr result