summary refs log tree commit diff stats
path: root/tests/deps/jester-#head/jester/patterns.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tests/deps/jester-#head/jester/patterns.nim')
-rw-r--r--tests/deps/jester-#head/jester/patterns.nim141
1 files changed, 141 insertions, 0 deletions
diff --git a/tests/deps/jester-#head/jester/patterns.nim b/tests/deps/jester-#head/jester/patterns.nim
new file mode 100644
index 000000000..52b0d3a15
--- /dev/null
+++ b/tests/deps/jester-#head/jester/patterns.nim
@@ -0,0 +1,141 @@
+# Copyright (C) 2012-2018 Dominik Picheta
+# MIT License - Look at license.txt for details.
+import parseutils, tables
+type
+  NodeType* = enum
+    NodeText, NodeField
+  Node* = object
+    typ*: NodeType
+    text*: string
+    optional*: bool
+  
+  Pattern* = seq[Node]
+
+#/show/@id/?
+proc parsePattern*(pattern: string): Pattern =
+  result = @[]
+  template addNode(result: var Pattern, theT: NodeType, theText: string,
+                   isOptional: bool): typed =
+    block:
+      var newNode: Node
+      newNode.typ = theT
+      newNode.text = theText
+      newNode.optional = isOptional
+      result.add(newNode)
+
+  template `{}`(s: string, i: int): char =
+    if i >= len(s):
+      '\0'
+    else:
+      s[i]
+
+  var i = 0
+  var text = ""
+  while i < pattern.len():
+    case pattern[i]
+    of '@':
+      # Add the stored text.
+      if text != "":
+        result.addNode(NodeText, text, false)
+        text = ""
+      # Parse named parameter.
+      inc(i) # Skip @
+      var nparam = ""
+      i += pattern.parseUntil(nparam, {'/', '?'}, i)
+      var optional = pattern{i} == '?'
+      result.addNode(NodeField, nparam, optional)
+      if pattern{i} == '?': inc(i) # Only skip ?. / should not be skipped.
+    of '?':
+      var optionalChar = text[^1]
+      setLen(text, text.len-1) # Truncate ``text``.
+      # Add the stored text.
+      if text != "":
+        result.addNode(NodeText, text, false)
+        text = ""
+      # Add optional char.
+      inc(i) # Skip ?
+      result.addNode(NodeText, $optionalChar, true)
+    of '\\':
+      inc i # Skip \
+      if pattern[i] notin {'?', '@', '\\'}:
+        raise newException(ValueError, 
+                "This character does not require escaping: " & pattern[i])
+      text.add(pattern{i})
+      inc i # Skip ``pattern[i]``
+    else:
+      text.add(pattern{i})
+      inc(i)
+  
+  if text != "":
+    result.addNode(NodeText, text, false)
+
+proc findNextText(pattern: Pattern, i: int, toNode: var Node): bool =
+  ## Finds the next NodeText in the pattern, starts looking from ``i``.
+  result = false
+  for n in i..pattern.len()-1:
+    if pattern[n].typ == NodeText:
+      toNode = pattern[n]
+      return true
+
+proc check(n: Node, s: string, i: int): bool =
+  let cutTo = (n.text.len-1)+i
+  if cutTo > s.len-1: return false
+  return s.substr(i, cutTo) == n.text
+
+proc match*(pattern: Pattern, s: string):
+      tuple[matched: bool, params: Table[string, string]] =
+  var i = 0 # Location in ``s``.
+
+  result.matched = true
+  result.params = initTable[string, string]()
+
+  for ncount, node in pattern:
+    case node.typ
+    of NodeText:
+      if node.optional:
+        if check(node, s, i):
+          inc(i, node.text.len) # Skip over this optional character.
+        else:
+          # If it's not there, we have nothing to do. It's optional after all.
+          discard
+      else:
+        if check(node, s, i):
+          inc(i, node.text.len) # Skip over this
+        else:
+          # No match.
+          result.matched = false
+          return
+    of NodeField:
+      var nextTxtNode: Node
+      var stopChar = '/'
+      if findNextText(pattern, ncount, nextTxtNode):
+        stopChar = nextTxtNode.text[0]
+      var matchNamed = ""
+      i += s.parseUntil(matchNamed, stopChar, i)
+      result.params[node.text] = matchNamed
+      if matchNamed == "" and not node.optional:
+        result.matched = false
+        return
+
+  if s.len != i:
+    result.matched = false
+
+when isMainModule:
+  let f = parsePattern("/show/@id/test/@show?/?")
+  doAssert match(f, "/show/12/test/hallo/").matched
+  doAssert match(f, "/show/2131726/test/jjjuuwąąss").matched
+  doAssert(not match(f, "/").matched)
+  doAssert(not match(f, "/show//test//").matched)
+  doAssert(match(f, "/show/asd/test//").matched)
+  doAssert(not match(f, "/show/asd/asd/test/jjj/").matched)
+  doAssert(match(f, "/show/@łę¶ŧ←/test/asd/").params["id"] == "@łę¶ŧ←")
+
+  let f2 = parsePattern("/test42/somefile.?@ext?/?")
+  doAssert(match(f2, "/test42/somefile/").params["ext"] == "")
+  doAssert(match(f2, "/test42/somefile.txt").params["ext"] == "txt")
+  doAssert(match(f2, "/test42/somefile.txt/").params["ext"] == "txt")
+  
+  let f3 = parsePattern(r"/test32/\@\\\??")
+  doAssert(match(f3, r"/test32/@\").matched)
+  doAssert(not match(f3, r"/test32/@\\").matched)
+  doAssert(match(f3, r"/test32/@\?").matched)