diff options
-rw-r--r-- | compiler/ropes.nim | 129 | ||||
-rw-r--r-- | lib/pure/ropes.nim | 143 |
2 files changed, 142 insertions, 130 deletions
diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 7572dba70..edac8e9d0 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -82,6 +82,7 @@ var errorHandler*: proc(err: RopesError, msg: string, useWarning = false) # avoid dependency on msgs.nim proc len*(a: Rope): int = + ## the rope's length if a == nil: result = 0 else: result = a.length @@ -134,6 +135,7 @@ proc insertInCache(s: string): Rope = cache[h] = result proc rope*(s: string): Rope = + ## Converts a string to a rope. if s.len == 0: result = nil else: @@ -141,34 +143,14 @@ proc rope*(s: string): Rope = assert(ropeInvariant(result)) proc rope*(i: BiggestInt): Rope = + ## Converts an int to a rope. inc gCacheIntTries result = rope($i) proc rope*(f: BiggestFloat): Rope = + ## Converts a float to a rope. result = rope($f) -proc ropeSeqInsert(rs: var RopeSeq, r: Rope, at: Natural) = - var length = len(rs) - if at > length: - setLen(rs, at + 1) - else: - setLen(rs, length + 1) # move old rope elements: - for i in countdown(length, at + 1): - rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it - rs[at] = r - -proc newRecRopeToStr(result: var string, resultLen: var int, r: Rope) = - var stack = @[r] - while len(stack) > 0: - var it = pop(stack) - while it.data == nil: - add(stack, it.right) - it = it.left - assert(it.data != nil) - copyMem(addr(result[resultLen]), addr(it.data[0]), it.length) - inc(resultLen, it.length) - assert(resultLen <= len(result)) - proc `&`*(a, b: Rope): Rope = if a == nil: result = b @@ -181,45 +163,46 @@ proc `&`*(a, b: Rope): Rope = result.right = b proc `&`*(a: Rope, b: string): Rope = + ## the concatenation operator for ropes. result = a & rope(b) proc `&`*(a: string, b: Rope): Rope = + ## the concatenation operator for ropes. result = rope(a) & b proc `&`*(a: openArray[Rope]): Rope = + ## the concatenation operator for an openarray of ropes. for i in countup(0, high(a)): result = result & a[i] proc add*(a: var Rope, b: Rope) = + ## adds `b` to the rope `a`. a = a & b proc add*(a: var Rope, b: string) = + ## adds `b` to the rope `a`. a = a & b -proc `$`*(p: Rope): string = - if p == nil: - result = "" - else: - result = newString(p.length) - var resultLen = 0 - newRecRopeToStr(result, resultLen, p) - -proc ropeConcat*(a: varargs[Rope]): Rope = - # not overloaded version of concat to speed-up `rfmt` a little bit - for i in countup(0, high(a)): result = result & a[i] - -proc prepend*(a: var Rope, b: Rope) = a = b & a -proc prepend*(a: var Rope, b: string) = a = b & a - -proc writeRope*(f: File, c: Rope) = - var stack = @[c] - while len(stack) > 0: - var it = pop(stack) - while it.data == nil: - add(stack, it.right) - it = it.left - assert(it != nil) - assert(it.data != nil) - write(f, it.data) +iterator leaves*(r: Rope): string = + ## iterates over any leaf string in the rope `r`. + if r != nil: + var stack = @[r] + while stack.len > 0: + var it = stack.pop + while isNil(it.data): + stack.add(it.right) + it = it.left + assert(it != nil) + assert(it.data != nil) + yield it.data + +iterator items*(r: Rope): char = + ## iterates over any character in the rope `r`. + for s in leaves(r): + for c in items(s): yield c + +proc writeRope*(f: File, r: Rope) = + ## writes a rope to a file. + for s in leaves(r): write(f, s) proc writeRope*(head: Rope, filename: string, useWarning = false) = var f: File @@ -229,6 +212,19 @@ proc writeRope*(head: Rope, filename: string, useWarning = false) = else: errorHandler(rCannotOpenFile, filename, useWarning) +proc `$`*(r: Rope): string = + ## converts a rope back to a string. + result = newString(r.len) + setLen(result, 0) + for s in leaves(r): add(result, s) + +proc ropeConcat*(a: varargs[Rope]): Rope = + # not overloaded version of concat to speed-up `rfmt` a little bit + for i in countup(0, high(a)): result = result & a[i] + +proc prepend*(a: var Rope, b: Rope) = a = b & a +proc prepend*(a: var Rope, b: string) = a = b & a + var rnl* = tnl.newRope softRnl* = tnl.newRope @@ -291,6 +287,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = assert(ropeInvariant(result)) proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) = + ## shortcut for ``add(c, frmt % args)``. add(c, frmt % args) when true: @@ -307,12 +304,17 @@ else: const bufSize = 1024 # 1 KB is reasonable -proc auxEqualsFile(r: Rope, f: File, buf: var array[bufSize, char], - bpos, blen: var int): bool = - if r.data != nil: - var dpos = 0 - let dlen = r.data.len - while dpos < dlen: +proc equalsFile*(r: Rope, f: File): bool = + ## returns true if the contents of the file `f` equal `r`. + var + buf: array[bufSize, char] + bpos = buf.len + blen = buf.len + + for s in leaves(r): + var spos = 0 + let slen = s.len + while spos < slen: if bpos == blen: # Read more data bpos = 0 @@ -320,26 +322,19 @@ proc auxEqualsFile(r: Rope, f: File, buf: var array[bufSize, char], if blen == 0: # no more data in file result = false return - let n = min(blen - bpos, dlen - dpos) - if not equalMem(addr(buf[bpos]), addr(r.data[dpos]), n): + let n = min(blen - bpos, slen - spos) + # TODO There's gotta be a better way of comparing here... + if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n): result = false return - dpos += n + spos += n bpos += n - result = true - else: - result = auxEqualsFile(r.left, f, buf, bpos, blen) and - auxEqualsFile(r.right, f, buf, bpos, blen) -proc equalsFile*(r: Rope, f: File): bool = - var - buf: array[bufSize, char] - bpos = bufSize - blen = bufSize - result = auxEqualsFile(r, f, buf, bpos, blen) and - readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all + result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all proc equalsFile*(r: Rope, filename: string): bool = + ## returns true if the contents of the file `f` equal `r`. If `f` does not + ## exist, false is returned. var f: File result = open(f, filename) if result: diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 3959b930f..5c7fedfe3 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -52,9 +52,9 @@ proc len*(a: Rope): int {.rtl, extern: "nro$1".} = ## the rope's length if a == nil: result = 0 else: result = a.length - + proc newRope(): Rope = new(result) -proc newRope(data: string): Rope = +proc newRope(data: string): Rope = new(result) result.length = len(data) result.data = data @@ -65,18 +65,18 @@ var when countCacheMisses: var misses, hits: int - -proc splay(s: string, tree: Rope, cmpres: var int): Rope = + +proc splay(s: string, tree: Rope, cmpres: var int): Rope = var c: int var t = tree N.left = nil N.right = nil # reset to nil var le = N var r = N - while true: + while true: c = cmp(s, t.data) - if c < 0: - if (t.left != nil) and (s < t.left.data): + if c < 0: + if (t.left != nil) and (s < t.left.data): var y = t.left t.left = y.right y.right = t @@ -85,8 +85,8 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = r.left = t r = t t = t.left - elif c > 0: - if (t.right != nil) and (s > t.right.data): + elif c > 0: + if (t.right != nil) and (s > t.right.data): var y = t.right t.right = y.left y.left = t @@ -95,8 +95,8 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = le.right = t le = t t = t.right - else: - break + else: + break cmpres = c le.right = t.left r.left = t.right @@ -104,50 +104,50 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = t.right = N.left result = t -proc insertInCache(s: string, tree: Rope): Rope = +proc insertInCache(s: string, tree: Rope): Rope = var t = tree - if t == nil: + if t == nil: result = newRope(s) when countCacheMisses: inc(misses) return var cmp: int t = splay(s, t, cmp) - if cmp == 0: + if cmp == 0: # We get here if it's already in the Tree # Don't add it again result = t when countCacheMisses: inc(hits) - else: + else: when countCacheMisses: inc(misses) result = newRope(s) - if cmp < 0: + if cmp < 0: result.left = t.left result.right = t t.left = nil - else: + else: # i > t.item: result.right = t.right result.left = t t.right = nil proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = - ## Converts a string to a rope. - if s.len == 0: + ## Converts a string to a rope. + if s.len == 0: result = nil - elif cacheEnabled: + elif cacheEnabled: result = insertInCache(s, cache) cache = result - else: + else: result = newRope(s) - -proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = - ## Converts an int to a rope. + +proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = + ## Converts an int to a rope. result = rope($i) proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} = - ## Converts a float to a rope. + ## Converts a float to a rope. result = rope($f) - + proc enableCache*() {.rtl, extern: "nro$1".} = ## Enables the caching of leaves. This reduces the memory footprint at ## the cost of runtime efficiency. @@ -160,9 +160,9 @@ proc disableCache*() {.rtl, extern: "nro$1".} = proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = ## the concatenation operator for ropes. - if a == nil: + if a == nil: result = b - elif b == nil: + elif b == nil: result = a else: result = newRope() @@ -177,16 +177,16 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = else: result.left = a result.right = b - -proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = + +proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = ## the concatenation operator for ropes. result = a & rope(b) - -proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} = + +proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} = ## the concatenation operator for ropes. result = rope(a) & b - -proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} = + +proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} = ## the concatenation operator for an openarray of ropes. for i in countup(0, high(a)): result = result & a[i] @@ -219,7 +219,7 @@ iterator leaves*(r: Rope): string = ## iterates over any leaf string in the rope `r`. if r != nil: var stack = @[r] - while stack.len > 0: + while stack.len > 0: var it = stack.pop while isConc(it): stack.add(it.right) @@ -227,7 +227,7 @@ iterator leaves*(r: Rope): string = assert(it != nil) assert(it.data != nil) yield it.data - + iterator items*(r: Rope): char = ## iterates over any character in the rope `r`. for s in leaves(r): @@ -237,7 +237,7 @@ proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} = ## writes a rope to a file. for s in leaves(r): write(f, s) -proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= +proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= ## converts a rope back to a string. result = newString(r.len) setLen(result, 0) @@ -251,25 +251,25 @@ when false: new(result) result.length = -idx - proc compileFrmt(frmt: string): Rope = + proc compileFrmt(frmt: string): Rope = var i = 0 var length = len(frmt) result = nil var num = 0 - while i < length: - if frmt[i] == '$': + while i < length: + if frmt[i] == '$': inc(i) case frmt[i] - of '$': + of '$': add(result, "$") inc(i) - of '#': + of '#': inc(i) add(result, compiledArg(num+1)) inc(num) - of '0'..'9': + of '0'..'9': var j = 0 - while true: + while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) if frmt[i] notin {'0'..'9'}: break @@ -285,10 +285,10 @@ when false: add(s, compiledArg(j)) else: raise newException(EInvalidValue, "invalid format string") var start = i - while i < length: + while i < length: if frmt[i] != '$': inc(i) - else: break - if i - 1 >= start: + else: break + if i - 1 >= start: add(result, substr(frmt, start, i-1)) proc `%`*(frmt: string, args: openArray[Rope]): Rope {. @@ -340,29 +340,46 @@ proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. ## shortcut for ``add(c, frmt % args)``. add(c, frmt % args) +const + bufSize = 1024 # 1 KB is reasonable + proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = ## returns true if the contents of the file `f` equal `r`. - var bufSize = 1024 # reasonable start value - var buf = alloc(bufSize) + var + buf: array[bufSize, char] + bpos = buf.len + blen = buf.len + for s in leaves(r): - if s.len > bufSize: - bufSize = max(bufSize * 2, s.len) - buf = realloc(buf, bufSize) - var readBytes = readBuffer(f, buf, s.len) - result = readBytes == s.len and equalMem(buf, cstring(s), s.len) - if not result: break - if result: - result = readBuffer(f, buf, 1) == 0 # really at the end of file? - dealloc(buf) + var spos = 0 + let slen = s.len + while spos < slen: + if bpos == blen: + # Read more data + bpos = 0 + blen = readBuffer(f, addr(buf[0]), buf.len) + if blen == 0: # no more data in file + result = false + return + let n = min(blen - bpos, slen - spos) + # TODO There's gotta be a better way of comparing here... + if not equalMem(addr(buf[bpos]), + cast[pointer](cast[int](cstring(s))+spos), n): + result = false + return + spos += n + bpos += n + + result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all -proc equalsFile*(r: Rope, f: string): bool {.rtl, extern: "nro$1Str".} = +proc equalsFile*(r: Rope, filename: string): bool {.rtl, extern: "nro$1Str".} = ## returns true if the contents of the file `f` equal `r`. If `f` does not ## exist, false is returned. - var bin: File - result = open(bin, f) + var f: File + result = open(f, filename) if result: - result = equalsFile(r, bin) - close(bin) + result = equalsFile(r, f) + close(f) new(N) # init dummy node for splay algorithm |