Nim: strscans.scanp documentation example throws error

Created on 7 Oct 2018  路  1Comment  路  Source: nim-lang/Nim

The example from here:

const
  etc_passwd = """root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
messagebus:x:103:107::/var/run/dbus:/bin/false
"""

proc parsePasswd(content: string): seq[string] =
  result = @[]
  var idx = 0
  while true:
    var entry = ""
    if scanp(content, idx, +(~{'\L', '\0'} -> entry.add($_)), '\L'):
      result.add entry
    else:
      break

when called with echo parsePasswd(etc_passwd) throws: Error: unhandled exception: index out of bounds [IndexError]

High Priority Stdlib

Most helpful comment

Let's turn this into a intermediate(?) hacktoberfest task.

A few hints:

  • Have a look at how the scanp DSL is expanded by using -d:debugScanp
  • Have a look at how atom is implemented
  • Have a look at how the ~ (not) operator is implemented
  • atom checks for for EOF and the character check itself so ~(expr) will return true if:

    • expr fails

    • we reach the EOF!

  • This is bad because we don't care about the EOF and, now that the trailing NULL is not indexable anymore, we crash!

A sketch of a possible solution to get you started:

diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 77763ff43..6d78f6f52 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -474,15 +474,15 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =

   template interf(x): untyped = bindSym(x, brForceOpen)

-  proc toIfChain(n: seq[StmtTriple]; idx, res: NimNode; start: int): NimNode =
+  proc toIfChain(n: seq[StmtTriple]; input, idx, res: NimNode; start: int): NimNode =
     if start >= n.len: return newAssignment(res, newLit true)
     var ifs: NimNode = nil
     if n[start].cond.kind == nnkEmpty:
-      ifs = toIfChain(n, idx, res, start+1)
+      ifs = toIfChain(n, input, idx, res, start+1)
     else:
-      ifs = newIfStmt((n[start].cond,
+      ifs = newIfStmt((infix(newCall(ident"hasNxt", input, idx), "and", n[start].cond),
                       newTree(nnkStmtList, n[start].action,
-                              toIfChain(n, idx, res, start+1))))
+                              toIfChain(n, input, idx, res, start+1))))
     result = newTree(nnkStmtList, n[start].init, ifs)

   proc attach(x, attached: NimNode): NimNode =
@@ -609,7 +609,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
         for x in it: conds.add atm(x, input, idx, attached)
         var res = genSym(nskVar, "res")
         result = (newStmtList(newVarStmt(res, newLit false),
-            toIfChain(conds, idx, res, 0)), res, newEmptyNode())
+            toIfChain(conds, input, idx, res, 0)), res, newEmptyNode())
     else:
       error("invalid pattern")

@@ -620,7 +620,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
   var conds: seq[StmtTriple] = @[]
   for it in pattern:
     conds.add atm(it, input, idx, nil)
-  result.add toIfChain(conds, idx, res, 0)
+  result.add toIfChain(conds, input, idx, res, 0)
   result.add res
   when defined(debugScanp):
     echo repr result

I think this makes it possible to drop the idx < input.len in atom since we now always check that beforehand so please _check_ if that's possible.

>All comments

Let's turn this into a intermediate(?) hacktoberfest task.

A few hints:

  • Have a look at how the scanp DSL is expanded by using -d:debugScanp
  • Have a look at how atom is implemented
  • Have a look at how the ~ (not) operator is implemented
  • atom checks for for EOF and the character check itself so ~(expr) will return true if:

    • expr fails

    • we reach the EOF!

  • This is bad because we don't care about the EOF and, now that the trailing NULL is not indexable anymore, we crash!

A sketch of a possible solution to get you started:

diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 77763ff43..6d78f6f52 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -474,15 +474,15 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =

   template interf(x): untyped = bindSym(x, brForceOpen)

-  proc toIfChain(n: seq[StmtTriple]; idx, res: NimNode; start: int): NimNode =
+  proc toIfChain(n: seq[StmtTriple]; input, idx, res: NimNode; start: int): NimNode =
     if start >= n.len: return newAssignment(res, newLit true)
     var ifs: NimNode = nil
     if n[start].cond.kind == nnkEmpty:
-      ifs = toIfChain(n, idx, res, start+1)
+      ifs = toIfChain(n, input, idx, res, start+1)
     else:
-      ifs = newIfStmt((n[start].cond,
+      ifs = newIfStmt((infix(newCall(ident"hasNxt", input, idx), "and", n[start].cond),
                       newTree(nnkStmtList, n[start].action,
-                              toIfChain(n, idx, res, start+1))))
+                              toIfChain(n, input, idx, res, start+1))))
     result = newTree(nnkStmtList, n[start].init, ifs)

   proc attach(x, attached: NimNode): NimNode =
@@ -609,7 +609,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
         for x in it: conds.add atm(x, input, idx, attached)
         var res = genSym(nskVar, "res")
         result = (newStmtList(newVarStmt(res, newLit false),
-            toIfChain(conds, idx, res, 0)), res, newEmptyNode())
+            toIfChain(conds, input, idx, res, 0)), res, newEmptyNode())
     else:
       error("invalid pattern")

@@ -620,7 +620,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
   var conds: seq[StmtTriple] = @[]
   for it in pattern:
     conds.add atm(it, input, idx, nil)
-  result.add toIfChain(conds, idx, res, 0)
+  result.add toIfChain(conds, input, idx, res, 0)
   result.add res
   when defined(debugScanp):
     echo repr result

I think this makes it possible to drop the idx < input.len in atom since we now always check that beforehand so please _check_ if that's possible.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zaxebo1 picture zaxebo1  路  4Comments

Vindaar picture Vindaar  路  3Comments

hlaaftana picture hlaaftana  路  3Comments

SolitudeSF picture SolitudeSF  路  3Comments

Tronic picture Tronic  路  3Comments