summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorVarriount <Varriount@users.noreply.github.com>2015-01-17 18:53:46 -0500
committerVarriount <Varriount@users.noreply.github.com>2015-01-17 18:53:46 -0500
commiteb929bd61c94fc5acf7e384502706fc41a4df2fd (patch)
tree69ba617482f1ef5670d189daf9de4ec5a083a113 /lib
parent9847f762e4bc6e01a3303b42a48d576a6751477d (diff)
parentae7ca46a092204e7dbfb642309600f036de65e6e (diff)
downloadNim-eb929bd61c94fc5acf7e384502706fc41a4df2fd.tar.gz
Merge pull request #1842 from def-/unicode-reversed
Add reversed proc to unicode module
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/unicode.nim50
1 files changed, 50 insertions, 0 deletions
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index c2eb001f6..b892ec8b4 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -1194,6 +1194,17 @@ proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
   if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
     return true
 
+proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
+  ## returns true iff `c` is a Unicode combining character
+  var c = RuneImpl(c)
+
+  # Optimized to return false immediately for ASCII
+  return c >= 0x0300 and (c <= 0x036f or
+    (c >= 0x1ab0 and c <= 0x1aff) or
+    (c >= 0x1dc0 and c <= 0x1dff) or
+    (c >= 0x20d0 and c <= 0x20ff) or
+    (c >= 0xfe20 and c <= 0xfe2f))
+
 iterator runes*(s: string): Rune =
   ## iterates over any unicode character of the string `s`.
   var
@@ -1220,9 +1231,48 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
     if result != 0: return
   result = a.len - b.len
 
+proc reversed*(s: string): string =
+  ## returns the reverse of `s`, interpreting it as unicode characters. Unicode
+  ## combining characters are correctly interpreted as well:
+  ##
+  ## .. code-block:
+  ##   assert reversed("Reverse this!") == "!siht esreveR"
+  ##   assert reversed("先秦兩漢") == "漢兩秦先"
+  ##   assert reversed("as⃝df̅") == "f̅ds⃝a"
+  ## assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+  var
+    i = 0
+    lastI = 0
+    newPos = len(s) - 1
+    blockPos = 0
+    r: Rune
+
+  template reverseUntil(pos): stmt =
+    var j = pos - 1
+    while j > blockPos:
+      result[newPos] = s[j]
+      dec j
+      dec newPos
+    blockPos = pos - 1
+
+  result = newString(len(s))
+
+  while i < len(s):
+    lastI = i
+    fastRuneAt(s, i, r, true)
+    if not isCombining(r):
+      reverseUntil(lastI)
+
+  reverseUntil(len(s))
+
 when isMainModule:
   let
     someString = "öÑ"
     someRunes = @[runeAt(someString, 0), runeAt(someString, 2)]
     compared = (someString == $someRunes)
   assert compared == true
+
+  assert reversed("Reverse this!") == "!siht esreveR"
+  assert reversed("先秦兩漢") == "漢兩秦先"
+  assert reversed("as⃝df̅") == "f̅ds⃝a"
+  assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"