summary refs log tree commit diff stats
path: root/lib/pure/parseutils.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/parseutils.nim')
-rwxr-xr-xlib/pure/parseutils.nim102
1 files changed, 102 insertions, 0 deletions
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index d3346ecde..1f4e54b96 100755
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -76,6 +76,18 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
     ident = substr(s, start, i-1)
     result = i-start
 
+proc parseIdent*(s: string, start = 0): string =
+  ## parses an identifier and stores it in ``ident``. 
+  ## Returns the parsed identifier or an empty string in case of an error.
+  result = ""
+  var i = start
+
+  if s[i] in IdentStartChars:
+    inc(i)
+    while s[i] in IdentChars: inc(i)
+    
+    result = substr(s, start, i-1)
+
 proc parseToken*(s: string, token: var string, validChars: set[char],
                  start = 0): int {.inline, deprecated.} =
   ## parses a token and stores it in ``token``. Returns
@@ -254,4 +266,94 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
   result = parseBiggestFloat(s, bf, start)
   number = bf
   
+proc isEscaped*(s: string, pos: int) : bool =
+  assert pos >= 0 and pos < s.len
+
+  var
+    backslashes = 0
+    j = pos - 1
+
+  while j >= 0:
+    if s[j] == '\\':
+      inc backslashes
+      dec j
+    else:
+      break
+
+  return backslashes mod 2 != 0
+
+type
+  TInterpolatedKind* = enum
+    ikString, ikExpr
+
+  TInterpStrFragment* = tuple[kind: TInterpolatedKind, value: string]
+
+iterator interpolatedFragments*(s: string): TInterpStrFragment =
+  var 
+    i = 0
+    tokenStart = 0
+
+  proc token(kind: TInterpolatedKind, value: string): TInterpStrFragment =
+    result.kind = kind
+    result.value = value
+
+  while i < s.len:
+    # The $ sign marks the start of an interpolation.
+    #
+    # It's followed either by a varialbe name or an opening bracket 
+    # (so it should be before the end of the string)
+    # if the dollar sign is escaped, don't trigger interpolation
+    if s[i] == '$' and i < (s.len - 1) and not isEscaped(s, i):
+      # Interpolation starts here.
+      # Return any string that we've ran over so far.
+      if i != tokenStart:
+        yield token(ikString, s[tokenStart..i-1])
+
+      var next = s[i+1]
+      if next == '{':
+        # Complex expression: ${foo(bar) in {1..100}}
+        # Find closing braket, while respecting any nested brackets
+        inc i
+        tokenStart = i + 1
+
+        var
+          brackets = {'{', '}'}
+          nestingCount = 1
+        
+        while i < s.len:
+          inc i, skipUntil(s, brackets, i+1) + 1
+          
+          if not isEscaped(s, i):
+            if s[i] == '}':
+              dec nestingCount
+              if nestingCount == 0: break
+            else:
+              inc nestingCount
+
+        yield token(ikExpr, s[tokenStart..(i-1)])
+
+        tokenStart = i + 1
+        
+      else:
+        tokenStart = i + 1
+        var identifier = parseIdent(s, i+1)
+        
+        if identifier.len >  0:
+          inc i, identifier.len
+
+          yield token(ikExpr, s[tokenStart..i])
+
+          tokenStart = i + 1
+
+        else:
+          raise newException(EInvalidValue, "Unable to parse a varible name at " & s[i..s.len])
+       
+    inc i
+  #end while
+
+  # We've reached the end of the string without finding a new interpolation.
+  # Return the last fragment at string.
+  if i != tokenStart:
+    yield token(ikString, s[tokenStart..i])
+
 {.pop.}