summary refs log tree commit diff stats
path: root/lib/pure/strscans.nim
diff options
context:
space:
mode:
authorJason Beetham <beefers331@gmail.com>2020-12-12 15:29:20 -0700
committerGitHub <noreply@github.com>2020-12-12 23:29:20 +0100
commit5a58440ebe4ba794f0127ebb6ad33824a92dfb58 (patch)
treef5eaa0476d456b6efd81c57c65b5adc784a5819a /lib/pure/strscans.nim
parentd15f63a0f2d431a516601530196ac01c26e00f53 (diff)
downloadNim-5a58440ebe4ba794f0127ebb6ad33824a92dfb58.tar.gz
Added strscans.scanTuple (#16300)
* Added since and changelog
Diffstat (limited to 'lib/pure/strscans.nim')
-rw-r--r--lib/pure/strscans.nim51
1 files changed, 50 insertions, 1 deletions
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 347dbc2ef..9c55bf3e3 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -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
\ No newline at end of file