diff options
-rwxr-xr-x | compiler/ecmasgen.nim | 44 | ||||
-rwxr-xr-x | compiler/ropes.nim | 782 | ||||
-rw-r--r-- | lib/pure/collections/sequtils.nim | 164 | ||||
-rwxr-xr-x | lib/pure/collections/tables.nim | 46 | ||||
-rwxr-xr-x | lib/pure/json.nim | 9 | ||||
-rwxr-xr-x | lib/system.nim | 6 | ||||
-rwxr-xr-x | lib/wrappers/lua/lua.nim | 39 | ||||
-rwxr-xr-x | tests/run/ttables.nim | 39 |
8 files changed, 565 insertions, 564 deletions
diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim index 25d147ebc..8899c904e 100755 --- a/compiler/ecmasgen.nim +++ b/compiler/ecmasgen.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -964,24 +964,45 @@ proc genDeref(p: var TProc, n: PNode, r: var TCompRes) = if a.kind != etyBaseIndex: InternalError(n.info, "genDeref") r.res = ropef("$1[$2]", [a.com, a.res]) +proc genArg(p: var TProc, n: PNode, r: var TCompRes) = + var a: TCompRes + gen(p, n, a) + if a.kind == etyBaseIndex: + app(r.res, a.com) + app(r.res, ", ") + app(r.res, a.res) + else: + app(r.res, mergeExpr(a)) + proc genArgs(p: var TProc, n: PNode, r: var TCompRes) = app(r.res, "(") for i in countup(1, sonsLen(n) - 1): if i > 1: app(r.res, ", ") - var a: TCompRes - gen(p, n.sons[i], a) - if a.kind == etyBaseIndex: - app(r.res, a.com) - app(r.res, ", ") - app(r.res, a.res) - else: - app(r.res, mergeExpr(a)) + genArg(p, n.sons[i], r) app(r.res, ")") proc genCall(p: var TProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) genArgs(p, n, r) +proc genInfixCall(p: var TProc, n: PNode, r: var TCompRes) = + gen(p, n.sons[1], r) + if r.kind == etyBaseIndex: + if r.com == nil: + GlobalError(n.info, "cannot invoke with infix syntax") + r.res = ropef("$1[0]", [r.res, r.com]) + r.com = nil + app(r.res, ".") + var op: TCompRes + gen(p, n.sons[0], op) + app(r.res, mergeExpr(op)) + + app(r.res, "(") + for i in countup(2, sonsLen(n) - 1): + if i > 2: app(r.res, ", ") + genArg(p, n.sons[i], r) + app(r.res, ")") + proc genEcho(p: var TProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") app(r.res, "rawEcho") @@ -1513,9 +1534,12 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = else: r.res = toRope(f.ToStrMaxPrecision) of nkBlockExpr: genBlock(p, n, r) of nkIfExpr: genIfExpr(p, n, r) - of nkCallKinds: + of nkCallKinds: if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): genMagic(p, n, r) + elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and + n.len >= 2: + genInfixCall(p, n, r) else: genCall(p, n, r) of nkCurly: genSetConstr(p, n, r) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 95dce8278..50c89e4d9 100755 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -58,538 +58,266 @@ import msgs, strutils, platform, hashes, crc, options -const - PayloadSize = 64_000 # dummy size for range checking - type TFormatStr* = string # later we may change it to CString for better # performance of the code generator (assignments # copy the format strings # though it is not necessary) - -when true: - # old version: - type - PRope* = ref TRope - TRope*{.acyclic.} = object of TObject # the empty rope is represented - # by nil to safe space - left*, right*: PRope - length*: int - data*: string # != nil if a leaf - - TRopeSeq* = seq[PRope] - - proc con*(a, b: PRope): PRope - proc con*(a: PRope, b: string): PRope - proc con*(a: string, b: PRope): PRope - proc con*(a: varargs[PRope]): PRope - 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 - - proc ropeLen(a: PRope): int = - if a == nil: result = 0 - else: result = a.length - - proc newRope(data: string = nil): PRope = - new(result) - if data != nil: - result.length = len(data) - result.data = data - - proc newMutableRope*(capacity = 30): PRope = - ## creates a new rope that supports direct modifications of the rope's - ## 'data' and 'length' fields. - new(result) - result.data = newStringOfCap(capacity) - - proc freezeMutableRope*(r: PRope) {.inline.} = - r.length = r.data.len - - var - cache: array[0..2048*2 -1, PRope] - - proc RopeInvariant(r: PRope): bool = - if r == nil: - result = true - else: - result = true # - # if r.data <> snil then - # result := true - # else begin - # result := (r.left <> nil) and (r.right <> nil); - # if result then result := ropeInvariant(r.left); - # if result then result := ropeInvariant(r.right); - # end - - proc insertInCache(s: string): PRope = - var h = hash(s) and high(cache) - result = cache[h] - if isNil(result) or result.data != s: - result = newRope(s) - cache[h] = result - - proc toRope(s: string): PRope = - if s.len == 0: - result = nil - else: - result = insertInCache(s) - assert(RopeInvariant(result)) - - proc RopeSeqInsert(rs: var TRopeSeq, r: PRope, 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: PRope) = - 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 ropeToStr(p: PRope): string = - if p == nil: - result = "" - else: - result = newString(p.length) - var resultLen = 0 - newRecRopeToStr(result, resultLen, p) - - proc con(a, b: PRope): PRope = - 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 con(a: PRope, b: string): PRope = result = con(a, toRope(b)) - 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 toRope(i: BiggestInt): PRope = result = toRope($i) - - proc app(a: var PRope, b: PRope) = a = con(a, b) - proc app(a: var PRope, b: string) = a = con(a, b) - proc prepend(a: var PRope, b: PRope) = a = con(b, a) - - proc writeRope*(f: TFile, c: PRope) = - 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) - - proc WriteRope*(head: PRope, filename: string, useWarning = false) = - var f: tfile - if open(f, filename, fmWrite): - if head != nil: WriteRope(f, head) - close(f) - else: - rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, - filename) - - proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = - var i = 0 - var length = len(frmt) + PRope* = ref TRope + TRope*{.acyclic.} = object of TObject # the empty rope is represented + # by nil to safe space + left*, right*: PRope + length*: int + data*: string # != nil if a leaf + + TRopeSeq* = seq[PRope] + +proc con*(a, b: PRope): PRope +proc con*(a: PRope, b: string): PRope +proc con*(a: string, b: PRope): PRope +proc con*(a: varargs[PRope]): PRope +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 + +proc ropeLen(a: PRope): int = + if a == nil: result = 0 + else: result = a.length + +proc newRope(data: string = nil): PRope = + new(result) + if data != nil: + result.length = len(data) + result.data = data + +proc newMutableRope*(capacity = 30): PRope = + ## creates a new rope that supports direct modifications of the rope's + ## 'data' and 'length' fields. + new(result) + result.data = newStringOfCap(capacity) + +proc freezeMutableRope*(r: PRope) {.inline.} = + r.length = r.data.len + +var + cache: array[0..2048*2 -1, PRope] + +proc RopeInvariant(r: PRope): bool = + if r == nil: + result = true + else: + result = true # + # if r.data <> snil then + # result := true + # else begin + # result := (r.left <> nil) and (r.right <> nil); + # if result then result := ropeInvariant(r.left); + # if result then result := ropeInvariant(r.right); + # end + +proc insertInCache(s: string): PRope = + var h = hash(s) and high(cache) + result = cache[h] + if isNil(result) or result.data != s: + result = newRope(s) + cache[h] = result + +proc toRope(s: string): PRope = + if s.len == 0: result = nil - var num = 0 - while i <= length - 1: - if frmt[i] == '$': - inc(i) # skip '$' - case frmt[i] - of '$': - app(result, "$") - inc(i) - of '#': - inc(i) - app(result, args[num]) - inc(num) - of '0'..'9': - var j = 0 - while true: - j = (j * 10) + Ord(frmt[i]) - ord('0') - inc(i) - if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break - num = j - if j > high(args) + 1: - internalError("ropes: invalid format string $" & $(j)) - else: - app(result, args[j - 1]) - of 'n': - if optLineDir notin gOptions: app(result, tnl) - inc i - of 'N': - app(result, tnl) - inc(i) - else: InternalError("ropes: invalid format string $" & frmt[i]) - var start = i - while i < length: - if frmt[i] != '$': inc(i) - else: break - if i - 1 >= start: - app(result, substr(frmt, start, i - 1)) - assert(RopeInvariant(result)) - - proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = - app(c, ropef(frmt, args)) - - const - bufSize = 1024 # 1 KB is reasonable - - proc auxRopeEqualsFile(r: PRope, bin: var tfile, buf: Pointer): bool = - if r.data != nil: - if r.length > bufSize: - internalError("ropes: token too long") - return - var readBytes = readBuffer(bin, buf, r.length) - result = readBytes == r.length and - equalMem(buf, addr(r.data[0]), r.length) # BUGFIX - else: - result = auxRopeEqualsFile(r.left, bin, buf) - if result: result = auxRopeEqualsFile(r.right, bin, buf) - - proc RopeEqualsFile(r: PRope, f: string): bool = - var bin: tfile - result = open(bin, f) - if not result: - return # not equal if file does not exist - var buf = alloc(BufSize) - result = auxRopeEqualsFile(r, bin, buf) - if result: - result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file? - dealloc(buf) - close(bin) - - proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 = - 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) + else: + result = insertInCache(s) + assert(RopeInvariant(result)) + +proc RopeSeqInsert(rs: var TRopeSeq, r: PRope, 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: PRope) = + 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 ropeToStr(p: PRope): string = + if p == nil: + result = "" + else: + result = newString(p.length) + var resultLen = 0 + newRecRopeToStr(result, resultLen, p) + +proc con(a, b: PRope): PRope = + 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 con(a: PRope, b: string): PRope = result = con(a, toRope(b)) +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 toRope(i: BiggestInt): PRope = result = toRope($i) + +proc app(a: var PRope, b: PRope) = a = con(a, b) +proc app(a: var PRope, b: string) = a = con(a, b) +proc prepend(a: var PRope, b: PRope) = a = con(b, a) + +proc writeRope*(f: TFile, c: PRope) = + 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) + +proc WriteRope*(head: PRope, filename: string, useWarning = false) = + var f: tfile + if open(f, filename, fmWrite): + if head != nil: WriteRope(f, head) + close(f) + else: + rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, + filename) + +proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = + var i = 0 + var length = len(frmt) + result = nil + var num = 0 + while i <= length - 1: + if frmt[i] == '$': + inc(i) # skip '$' + case frmt[i] + of '$': + app(result, "$") inc(i) - - proc crcFromRope(r: PRope): TCrc32 = - result = newCrcFromRopeAux(r, initCrc32) - - proc writeRopeIfNotEqual(r: PRope, filename: string): bool = - # returns true if overwritten - var c: TCrc32 - c = crcFromFile(filename) - if c != crcFromRope(r): - writeRope(r, filename) - result = true - else: - result = false - -else: - # optimized version with 'unsafeNew': - - type - PRope* = ref TRope - TRope*{.acyclic.} = object of TObject # the empty rope is represented - # by nil to safe space - left, right: PRope - L: int # < 0 if a leaf - d: array [0..PayloadSize, char] # != nil if a leaf - - TRopeSeq* = seq[PRope] - - proc con*(a, b: PRope): PRope - proc con*(a: PRope, b: string): PRope - proc con*(a: string, b: PRope): PRope - proc con*(a: varargs[PRope]): PRope - 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 - - proc ropeLen(a: PRope): int = - if a == nil: result = 0 - else: result = a.L.abs - - proc newRope(data: string = nil): PRope = - if data != nil: - unsafeNew(result, sizeof(TRope)-PayloadSize+len(data)+1) - result.L = -len(data) - # copy including '\0': - copyMem(addr result.d, cstring(data), len(data)) - else: - unsafeNew(result, sizeof(TRope)-PayloadSize+1) - - proc eqContent(r: PRope, s: string): bool = - assert r.L < 0 - if -r.L == s.len: - result = equalMem(addr(r.d), cstring(s), s.len) - - when false: - proc newMutableRope*(capacity = 30): PRope = - ## creates a new rope that supports direct modifications of the rope's - ## 'data' and 'length' fields. - new(result) - result.data = newStringOfCap(capacity) - - proc freezeMutableRope*(r: PRope) {.inline.} = - r.length = r.data.len - - var - cache: array[0..2048*2 -1, PRope] - - proc RopeInvariant(r: PRope): bool = - if r == nil: - result = true - else: - result = true # - # if r.data <> snil then - # result := true - # else begin - # result := (r.left <> nil) and (r.right <> nil); - # if result then result := ropeInvariant(r.left); - # if result then result := ropeInvariant(r.right); - # end - - proc insertInCache(s: string): PRope = - var h = hash(s) and high(cache) - result = cache[h] - if isNil(result) or not eqContent(result, s): - result = newRope(s) - cache[h] = result - - proc toRope(s: string): PRope = - if s.len == 0: - result = nil - else: - result = insertInCache(s) - assert(RopeInvariant(result)) - - proc RopeSeqInsert(rs: var TRopeSeq, r: PRope, 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: PRope) = - var stack = @[r] - while len(stack) > 0: - var it = pop(stack) - while it.L >= 0: - add(stack, it.right) - it = it.left - assert(it.L < 0) - CopyMem(addr(result[resultLen]), addr(it.d[0]), -it.L) - Inc(resultLen, -it.L) - assert(resultLen <= len(result)) - - proc ropeToStr(p: PRope): string = - if p == nil: - result = "" - else: - result = newString(p.L.abs) - var resultLen = 0 - newRecRopeToStr(result, resultLen, p) - - proc con(a, b: PRope): PRope = - if a == nil: result = b - elif b == nil: result = a - else: - result = newRope() - result.L = a.L.abs + b.L.abs - result.left = a - result.right = b - - proc con(a: PRope, b: string): PRope = result = con(a, toRope(b)) - 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 toRope(i: BiggestInt): PRope = result = toRope($i) - - proc app(a: var PRope, b: PRope) = a = con(a, b) - proc app(a: var PRope, b: string) = a = con(a, b) - proc prepend(a: var PRope, b: PRope) = a = con(b, a) - - proc writeRope*(f: TFile, c: PRope) = - var stack = @[c] - while len(stack) > 0: - var it = pop(stack) - while it.L >= 0: - add(stack, it.right) - it = it.left - assert(it != nil) - assert(it.L < 0) - let L = -it.L - if writeBuffer(f, cstring(it.d), L) != L: - InternalError("cannot store file") - - proc WriteRope*(head: PRope, filename: string, useWarning = false) = - var f: tfile - if open(f, filename, fmWrite): - if head != nil: WriteRope(f, head) - close(f) - else: - rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, - filename) - - proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = - var i = 0 - var length = len(frmt) - result = nil - var num = 0 - while i <= length - 1: - if frmt[i] == '$': - inc(i) # skip '$' - case frmt[i] - of '$': - app(result, "$") - inc(i) - of '#': - inc(i) - app(result, args[num]) - inc(num) - of '0'..'9': - var j = 0 - while true: - j = (j * 10) + Ord(frmt[i]) - ord('0') - inc(i) - if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break - num = j - if j > high(args) + 1: - internalError("ropes: invalid format string $" & $(j)) - else: - app(result, args[j - 1]) - of 'n': - if optLineDir notin gOptions: app(result, tnl) - inc i - of 'N': - app(result, tnl) + of '#': + inc(i) + app(result, args[num]) + inc(num) + of '0'..'9': + var j = 0 + while true: + j = (j * 10) + Ord(frmt[i]) - ord('0') inc(i) - else: InternalError("ropes: invalid format string $" & frmt[i]) - var start = i - while i < length: - if frmt[i] != '$': inc(i) - else: break - if i - 1 >= start: - app(result, substr(frmt, start, i - 1)) - assert(RopeInvariant(result)) - - proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = - app(c, ropef(frmt, args)) - - const - bufSize = 1024 # 1 KB is reasonable - - proc auxRopeEqualsFile(r: PRope, bin: var tfile, buf: Pointer): bool = - if r.L < 0: - if -r.L > bufSize: - internalError("ropes: token too long") - return - var readBytes = readBuffer(bin, buf, -r.L) - result = readBytes == -r.L and - equalMem(buf, addr(r.d[0]), readBytes) - else: - result = auxRopeEqualsFile(r.left, bin, buf) - if result: result = auxRopeEqualsFile(r.right, bin, buf) - - proc RopeEqualsFile(r: PRope, f: string): bool = - var bin: tfile - result = open(bin, f) - if not result: - return # not equal if file does not exist - var buf = alloc(BufSize) - result = auxRopeEqualsFile(r, bin, buf) - if result: - result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file? - dealloc(buf) - close(bin) - - 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.L >= 0: - add(stack, it.right) - it = it.left - assert(it.L < 0) - var i = 0 - var L = -it.L - while i < L: - result = updateCrc32(it.d[i], result) + if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break + num = j + if j > high(args) + 1: + internalError("ropes: invalid format string $" & $(j)) + else: + app(result, args[j - 1]) + of 'n': + if optLineDir notin gOptions: app(result, tnl) + inc i + of 'N': + app(result, tnl) inc(i) - - proc crcFromRope(r: PRope): TCrc32 = - result = newCrcFromRopeAux(r, initCrc32) - - proc writeRopeIfNotEqual(r: PRope, filename: string): bool = - # returns true if overwritten - var c: TCrc32 - c = crcFromFile(filename) - if c != crcFromRope(r): - writeRope(r, filename) - result = true - else: - result = false + else: InternalError("ropes: invalid format string $" & frmt[i]) + var start = i + while i < length: + if frmt[i] != '$': inc(i) + else: break + if i - 1 >= start: + app(result, substr(frmt, start, i - 1)) + assert(RopeInvariant(result)) + +proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = + app(c, ropef(frmt, args)) + +const + bufSize = 1024 # 1 KB is reasonable + +proc auxRopeEqualsFile(r: PRope, bin: var tfile, buf: Pointer): bool = + if r.data != nil: + if r.length > bufSize: + internalError("ropes: token too long") + return + var readBytes = readBuffer(bin, buf, r.length) + result = readBytes == r.length and + equalMem(buf, addr(r.data[0]), r.length) # BUGFIX + else: + result = auxRopeEqualsFile(r.left, bin, buf) + if result: result = auxRopeEqualsFile(r.right, bin, buf) + +proc RopeEqualsFile(r: PRope, f: string): bool = + var bin: tfile + result = open(bin, f) + if not result: + return # not equal if file does not exist + var buf = alloc(BufSize) + result = auxRopeEqualsFile(r, bin, buf) + if result: + result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file? + dealloc(buf) + close(bin) + +proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 = + 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 + var c: TCrc32 + c = crcFromFile(filename) + if c != crcFromRope(r): + writeRope(r, filename) + result = true + else: + result = false diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 0fda5700e..e4ea22830 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -9,8 +9,16 @@ ## :Author: Alex Mitchell ## -## This module implements operations for the built-in `seq`:idx: type -## which were inspired by functional programming languages. +## This module implements operations for the built-in `seq`:idx: type which +## were inspired by functional programming languages. If you are looking for +## the typical `map` function which applies a function to every element in a +## sequence, it already exists as the `each` proc in the `system +## <system.html>`_ module in both mutable and immutable styles. +## +## Also, for functional style programming you may want to pass `anonymous procs +## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing. +## Anonymous procs can use `the special do notation <manual.html#do-notation>`_ +## which is more convenient in certain situations. ## ## **Note**: This interface will change as soon as the compiler supports ## closures and proper coroutines. @@ -19,7 +27,17 @@ when not defined(nimhygiene): {.pragma: dirty.} proc concat*[T](seqs: varargs[seq[T]]): seq[T] = - ## Takes several sequences' items and returns them inside of one sequence. + ## Takes several sequences' items and returns them inside a new sequence. + ## + ## Example: + ## + ## .. code-block:: nimrod + ## let + ## s1 = @[1, 2, 3] + ## s2 = @[4, 5] + ## s3 = @[6, 7] + ## total = concat(s1, s2, s3) + ## assert total == @[1, 2, 3, 4, 5, 6, 7] var L = 0 for seqitm in items(seqs): inc(L, len(seqitm)) newSeq(result, L) @@ -30,14 +48,42 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = inc(i) proc distnct*[T](seq1: seq[T]): seq[T] = - ## Removes duplicates from a sequence and returns it. + ## Returns a new sequence without duplicates. + ## + ## This proc is `misspelled` on purpose to avoid a clash with the keyword + ## ``distinct`` used to `define a derived type incompatible with its base + ## type <manual.html#distinct-type>`_. Example: + ## + ## .. code-block:: nimrod + ## let + ## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] + ## dup2 = @["a", "a", "c", "d", "d"] + ## unique1 = distnct(dup1) + ## unique2 = distnct(dup2) + ## assert unique1 == @[1, 3, 4, 2, 8] + ## assert unique2 == @["a", "c", "d"] result = @[] for itm in items(seq1): if not result.contains(itm): result.add(itm) proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = - ## Combines two sequences. If one sequence is too short, - ## the remaining items in the longer sequence are discarded. + ## Returns a new sequence with a combination of the two input sequences. + ## + ## For convenience you can access the returned tuples through the named + ## fields `a` and `b`. If one sequence is shorter, the remaining items in the + ## longer sequence are discarded. Example: + ## + ## .. code-block:: nimrod + ## let + ## short = @[1, 2, 3] + ## long = @[6, 5, 4, 3, 2, 1] + ## words = @["one", "two", "three"] + ## zip1 = zip(short, long) + ## zip2 = zip(short, words) + ## assert zip1 == @[(1, 6), (2, 5), (3, 4)] + ## assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] + ## assert zip1[2].b == 4 + ## assert zip2[2].b == "three" var m = min(seq1.len, seq2.len) newSeq(result, m) for i in 0 .. m-1: result[i] = (seq1[i], seq2[i]) @@ -45,18 +91,44 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T = ## Iterates through a sequence and yields every item that fulfills the ## predicate. + ## + ## Example: + ## + ## .. code-block:: nimrod + ## let numbers = @[1, 4, 5, 8, 9, 7, 4] + ## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): + ## echo($n) + ## # echoes 4, 8, 4 in separate lines for i in countup(0, len(seq1) -1): var item = seq1[i] if pred(item): yield seq1[i] proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] = - ## Returns all items in a sequence that fulfilled the predicate. + ## Returns a new sequence with all the items that fulfilled the predicate. + ## + ## Example: + ## + ## .. code-block:: nimrod + ## let + ## colors = @["red", "yellow", "black"] + ## f1 = filter(colors, proc(x: string): bool = x.len < 6) + ## f2 = filter(colors) do (x: string) -> bool : x.len > 5 + ## assert f1 == @["red", "black"] + ## assert f2 == @["yellow"] accumulateResult(filter(seq1, pred)) template filterIt*(seq1, pred: expr): expr {.immediate, dirty.} = - ## Finds a specific item in a sequence as long as the - ## predicate returns true. The predicate needs to be an expression - ## containing ``it``: ``filterIt("abcxyz", it == 'x')``. + ## Returns a new sequence with all the items that fulfilled the predicate. + ## + ## Unlike the `proc` version, the predicate needs to be an expression using + ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``. + ## Example: + ## + ## .. code-block:: nimrod + ## let + ## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] + ## acceptable = filterIt(temperatures, it < 50 and it > -10) + ## assert acceptable == @[-2.0, 24.5, 44.31] block: var result: type(seq1) = @[] for it in items(seq1): @@ -65,7 +137,79 @@ template filterIt*(seq1, pred: expr): expr {.immediate, dirty.} = template toSeq*(iter: expr): expr {.immediate.} = ## Transforms any iterator into a sequence. + ## + ## Example: + ## + ## .. code-block:: nimrod + ## let + ## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + ## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: + ## if x mod 2 == 1: + ## result = true) + ## assert odd_numbers == @[1, 3, 5, 7, 9] + ## var result {.gensym.}: seq[type(iter)] = @[] for x in iter: add(result, x) result +when isMainModule: + import strutils + proc toStr(x: int): string {.procvar.} = $x + # concat test + let + s1 = @[1, 2, 3] + s2 = @[4, 5] + s3 = @[6, 7] + total = concat(s1, s2, s3) + assert total == @[1, 2, 3, 4, 5, 6, 7] + + # duplicates test + let + dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] + dup2 = @["a", "a", "c", "d", "d"] + unique1 = distnct(dup1) + unique2 = distnct(dup2) + assert unique1 == @[1, 3, 4, 2, 8] + assert unique2 == @["a", "c", "d"] + + # zip test + let + short = @[1, 2, 3] + long = @[6, 5, 4, 3, 2, 1] + words = @["one", "two", "three"] + zip1 = zip(short, long) + zip2 = zip(short, words) + assert zip1 == @[(1, 6), (2, 5), (3, 4)] + assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] + assert zip1[2].b == 4 + assert zip2[2].b == "three" + + # filter proc test + let + colors = @["red", "yellow", "black"] + f1 = filter(colors, proc(x: string): bool = x.len < 6) + f2 = filter(colors) do (x: string) -> bool : x.len > 5 + assert f1 == @["red", "black"] + assert f2 == @["yellow"] + + # filter iterator test + let numbers = @[1, 4, 5, 8, 9, 7, 4] + for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): + echo($n) + # echoes 4, 8, 4 in separate lines + + # filterIt test + let + temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] + acceptable = filterIt(temperatures, it < 50 and it > -10) + assert acceptable == @[-2.0, 24.5, 44.31] + + # toSeq test + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + assert odd_numbers == @[1, 3, 5, 7, 9] + + echo "Finished doc tests" diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index fc394d1f8..4290af08a 100755 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -308,6 +308,52 @@ proc `$`*[A, B](t: TOrderedTable[A, B]): string = ## The `$` operator for ordered hash tables. dollarImpl() +proc sort*[A, B](t: var TOrderedTable[A, B], + cmp: proc (x,y: tuple[key: A, val: B]): int) = + ## sorts `t` according to `cmp`. This modifies the internal list + ## that kept the insertion order, so insertion order is lost after this + ## call but key lookup and insertions remain possible after `sort` (in + ## contrast to the `sort` for count tables). + var list = t.first + var + p, q, e, tail, oldhead: int + nmerges, psize, qsize, i: int + if t.counter == 0: return + var insize = 1 + while true: + p = list; oldhead = list + list = -1; tail = -1; nmerges = 0 + while p >= 0: + inc(nmerges) + q = p + psize = 0 + i = 0 + while i < insize: + inc(psize) + q = t.data[q].next + if q < 0: break + inc(i) + qsize = insize + while psize > 0 or (qsize > 0 and q >= 0): + if psize == 0: + e = q; q = t.data[q].next; dec(qsize) + elif qsize == 0 or q < 0: + e = p; p = t.data[p].next; dec(psize) + elif cmp((t.data[p].key, t.data[p].val), + (t.data[q].key, t.data[q].val)) <= 0: + e = p; p = t.data[p].next; dec(psize) + else: + e = q; q = t.data[q].next; dec(qsize) + if tail >= 0: t.data[tail].next = e + else: list = e + tail = e + p = q + t.data[tail].next = -1 + if nmerges <= 1: break + insize = insize * 2 + t.first = list + t.last = tail + # ------------------------------ count tables ------------------------------- type diff --git a/lib/pure/json.nim b/lib/pure/json.nim index cee135f08..cca1e4a70 100755 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -541,6 +541,11 @@ proc newJString*(s: String): PJsonNode = result.kind = JString result.str = s +proc newJStringMove(s: String): PJsonNode = + new(result) + result.kind = JString + shallowCopy(result.str, s) + proc newJInt*(n: biggestInt): PJsonNode = ## Creates a new `JInt PJsonNode`. new(result) @@ -809,7 +814,9 @@ proc parseJson(p: var TJsonParser): PJsonNode = ## Parses JSON from a JSON Parser `p`. case p.tok of tkString: - result = newJString(p.a) + # we capture 'p.a' here, so we need to give it a fresh buffer afterwards: + result = newJStringMove(p.a) + p.a = "" discard getTok(p) of tkInt: result = newJInt(parseBiggestInt(p.a)) diff --git a/lib/system.nim b/lib/system.nim index 3d17b1d6d..26e5ac228 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1461,14 +1461,14 @@ proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = setLen(s, L) proc each*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] = - ## The well-known ``map`` operation from functional programming. Applies + ## The well-known `map`:idx: operation from functional programming. Applies ## `op` to every item in `data` and returns the result as a sequence. newSeq(result, data.len) for i in 0..data.len-1: result[i] = op(data[i]) proc each*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) = - ## The well-known ``map`` operation from functional programming. Applies - ## `op` to every item in `data`. + ## The well-known `map`:idx: operation from functional programming. Applies + ## `op` to every item in `data` modifying it directly. for i in 0..data.len-1: op(data[i]) iterator fields*[T: tuple](x: T): TObject {. diff --git a/lib/wrappers/lua/lua.nim b/lib/wrappers/lua/lua.nim index 000e09993..0346c4285 100755 --- a/lib/wrappers/lua/lua.nim +++ b/lib/wrappers/lua/lua.nim @@ -35,19 +35,32 @@ #** In french or in english # -when defined(MACOSX): - const - NAME* = "liblua(|5.2|5.1|5.0).dylib" - LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib" -elif defined(UNIX): - const - NAME* = "liblua(|5.2|5.1|5.0).so(|.0)" - LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)" -else: - const - NAME* = "lua(|5.2|5.1|5.0).dll" - LIB_NAME* = "lua(|5.2|5.1|5.0).dll" - +when defined(useLuajit): + when defined(MACOSX): + const + NAME* = "libluajit.dylib" + LIB_NAME* = "libluajit.dylib" + elif defined(UNIX): + const + NAME* = "libluajit.so(|.0)" + LIB_NAME* = "libluajit.so(|.0)" + else: + const + NAME* = "luajit.dll" + LIB_NAME* = "luajit.dll" +else: + when defined(MACOSX): + const + NAME* = "liblua(|5.2|5.1|5.0).dylib" + LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib" + elif defined(UNIX): + const + NAME* = "liblua(|5.2|5.1|5.0).so(|.0)" + LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)" + else: + const + NAME* = "lua(|5.2|5.1|5.0).dll" + LIB_NAME* = "lua(|5.2|5.1|5.0).dll" const VERSION* = "Lua 5.1" diff --git a/tests/run/ttables.nim b/tests/run/ttables.nim index 3eb17a803..681ff5424 100755 --- a/tests/run/ttables.nim +++ b/tests/run/ttables.nim @@ -19,6 +19,25 @@ const "50": 344490, "60": 344491, "70": 344492, "80": 344497} + sorteddata = { + "---00": 346677844, + "0": 34404, + "1": 344004, + "10": 34484, + "11": 34474, + "12": 789, + "19": 34464, + "2": 344774, "20": 34454, + "3": 342244, "30": 34141244, + "34": 123456, + "4": 3412344, "40": 344114, + "5": 341232144, "50": 344490, + "6": 34214544, "60": 344491, + "7": 3434544, "70": 344492, + "8": 344544, "80": 344497, + "9": 34435644, + "90": 343} + block tableTest1: var t = initTable[tuple[x, y: int], string]() t[(0,0)] = "00" @@ -86,5 +105,25 @@ block countTableTest1: block SyntaxTest: var x = toTable[int, string]({:}) +proc orderedTableSortTest() = + var t = initOrderedTable[string, int](2) + for key, val in items(data): t[key] = val + for key, val in items(data): assert t[key] == val + t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key)) + var i = 0 + # `pairs` needs to yield in sorted order: + for key, val in pairs(t): + doAssert key == sorteddata[i][0] + doAssert val == sorteddata[i][1] + inc(i) + + # check that lookup still works: + for key, val in pairs(t): + doAssert val == t[key] + # check that insert still works: + t["newKeyHere"] = 80 + + +orderedTableSortTest() echo "true" |