summary refs log tree commit diff stats
path: root/lib/pure/strscans.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/strscans.nim')
-rw-r--r--lib/pure/strscans.nim52
1 files changed, 28 insertions, 24 deletions
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 73b53e3d6..16ef9e642 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -12,7 +12,7 @@ This module contains a `scanf`:idx: macro that can be used for extracting
 substrings from an input string. This is often easier than regular expressions.
 Some examples as an appetizer:
 
-.. code-block:: nim
+  ```nim
   # check if input string matches a triple of integers:
   const input = "(1,2,4)"
   var x, y, z: int
@@ -26,6 +26,7 @@ Some examples as an appetizer:
   var myfloat: float
   if scanf(input, "$i-$i-$i $w$s$f", year, month, day, identifier, myfloat):
     echo "yes, we have a match!"
+  ```
 
 As can be seen from the examples, strings are matched verbatim except for
 substrings starting with ``$``. These constructions are available:
@@ -35,7 +36,7 @@ substrings starting with ``$``. These constructions are available:
 ``$o``              Matches an octal integer. This uses ``parseutils.parseOct``.
 ``$i``              Matches a decimal integer. This uses ``parseutils.parseInt``.
 ``$h``              Matches a hex integer. This uses ``parseutils.parseHex``.
-``$f``              Matches a floating pointer number. Uses ``parseFloat``.
+``$f``              Matches a floating-point number. Uses ``parseFloat``.
 ``$w``              Matches an ASCII identifier: ``[A-Za-z_][A-Za-z_0-9]*``.
 ``$c``              Matches a single ASCII character.
 ``$s``              Skips optional whitespace.
@@ -52,7 +53,7 @@ substrings starting with ``$``. These constructions are available:
 =================   ========================================================
 
 Even though ``$*`` and ``$+`` look similar to the regular expressions ``.*``
-and ``.+`` they work quite differently, there is no non-deterministic
+and ``.+``, they work quite differently. There is no non-deterministic
 state machine involved and the matches are non-greedy. ``[$*]``
 matches ``[xyz]`` via ``parseutils.parseUntil``.
 
@@ -83,8 +84,7 @@ matches optional tokens without any result binding.
 In this example, we define a helper proc ``someSep`` that skips some separators
 which we then use in our scanf pattern to help us in the matching process:
 
-.. code-block:: nim
-
+  ```nim
   proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int =
     # Note: The parameters and return value must match to what ``scanf`` requires
     result = 0
@@ -92,11 +92,11 @@ which we then use in our scanf pattern to help us in the matching process:
 
   if scanf(input, "$w$[someSep]$w", key, value):
     ...
+  ```
 
-It also possible to pass arguments to a user definable matcher:
-
-.. code-block:: nim
+It is also possible to pass arguments to a user definable matcher:
 
+  ```nim
   proc ndigits(input: string; intVal: var int; start: int; n: int): int =
     # matches exactly ``n`` digits. Matchers need to return 0 if nothing
     # matched or otherwise the number of processed chars.
@@ -115,6 +115,7 @@ It also possible to pass arguments to a user definable matcher:
   var year, month, day: int
   if scanf("2013-01-03", "${ndigits(4)}-${ndigits(2)}-${ndigits(2)}$.", year, month, day):
     ...
+  ```
 
 
 The scanp macro
@@ -145,8 +146,7 @@ not implemented.
 
 Simple example that parses the ``/etc/passwd`` file line by line:
 
-.. code-block:: nim
-
+  ```nim
   const
     etc_passwd = """root:x:0:0:root:/root:/bin/bash
   daemon:x:1:1:daemon:/usr/sbin:/bin/sh
@@ -165,16 +165,16 @@ Simple example that parses the ``/etc/passwd`` file line by line:
         result.add entry
       else:
         break
+  ```
 
 The ``scanp`` maps the grammar code into Nim code that performs the parsing.
 The parsing is performed with the help of 3 helper templates that that can be
 implemented for a custom type.
 
 These templates need to be named ``atom`` and ``nxt``. ``atom`` should be
-overloaded to handle both single characters and sets of character.
-
-.. code-block:: nim
+overloaded to handle both `char` and `set[char]`.
 
+  ```nim
   import std/streams
 
   template atom(input: Stream; idx: int; c: char): bool =
@@ -190,11 +190,11 @@ overloaded to handle both single characters and sets of character.
 
   if scanp(content, idx, +( ~{'\L', '\0'} -> entry.add(peekChar($input))), '\L'):
     result.add entry
+  ```
 
 Calling ordinary Nim procs inside the macro is possible:
 
-.. code-block:: nim
-
+  ```nim
   proc digits(s: string; intVal: var int; start: int): int =
     var x = 0
     while result+start < s.len and s[result+start] in {'0'..'9'} and s[result+start] != ':':
@@ -220,12 +220,12 @@ Calling ordinary Nim procs inside the macro is possible:
           result.add login & " " & homedir
       else:
         break
+  ```
 
 When used for matching, keep in mind that likewise scanf, no backtracking
 is performed.
 
-.. code-block:: nim
-
+  ```nim
   proc skipUntil(s: string; until: string; unless = '\0'; start: int): int =
     # Skips all characters until the string `until` is found. Returns 0
     # if the char `unless` is found first or the end is reached.
@@ -256,12 +256,12 @@ is performed.
 
   for r in collectLinks(body):
     echo r
+  ```
 
 In this example both macros are combined seamlessly in order to maximise
 efficiency and perform different checks.
 
-.. code-block:: nim
-
+  ```nim
   iterator parseIps*(soup: string): string =
     ## ipv4 only!
     const digits = {'0'..'9'}
@@ -279,13 +279,17 @@ efficiency and perform different checks.
           yield buf
       buf.setLen(0) # need to clear `buf` each time, cause it might contain garbage
       idx.inc
-
+  ```
 ]##
 
 
-import macros, parseutils
+import std/[macros, parseutils]
 import std/private/since
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
 proc conditionsToIfChain(n, idx, res: NimNode; start: int): NimNode =
   assert n.kind == nnkStmtList
   if start >= n.len: return newAssignment(res, newLit true)
@@ -468,7 +472,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
 
 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 
+  ## 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)`
@@ -508,7 +512,7 @@ macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[
           inc userMatches
       else: discard
     inc p
-  result.add newPar(newCall(ident("scanf"), input, newStrLitNode(pattern)))
+  result.add nnkTupleConstr.newTree(newCall(ident("scanf"), input, newStrLitNode(pattern)))
   for arg in arguments:
     result[^1][0].add arg
     result[^1].add arg
@@ -584,7 +588,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
           action
 
       # (x) a  # bind action a to (x)
-      if it[0].kind == nnkPar and it.len == 2:
+      if it[0].kind in {nnkPar, nnkTupleConstr} and it.len == 2:
         result = atm(it[0], input, idx, placeholder(it[1], input, idx))
       elif it.kind == nnkInfix and it[0].eqIdent"->":
         # bind matching to some action: