diff options
59 files changed, 1124 insertions, 321 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4b52b1c92..8ae32492a 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -75,7 +75,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc = ga('send', 'pageview'); </script> - """ % [config["doc.googleAnalytics"]] + """ % [config.getOrDefault"doc.googleAnalytics"] else: result.analytics = "" diff --git a/compiler/importer.nim b/compiler/importer.nim index d619725db..c121059fd 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -121,7 +121,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = if s.kind != skEnumField: if s.kind notin ExportableSymKinds: internalError(s.info, "importAllSymbols: " & $s.kind) - if exceptSet.empty or s.name.id notin exceptSet: + if exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) s = nextIter(i, fromMod.tab) @@ -138,7 +138,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) = let s = a.sym if s.kind == skModule: importAllSymbolsExcept(c, s, exceptSet) - elif exceptSet.empty or s.name.id notin exceptSet: + elif exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) of nkExportExceptStmt: localError(n.info, errGenerated, "'export except' not implemented") diff --git a/compiler/main.nim b/compiler/main.nim index 3bb6fc343..2ee07a443 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -15,7 +15,7 @@ import wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, jsgen, json, nversion, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, - tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists + docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists from magicsys import systemModule, resetSysTypes diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 9b647e67d..c6c2ab058 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -48,7 +48,7 @@ proc addPackage(packages: StringTableRef, p: string) = let name = p.substr(0, x-1) if x < p.len: let version = p.substr(x+1) - if packages[name] <. version: + if packages.getOrDefault(name) <. version: packages[name] = version else: packages[name] = latest diff --git a/compiler/options.nim b/compiler/options.nim index adb340fea..98224a11d 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -86,7 +86,7 @@ type # please make sure we have under 32 options gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational IdeCmd* = enum - ideNone, ideSug, ideCon, ideDef, ideUse + ideNone, ideSug, ideCon, ideDef, ideUse, ideDus var gIdeCmd*: IdeCmd @@ -173,7 +173,7 @@ proc existsConfigVar*(key: string): bool = result = hasKey(gConfigVars, key) proc getConfigVar*(key: string): string = - result = gConfigVars[key] + result = gConfigVars.getOrDefault key proc setConfigVar*(key, val: string) = gConfigVars[key] = val @@ -421,6 +421,7 @@ proc parseIdeCmd*(s: string): IdeCmd = of "con": ideCon of "def": ideDef of "use": ideUse + of "dus": ideDus else: ideNone proc `$`*(c: IdeCmd): string = @@ -429,4 +430,5 @@ proc `$`*(c: IdeCmd): string = of ideCon: "con" of ideDef: "def" of ideUse: "use" + of ideDus: "dus" of ideNone: "none" diff --git a/compiler/rodread.nim b/compiler/rodread.nim index e4530c2cc..2a85c8975 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -372,7 +372,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = else: internalError(info, "decodeSym: no ident") #echo "decoding: {", ident.s - result = r.syms[id] + result = r.syms.getOrDefault(id) if result == nil: new(result) result.id = id @@ -491,7 +491,7 @@ proc processCompilerProcs(r: PRodReader, module: PSym) = inc(r.pos) var key = decodeVInt(r.s, r.pos) inc(r.pos) # #10 - var s = r.syms[key] + var s = r.syms.getOrDefault(key) if s == nil: s = newStub(r, w, key) s.owner = module @@ -737,7 +737,7 @@ proc getReader(moduleId: int): PRodReader = return nil proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = - result = r.syms[id] + result = r.syms.getOrDefault(id) if result == nil: # load the symbol: var d = iiTableGet(r.index.tab, id) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index ed21aa4ea..18d723315 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -309,6 +309,10 @@ proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} = findUsages(info, s) elif gIdeCmd == ideDef: findDefinition(info, s) + elif gIdeCmd == ideDus and s != nil: + if isTracked(info, s.name.s.len): + suggestResult(symToSuggest(s, isLocal=false, $ideDef)) + findUsages(info, s) proc markUsed(info: TLineInfo; s: PSym) = incl(s.flags, sfUsed) @@ -366,7 +370,7 @@ proc suggestExpr*(c: PContext, node: PNode) = suggestCall(c, a, n, outputs) dec(c.compilesContextId) - if outputs > 0 and gIdeCmd != ideUse: suggestQuit() + if outputs > 0 and gIdeCmd notin {ideUse, ideDus}: suggestQuit() proc suggestStmt*(c: PContext, n: PNode) = suggestExpr(c, n) diff --git a/compiler/transf.nim b/compiler/transf.nim index 0ea9f7d80..92319ac19 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -825,11 +825,13 @@ proc flattenStmts(n: PNode) = var goOn = true while goOn: goOn = false - for i in 0..<n.len: + var i = 0 + while i < n.len: let it = n[i] if it.kind in {nkStmtList, nkStmtListExpr}: n.sons[i..i] = it.sons[0..<it.len] goOn = true + inc i proc liftDeferAux(n: PNode) = if n.kind in {nkStmtList, nkStmtListExpr}: diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 5dd27feda..576b0565f 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -30,6 +30,14 @@ proc setResult*(a: VmArgs; v: string) = s[a.ra].node = newNode(nkStrLit) s[a.ra].node.strVal = v +proc setResult*(a: VmArgs; n: PNode) = + var s: seq[TFullReg] + move(s, cast[seq[TFullReg]](a.slots)) + if s[a.ra].kind != rkNode: + myreset(s[a.ra]) + s[a.ra].kind = rkNode + s[a.ra].node = n + proc setResult*(a: VmArgs; v: seq[string]) = var s: seq[TFullReg] move(s, cast[seq[TFullReg]](a.slots)) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 1670dd4c4..c08c5d249 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -239,7 +239,7 @@ proc loadAny(p: var JsonParser, t: PType, result = newNode(nkNilLit) next(p) of jsonInt: - result = tab[p.getInt] + result = tab.getOrDefault(p.getInt) if result.isNil: raiseParseErr(p, "cannot load object with address " & $p.getInt) next(p) diff --git a/examples/maximum.nim b/examples/maximum.nim index aa3fe375a..6552a8144 100644 --- a/examples/maximum.nim +++ b/examples/maximum.nim @@ -1,6 +1,6 @@ # Test high level features -import strutils +import strutils, sequtils echo "Give a list of numbers (separated by spaces): " stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!") diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 30081bb19..60bb6c77f 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -28,6 +28,8 @@ import pcre, strutils, rtarrays +{.deprecated.} + const MaxSubpatterns* = 20 ## defines the maximum number of subpatterns that can be captured. @@ -46,7 +48,7 @@ type h: ptr Pcre e: ptr ExtraData - Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression + Regex* = ref RegexDesc ## a compiled regular expression RegexError* = object of ValueError ## is raised if the pattern is no valid regular expression. diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 83e1b8b9f..4a0304a7c 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -139,7 +139,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, g.seenIndexTerms = initTable[string, int]() g.msgHandler = msgHandler - let s = config["split.item.toc"] + let s = config.getOrDefault"split.item.toc" if s != "": g.splitAfter = parseInt(s) for i in low(g.meta)..high(g.meta): g.meta[i] = "" @@ -341,10 +341,10 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = ## previously appeared to give a different identifier value for each. let refname = n.rstnodeToRefname if d.seenIndexTerms.hasKey(refname): - d.seenIndexTerms[refname] = d.seenIndexTerms[refname] + 1 + d.seenIndexTerms[refname] = d.seenIndexTerms.getOrDefault(refname) + 1 else: d.seenIndexTerms[refname] = 1 - let id = refname & '_' & $d.seenIndexTerms[refname] + let id = refname & '_' & $d.seenIndexTerms.getOrDefault(refname) var term = "" renderAux(d, n, term) @@ -518,7 +518,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string = sort(titles, cmp) for title in titles: - let tocList = generateDocumentationTOC(docs[title]) + let tocList = generateDocumentationTOC(docs.getOrDefault(title)) result.add("<ul><li><a href=\"" & title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n") @@ -786,7 +786,8 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, """<img src="$1" width="15" height="17" hspace="2" vspace="2" class="smiley" />""", - "\\includegraphics{$1}", [d.config["doc.smiley_format"] % n.text]) + "\\includegraphics{$1}", + [d.config.getOrDefault"doc.smiley_format" % n.text]) proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = ## Parses useful fields which can appear before a code block. @@ -844,8 +845,8 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string): inc d.listingCounter let id = $d.listingCounter if not params.numberLines: - result = (d.config["doc.listing_start"] % id, - d.config["doc.listing_end"] % id) + result = (d.config.getOrDefault"doc.listing_start" % id, + d.config.getOrDefault"doc.listing_end" % id) return var codeLines = 1 + code.strip.countLines @@ -856,9 +857,11 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string): result.beginTable.add($line & "\n") line.inc codeLines.dec - result.beginTable.add("</pre></td><td>" & (d.config["doc.listing_start"] % id)) - result.endTable = (d.config["doc.listing_end"] % id) & - "</td></tr></tbody></table>" & (d.config["doc.listing_button"] % id) + result.beginTable.add("</pre></td><td>" & ( + d.config.getOrDefault"doc.listing_start" % id)) + result.endTable = (d.config.getOrDefault"doc.listing_end" % id) & + "</td></tr></tbody></table>" & ( + d.config.getOrDefault"doc.listing_button" % id) proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = ## Renders a code block, appending it to `result`. diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 5d74896bf..590b52c1a 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -212,7 +212,7 @@ proc processClient(client: AsyncSocket, address: string, if request.reqMethod == "post": # Check for Expect header if request.headers.hasKey("Expect"): - if request.headers["Expect"].toLower == "100-continue": + if request.headers.getOrDefault("Expect").toLower == "100-continue": await client.sendStatus("100 Continue") else: await client.sendStatus("417 Expectation Failed") @@ -221,7 +221,8 @@ proc processClient(client: AsyncSocket, address: string, # - Check for Content-length header if request.headers.hasKey("Content-Length"): var contentLength = 0 - if parseInt(request.headers["Content-Length"], contentLength) == 0: + if parseInt(request.headers.getOrDefault("Content-Length"), + contentLength) == 0: await request.respond(Http400, "Bad Request. Invalid Content-Length.") continue else: @@ -232,16 +233,18 @@ proc processClient(client: AsyncSocket, address: string, continue case request.reqMethod - of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": + of "get", "post", "head", "put", "delete", "trace", "options", + "connect", "patch": await callback(request) else: - await request.respond(Http400, "Invalid request method. Got: " & request.reqMethod) + await request.respond(Http400, "Invalid request method. Got: " & + request.reqMethod) # Persistent connections if (request.protocol == HttpVer11 and - request.headers["connection"].normalize != "close") or + request.headers.getOrDefault("connection").normalize != "close") or (request.protocol == HttpVer10 and - request.headers["connection"].normalize == "keep-alive"): + request.headers.getOrDefault("connection").normalize == "keep-alive"): # In HTTP 1.1 we assume that connection is persistent. Unless connection # header states otherwise. # In HTTP 1.0 we assume that the connection should not be persistent. diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index cfd768f91..200a4adf1 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -387,7 +387,7 @@ var proc getCookie*(name: string): TaintedString = ## Gets a cookie. If no cookie of `name` exists, "" is returned. if gcookies == nil: gcookies = parseCookies(getHttpCookie()) - result = TaintedString(gcookies[name]) + result = TaintedString(gcookies.getOrDefault(name)) proc existsCookie*(name: string): bool = ## Checks if a cookie of `name` exists. diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 09b20fd45..8c507d4fb 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -11,6 +11,8 @@ ## container for a set or a mapping of strings. Based on the excellent paper ## by Adam Langley. +include "system/inclrtl" + type NodeObj[T] = object {.acyclic.} byte: int ## byte index of the difference @@ -140,20 +142,32 @@ proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = var n = rawInsert(c, key) n.val = val -proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = - ## retrieves the value at ``c[key]``. If `key` is not in `t`, - ## default empty value for the type `B` is returned - ## and no exception is raised. One can check with ``hasKey`` whether the key - ## exists. +template get[T](c: CritBitTree[T], key: string): T {.immediate.} = let n = rawGet(c, key) if n != nil: result = n.val + else: + when compiles($key): + raise newException(KeyError, "key not found: " & $key) + else: + raise newException(KeyError, "key not found") -proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline.} = +proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline, deprecatedGet.} = + ## retrieves the value at ``c[key]``. If `key` is not in `t`, the + ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether + ## the key exists. + get(c, key) + +proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline, + deprecatedGet.} = ## retrieves the value at ``c[key]``. The value can be modified. ## If `key` is not in `t`, the ``KeyError`` exception is raised. - let n = rawGet(c, key) - if n != nil: result = n.val - else: raise newException(KeyError, "key not found: " & $key) + get(c, key) + +proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline, deprecated.} = + ## retrieves the value at ``c[key]``. The value can be modified. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Use ```[]``` instead. + get(c, key) proc excl*[T](c: var CritBitTree[T], key: string) = ## removes `key` (and its associated value) from the set `c`. diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index 38bc9d462..603a4b595 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -138,6 +138,8 @@ proc initIntSet*: IntSet = result.counter = 0 result.head = nil +proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil + proc assign*(dest: var IntSet, src: IntSet) = ## copies `src` to `dest`. `dest` does not need to be initialized by ## `initIntSet`. diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index e6ea19a6b..71babe93b 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -47,7 +47,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = result[i] = itm inc(i) -proc repeat*[T](s: seq[T], n: Natural): seq[T] = +proc cycle*[T](s: seq[T], n: Natural): seq[T] = ## Returns a new sequence with the items of `s` repeated `n` times. ## ## Example: @@ -56,15 +56,29 @@ proc repeat*[T](s: seq[T], n: Natural): seq[T] = ## ## let ## s = @[1, 2, 3] - ## total = s.repeat(3) + ## total = s.cycle(3) ## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3] result = newSeq[T](n * s.len) var o = 0 - for x in 1..n: + for x in 0..<n: for e in s: result[o] = e inc o +proc repeat*[T](x: T, n: Natural): seq[T] = + ## Returns a new sequence with the item `x` repeated `n` times. + ## + ## Example: + ## + ## .. code-block: + ## + ## let + ## total = repeat(5, 3) + ## assert total == @[5, 5, 5] + result = newSeq[T](n) + for i in 0..<n: + result[i] = x + proc deduplicate*[T](seq1: seq[T]): seq[T] = ## Returns a new sequence without duplicates. ## @@ -169,6 +183,77 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = first = last +proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): + seq[S]{.inline.} = + ## Returns a new sequence with the results of `op` applied to every item in + ## `data`. + ## + ## Since the input is not modified you can use this version of ``map`` to + ## transform the type of the elements in the input sequence. Example: + ## + ## .. code-block:: nim + ## let + ## a = @[1, 2, 3, 4] + ## b = map(a, proc(x: int): string = $x) + ## assert b == @["1", "2", "3", "4"] + newSeq(result, data.len) + for i in 0..data.len-1: result[i] = op(data[i]) + +proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) + {.deprecated.} = + ## Applies `op` to every item in `data` modifying it directly. + ## + ## Note that this version of ``map`` requires your input and output types to + ## be the same, since they are modified in-place. Example: + ## + ## .. code-block:: nim + ## var a = @["1", "2", "3", "4"] + ## echo repr(a) + ## # --> ["1", "2", "3", "4"] + ## map(a, proc(x: var string) = x &= "42") + ## echo repr(a) + ## # --> ["142", "242", "342", "442"] + ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead. + for i in 0..data.len-1: op(data[i]) + +proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.}) + {.inline.} = + ## Applies `op` to every item in `data` modifying it directly. + ## + ## Note that this requires your input and output types to + ## be the same, since they are modified in-place. + ## The parameter function takes a ``var T`` type parameter. + ## Example: + ## + ## .. code-block:: nim + ## var a = @["1", "2", "3", "4"] + ## echo repr(a) + ## # --> ["1", "2", "3", "4"] + ## map(a, proc(x: var string) = x &= "42") + ## echo repr(a) + ## # --> ["142", "242", "342", "442"] + ## + for i in 0..data.len-1: op(data[i]) + +proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.}) + {.inline.} = + ## Applies `op` to every item in `data` modifying it directly. + ## + ## Note that this requires your input and output types to + ## be the same, since they are modified in-place. + ## The parameter function takes and returns a ``T`` type variable. + ## Example: + ## + ## .. code-block:: nim + ## var a = @["1", "2", "3", "4"] + ## echo repr(a) + ## # --> ["1", "2", "3", "4"] + ## map(a, proc(x: string): string = x & "42") + ## echo repr(a) + ## # --> ["142", "242", "342", "442"] + ## + for i in 0..data.len-1: data[i] = op(data[i]) + iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T = ## Iterates through a sequence and yields every item that fulfills the @@ -181,11 +266,12 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T = ## 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] + for i in 0..<seq1.len: + if pred(seq1[i]): + yield seq1[i] -proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] = +proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] + {.inline.} = ## Returns a new sequence with all the items that fulfilled the predicate. ## ## Example: @@ -197,9 +283,13 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] = ## f2 = filter(colors) do (x: string) -> bool : x.len > 5 ## assert f1 == @["red", "black"] ## assert f2 == @["yellow"] - accumulateResult(filter(seq1, pred)) + result = newSeq[T]() + for i in 0..<seq1.len: + if pred(seq1[i]): + result.add(seq1[i]) -proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) = +proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) + {.inline.} = ## Keeps the items in the passed sequence if they fulfilled the predicate. ## Same as the ``filter`` proc, but modifies the sequence directly. ## @@ -213,7 +303,7 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) = for i in 0 .. <len(seq1): if pred(seq1[i]): if pos != i: - seq1[pos] = seq1[i] + shallowCopy(seq1[pos], seq1[i]) inc(pos) setLen(seq1, pos) @@ -268,7 +358,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) = inc(j) -template filterIt*(seq1, pred: expr): expr {.immediate.} = +template filterIt*(seq1, pred: expr): expr = ## Returns a new sequence with all the items that fulfilled the predicate. ## ## Unlike the `proc` version, the predicate needs to be an expression using @@ -282,12 +372,12 @@ template filterIt*(seq1, pred: expr): expr {.immediate.} = ## notAcceptable = filterIt(temperatures, it > 50 or it < -10) ## assert acceptable == @[-2.0, 24.5, 44.31] ## assert notAcceptable == @[-272.15, 99.9, -113.44] - var result {.gensym.}: type(seq1) = @[] + var result {.gensym.} = newSeq[type(seq1[0])]() for it {.inject.} in items(seq1): if pred: result.add(it) result -template keepItIf*(varSeq, pred: expr) = +template keepItIf*(varSeq: seq, pred: expr) = ## Convenience template around the ``keepIf`` proc to reduce typing. ## ## Unlike the `proc` version, the predicate needs to be an expression using @@ -303,10 +393,71 @@ template keepItIf*(varSeq, pred: expr) = let it {.inject.} = varSeq[i] if pred: if pos != i: - varSeq[pos] = varSeq[i] + shallowCopy(varSeq[pos], varSeq[i]) inc(pos) setLen(varSeq, pos) +proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = + ## Iterates through a sequence and checks if every item fulfills the + ## predicate. + ## + ## Example: + ## + ## .. code-block:: + ## let numbers = @[1, 4, 5, 8, 9, 7, 4] + ## assert all(numbers, proc (x: int): bool = return x < 10) == true + ## assert all(numbers, proc (x: int): bool = return x < 9) == false + for i in seq1: + if not pred(i): + return false + return true + +template allIt*(seq1, pred: expr): bool {.immediate.} = + ## Checks if every item fulfills the predicate. + ## + ## Example: + ## + ## .. code-block:: + ## let numbers = @[1, 4, 5, 8, 9, 7, 4] + ## assert allIt(numbers, it < 10) == true + ## assert allIt(numbers, it < 9) == false + var result {.gensym.} = true + for it {.inject.} in items(seq1): + if not pred: + result = false + break + result + +proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = + ## Iterates through a sequence and checks if some item fulfills the + ## predicate. + ## + ## Example: + ## + ## .. code-block:: + ## let numbers = @[1, 4, 5, 8, 9, 7, 4] + ## assert any(numbers, proc (x: int): bool = return x > 8) == true + ## assert any(numbers, proc (x: int): bool = return x > 9) == false + for i in seq1: + if pred(i): + return true + return false + +template anyIt*(seq1, pred: expr): bool {.immediate.} = + ## Checks if some item fulfills the predicate. + ## + ## Example: + ## + ## .. code-block:: + ## let numbers = @[1, 4, 5, 8, 9, 7, 4] + ## assert anyIt(numbers, it > 8) == true + ## assert anyIt(numbers, it > 9) == false + var result {.gensym.} = false + for it {.inject.} in items(seq1): + if pred: + result = true + break + result template toSeq*(iter: expr): expr {.immediate.} = ## Transforms any iterator into a sequence. @@ -320,14 +471,19 @@ template toSeq*(iter: expr): expr {.immediate.} = ## if x mod 2 == 1: ## result = true) ## assert odd_numbers == @[1, 3, 5, 7, 9] - ## - ## **Note**: Since this is an immediate macro, you cannot always invoke this - ## as ``x.toSeq``, depending on the ``x``. - ## See `this <manual.html#limitations-of-the-method-call-syntax>`_ - ## for an explanation. - var result {.gensym.}: seq[type(iter)] = @[] - for x in iter: add(result, x) - result + + when compiles(iter.len): + var i = 0 + var result = newSeq[type(iter)](iter.len) + for x in iter: + result[i] = x + inc i + result + else: + var result: seq[type(iter)] = @[] + for x in iter: + result.add(x) + result template foldl*(sequence, operation: expr): expr = ## Template to fold a sequence from left to right, returning the accumulation. @@ -358,7 +514,7 @@ template foldl*(sequence, operation: expr): expr = assert sequence.len > 0, "Can't fold empty sequences" var result {.gensym.}: type(sequence[0]) result = sequence[0] - for i in countup(1, sequence.len - 1): + for i in 1..<sequence.len: let a {.inject.} = result b {.inject.} = sequence[i] @@ -401,7 +557,7 @@ template foldr*(sequence, operation: expr): expr = result = operation result -template mapIt*(seq1, typ, op: expr): expr = +template mapIt*(seq1, typ, op: expr): expr {.deprecated.}= ## Convenience template around the ``map`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an @@ -414,13 +570,45 @@ template mapIt*(seq1, typ, op: expr): expr = ## nums = @[1, 2, 3, 4] ## strings = nums.mapIt(string, $(4 * it)) ## assert strings == @["4", "8", "12", "16"] + ## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)`` + ## template instead. var result {.gensym.}: seq[typ] = @[] for it {.inject.} in items(seq1): result.add(op) result -template mapIt*(varSeq, op: expr) = - ## Convenience template around the mutable ``map`` proc to reduce typing. + +template mapIt*(seq1, op: expr): expr = + ## Convenience template around the ``map`` proc to reduce typing. + ## + ## The template injects the ``it`` variable which you can use directly in an + ## expression. Example: + ## + ## .. code-block:: + ## let + ## nums = @[1, 2, 3, 4] + ## strings = nums.mapIt($(4 * it)) + ## assert strings == @["4", "8", "12", "16"] + type outType = type(( + block: + var it{.inject.}: type(items(seq1)); + op)) + var result: seq[outType] + when compiles(seq1.len): + let s = seq1 + var i = 0 + result = newSeq[outType](s.len) + for it {.inject.} in s: + result[i] = op + i += 1 + else: + result = @[] + for it {.inject.} in seq1: + result.add(op) + result + +template applyIt*(varSeq, op: expr) = + ## Convenience template around the mutable ``apply`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an ## expression. The expression has to return the same type as the sequence you @@ -428,12 +616,14 @@ template mapIt*(varSeq, op: expr) = ## ## .. code-block:: ## var nums = @[1, 2, 3, 4] - ## nums.mapIt(it * 3) + ## nums.applyIt(it * 3) ## assert nums[0] + nums[3] == 15 - for i in 0 .. <len(varSeq): + for i in 0 .. <varSeq.len: let it {.inject.} = varSeq[i] varSeq[i] = op + + template newSeqWith*(len: int, init: expr): expr = ## creates a new sequence, calling `init` to initialize each value. Example: ## @@ -513,6 +703,38 @@ when isMainModule: keepItIf(candidates, it.len == 3 and it[0] == 'b') assert candidates == @["bar", "baz"] + block: # any + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + len0seq : seq[int] = @[] + assert any(numbers, proc (x: int): bool = return x > 8) == true + assert any(numbers, proc (x: int): bool = return x > 9) == false + assert any(len0seq, proc (x: int): bool = return true) == false + + block: # anyIt + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + len0seq : seq[int] = @[] + assert anyIt(numbers, it > 8) == true + assert anyIt(numbers, it > 9) == false + assert anyIt(len0seq, true) == false + + block: # all + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + len0seq : seq[int] = @[] + assert all(numbers, proc (x: int): bool = return x < 10) == true + assert all(numbers, proc (x: int): bool = return x < 9) == false + assert all(len0seq, proc (x: int): bool = return false) == true + + block: # allIt + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + len0seq : seq[int] = @[] + assert allIt(numbers, it < 10) == true + assert allIt(numbers, it < 9) == false + assert allIt(len0seq, false) == true + block: # toSeq test let numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] @@ -568,8 +790,8 @@ when isMainModule: block: # mapIt tests var nums = @[1, 2, 3, 4] - strings = nums.mapIt(string, $(4 * it)) - nums.mapIt(it * 3) + strings = nums.mapIt($(4 * it)) + nums.applyIt(it * 3) assert nums[0] + nums[3] == 15 block: # distribute tests @@ -605,15 +827,19 @@ when isMainModule: seq2D[0][1] = true doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] - block: # repeat tests + block: # cycle tests let a = @[1, 2, 3] b: seq[int] = @[] - doAssert a.repeat(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] - doAssert a.repeat(0) == @[] - #doAssert a.repeat(-1) == @[] # will not compile! - doAssert b.repeat(3) == @[] + doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert a.cycle(0) == @[] + #doAssert a.cycle(-1) == @[] # will not compile! + doAssert b.cycle(3) == @[] + + block: # repeat tests + assert repeat(10, 5) == @[10, 10, 10, 10, 10] + assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]] when not defined(testing): echo "Finished doc tests" diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 3d4de8fdc..abe9cf85e 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -154,9 +154,9 @@ proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} = proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} = rawGetImpl() -proc mget*[A](s: var HashSet[A], key: A): var A = +proc `[]`*[A](s: var HashSet[A], key: A): var A = ## returns the element that is actually stored in 's' which has the same - ## value as 'key' or raises the ``EInvalidKey`` exception. This is useful + ## value as 'key' or raises the ``KeyError`` exception. This is useful ## when one overloaded 'hash' and '==' but still needs reference semantics ## for sharing. assert s.isValid, "The set needs to be initialized." @@ -165,6 +165,13 @@ proc mget*[A](s: var HashSet[A], key: A): var A = if index >= 0: result = s.data[index].key else: raise newException(KeyError, "key not found: " & $key) +proc mget*[A](s: var HashSet[A], key: A): var A {.deprecated.} = + ## returns the element that is actually stored in 's' which has the same + ## value as 'key' or raises the ``KeyError`` exception. This is useful + ## when one overloaded 'hash' and '==' but still needs reference semantics + ## for sharing. Use ```[]``` instead. + s[key] + proc contains*[A](s: HashSet[A], key: A): bool = ## Returns true iff `key` is in `s`. ## diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index be6b755ed..329b2a1cb 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -68,6 +68,8 @@ import hashes, math +include "system/inclrtl" + type KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B] KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]] @@ -96,18 +98,10 @@ proc len*[A, B](t: Table[A, B]): int = ## returns the number of keys in `t`. result = t.counter -proc `[]`*[A, B](t: Table[A, B], key: A): B = - ## retrieves the value at ``t[key]``. If `key` is not in `t`, - ## default empty value for the type `B` is returned - ## and no exception is raised. One can check with ``hasKey`` whether the key - ## exists. - var hc: Hash - var index = rawGet(t, key, hc) - if index >= 0: result = t.data[index].val - -proc mget*[A, B](t: var Table[A, B], key: A): var B = +template get(t, key): untyped {.immediate.} = ## retrieves the value at ``t[key]``. The value can be modified. ## If `key` is not in `t`, the ``KeyError`` exception is raised. + mixin rawGet var hc: Hash var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val @@ -117,6 +111,31 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B = else: raise newException(KeyError, "key not found") +template getOrDefaultImpl(t, key): untyped {.immediate.} = + mixin rawGet + var hc: Hash + var index = rawGet(t, key, hc) + if index >= 0: result = t.data[index].val + +proc `[]`*[A, B](t: Table[A, B], key: A): B {.deprecatedGet.} = + ## retrieves the value at ``t[key]``. If `key` is not in `t`, the + ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether + ## the key exists. + get(t, key) + +proc `[]`*[A, B](t: var Table[A, B], key: A): var B {.deprecatedGet.} = + ## retrieves the value at ``t[key]``. The value can be modified. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + get(t, key) + +proc mget*[A, B](t: var Table[A, B], key: A): var B {.deprecated.} = + ## retrieves the value at ``t[key]``. The value can be modified. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. Use ```[]``` + ## instead. + get(t, key) + +proc getOrDefault*[A, B](t: Table[A, B], key: A): B = getOrDefaultImpl(t, key) + iterator allValues*[A, B](t: Table[A, B]; key: A): B = ## iterates over any value in the table `t` that belongs to the given `key`. var h: Hash = hash(key) and high(t.data) @@ -276,17 +295,19 @@ iterator mvalues*[A, B](t: TableRef[A, B]): var B = for h in 0..high(t.data): if isFilled(t.data[h].hcode): yield t.data[h].val -proc `[]`*[A, B](t: TableRef[A, B], key: A): B = - ## retrieves the value at ``t[key]``. If `key` is not in `t`, - ## default empty value for the type `B` is returned - ## and no exception is raised. One can check with ``hasKey`` whether the key - ## exists. +proc `[]`*[A, B](t: TableRef[A, B], key: A): var B {.deprecatedGet.} = + ## retrieves the value at ``t[key]``. If `key` is not in `t`, the + ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether + ## the key exists. result = t[][key] -proc mget*[A, B](t: TableRef[A, B], key: A): var B = +proc mget*[A, B](t: TableRef[A, B], key: A): var B {.deprecated.} = ## retrieves the value at ``t[key]``. The value can be modified. - ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. - t[].mget(key) + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Use ```[]``` instead. + t[][key] + +proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = getOrDefault(t[], key) proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way @@ -399,22 +420,26 @@ proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int = rawGetImpl() -proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = - ## retrieves the value at ``t[key]``. If `key` is not in `t`, - ## default empty value for the type `B` is returned - ## and no exception is raised. One can check with ``hasKey`` whether the key - ## exists. - var hc: Hash - var index = rawGet(t, key, hc) - if index >= 0: result = t.data[index].val +proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B {.deprecatedGet.} = + ## retrieves the value at ``t[key]``. If `key` is not in `t`, the + ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether + ## the key exists. + get(t, key) -proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B = +proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B{.deprecatedGet.} = ## retrieves the value at ``t[key]``. The value can be modified. - ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. - var hc: Hash - var index = rawGet(t, key, hc) - if index >= 0: result = t.data[index].val - else: raise newException(KeyError, "key not found: " & $key) + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + get(t, key) + +proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B {.deprecated.} = + ## retrieves the value at ``t[key]``. The value can be modified. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Use ```[]``` instead. + get(t, key) + +proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B = + getOrDefaultImpl(t, key) + proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. @@ -572,17 +597,20 @@ iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B = forAllOrderedPairs: yield t.data[h].val -proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): B = - ## retrieves the value at ``t[key]``. If `key` is not in `t`, - ## default empty value for the type `B` is returned - ## and no exception is raised. One can check with ``hasKey`` whether the key - ## exists. +proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = + ## retrieves the value at ``t[key]``. If `key` is not in `t`, the + ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether + ## the key exists. result = t[][key] -proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B = +proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B {.deprecated.} = ## retrieves the value at ``t[key]``. The value can be modified. - ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. - result = t[].mget(key) + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Use ```[]``` instead. + result = t[][key] + +proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B = + getOrDefault(t[], key) proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B = ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way @@ -683,19 +711,35 @@ proc rawGet[A](t: CountTable[A], key: A): int = h = nextTry(h, high(t.data)) result = -1 - h # < 0 => MISSING; insert idx = -1 - result -proc `[]`*[A](t: CountTable[A], key: A): int = - ## retrieves the value at ``t[key]``. If `key` is not in `t`, - ## 0 is returned. One can check with ``hasKey`` whether the key - ## exists. +template ctget(t, key: untyped): untyped {.immediate.} = var index = rawGet(t, key) if index >= 0: result = t.data[index].val + else: + when compiles($key): + raise newException(KeyError, "key not found: " & $key) + else: + raise newException(KeyError, "key not found") + +proc `[]`*[A](t: CountTable[A], key: A): int {.deprecatedGet.} = + ## retrieves the value at ``t[key]``. If `key` is not in `t`, + ## the ``KeyError`` exception is raised. One can check with ``hasKey`` + ## whether the key exists. + ctget(t, key) -proc mget*[A](t: var CountTable[A], key: A): var int = +proc `[]`*[A](t: var CountTable[A], key: A): var int {.deprecatedGet.} = ## retrieves the value at ``t[key]``. The value can be modified. - ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ctget(t, key) + +proc mget*[A](t: var CountTable[A], key: A): var int {.deprecated.} = + ## retrieves the value at ``t[key]``. The value can be modified. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Use ```[]``` instead. + ctget(t, key) + +proc getOrDefault*[A](t: CountTable[A], key: A): int = var index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: raise newException(KeyError, "key not found: " & $key) proc hasKey*[A](t: CountTable[A], key: A): bool = ## returns true iff `key` is in the table `t`. @@ -831,16 +875,19 @@ iterator mvalues*[A](t: CountTableRef[A]): var int = for h in 0..high(t.data): if t.data[h].val != 0: yield t.data[h].val -proc `[]`*[A](t: CountTableRef[A], key: A): int = - ## retrieves the value at ``t[key]``. If `key` is not in `t`, - ## 0 is returned. One can check with ``hasKey`` whether the key - ## exists. +proc `[]`*[A](t: CountTableRef[A], key: A): var int {.deprecatedGet.} = + ## retrieves the value at ``t[key]``. The value can be modified. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. result = t[][key] -proc mget*[A](t: CountTableRef[A], key: A): var int = +proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} = ## retrieves the value at ``t[key]``. The value can be modified. - ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. - result = t[].mget(key) + ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Use ```[]``` instead. + result = t[][key] + +proc getOrDefault*[A](t: CountTableRef[A], key: A): int = + getOrDefaultImpl(t, key) proc hasKey*[A](t: CountTableRef[A], key: A): bool = ## returns true iff `key` is in the table `t`. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 61c16129b..11af81149 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -8,9 +8,10 @@ # ## This module implements efficient computations of hash values for diverse -## Nim types. All the procs are based on these two building blocks: the `!& -## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_ -## used to *finish* the hash value. If you want to implement hash procs for +## Nim types. All the procs are based on these two building blocks: +## - `!& proc <#!&>`_ used to start or mix a hash value, and +## - `!$ proc <#!$>`_ used to *finish* the hash value. +## If you want to implement hash procs for ## your custom types you will end up writing the following kind of skeleton of ## code: ## @@ -108,7 +109,7 @@ proc hash*(x: int): Hash {.inline.} = result = x proc hash*(x: int64): Hash {.inline.} = - ## efficient hashing of integers + ## efficient hashing of int64 integers result = toU32(x) proc hash*(x: char): Hash {.inline.} = @@ -126,6 +127,16 @@ proc hash*(x: string): Hash = h = h !& ord(x[i]) result = !$h +proc hash*(sBuf: string, sPos, ePos: int): Hash = + ## efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos` + ## + ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)`` + var h: Hash = 0 + for i in sPos..ePos: + h = h !& ord(sBuf[i]) + result = !$h + proc hashIgnoreStyle*(x: string): Hash = ## efficient hashing of strings; style is ignored var h: Hash = 0 @@ -145,6 +156,27 @@ proc hashIgnoreStyle*(x: string): Hash = result = !$h +proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash = + ## efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos`; style is ignored + ## + ## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent + ## to ``hashIgnoreStyle(myBuf)`` + var h: Hash = 0 + var i = sPos + while i <= ePos: + var c = sBuf[i] + if c == '_': + inc(i) + elif isMagicIdentSeparatorRune(cstring(sBuf), i): + inc(i, magicIdentSeparatorRuneByteWidth) + else: + if c in {'A'..'Z'}: + c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() + h = h !& ord(c) + inc(i) + result = !$h + proc hashIgnoreCase*(x: string): Hash = ## efficient hashing of strings; case is ignored var h: Hash = 0 @@ -155,7 +187,22 @@ proc hashIgnoreCase*(x: string): Hash = h = h !& ord(c) result = !$h +proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = + ## efficient hashing of a string buffer, from starting + ## position `sPos` to ending position `ePos`; case is ignored + ## + ## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent + ## to ``hashIgnoreCase(myBuf)`` + var h: Hash = 0 + for i in sPos..ePos: + var c = sBuf[i] + if c in {'A'..'Z'}: + c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() + h = h !& ord(c) + result = !$h + proc hash*(x: float): Hash {.inline.} = + ## efficient hashing of floats. var y = x + 1.0 result = cast[ptr Hash](addr(y))[] @@ -173,10 +220,29 @@ proc hash*[T: tuple](x: T): Hash = result = !$result proc hash*[A](x: openArray[A]): Hash = + ## efficient hashing of arrays and sequences. for it in items(x): result = result !& hash(it) result = !$result +proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = + ## efficient hashing of portions of arrays and sequences. + ## + ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)`` + for i in sPos..ePos: + result = result !& hash(aBuf[i]) + result = !$result + proc hash*[A](x: set[A]): Hash = + ## efficient hashing of sets. for it in items(x): result = result !& hash(it) result = !$result +when isMainModule: + doAssert( hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) ) + doAssert( hashIgnoreCase("aa bb aaaa1234") == hash("aa bb aaaa1234") ) + doAssert( hashIgnoreStyle("aa bb aaaa1234") == hashIgnoreCase("aa bb aaaa1234") ) + let xx = @['H','e','l','l','o'] + let ss = "Hello" + doAssert( hash(xx) == hash(ss) ) + doAssert( hash(xx) == hash(xx, 0, xx.high) ) + doAssert( hash(ss) == hash(ss, 0, ss.high) ) diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 7a900daae..aa55b5ade 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -77,7 +77,7 @@ type ## console FileLogger* = ref object of Logger ## logger that writes the messages to a file - f: File + file*: File ## the wrapped file. RollingFileLogger* = ref object of FileLogger ## logger that writes the ## messages to a file and @@ -92,7 +92,9 @@ type {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} -proc substituteLog(frmt: string, level: Level, args: varargs[string, `$`]): string = +proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string = + ## Format a log message using the ``frmt`` format string, ``level`` and varargs. + ## See the module documentation for the format string syntax. var msgLen = 0 for arg in args: msgLen += arg.len @@ -124,7 +126,7 @@ proc substituteLog(frmt: string, level: Level, args: varargs[string, `$`]): stri method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {. raises: [Exception], - tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} = + tags: [TimeEffect, WriteIOEffect, ReadIOEffect], base.} = ## Override this method in custom loggers. Default implementation does ## nothing. discard @@ -133,15 +135,17 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = ## Logs to the console using ``logger`` only. if level >= logger.levelThreshold: writeLine(stdout, substituteLog(logger.fmtStr, level, args)) + if level in {lvlError, lvlFatal}: flushFile(stdout) method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) = ## Logs to a file using ``logger`` only. if level >= logger.levelThreshold: - writeLine(logger.f, substituteLog(logger.fmtStr, level, args)) + writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) + if level in {lvlError, lvlFatal}: flushFile(logger.file) proc defaultFilename*(): string = ## Returns the default filename for a logger. - var (path, name, ext) = splitFile(getAppFilename()) + var (path, name, _) = splitFile(getAppFilename()) result = changeFileExt(path / name, "log") proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): ConsoleLogger = @@ -160,14 +164,14 @@ proc newFileLogger*(filename = defaultFilename(), ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size). new(result) result.levelThreshold = levelThreshold - result.f = open(filename, mode, bufSize = bufSize) + result.file = open(filename, mode, bufSize = bufSize) result.fmtStr = fmtStr # ------ proc countLogLines(logger: RollingFileLogger): int = result = 0 - for line in logger.f.lines(): + for line in logger.file.lines(): result.inc() proc countFiles(filename: string): int = @@ -200,7 +204,7 @@ proc newRollingFileLogger*(filename = defaultFilename(), result.fmtStr = fmtStr result.maxLines = maxLines result.bufSize = bufSize - result.f = open(filename, mode, bufSize=result.bufSize) + result.file = open(filename, mode, bufSize=result.bufSize) result.curLine = 0 result.baseName = filename result.baseMode = mode @@ -222,13 +226,14 @@ method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) ## Logs to a file using rolling ``logger`` only. if level >= logger.levelThreshold: if logger.curLine >= logger.maxLines: - logger.f.close() + logger.file.close() rotate(logger) logger.logFiles.inc logger.curLine = 0 - logger.f = open(logger.baseName, logger.baseMode, bufSize = logger.bufSize) + logger.file = open(logger.baseName, logger.baseMode, bufSize = logger.bufSize) - writeLine(logger.f, substituteLog(logger.fmtStr, level, args)) + writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) + if level in {lvlError, lvlFatal}: flushFile(logger.file) logger.curLine.inc # -------- diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 173cd1e81..134581a06 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -176,7 +176,7 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) = setPointer(a, nil) next(p) of jsonInt: - setPointer(a, t[p.getInt]) + setPointer(a, t.getOrDefault(p.getInt)) next(p) of jsonArrayStart: next(p) diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 5ee301b15..44b9ed0d4 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -9,8 +9,6 @@ ## Module for computing MD5 checksums. -import unsigned - type MD5State = array[0..3, uint32] MD5Block = array[0..15, uint32] diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index 642419e64..1e315afb4 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -499,7 +499,7 @@ proc newMimetypes*(): MimeDB = proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string = ## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext`` ## could not be found. - result = mimedb.mimes[ext] + result = mimedb.mimes.getOrDefault(ext) if result == "": return default diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index 667ca82d7..e9f5bee0a 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -26,8 +26,10 @@ when not declared(getEnv) or defined(nimscript): ## to an environment variable ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write - ## operation to the directory structure - WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to + ## operation to the directory + ## structure + WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write + ## operation to ## the directory structure OSErrorCode* = distinct int32 ## Specifies an OS Error Code. @@ -63,13 +65,13 @@ when not declared(getEnv) or defined(nimscript): AltSep* = '/' ## An alternative character used by the operating system to separate ## pathname components, or the same as `DirSep` if only one separator - ## character exists. This is set to '/' on Windows systems where `DirSep` - ## is a backslash. + ## character exists. This is set to '/' on Windows systems + ## where `DirSep` is a backslash. PathSep* = ':' ## The character conventionally used by the operating system to separate - ## search patch components (as in PATH), such as ':' for POSIX or ';' for - ## Windows. + ## search patch components (as in PATH), such as ':' for POSIX + ## or ';' for Windows. FileSystemCaseSensitive* = true ## true if the file system is case sensitive, false otherwise. Used by @@ -104,7 +106,8 @@ when not declared(getEnv) or defined(nimscript): # MacOS directory separator is a colon ":" which is the only character not # allowed in filenames. # - # A path containing no colon or which begins with a colon is a partial path. + # A path containing no colon or which begins with a colon is a partial + # path. # E.g. ":kalle:petter" ":kalle" "kalle" # # All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:" @@ -206,9 +209,9 @@ when not declared(getEnv) or defined(nimscript): proc joinPath*(parts: varargs[string]): string {.noSideEffect, rtl, extern: "nos$1OpenArray".} = - ## The same as `joinPath(head, tail)`, but works with any number of directory - ## parts. You need to pass at least one element or the proc will assert in - ## debug builds and crash on release builds. + ## The same as `joinPath(head, tail)`, but works with any number of + ## directory parts. You need to pass at least one element or the proc + ## will assert in debug builds and crash on release builds. result = parts[0] for i in 1..high(parts): result = joinPath(result, parts[i]) @@ -316,8 +319,8 @@ when not declared(getEnv) or defined(nimscript): if inclusive: yield path proc `/../` * (head, tail: string): string {.noSideEffect.} = - ## The same as ``parentDir(head) / tail`` unless there is no parent directory. - ## Then ``head / tail`` is performed instead. + ## The same as ``parentDir(head) / tail`` unless there is no parent + ## directory. Then ``head / tail`` is performed instead. let sepPos = parentDirPos(head) if sepPos >= 0: result = substr(head, 0, sepPos-1) / tail @@ -500,7 +503,8 @@ when defined(nimdoc) and not declared(os): proc existsFile(x: string): bool = discard when declared(getEnv) or defined(nimscript): - proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = + proc getHomeDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the home directory of the current user. ## ## This proc is wrapped by the expandTilde proc for the convenience of @@ -508,18 +512,21 @@ when declared(getEnv) or defined(nimscript): when defined(windows): return string(getEnv("USERPROFILE")) & "\\" else: return string(getEnv("HOME")) & "/" - proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = + proc getConfigDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the config directory of the current user for applications. when defined(windows): return string(getEnv("APPDATA")) & "\\" else: return string(getEnv("HOME")) & "/.config/" - proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = + proc getTempDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the temporary directory of the current user for applications to ## save temporary files in. when defined(windows): return string(getEnv("TEMP")) & "\\" else: return "/tmp/" - proc expandTilde*(path: string): string {.tags: [ReadEnvEffect, ReadIOEffect].} = + proc expandTilde*(path: string): string {. + tags: [ReadEnvEffect, ReadIOEffect].} = ## Expands a path starting with ``~/`` to a full path. ## ## If `path` starts with the tilde character and is followed by `/` or `\\` @@ -527,8 +534,8 @@ when declared(getEnv) or defined(nimscript): ## the getHomeDir() proc, otherwise the input path will be returned without ## modification. ## - ## The behaviour of this proc is the same on the Windows platform despite not - ## having this convention. Example: + ## The behaviour of this proc is the same on the Windows platform despite + ## not having this convention. Example: ## ## .. code-block:: nim ## let configFile = expandTilde("~" / "appname.cfg") @@ -549,7 +556,8 @@ when declared(getEnv) or defined(nimscript): yield substr(s, first, last-1) inc(last) - proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} = + proc findExe*(exe: string): string {. + tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} = ## Searches for `exe` in the current working directory and then ## in directories listed in the ``PATH`` environment variable. ## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe` diff --git a/lib/deprecated/pure/parseopt.nim b/lib/pure/parseopt.nim index 218f5ab81..218f5ab81 100644 --- a/lib/deprecated/pure/parseopt.nim +++ b/lib/pure/parseopt.nim diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index 1d54b4591..711e4a897 100644 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -145,8 +145,8 @@ proc next*(s: var ScgiState, timeout: int = -1): bool = L = L * 10 + ord(d) - ord('0') recvBuffer(s, L+1) s.headers = parseHeaders(s.input, L) - if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected") - L = parseInt(s.headers["CONTENT_LENGTH"]) + if s.headers.getOrDefault("SCGI") != "1": raiseScgiError("SCGI Version 1 expected") + L = parseInt(s.headers.getOrDefault("CONTENT_LENGTH")) recvBuffer(s, L) return true @@ -221,10 +221,10 @@ proc handleClientRead(client: AsyncClient, s: AsyncScgiState) = case ret of ReadFullLine: client.headers = parseHeaders(client.input, client.input.len-1) - if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected") + if client.headers.getOrDefault("SCGI") != "1": raiseScgiError("SCGI Version 1 expected") client.input = "" # For next part - let contentLen = parseInt(client.headers["CONTENT_LENGTH"]) + let contentLen = parseInt(client.headers.getOrDefault("CONTENT_LENGTH")) if contentLen > 0: client.mode = ClientReadContent else: @@ -232,7 +232,8 @@ proc handleClientRead(client: AsyncClient, s: AsyncScgiState) = checkCloseSocket(client) of ReadPartialLine, ReadDisconnected, ReadNone: return of ClientReadContent: - let L = parseInt(client.headers["CONTENT_LENGTH"])-client.input.len + let L = parseInt(client.headers.getOrDefault("CONTENT_LENGTH")) - + client.input.len if L > 0: let ret = recvBufferAsync(client, L) case ret diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 1ce9067a7..1c761cd92 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -101,21 +101,31 @@ proc rawGet(t: StringTableRef, key: string): int = h = nextTry(h, high(t.data)) result = - 1 -proc `[]`*(t: StringTableRef, key: string): string {.rtl, extern: "nstGet".} = - ## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned - ## and no exception is raised. One can check with ``hasKey`` whether the key - ## exists. +template get(t: StringTableRef, key: string): stmt {.immediate.} = var index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: result = "" + else: + when compiles($key): + raise newException(KeyError, "key not found: " & $key) + else: + raise newException(KeyError, "key not found") -proc mget*(t: StringTableRef, key: string): var string {. - rtl, extern: "nstTake".} = +proc `[]`*(t: StringTableRef, key: string): var string {. + rtl, extern: "nstTake", deprecatedGet.} = ## retrieves the location at ``t[key]``. If `key` is not in `t`, the - ## ``KeyError`` exception is raised. + ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether + ## the key exists. + get(t, key) + +proc mget*(t: StringTableRef, key: string): var string {.deprecated.} = + ## retrieves the location at ``t[key]``. If `key` is not in `t`, the + ## ``KeyError`` exception is raised. Use ```[]``` instead. + get(t, key) + +proc getOrDefault*(t: StringTableRef; key: string): string = var index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: raise newException(KeyError, "key does not exist: " & key) + else: result = "" proc hasKey*(t: StringTableRef, key: string): bool {.rtl, extern: "nst$1".} = ## returns true iff `key` is in the table `t`. @@ -152,7 +162,7 @@ proc raiseFormatException(s: string) = raise e proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = - if hasKey(t, key): return t[key] + if hasKey(t, key): return t.getOrDefault(key) # hm difficult: assume safety in taint mode here. XXX This is dangerous! if useEnvironment in flags: result = os.getEnv(key).string else: result = "" @@ -248,7 +258,7 @@ when isMainModule: assert x["k"] == "v" assert x["11"] == "22" assert x["565"] == "67" - x.mget("11") = "23" + x["11"] = "23" assert x["11"] == "23" x.clear(modeCaseInsensitive) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index eacea72e4..a78fed4b9 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -60,6 +60,132 @@ const ## doAssert "01234".find(invalid) == -1 ## doAssert "01A34".find(invalid) == 2 +proc isAlpha*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaChar".}= + ## Checks whether or not `c` is alphabetical. + ## + ## This checks a-z, A-Z ASCII characters only. + return c in Letters + +proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaNumericChar".}= + ## Checks whether or not `c` is alphanumeric. + ## + ## This checks a-z, A-Z, 0-9 ASCII characters only. + return c in Letters or c in Digits + +proc isDigit*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsDigitChar".}= + ## Checks whether or not `c` is a number. + ## + ## This checks 0-9 ASCII characters only. + return c in Digits + +proc isSpace*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsSpaceChar".}= + ## Checks whether or not `c` is a whitespace character. + return c in Whitespace + +proc isLower*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsLowerChar".}= + ## Checks whether or not `c` is a lower case character. + ## + ## This checks ASCII characters only. + return c in {'a'..'z'} + +proc isUpper*(c: char): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsUpperChar".}= + ## Checks whether or not `c` is an upper case character. + ## + ## This checks ASCII characters only. + return c in {'A'..'Z'} + +proc isAlpha*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaStr".}= + ## Checks whether or not `s` is alphabetical. + ## + ## This checks a-z, A-Z ASCII characters only. + ## Returns true if all characters in `s` are + ## alphabetic and there is at least one character + ## in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isAlpha() and result + +proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsAlphaNumericStr".}= + ## Checks whether or not `s` is alphanumeric. + ## + ## This checks a-z, A-Z, 0-9 ASCII characters only. + ## Returns true if all characters in `s` are + ## alpanumeric and there is at least one character + ## in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isAlphaNumeric() and result + +proc isDigit*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsDigitStr".}= + ## Checks whether or not `s` is a numeric value. + ## + ## This checks 0-9 ASCII characters only. + ## Returns true if all characters in `s` are + ## numeric and there is at least one character + ## in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isDigit() and result + +proc isSpace*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsSpaceStr".}= + ## Checks whether or not `s` is completely whitespace. + ## + ## Returns true if all characters in `s` are whitespace + ## characters and there is at least one character in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isSpace() and result + +proc isLower*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsLowerStr".}= + ## Checks whether or not `s` contains all lower case characters. + ## + ## This checks ASCII characters only. + ## Returns true if all characters in `s` are lower case + ## and there is at least one character in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isLower() and result + +proc isUpper*(s: string): bool {.noSideEffect, procvar, + rtl, extern: "nsuIsUpperStr".}= + ## Checks whether or not `s` contains all upper case characters. + ## + ## This checks ASCII characters only. + ## Returns true if all characters in `s` are upper case + ## and there is at least one character in `s`. + if s.len() == 0: + return false + + result = true + for c in s: + result = c.isUpper() and result + proc toLower*(c: char): char {.noSideEffect, procvar, rtl, extern: "nsuToLowerChar".} = ## Converts `c` into lower case. @@ -1526,3 +1652,55 @@ when isMainModule: doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" + + doAssert isAlpha('r') + doAssert isAlpha('A') + doAssert(not isAlpha('$')) + + doAssert isAlpha("Rasp") + doAssert isAlpha("Args") + doAssert(not isAlpha("$Tomato")) + + doAssert isAlphaNumeric('3') + doAssert isAlphaNumeric('R') + doAssert(not isAlphaNumeric('!')) + + doAssert isAlphaNumeric("34ABc") + doAssert isAlphaNumeric("Rad") + doAssert isAlphaNumeric("1234") + doAssert(not isAlphaNumeric("@nose")) + + doAssert isDigit('3') + doAssert(not isDigit('a')) + doAssert(not isDigit('%')) + + doAssert isDigit("12533") + doAssert(not isDigit("12.33")) + doAssert(not isDigit("A45b")) + + doAssert isSpace('\t') + doAssert isSpace('\l') + doAssert(not isSpace('A')) + + doAssert isSpace("\t\l \v\r\f") + doAssert isSpace(" ") + doAssert(not isSpace("ABc \td")) + + doAssert isLower('a') + doAssert isLower('z') + doAssert(not isLower('A')) + doAssert(not isLower('5')) + doAssert(not isLower('&')) + + doAssert isLower("abcd") + doAssert(not isLower("abCD")) + doAssert(not isLower("33aa")) + + doAssert isUpper('A') + doAssert(not isUpper('b')) + doAssert(not isUpper('5')) + doAssert(not isUpper('%')) + + doAssert isUpper("ABC") + doAssert(not isUpper("AAcc")) + doAssert(not isUpper("A#$")) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index a0f7b955e..aca9d51e2 100755 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -150,6 +150,8 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} = try: when declared(testSetupIMPLFlag): testSetupIMPL() body + when declared(testTeardownIMPLFlag): + defer: testTeardownIMPL() except: when not defined(js): @@ -158,7 +160,6 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} = fail() finally: - when declared(testTeardownIMPLFlag): testTeardownIMPL() testDone name, testStatusIMPL proc checkpoint*(msg: string) = diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 7c97a0a56..a9fc8998a 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -139,11 +139,16 @@ proc delete*(n: XmlNode, i: Natural) {.noSideEffect.} = assert n.k == xnElement n.s.delete(i) -proc mget* (n: var XmlNode, i: int): var XmlNode {.inline.} = +proc `[]`* (n: var XmlNode, i: int): var XmlNode {.inline.} = ## returns the `i`'th child of `n` so that it can be modified assert n.k == xnElement result = n.s[i] +proc mget*(n: var XmlNode, i: int): var XmlNode {.inline, deprecated.} = + ## returns the `i`'th child of `n` so that it can be modified. Use ```[]``` + ## instead. + n[i] + iterator items*(n: XmlNode): XmlNode {.inline.} = ## iterates over any child of `n`. assert n.k == xnElement @@ -152,7 +157,7 @@ iterator items*(n: XmlNode): XmlNode {.inline.} = iterator mitems*(n: var XmlNode): var XmlNode {.inline.} = ## iterates over any child of `n`. assert n.k == xnElement - for i in 0 .. n.len-1: yield mget(n, i) + for i in 0 .. n.len-1: yield n[i] proc attrs*(n: XmlNode): XmlAttributes {.inline.} = ## gets the attributes belonging to `n`. @@ -337,7 +342,7 @@ proc attr*(n: XmlNode, name: string): string = ## Returns "" on failure. assert n.kind == xnElement if n.attrs == nil: return "" - return n.attrs[name] + return n.attrs.getOrDefault(name) proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) = ## Iterates over all the children of `n` returning those matching `tag`. diff --git a/lib/system.nim b/lib/system.nim index 1d2ca6a9a..89de08c6f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1260,6 +1260,15 @@ const hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") +when defined(boehmgc): + when defined(windows): + const boehmLib = "boehmgc.dll" + elif defined(macosx): + const boehmLib = "libgc.dylib" + else: + const boehmLib = "libgc.so.1" + {.pragma: boehmGC, noconv, dynlib: boehmLib.} + when taintMode: type TaintedString* = distinct string ## a distinct string type that ## is `tainted`:idx:. It is an alias for @@ -2160,53 +2169,6 @@ proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = result = s[L] setLen(s, L) -proc each*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] {. - deprecated.} = - ## The well-known ``map`` operation from functional programming. Applies - ## `op` to every item in `data` and returns the result as a sequence. - ## - ## **Deprecated since version 0.9:** Use the ``map`` proc instead. - 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.}) {. - deprecated.} = - ## The well-known ``map`` operation from functional programming. Applies - ## `op` to every item in `data` modifying it directly. - ## - ## **Deprecated since version 0.9:** Use the ``map`` proc instead. - for i in 0..data.len-1: op(data[i]) - -proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] = - ## Returns a new sequence with the results of `op` applied to every item in - ## `data`. - ## - ## Since the input is not modified you can use this version of ``map`` to - ## transform the type of the elements in the input sequence. Example: - ## - ## .. code-block:: nim - ## let - ## a = @[1, 2, 3, 4] - ## b = map(a, proc(x: int): string = $x) - ## assert b == @["1", "2", "3", "4"] - newSeq(result, data.len) - for i in 0..data.len-1: result[i] = op(data[i]) - -proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) = - ## Applies `op` to every item in `data` modifying it directly. - ## - ## Note that this version of ``map`` requires your input and output types to - ## be the same, since they are modified in-place. Example: - ## - ## .. code-block:: nim - ## var a = @["1", "2", "3", "4"] - ## echo repr(a) - ## # --> ["1", "2", "3", "4"] - ## map(a, proc(x: var string) = x &= "42") - ## echo repr(a) - ## # --> ["142", "242", "342", "442"] - for i in 0..data.len-1: op(data[i]) - iterator fields*[T: tuple|object](x: T): RootObj {. magic: "Fields", noSideEffect.} ## iterates over every field of `x`. Warning: This really transforms diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 201a99ca7..3caeefcbc 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -51,3 +51,8 @@ when defined(nimlocks): {.pragma: benign, gcsafe, locks: 0.} else: {.pragma: benign, gcsafe.} + +when defined(nimTableGet): + {.pragma: deprecatedGet, deprecated.} +else: + {.pragma: deprecatedGet.} diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 8a946716d..1c13f3ff8 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -66,41 +66,34 @@ proc raiseOutOfMem() {.noinline.} = quit(1) when defined(boehmgc): - when defined(windows): - const boehmLib = "boehmgc.dll" - elif defined(macosx): - const boehmLib = "libgc.dylib" - else: - const boehmLib = "libgc.so.1" - - proc boehmGCinit {.importc: "GC_init", dynlib: boehmLib.} - proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.} - proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.} + proc boehmGCinit {.importc: "GC_init", boehmGC.} + proc boehmGC_disable {.importc: "GC_disable", boehmGC.} + proc boehmGC_enable {.importc: "GC_enable", boehmGC.} proc boehmGCincremental {. - importc: "GC_enable_incremental", dynlib: boehmLib.} - proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.} - proc boehmAlloc(size: int): pointer {. - importc: "GC_malloc", dynlib: boehmLib.} + importc: "GC_enable_incremental", boehmGC.} + proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.} + proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.} proc boehmAllocAtomic(size: int): pointer {. - importc: "GC_malloc_atomic", dynlib: boehmLib.} + importc: "GC_malloc_atomic", boehmGC.} proc boehmRealloc(p: pointer, size: int): pointer {. - importc: "GC_realloc", dynlib: boehmLib.} - proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.} + importc: "GC_realloc", boehmGC.} + proc boehmDealloc(p: pointer) {.importc: "GC_free", boehmGC.} + when hasThreadSupport: + proc boehmGC_allow_register_threads {. + importc: "GC_allow_register_threads", boehmGC.} - proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", dynlib: boehmLib.} + proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", boehmGC.} ## Return the number of bytes in the heap. Excludes collector private ## data structures. Includes empty blocks and fragmentation loss. ## Includes some pages that were allocated but never written. - proc boehmGetFreeBytes: int {.importc: "GC_get_free_bytes", dynlib: boehmLib.} + proc boehmGetFreeBytes: int {.importc: "GC_get_free_bytes", boehmGC.} ## Return a lower bound on the number of free bytes in the heap. - proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", - dynlib: boehmLib.} + proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", boehmGC.} ## Return the number of bytes allocated since the last collection. - proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", - dynlib: boehmLib.} + proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", boehmGC.} ## Return the total number of bytes allocated in this process. ## Never decreases. @@ -157,7 +150,9 @@ when defined(boehmgc): proc setStackBottom(theStackBottom: pointer) = discard proc initGC() = - when defined(macosx): boehmGCinit() + boehmGCinit() + when hasThreadSupport: + boehmGC_allow_register_threads() proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = if ntfNoRefs in typ.flags: result = allocAtomic(size) @@ -204,9 +199,6 @@ elif defined(gogc): else: const goLib = "libgo.so" - proc `div`[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.} - proc `-`[T: SomeUnsignedInt](x, y: T): T {.magic: "SubU", noSideEffect.} - proc roundup(x, v: int): int {.inline.} = result = (x + (v-1)) and not (v-1) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index c7cb8d9df..c5de841f8 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -304,22 +304,53 @@ type when not defined(boehmgc) and not hasSharedHeap and not defined(gogc): proc deallocOsPages() +when defined(boehmgc): + type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.} + proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer) + {.importc: "GC_call_with_stack_base", boehmGC.} + proc boehmGC_register_my_thread(sb: pointer) + {.importc: "GC_register_my_thread", boehmGC.} + proc boehmGC_unregister_my_thread() + {.importc: "GC_unregister_my_thread", boehmGC.} + + proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv.} = + boehmGC_register_my_thread(sb) + let thrd = cast[ptr Thread[TArg]](thrd) + when TArg is void: + thrd.dataFn() + else: + thrd.dataFn(thrd.data) + boehmGC_unregister_my_thread() +else: + proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) = + when TArg is void: + thrd.dataFn() + else: + thrd.dataFn(thrd.data) + +proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = + when defined(boehmgc): + boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd) + elif not defined(nogc) and not defined(gogc): + var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} = + threadProcWrapDispatch[TArg] + when not hasSharedHeap: + # init the GC for refc/markandsweep + setStackBottom(addr(p)) + initGC() + when declared(registerThread): + thrd.stackBottom = addr(thrd) + registerThread(thrd) + p(thrd) + when declared(registerThread): unregisterThread(thrd) + when declared(deallocOsPages): deallocOsPages() + else: + threadProcWrapDispatch(thrd) + template threadProcWrapperBody(closure: expr) {.immediate.} = when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) - var t = cast[ptr Thread[TArg]](closure) - when useStackMaskHack: - var tls: ThreadLocalStorage - when not defined(boehmgc) and not defined(gogc) and not defined(nogc) and not hasSharedHeap: - # init the GC for this thread: - setStackBottom(addr(t)) - initGC() - when declared(registerThread): - t.stackBottom = addr(t) - registerThread(t) - when TArg is void: t.dataFn() - else: t.dataFn(t.data) - when declared(registerThread): unregisterThread(t) - when declared(deallocOsPages): deallocOsPages() + var thrd = cast[ptr Thread[TArg]](closure) + threadProcWrapStackFrame(thrd) # Since an unhandled exception terminates the whole process (!), there is # no need for a ``try finally`` here, nor would it be correct: The current # exception is tried to be re-raised by the code-gen after the ``finally``! @@ -327,7 +358,7 @@ template threadProcWrapperBody(closure: expr) {.immediate.} = # page! # mark as not running anymore: - t.dataFn = nil + thrd.dataFn = nil {.push stack_trace:off.} when defined(windows): diff --git a/readme.md b/readme.md index 80723cabc..4c996ebae 100644 --- a/readme.md +++ b/readme.md @@ -79,22 +79,29 @@ All rights reserved. # Build Status [**Build Waterfall**][waterfall] -| | Linux | Windows | Mac | -| ------ | ----- | ------- | --- | -| x86 | ![linux-x86][linux-x86-img] | ![windows-x86][windows-x86-img] | ![mac-x86][mac-x86-img] | -| x86_64 | ![linux-x86_64][linux-x86_64-img] | ![windows-x86_64][windows-x86_64-img] | ![mac-x86_64][mac-x86_64-img] | -| arm | ![linux-armv5][linux-arm5-img]<br/> ![linux-armv6][linux-arm6-img]<br/> ![linux-armv7][linux-arm7-img] | | | +| | Linux | Windows | Mac | +| ------ | ----- | ------- | --- | +| x86 | [![linux-x86][linux-x86-img]][linux-x86] | [![windows-x86][windows-x86-img]][windows-x86] | +| x86_64 | [![linux-x86_64][linux-x86_64-img]][linux-x86_64] | [![windows-x86_64][windows-x86_64-img]][windows-x86_64] | [![mac-x86_64][mac-x86_64-img]][mac-x86_64] | +| arm | [![linux-armv5][linux-arm5-img]][linux-arm5]<br/> [![linux-armv6][linux-arm6-img]][linux-arm6]<br/> [![linux-armv7][linux-arm7-img]][linux-arm7] +[linux-x86]: http://buildbot.nim-lang.org/builders/linux-x32-builder [linux-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x32-builder +[linux-x86_64]: http://buildbot.nim-lang.org/builders/linux-x64-builder [linux-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x64-builder +[linux-arm5]: http://buildbot.nim-lang.org/builders/linux-arm5-builder [linux-arm5-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm5-builder +[linux-arm6]: http://buildbot.nim-lang.org/builders/linux-arm6-builder [linux-arm6-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm6-builder +[linux-arm7]: http://buildbot.nim-lang.org/builders/linux-arm7-builder [linux-arm7-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm7-builder +[windows-x86]: http://buildbot.nim-lang.org/builders/windows-x32-builder [windows-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x32-builder +[windows-x86_64]: http://buildbot.nim-lang.org/builders/windows-x64-builder [windows-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x64-builder -[mac-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x32-builder +[mac-x86_64]: http://buildbot.nim-lang.org/builders/mac-x64-builder [mac-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x64-builder [waterfall]: http://buildbot.nim-lang.org/waterfall diff --git a/tests/actiontable/tactiontable.nim b/tests/actiontable/tactiontable.nim index 18b0fd388..4560d0f7f 100644 --- a/tests/actiontable/tactiontable.nim +++ b/tests/actiontable/tactiontable.nim @@ -24,4 +24,3 @@ var "D": action4}.toTable actionTable["C"]("arg") - diff --git a/tests/closure/tclosure4.nim b/tests/closure/tclosure4.nim index 8e08376b6..10c7cac54 100644 --- a/tests/closure/tclosure4.nim +++ b/tests/closure/tclosure4.nim @@ -1,5 +1,5 @@ -import json, tables +import json, tables, sequtils proc run(json_params: TTable) = let json_elems = json_params["files"].elems diff --git a/tests/collections/tapply.nim b/tests/collections/tapply.nim new file mode 100644 index 000000000..2b7464216 --- /dev/null +++ b/tests/collections/tapply.nim @@ -0,0 +1,11 @@ +discard """ + output: '''true''' +""" + +import sequtils + +var x = @[1, 2, 3] +x.apply(proc(x: var int) = x = x+10) +x.apply(proc(x: int): int = x+100) +x.applyIt(it+5000) +echo x == @[5111, 5112, 5113] diff --git a/tests/collections/tmapit.nim b/tests/collections/tmapit.nim new file mode 100644 index 000000000..b2afa9429 --- /dev/null +++ b/tests/collections/tmapit.nim @@ -0,0 +1,33 @@ +discard """ + output: '''true +true''' +""" + +import sequtils + +var x = @[1, 2, 3] +# This mapIt call will run with preallocation because ``len`` is available. +var y = x.mapIt($(it+10)) +echo y == @["11", "12", "13"] + +type structureWithoutLen = object + a: array[5, int] + +iterator items(s: structureWithoutLen): int {.inline.} = + yield s.a[0] + yield s.a[1] + yield s.a[2] + yield s.a[3] + yield s.a[4] + +var st: structureWithoutLen +st.a[0] = 0 +st.a[1] = 1 +st.a[2] = 2 +st.a[3] = 3 +st.a[4] = 4 + +# this will run without preallocating the result +# since ``len`` is not available +var r = st.mapIt($(it+10)) +echo r == @["10", "11", "12", "13", "14"] diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index a10606843..a8a182a78 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -60,8 +60,12 @@ block tableTest2: t["123"] = 1.5 # test overwriting assert t["123"] == 1.5 - assert t["111"] == 0.0 # deleted + try: + echo t["111"] # deleted + except KeyError: + discard assert(not hasKey(t, "111")) + assert "123" in t assert("111" notin t) diff --git a/tests/collections/ttablesref.nim b/tests/collections/ttablesref.nim index 0b641ebc7..32494f1f2 100644 --- a/tests/collections/ttablesref.nim +++ b/tests/collections/ttablesref.nim @@ -60,8 +60,10 @@ block tableTest2: t["123"] = 1.5 # test overwriting assert t["123"] == 1.5 - assert t["111"] == 0.0 # deleted - assert "123" in t + try: + echo t["111"] # deleted + except KeyError: + discard assert(not hasKey(t, "111")) assert "111" notin t diff --git a/tests/generics/tinferredgenericprocs.nim b/tests/generics/tinferredgenericprocs.nim index 5cbeabb94..359c71ba8 100644 --- a/tests/generics/tinferredgenericprocs.nim +++ b/tests/generics/tinferredgenericprocs.nim @@ -5,6 +5,7 @@ discard """ 3''' """ +import sequtils # https://github.com/Araq/Nim/issues/797 proc foo[T](s:T):string = $s diff --git a/tests/generics/tmap_auto.nim b/tests/generics/tmap_auto.nim index dea9b571f..572556722 100644 --- a/tests/generics/tmap_auto.nim +++ b/tests/generics/tmap_auto.nim @@ -1,4 +1,4 @@ -import future +import future, sequtils let x = map(@[1, 2, 3], x => x+10) assert x == @[11, 12, 13] diff --git a/tests/misc/teventemitter.nim b/tests/misc/teventemitter.nim index 32cc81be4..7da1a2522 100644 --- a/tests/misc/teventemitter.nim +++ b/tests/misc/teventemitter.nim @@ -18,7 +18,7 @@ proc on*(emitter: var EventEmitter, event: string, if not hasKey(emitter.events, event): var list: DoublyLinkedList[proc(e: EventArgs) {.nimcall.}] add(emitter.events, event, list) #if not, add it. - append(emitter.events.mget(event), fn) + append(emitter.events[event], fn) proc initEmitter(emitter: var EventEmitter) = emitter.events = initTable[string, @@ -30,4 +30,3 @@ var initEmitter(ee) ee.on("print", proc(e: EventArgs) = echo("pie")) ee.emit("print", args) - diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index a2166576d..519a868d5 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -3,6 +3,9 @@ mode = ScriptMode.Whatif exec "gcc -v" +# test that ospaths actually compiles: +import ospaths + --forceBuild task listDirs, "lists every subdirectory": diff --git a/tests/overload/toverprc.nim b/tests/overload/toverprc.nim index 78831f744..112eae096 100644 --- a/tests/overload/toverprc.nim +++ b/tests/overload/toverprc.nim @@ -5,7 +5,7 @@ yay''' # Test overloading of procs when used as function pointers -import strutils +import strutils, sequtils proc parseInt(x: float): int {.noSideEffect.} = discard proc parseInt(x: bool): int {.noSideEffect.} = discard diff --git a/tests/parser/tcommand_as_expr.nim b/tests/parser/tcommand_as_expr.nim index 730e9cbb7..a244c8767 100644 --- a/tests/parser/tcommand_as_expr.nim +++ b/tests/parser/tcommand_as_expr.nim @@ -5,6 +5,7 @@ discard """ 77''' """ #import math +import sequtils proc optarg(x:int, y:int = 0):int = x + 3 * y proc singlearg(x:int):int = 20*x diff --git a/tests/stdlib/tmget.nim b/tests/stdlib/tmget.nim new file mode 100644 index 000000000..5792b6282 --- /dev/null +++ b/tests/stdlib/tmget.nim @@ -0,0 +1,141 @@ +discard """ + output: '''Can't access 6 +10 +11 +Can't access 6 +10 +11 +Can't access 6 +10 +11 +Can't access 6 +10 +11 +Can't access 6 +10 +11 +Can't access 6 +10 +11 +Can't access 6 +5 +Can't access 6 +10 +11 +Can't access 6 +10 +11''' +""" + +import tables + +block: + var x = initTable[int, int]() + x[5] = 10 + try: + echo x[6] + except KeyError: + echo "Can't access 6" + echo x[5] + x[5] += 1 + var c = x[5] + echo c + +block: + var x = newTable[int, int]() + x[5] = 10 + try: + echo x[6] + except KeyError: + echo "Can't access 6" + echo x[5] + x[5] += 1 + var c = x[5] + echo c + +block: + var x = initOrderedTable[int, int]() + x[5] = 10 + try: + echo x[6] + except KeyError: + echo "Can't access 6" + echo x[5] + x[5] += 1 + var c = x[5] + echo c + +block: + var x = newOrderedTable[int, int]() + x[5] = 10 + try: + echo x[6] + except KeyError: + echo "Can't access 6" + echo x[5] + x[5] += 1 + var c = x[5] + echo c + +block: + var x = initCountTable[int]() + x[5] = 10 + try: + echo x[6] + except KeyError: + echo "Can't access 6" + echo x[5] + x[5] += 1 + var c = x[5] + echo c + +block: + var x = newCountTable[int]() + x[5] = 10 + try: + echo x[6] + except KeyError: + echo "Can't access 6" + echo x[5] + x[5] += 1 + var c = x[5] + echo c + +import sets + +block: + var x = initSet[int]() + x.incl 5 + try: + echo x[6] + except KeyError: + echo "Can't access 6" + echo x[5] + +import critbits + +block: + var x: CritBitTree[int] + x["5"] = 10 + try: + echo x["6"] + except KeyError: + echo "Can't access 6" + echo x["5"] + x["5"] += 1 + var c = x["5"] + echo c + +import strtabs + +block: + var x = newStringTable() + x["5"] = "10" + try: + echo x["6"] + except KeyError: + echo "Can't access 6" + echo x["5"] + x["5"][1] = '1' + var c = x["5"] + echo c diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim index 544ad0334..27ff344e5 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -132,5 +132,5 @@ block: </Students>""") for x in d.mitems: x = <>Student(Name=x.attrs["Name"] & "foo") - d.mget(1).attrs["Name"] = "bar" + d[1].attrs["Name"] = "bar" echo d diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 4d2a2a340..4b210c23b 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -1,4 +1,4 @@ -import unittest +import unittest, sequtils proc doThings(spuds: var int): int = diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim index 0a6d694f6..df695fcd6 100644 --- a/tests/template/twrongmapit.nim +++ b/tests/template/twrongmapit.nim @@ -27,6 +27,6 @@ when ATTEMPT == 0: # bug #1543 import sequtils -(var i = @[""];i).mapIt(it) +(var i = @[""];i).applyIt(it) # now works: echo "##", i[0], "##" diff --git a/tests/testament/backend.nim b/tests/testament/backend.nim index 269835607..671b5c8b7 100644 --- a/tests/testament/backend.nim +++ b/tests/testament/backend.nim @@ -8,7 +8,7 @@ import strutils, db_sqlite, os, osproc -var db: TDbConn +var db: DbConn proc createDb() = db.exec(sql""" @@ -61,7 +61,7 @@ var proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip -proc getMachine*(db: TDbConn): MachineId = +proc getMachine*(db: DbConn): MachineId = var name = "hostname"() if name.len == 0: name = when defined(posix): getenv"HOSTNAME".string @@ -76,7 +76,7 @@ proc getMachine*(db: TDbConn): MachineId = result = db.insertId(sql"insert into Machine(name, os, cpu) values (?,?,?)", name, system.hostOS, system.hostCPU).MachineId -proc getCommit(db: TDbConn): CommitId = +proc getCommit(db: DbConn): CommitId = const commLen = "commit ".len let hash = "git log -n 1"()[commLen..commLen+10] let branch = "git symbolic-ref --short HEAD"() diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index d9fafaf7f..3bb18d8a2 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -119,12 +119,21 @@ proc gcTests(r: var TResults, cat: Category, options: string) = testSpec r, makeTest("tests/gc" / filename, options & " -d:release -d:useRealtimeGC", cat, actionRun) - template test(filename: expr): stmt = + template testWithoutBoehm(filename: expr): stmt = testWithoutMs filename testSpec r, makeTest("tests/gc" / filename, options & " --gc:markAndSweep", cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & " -d:release --gc:markAndSweep", cat, actionRun) + template test(filename: expr): stmt = + testWithoutBoehm filename + when not defined(windows): + # AR: cannot find any boehm.dll on the net, right now, so disabled + # for windows: + testSpec r, makeTest("tests/gc" / filename, options & + " --gc:boehm", cat, actionRun) + testSpec r, makeTest("tests/gc" / filename, options & + " -d:release --gc:boehm", cat, actionRun) test "gcemscripten" test "growobjcrash" @@ -136,9 +145,9 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "gcleak4" # Disabled because it works and takes too long to run: #test "gcleak5" - test "weakrefs" + testWithoutBoehm "weakrefs" test "cycleleak" - test "closureleak" + testWithoutBoehm "closureleak" testWithoutMs "refarrayleak" test "stackrefleak" diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim index 98ccf1170..15960f09a 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -109,7 +109,7 @@ div.tabContent.hide { display: none; } proc td(s: string): string = result = "<td>" & s.substr(0, 200).xmlEncode & "</td>" -proc getCommit(db: TDbConn, c: int): string = +proc getCommit(db: DbConn, c: int): string = var commit = c for thisCommit in db.rows(sql"select id from [Commit] order by id desc"): if commit == 0: result = thisCommit[0] diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index a5e622010..451bee1d1 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -65,7 +65,7 @@ proc callCompiler(cmdTemplate, filename, options: string, let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell]) var p = startProcess(command=c[0], args=c[1.. ^1], - options={poStdErrToStdOut, poUseShell}) + options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream var suc = "" var err = "" @@ -215,7 +215,7 @@ proc generatedFile(path, name: string, target: TTarget): string = proc codegenCheck(test: TTest, check: string, given: var TSpec) = try: - let (path, name, ext2) = test.name.splitFile + let (path, name, _) = test.name.splitFile let genFile = generatedFile(path, name, test.target) let contents = readFile(genFile).string if check[0] == '\\': @@ -307,7 +307,7 @@ proc testSpec(r: var TResults, test: TTest) = let isJsTarget = test.target == targetJS var exeFile: string if isJsTarget: - let (dir, file, ext) = splitFile(tname) + let (dir, file, _) = splitFile(tname) exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache" else: exeFile = changeFileExt(tname, ExeExt) @@ -352,7 +352,7 @@ proc testSpec(r: var TResults, test: TTest) = proc testNoSpec(r: var TResults, test: TTest) = # does not extract the spec because the file is not supposed to have any - let tname = test.name.addFileExt(".nim") + #let tname = test.name.addFileExt(".nim") inc(r.total) let given = callCompiler(cmdTemplate, test.name, test.options, test.target) r.addResult(test, "", given.msg, given.err) @@ -368,7 +368,7 @@ proc testC(r: var TResults, test: TTest) = r.addResult(test, "", given.msg, given.err) elif test.action == actionRun: let exeFile = changeFileExt(test.name, ExeExt) - var (buf, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUseShell}) + var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath}) if exitCode != 0: given.err = reExitCodesDiffer if given.err == reSuccess: inc(r.passed) diff --git a/web/news.txt b/web/news.txt index 2b6079620..89e075b6c 100644 --- a/web/news.txt +++ b/web/news.txt @@ -8,7 +8,11 @@ News Changes affecting backwards compatibility ----------------------------------------- - + - ``tables.[]``, ``strtabs.[]``, ``critbits.[]`` **now raise** + the ``KeyError`` **exception when the key does not exist**! Use the + new ``getOrDefault`` instead to get the old behaviour. Compile all your + code with ``-d:nimTableGet`` to get a listing of where your code + uses ``[]``! - The ``rawsockets`` module has been renamed to ``nativesockets`` to avoid confusion with TCP/IP raw sockets, so ``newNativeSocket`` should be used instead of ``newRawSocket``. @@ -68,10 +72,10 @@ News * ``libeay32.dll``: Split into ``libeay32.dll`` and ``libeay64.dll``. Compile with ``-d:nimOldDLLs`` to make the stdlib use the old DLL names. - - Nim VM now treats objects as nkObjConstr nodes, and not nkPar nodes as it - was previously. Macros that generate nkPar nodes when object is expected are - likely to break. Macros that expect nkPar nodes to which objects are passed - are likely to break as well. + - Nim VM now treats objects as ``nkObjConstr`` nodes, and not ``nkPar`` nodes + as it was previously. Macros that generate ``nkPar`` nodes when object is + expected are likely to break. Macros that expect ``nkPar`` nodes to which + objects are passed are likely to break as well. - Base methods now need to be annotated with the ``base`` pragma. This makes multi methods less error-prone to use with the effect system. - Nim's parser directive ``#!`` is now ``#?`` in order to produce no conflicts @@ -86,8 +90,14 @@ News echo f(0, "abc") - The ``ftpclient`` module is now deprecated in favour of the - ``asyncdispatch`` module. - + ``asyncftpclient`` module. + - In sequtils.nim renamed ``repeat`` function to ``cycle`` (concatenating + a sequence by itself the given times), and also introduced ``repeat``, + which repeats an element the given times. + - The function ``map`` is moved to sequtils.nim. The inplace ``map`` version + is renamed to ``apply``. + - The template ``mapIt`` now doesn't require the result's type parameter. + Also the inplace ``mapIt`` is renamed to ``apply``. Library Additions ----------------- diff --git a/web/website.ini b/web/website.ini index dcfea8bf4..0a2117564 100644 --- a/web/website.ini +++ b/web/website.ini @@ -34,8 +34,8 @@ doc: "tools;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.txt" doc: "nimfix.txt;nimsuggest.txt;nep1.txt;nims.txt" pdf: "manual.txt;lib;tut1;tut2;nimc;niminst;gc" srcdoc2: "system.nim;system/nimscript;pure/ospaths" -srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" -srcdoc2: "impure/re;pure/sockets;pure/typetraits" +srcdoc2: "core/macros;pure/marshal;core/typeinfo" +srcdoc2: "impure/re;pure/typetraits" srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim" srcdoc: "system/threads.nim;system/channels.nim;js/dom" srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm" @@ -52,12 +52,13 @@ srcdoc2: "pure/json;pure/base64;pure/scgi" srcdoc2: "pure/collections/tables;pure/collections/sets;pure/collections/lists" srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings" srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies" -srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits" -srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri" +srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits" +srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;pure/oids;pure/endians;pure/uri" srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" srcdoc2: "packages/docutils/rst;packages/docutils/rstast" srcdoc2: "packages/docutils/rstgen;pure/logging;pure/asyncdispatch;pure/asyncnet" -srcdoc2: "pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future" +srcdoc2: "deprecated/pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future" +srcdoc2: "deprecated/pure/ftpclient" srcdoc2: "pure/asyncfile" srcdoc2: "pure/md5;pure/rationals" srcdoc2: "posix/posix" |