Merge pull request #2428 from arnetheduck/comp-lib-ropes

Comp lib ropes
This commit is contained in:
Andreas Rumpf 2015-04-06 00:32:08 +02:00
commit 99e0fb90e0
15 changed files with 200 additions and 189 deletions

View file

@ -452,17 +452,16 @@ proc debug(n: PSym) =
elif n.kind == skUnknown: elif n.kind == skUnknown:
msgWriteln("skUnknown") msgWriteln("skUnknown")
else: else:
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) #writeln(stdout, $symToYaml(n, 0, 1))
msgWriteln("$1_$2: $3, $4, $5, $6" % [ msgWriteln("$1_$2: $3, $4, $5, $6" % [
n.name.s, $n.id, flagsToStr(n.flags).ropeToStr, n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
flagsToStr(n.loc.flags).ropeToStr, lineInfoToStr(n.info).ropeToStr, $lineInfoToStr(n.info), $n.kind])
$n.kind])
proc debug(n: PType) = proc debug(n: PType) =
msgWriteln(ropeToStr(debugType(n))) msgWriteln($debugType(n))
proc debug(n: PNode) = proc debug(n: PNode) =
msgWriteln(ropeToStr(debugTree(n, 0, 100))) msgWriteln($debugTree(n, 0, 100))
const const
EmptySeq = @[] EmptySeq = @[]

View file

@ -243,24 +243,24 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeNode(w, n.info, n.sons[i], result) encodeNode(w, n.info, n.sons[i], result)
add(result, ')') add(result, ')')
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
var oldLen = result.len var oldLen = result.len
result.add('<') result.add('<')
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
if loc.s != low(loc.s): if loc.s != low(loc.s):
add(result, '*') add(result, '*')
encodeVInt(ord(loc.s), result) encodeVInt(ord(loc.s), result)
if loc.flags != {}: if loc.flags != {}:
add(result, '$') add(result, '$')
encodeVInt(cast[int32](loc.flags), result) encodeVInt(cast[int32](loc.flags), result)
if loc.t != nil: if loc.t != nil:
add(result, '^') add(result, '^')
encodeVInt(cast[int32](loc.t.id), result) encodeVInt(cast[int32](loc.t.id), result)
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr(ropeToStr(loc.r), result) encodeStr($loc.r, result)
if loc.a != 0: if loc.a != 0:
add(result, '?') add(result, '?')
encodeVInt(loc.a, result) encodeVInt(loc.a, result)
if oldLen + 1 == result.len: if oldLen + 1 == result.len:
@ -317,7 +317,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
encodeStr(ropeToStr(lib.name), result) encodeStr($lib.name, result)
add(result, '|') add(result, '|')
encodeNode(w, info, lib.path, result) encodeNode(w, info, lib.path, result)

View file

@ -413,7 +413,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call 'ropeToStr' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil internalAssert pat != nil
if pat.contains({'#', '(', '@', '\''}): if pat.contains({'#', '(', '@', '\''}):
@ -462,7 +462,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call 'ropeToStr' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil internalAssert pat != nil
var start = 3 var start = 3

View file

@ -1730,7 +1730,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
mParseBiggestFloat: mParseBiggestFloat:
var opr = e.sons[0].sym var opr = e.sons[0].sym
if lfNoDecl notin opr.loc.flags: if lfNoDecl notin opr.loc.flags:
discard cgsym(p.module, opr.loc.r.ropeToStr) discard cgsym(p.module, $opr.loc.r)
genCall(p, e, d) genCall(p, e, d)
of mReset: genReset(p, e) of mReset: genReset(p, e)
of mEcho: genEcho(p, e[1].skipConv) of mEcho: genEcho(p, e[1].skipConv)

View file

@ -79,7 +79,7 @@ proc writeTypeCache(a: TIdTable, s: var string) =
s.add(' ') s.add(' ')
encodeVInt(id, s) encodeVInt(id, s)
s.add(':') s.add(':')
encodeStr(PRope(value).ropeToStr, s) encodeStr($PRope(value), s)
inc i inc i
s.add('}') s.add('}')
@ -280,11 +280,11 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
proc mergeRequired*(m: BModule): bool = proc mergeRequired*(m: BModule): bool =
for i in cfsHeaders..cfsProcs: for i in cfsHeaders..cfsProcs:
if m.s[i] != nil: if m.s[i] != nil:
#echo "not empty: ", i, " ", ropeToStr(m.s[i]) #echo "not empty: ", i, " ", m.s[i]
return true return true
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):
if m.initProc.s(i) != nil: if m.initProc.s(i) != nil:
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i]) #echo "not empty: ", i, " ", m.initProc.s[i]
return true return true
proc mergeFiles*(cfilename: string, m: BModule) = proc mergeFiles*(cfilename: string, m: BModule) =

View file

@ -936,7 +936,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}: if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[i], a) initLocExpr(p, t.sons[i], a)
res.add(rdLoc(a).ropeToStr) res.add($rdLoc(a))
else: else:
var r = sym.loc.r var r = sym.loc.r
if r == nil: if r == nil:
@ -944,7 +944,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
# it doesn't matter much: # it doesn't matter much:
r = mangleName(sym) r = mangleName(sym)
sym.loc.r = r # but be consequent! sym.loc.r = r # but be consequent!
res.add(r.ropeToStr) res.add($r)
else: internalError(t.sons[i].info, "genAsmOrEmitStmt()") else: internalError(t.sons[i].info, "genAsmOrEmitStmt()")
if isAsmStmt and hasGnuAsm in CC[cCompiler].props: if isAsmStmt and hasGnuAsm in CC[cCompiler].props:

View file

@ -77,13 +77,13 @@ proc mangleName(s: PSym): PRope =
# These are not properly scoped now - we need to add blocks # These are not properly scoped now - we need to add blocks
# around for loops in transf # around for loops in transf
if keepOrigName: if keepOrigName:
result = s.name.s.mangle.newRope result = s.name.s.mangle.toRope
else: else:
app(result, newRope(mangle(s.name.s))) app(result, toRope(mangle(s.name.s)))
app(result, ~"_") app(result, ~"_")
app(result, toRope(s.id)) app(result, toRope(s.id))
else: else:
app(result, newRope(mangle(s.name.s))) app(result, toRope(mangle(s.name.s)))
app(result, ~"_") app(result, ~"_")
app(result, toRope(s.id)) app(result, toRope(s.id))
s.loc.r = result s.loc.r = result

View file

@ -120,7 +120,7 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope =
while frmt[i] in Digits: while frmt[i] in Digits:
j = (j * 10) + ord(frmt[i]) - ord('0') j = (j * 10) + ord(frmt[i]) - ord('0')
inc(i) inc(i)
app(result, cgsym(m, args[j-1].ropeToStr)) app(result, cgsym(m, $args[j-1]))
var start = i var start = i
while i < length: while i < length:
if frmt[i] != '$' and frmt[i] != '#': inc(i) if frmt[i] != '$' and frmt[i] != '#': inc(i)
@ -555,8 +555,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
params.app(rdLoc(a)) params.app(rdLoc(a))
params.app(", ") params.app(", ")
let load = ropef("\t$1 = ($2) ($3$4));$n", let load = ropef("\t$1 = ($2) ($3$4));$n",
[tmp, getTypeDesc(m, sym.typ), [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)])
params, makeCString(ropeToStr(extname))])
var last = lastSon(n) var last = lastSon(n)
if last.kind == nkHiddenStdConv: last = last.sons[1] if last.kind == nkHiddenStdConv: last = last.sons[1]
internalAssert(last.kind == nkStrLit) internalAssert(last.kind == nkStrLit)
@ -570,8 +569,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
else: else:
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
"\t$1 = ($2) #nimGetProcAddr($3, $4);$n", "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ), [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
lib.name, makeCString(ropeToStr(extname))])
appf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)]) appf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)])
proc varInDynamicLib(m: BModule, sym: PSym) = proc varInDynamicLib(m: BModule, sym: PSym) =
@ -584,8 +582,7 @@ proc varInDynamicLib(m: BModule, sym: PSym) =
inc(m.labels, 2) inc(m.labels, 2)
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
"$1 = ($2*) #nimGetProcAddr($3, $4);$n", "$1 = ($2*) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ), [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
lib.name, makeCString(ropeToStr(extname))])
appf(m.s[cfsVars], "$2* $1;$n", appf(m.s[cfsVars], "$2* $1;$n",
[sym.loc.r, getTypeDesc(m, sym.loc.t)]) [sym.loc.r, getTypeDesc(m, sym.loc.t)])
@ -1255,7 +1252,7 @@ proc writeModule(m: BModule, pending: bool) =
var code = genModule(m, cfile) var code = genModule(m, cfile)
when hasTinyCBackend: when hasTinyCBackend:
if gCmd == cmdRun: if gCmd == cmdRun:
tccgen.compileCCode(ropeToStr(code)) tccgen.compileCCode($code)
return return
if shouldRecompile(code, cfile): if shouldRecompile(code, cfile):

View file

@ -436,7 +436,7 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
if not isVisible(nameNode): return if not isVisible(nameNode): return
var var
name = getName(d, nameNode) name = getName(d, nameNode)
comm = genRecComment(d, n).ropeToStr() comm = $genRecComment(d, n)
r: TSrcGen r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
@ -635,7 +635,7 @@ proc commandJSON*() =
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true
var json = generateJson(d, ast) var json = generateJson(d, ast)
var content = newRope(pretty(json)) var content = toRope(pretty(json))
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:
writeRope(stdout, content) writeRope(stdout, content)

View file

@ -874,7 +874,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr") if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr")
var f = b.sons[1].sym var f = b.sons[1].sym
if f.loc.r == nil: f.loc.r = mangleName(f) if f.loc.r == nil: f.loc.r = mangleName(f)
r.res = makeJSString(ropeToStr(f.loc.r)) r.res = makeJSString($f.loc.r)
internalAssert a.typ != etyBaseIndex internalAssert a.typ != etyBaseIndex
r.address = a.res r.address = a.res
r.kind = resExpr r.kind = resExpr

View file

@ -494,20 +494,16 @@ proc toCChar*(c: char): string =
else: result = $(c) else: result = $(c)
proc makeCString*(s: string): PRope = proc makeCString*(s: string): PRope =
# BUGFIX: We have to split long strings into many ropes. Otherwise
# this could trigger an internalError(). See the ropes module for
# further information.
const const
MaxLineLength = 64 MaxLineLength = 64
result = nil result = nil
var res = "\"" var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
add(res, "\"")
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
add(res, '\"') add(res, '\"')
add(res, tnl) add(res, tnl)
app(result, toRope(res)) # reset: add(res, '\"')
setLen(res, 1)
res[0] = '\"'
add(res, toCChar(s[i])) add(res, toCChar(s[i]))
add(res, '\"') add(res, '\"')
app(result, toRope(res)) app(result, toRope(res))
@ -776,7 +772,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
proc writeSurroundingSrc(info: TLineInfo) = proc writeSurroundingSrc(info: TLineInfo) =
const indent = " " const indent = " "
msgWriteln(indent & info.sourceLine.ropeToStr) msgWriteln(indent & $info.sourceLine)
msgWriteln(indent & spaces(info.col) & '^') msgWriteln(indent & spaces(info.col) & '^')
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
@ -877,8 +873,6 @@ ropes.errorHandler = proc (err: TRopesError, msg: string, useWarning: bool) =
case err case err
of rInvalidFormatStr: of rInvalidFormatStr:
internalError("ropes: invalid format string: " & msg) internalError("ropes: invalid format string: " & msg)
of rTokenTooLong:
internalError("ropes: token too long: " & msg)
of rCannotOpenFile: of rCannotOpenFile:
rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)

View file

@ -105,7 +105,7 @@ proc validateExternCName(s: PSym, info: TLineInfo) =
## Valid identifiers are those alphanumeric including the underscore not ## Valid identifiers are those alphanumeric including the underscore not
## starting with a number. If the check fails, a generic error will be ## starting with a number. If the check fails, a generic error will be
## displayed to the user. ## displayed to the user.
let target = ropeToStr(s.loc.r) let target = $s.loc.r
if target.len < 1 or target[0] notin IdentStartChars or if target.len < 1 or target[0] notin IdentStartChars or
not target.allCharsInSet(IdentChars): not target.allCharsInSet(IdentChars):
localError(info, errGenerated, "invalid exported symbol") localError(info, errGenerated, "invalid exported symbol")

View file

@ -186,7 +186,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr(ropeToStr(loc.r), result) encodeStr($loc.r, result)
if oldLen + 1 == result.len: if oldLen + 1 == result.len:
# no data was necessary, so remove the '<' again: # no data was necessary, so remove the '<' again:
setLen(result, oldLen) setLen(result, oldLen)
@ -241,7 +241,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
encodeStr(ropeToStr(lib.name), result) encodeStr($lib.name, result)
add(result, '|') add(result, '|')
encodeNode(w, info, lib.path, result) encodeNode(w, info, lib.path, result)

View file

@ -56,77 +56,63 @@
# To cache them they are inserted in a `cache` array. # To cache them they are inserted in a `cache` array.
import import
strutils, platform, hashes, crc, options platform, hashes
type type
TFormatStr* = string # later we may change it to CString for better FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments # performance of the code generator (assignments
# copy the format strings # copy the format strings
# though it is not necessary) # though it is not necessary)
PRope* = ref TRope Rope* = ref RopeObj
TRope*{.acyclic.} = object of RootObj # the empty rope is represented RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space # by nil to safe space
left*, right*: PRope left*, right*: Rope
length*: int length*: int
data*: string # != nil if a leaf data*: string # != nil if a leaf
TRopeSeq* = seq[PRope] RopeSeq* = seq[Rope]
TRopesError* = enum RopesError* = enum
rCannotOpenFile rCannotOpenFile
rInvalidFormatStr rInvalidFormatStr
rTokenTooLong
proc con*(a, b: PRope): PRope {.deprecated: [TFormatStr: FormatStr].}
proc con*(a: PRope, b: string): PRope {.deprecated: [PRope: Rope].}
proc con*(a: string, b: PRope): PRope {.deprecated: [TRopeSeq: RopeSeq].}
proc con*(a: varargs[PRope]): PRope {.deprecated: [TRopesError: RopesError].}
proc app*(a: var PRope, b: PRope)
proc app*(a: var PRope, b: string)
proc prepend*(a: var PRope, b: PRope)
proc toRope*(s: string): PRope
proc toRope*(i: BiggestInt): PRope
proc ropeLen*(a: PRope): int
proc writeRopeIfNotEqual*(r: PRope, filename: string): bool
proc ropeToStr*(p: PRope): string
proc ropef*(frmt: TFormatStr, args: varargs[PRope]): PRope
proc appf*(c: var PRope, frmt: TFormatStr, args: varargs[PRope])
proc ropeEqualsFile*(r: PRope, f: string): bool
# returns true if the rope r is the same as the contents of file f
proc ropeInvariant*(r: PRope): bool
# exported for debugging
# implementation # implementation
var errorHandler*: proc(err: TRopesError, msg: string, useWarning = false) var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
# avoid dependency on msgs.nim # avoid dependency on msgs.nim
proc ropeLen(a: PRope): int = proc len*(a: Rope): int =
if a == nil: result = 0 if a == nil: result = 0
else: result = a.length else: result = a.length
proc newRope*(data: string = nil): PRope = proc newRope(data: string = nil): Rope =
new(result) new(result)
if data != nil: if data != nil:
result.length = len(data) result.length = len(data)
result.data = data result.data = data
proc newMutableRope*(capacity = 30): PRope = proc newMutableRope*(capacity = 30): Rope =
## creates a new rope that supports direct modifications of the rope's ## creates a new rope that supports direct modifications of the rope's
## 'data' and 'length' fields. ## 'data' and 'length' fields.
new(result) new(result)
result.data = newStringOfCap(capacity) result.data = newStringOfCap(capacity)
proc freezeMutableRope*(r: PRope) {.inline.} = proc freezeMutableRope*(r: Rope) {.inline.} =
r.length = r.data.len r.length = r.data.len
var var
cache: array[0..2048*2 - 1, PRope] cache: array[0..2048*2 - 1, Rope]
proc resetRopeCache* = proc resetRopeCache* =
for i in low(cache)..high(cache): for i in low(cache)..high(cache):
cache[i] = nil cache[i] = nil
proc ropeInvariant(r: PRope): bool = proc ropeInvariant(r: Rope): bool =
if r == nil: if r == nil:
result = true result = true
else: else:
@ -143,7 +129,7 @@ var gCacheTries* = 0
var gCacheMisses* = 0 var gCacheMisses* = 0
var gCacheIntTries* = 0 var gCacheIntTries* = 0
proc insertInCache(s: string): PRope = proc insertInCache(s: string): Rope =
inc gCacheTries inc gCacheTries
var h = hash(s) and high(cache) var h = hash(s) and high(cache)
result = cache[h] result = cache[h]
@ -152,14 +138,27 @@ proc insertInCache(s: string): PRope =
result = newRope(s) result = newRope(s)
cache[h] = result cache[h] = result
proc toRope(s: string): PRope = proc rope*(s: string): Rope =
if s.len == 0: if s.len == 0:
result = nil result = nil
else: else:
result = insertInCache(s) result = insertInCache(s)
assert(ropeInvariant(result)) assert(ropeInvariant(result))
proc ropeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) = proc rope*(i: BiggestInt): Rope =
inc gCacheIntTries
result = rope($i)
proc rope*(f: BiggestFloat): Rope =
result = rope($f)
# TODO Old names - change invokations to rope
proc toRope*(s: string): Rope {.deprecated.} =
result = rope(s)
proc toRope*(i: BiggestInt): Rope {.deprecated.} =
result = rope(i)
proc ropeSeqInsert(rs: var RopeSeq, r: Rope, at: Natural) =
var length = len(rs) var length = len(rs)
if at > length: if at > length:
setLen(rs, at + 1) setLen(rs, at + 1)
@ -169,7 +168,7 @@ proc ropeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) =
rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it
rs[at] = r rs[at] = r
proc newRecRopeToStr(result: var string, resultLen: var int, r: PRope) = proc newRecRopeToStr(result: var string, resultLen: var int, r: Rope) =
var stack = @[r] var stack = @[r]
while len(stack) > 0: while len(stack) > 0:
var it = pop(stack) var it = pop(stack)
@ -181,7 +180,33 @@ proc newRecRopeToStr(result: var string, resultLen: var int, r: PRope) =
inc(resultLen, it.length) inc(resultLen, it.length)
assert(resultLen <= len(result)) assert(resultLen <= len(result))
proc ropeToStr(p: PRope): string = proc `&`*(a, b: Rope): Rope =
if a == nil:
result = b
elif b == nil:
result = a
else:
result = newRope()
result.length = a.length + b.length
result.left = a
result.right = b
proc `&`*(a: Rope, b: string): Rope =
result = a & rope(b)
proc `&`*(a: string, b: Rope): Rope =
result = rope(a) & b
proc `&`*(a: openArray[Rope]): Rope =
for i in countup(0, high(a)): result = result & a[i]
proc add*(a: var Rope, b: Rope) =
a = a & b
proc add*(a: var Rope, b: string) =
a = a & b
proc `$`*(p: Rope): string =
if p == nil: if p == nil:
result = "" result = ""
else: else:
@ -189,34 +214,24 @@ proc ropeToStr(p: PRope): string =
var resultLen = 0 var resultLen = 0
newRecRopeToStr(result, resultLen, p) newRecRopeToStr(result, resultLen, p)
proc con(a, b: PRope): PRope = # TODO Old names - change invokations to `&`
if a == nil: result = b proc con*(a, b: Rope): Rope {.deprecated.} = a & b
elif b == nil: result = a proc con*(a: Rope, b: string): Rope {.deprecated.} = a & b
else: proc con*(a: string, b: Rope): Rope {.deprecated.} = a & b
result = newRope() proc con*(a: varargs[Rope]): Rope {.deprecated.} = `&`(a)
result.length = a.length + b.length
result.left = a
result.right = b
proc con(a: PRope, b: string): PRope = result = con(a, toRope(b)) proc ropeConcat*(a: varargs[Rope]): Rope =
proc con(a: string, b: PRope): PRope = result = con(toRope(a), b)
proc con(a: varargs[PRope]): PRope =
for i in countup(0, high(a)): result = con(result, a[i])
proc ropeConcat*(a: varargs[PRope]): PRope =
# not overloaded version of concat to speed-up `rfmt` a little bit # not overloaded version of concat to speed-up `rfmt` a little bit
for i in countup(0, high(a)): result = con(result, a[i]) for i in countup(0, high(a)): result = con(result, a[i])
proc toRope(i: BiggestInt): PRope = # TODO Old names - change invokations to add
inc gCacheIntTries proc app*(a: var Rope, b: Rope) {.deprecated.} = add(a, b)
result = toRope($i) proc app*(a: var Rope, b: string) {.deprecated.} = add(a, b)
proc app(a: var PRope, b: PRope) = a = con(a, b) proc prepend*(a: var Rope, b: Rope) = a = b & a
proc app(a: var PRope, b: string) = a = con(a, b) proc prepend*(a: var Rope, b: string) = a = b & a
proc prepend(a: var PRope, b: PRope) = a = con(b, a)
proc writeRope*(f: File, c: PRope) = proc writeRope*(f: File, c: Rope) =
var stack = @[c] var stack = @[c]
while len(stack) > 0: while len(stack) > 0:
var it = pop(stack) var it = pop(stack)
@ -227,7 +242,7 @@ proc writeRope*(f: File, c: PRope) =
assert(it.data != nil) assert(it.data != nil)
write(f, it.data) write(f, it.data)
proc writeRope*(head: PRope, filename: string, useWarning = false) = proc writeRope*(head: Rope, filename: string, useWarning = false) =
var f: File var f: File
if open(f, filename, fmWrite): if open(f, filename, fmWrite):
if head != nil: writeRope(f, head) if head != nil: writeRope(f, head)
@ -239,38 +254,52 @@ var
rnl* = tnl.newRope rnl* = tnl.newRope
softRnl* = tnl.newRope softRnl* = tnl.newRope
proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = proc `%`*(frmt: TFormatStr, args: openArray[Rope]): Rope =
var i = 0 var i = 0
var length = len(frmt) var length = len(frmt)
result = nil result = nil
var num = 0 var num = 0
while i <= length - 1: while i < length:
if frmt[i] == '$': if frmt[i] == '$':
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '$': of '$':
app(result, "$") add(result, "$")
inc(i) inc(i)
of '#': of '#':
inc(i) inc(i)
app(result, args[num]) add(result, args[num])
inc(num) inc(num)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
while true: while true:
j = (j * 10) + ord(frmt[i]) - ord('0') j = j * 10 + ord(frmt[i]) - ord('0')
inc(i) inc(i)
if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if frmt[i] notin {'0'..'9'}: break
num = j num = j
if j > high(args) + 1: if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j)) errorHandler(rInvalidFormatStr, $(j))
else: else:
app(result, args[j - 1]) add(result, args[j-1])
of '{':
inc(i)
var j = 0
while frmt[i] in {'0'..'9'}:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
num = j
if frmt[i] == '}': inc(i)
else: errorHandler(rInvalidFormatStr, $(frmt[i]))
if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j))
else:
add(result, args[j-1])
of 'n': of 'n':
app(result, softRnl) add(result, softRnl)
inc i inc(i)
of 'N': of 'N':
app(result, rnl) add(result, rnl)
inc(i) inc(i)
else: else:
errorHandler(rInvalidFormatStr, $(frmt[i])) errorHandler(rInvalidFormatStr, $(frmt[i]))
@ -279,83 +308,74 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
if frmt[i] != '$': inc(i) if frmt[i] != '$': inc(i)
else: break else: break
if i - 1 >= start: if i - 1 >= start:
app(result, substr(frmt, start, i - 1)) add(result, substr(frmt, start, i - 1))
assert(ropeInvariant(result)) assert(ropeInvariant(result))
proc addf*(c: var Rope, frmt: TFormatStr, args: openArray[Rope]) =
add(c, frmt % args)
# TODO Compatibility names
proc ropef*(frmt: TFormatStr, args: varargs[Rope]): Rope {.deprecated.} =
result = frmt % args
proc appf*(c: var Rope, frmt: TFormatStr, args: varargs[Rope]) {.deprecated.} =
addf(c, frmt, args)
when true: when true:
template `~`*(r: string): PRope = r.ropef template `~`*(r: string): Rope = r.ropef
else: else:
{.push stack_trace: off, line_trace: off.} {.push stack_trace: off, line_trace: off.}
proc `~`*(r: static[string]): PRope = proc `~`*(r: static[string]): Rope =
# this is the new optimized "to rope" operator # this is the new optimized "to rope" operator
# the mnemonic is that `~` looks a bit like a rope :) # the mnemonic is that `~` looks a bit like a rope :)
var r {.global.} = r.ropef var r {.global.} = r.ropef
return r return r
{.pop.} {.pop.}
proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) =
app(c, ropef(frmt, args))
const const
bufSize = 1024 # 1 KB is reasonable bufSize = 1024 # 1 KB is reasonable
proc auxRopeEqualsFile(r: PRope, bin: var File, buf: pointer): bool = proc auxEqualsFile(r: Rope, f: File, buf: var array[bufSize, char],
bpos, blen: var int): bool =
if r.data != nil: if r.data != nil:
if r.length > bufSize: var dpos = 0
errorHandler(rTokenTooLong, r.data) let dlen = r.data.len
return while dpos < dlen:
var readBytes = readBuffer(bin, buf, r.length) if bpos == blen:
result = readBytes == r.length and # Read more data
equalMem(buf, addr(r.data[0]), r.length) # BUGFIX 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, dlen - dpos)
if not equalMem(addr(buf[bpos]), addr(r.data[dpos]), n):
result = false
return
dpos += n
bpos += n
result = true
else: else:
result = auxRopeEqualsFile(r.left, bin, buf) result = auxEqualsFile(r.left, f, buf, bpos, blen) and
if result: result = auxRopeEqualsFile(r.right, bin, buf) auxEqualsFile(r.right, f, buf, bpos, blen)
proc ropeEqualsFile(r: PRope, f: string): bool = proc equalsFile*(r: Rope, f: File): bool =
var bin: File var
result = open(bin, f) buf: array[bufSize, char]
if not result: bpos = bufSize
return # not equal if file does not exist blen = bufSize
var buf = alloc(bufSize) result = auxEqualsFile(r, f, buf, bpos, blen) and
result = auxRopeEqualsFile(r, bin, buf) readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
proc equalsFile*(r: Rope, filename: string): bool =
var f: File
result = open(f, filename)
if result: if result:
result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file? result = equalsFile(r, f)
dealloc(buf) close(f)
close(bin)
proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 = proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
if r.data != nil:
result = startVal
for i in countup(0, len(r.data) - 1):
result = updateCrc32(r.data[i], result)
else:
result = crcFromRopeAux(r.left, startVal)
result = crcFromRopeAux(r.right, result)
proc newCrcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
# XXX profiling shows this is actually expensive
var stack: TRopeSeq = @[r]
result = startVal
while len(stack) > 0:
var it = pop(stack)
while it.data == nil:
add(stack, it.right)
it = it.left
assert(it.data != nil)
var i = 0
var L = len(it.data)
while i < L:
result = updateCrc32(it.data[i], result)
inc(i)
proc crcFromRope(r: PRope): TCrc32 =
result = newCrcFromRopeAux(r, InitCrc32)
proc writeRopeIfNotEqual(r: PRope, filename: string): bool =
# returns true if overwritten # returns true if overwritten
var c: TCrc32 if not equalsFile(r, filename):
c = crcFromFile(filename)
if c != crcFromRope(r):
writeRope(r, filename) writeRope(r, filename)
result = true result = true
else: else:

View file

@ -290,8 +290,8 @@ when false:
else: break else: break
if i - 1 >= start: if i - 1 >= start:
add(result, substr(frmt, start, i-1)) add(result, substr(frmt, start, i-1))
proc `%`*(frmt: string, args: openArray[Rope]): Rope {. proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
rtl, extern: "nroFormat".} = rtl, extern: "nroFormat".} =
## `%` substitution operator for ropes. Does not support the ``$identifier`` ## `%` substitution operator for ropes. Does not support the ``$identifier``
## nor ``${identifier}`` notations. ## nor ``${identifier}`` notations.
@ -299,23 +299,23 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
var length = len(frmt) var length = len(frmt)
result = nil result = nil
var num = 0 var num = 0
while i < length: while i < length:
if frmt[i] == '$': if frmt[i] == '$':
inc(i) inc(i)
case frmt[i] case frmt[i]
of '$': of '$':
add(result, "$") add(result, "$")
inc(i) inc(i)
of '#': of '#':
inc(i) inc(i)
add(result, args[num]) add(result, args[num])
inc(num) inc(num)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
while true: while true:
j = j * 10 + ord(frmt[i]) - ord('0') j = j * 10 + ord(frmt[i]) - ord('0')
inc(i) inc(i)
if frmt[i] notin {'0'..'9'}: break if frmt[i] notin {'0'..'9'}: break
add(result, args[j-1]) add(result, args[j-1])
of '{': of '{':
inc(i) inc(i)
@ -325,13 +325,14 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
inc(i) inc(i)
if frmt[i] == '}': inc(i) if frmt[i] == '}': inc(i)
else: raise newException(ValueError, "invalid format string") else: raise newException(ValueError, "invalid format string")
add(result, args[j-1]) add(result, args[j-1])
else: raise newException(ValueError, "invalid format string") else: raise newException(ValueError, "invalid format string")
var start = i var start = i
while i < length: while i < length:
if frmt[i] != '$': inc(i) if frmt[i] != '$': inc(i)
else: break else: break
if i - 1 >= start: if i - 1 >= start:
add(result, substr(frmt, start, i - 1)) add(result, substr(frmt, start, i - 1))
proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {.