Merge remote-tracking branch 'origin/devel' into appveyor

This commit is contained in:
Aman Gupta 2015-10-13 15:25:40 -07:00
commit e2dbf222e6
59 changed files with 1127 additions and 324 deletions

View file

@ -75,7 +75,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
""" % [config["doc.googleAnalytics"]] """ % [config.getOrDefault"doc.googleAnalytics"]
else: else:
result.analytics = "" result.analytics = ""

View file

@ -121,7 +121,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
if s.kind != skEnumField: if s.kind != skEnumField:
if s.kind notin ExportableSymKinds: if s.kind notin ExportableSymKinds:
internalError(s.info, "importAllSymbols: " & $s.kind) 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) rawImportSymbol(c, s)
s = nextIter(i, fromMod.tab) s = nextIter(i, fromMod.tab)
@ -138,7 +138,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
let s = a.sym let s = a.sym
if s.kind == skModule: if s.kind == skModule:
importAllSymbolsExcept(c, s, exceptSet) 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) rawImportSymbol(c, s)
of nkExportExceptStmt: of nkExportExceptStmt:
localError(n.info, errGenerated, "'export except' not implemented") localError(n.info, errGenerated, "'export except' not implemented")

View file

@ -15,7 +15,7 @@ import
wordrecg, sem, semdata, idents, passes, docgen, extccomp, wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, jsgen, json, nversion, cgen, jsgen, json, nversion,
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, 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 from magicsys import systemModule, resetSysTypes

View file

@ -48,7 +48,7 @@ proc addPackage(packages: StringTableRef, p: string) =
let name = p.substr(0, x-1) let name = p.substr(0, x-1)
if x < p.len: if x < p.len:
let version = p.substr(x+1) let version = p.substr(x+1)
if packages[name] <. version: if packages.getOrDefault(name) <. version:
packages[name] = version packages[name] = version
else: else:
packages[name] = latest packages[name] = latest

View file

@ -86,7 +86,7 @@ type # please make sure we have under 32 options
gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
IdeCmd* = enum IdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse ideNone, ideSug, ideCon, ideDef, ideUse, ideDus
var var
gIdeCmd*: IdeCmd gIdeCmd*: IdeCmd
@ -173,7 +173,7 @@ proc existsConfigVar*(key: string): bool =
result = hasKey(gConfigVars, key) result = hasKey(gConfigVars, key)
proc getConfigVar*(key: string): string = proc getConfigVar*(key: string): string =
result = gConfigVars[key] result = gConfigVars.getOrDefault key
proc setConfigVar*(key, val: string) = proc setConfigVar*(key, val: string) =
gConfigVars[key] = val gConfigVars[key] = val
@ -421,6 +421,7 @@ proc parseIdeCmd*(s: string): IdeCmd =
of "con": ideCon of "con": ideCon
of "def": ideDef of "def": ideDef
of "use": ideUse of "use": ideUse
of "dus": ideDus
else: ideNone else: ideNone
proc `$`*(c: IdeCmd): string = proc `$`*(c: IdeCmd): string =
@ -429,4 +430,5 @@ proc `$`*(c: IdeCmd): string =
of ideCon: "con" of ideCon: "con"
of ideDef: "def" of ideDef: "def"
of ideUse: "use" of ideUse: "use"
of ideDus: "dus"
of ideNone: "none" of ideNone: "none"

View file

@ -372,7 +372,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
else: else:
internalError(info, "decodeSym: no ident") internalError(info, "decodeSym: no ident")
#echo "decoding: {", ident.s #echo "decoding: {", ident.s
result = r.syms[id] result = r.syms.getOrDefault(id)
if result == nil: if result == nil:
new(result) new(result)
result.id = id result.id = id
@ -491,7 +491,7 @@ proc processCompilerProcs(r: PRodReader, module: PSym) =
inc(r.pos) inc(r.pos)
var key = decodeVInt(r.s, r.pos) var key = decodeVInt(r.s, r.pos)
inc(r.pos) # #10 inc(r.pos) # #10
var s = r.syms[key] var s = r.syms.getOrDefault(key)
if s == nil: if s == nil:
s = newStub(r, w, key) s = newStub(r, w, key)
s.owner = module s.owner = module
@ -737,7 +737,7 @@ proc getReader(moduleId: int): PRodReader =
return nil return nil
proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
result = r.syms[id] result = r.syms.getOrDefault(id)
if result == nil: if result == nil:
# load the symbol: # load the symbol:
var d = iiTableGet(r.index.tab, id) var d = iiTableGet(r.index.tab, id)

View file

@ -309,6 +309,10 @@ proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
findUsages(info, s) findUsages(info, s)
elif gIdeCmd == ideDef: elif gIdeCmd == ideDef:
findDefinition(info, s) 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) = proc markUsed(info: TLineInfo; s: PSym) =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -366,7 +370,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
suggestCall(c, a, n, outputs) suggestCall(c, a, n, outputs)
dec(c.compilesContextId) 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) = proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n) suggestExpr(c, n)

View file

@ -825,11 +825,13 @@ proc flattenStmts(n: PNode) =
var goOn = true var goOn = true
while goOn: while goOn:
goOn = false goOn = false
for i in 0..<n.len: var i = 0
while i < n.len:
let it = n[i] let it = n[i]
if it.kind in {nkStmtList, nkStmtListExpr}: if it.kind in {nkStmtList, nkStmtListExpr}:
n.sons[i..i] = it.sons[0..<it.len] n.sons[i..i] = it.sons[0..<it.len]
goOn = true goOn = true
inc i
proc liftDeferAux(n: PNode) = proc liftDeferAux(n: PNode) =
if n.kind in {nkStmtList, nkStmtListExpr}: if n.kind in {nkStmtList, nkStmtListExpr}:

View file

@ -30,6 +30,14 @@ proc setResult*(a: VmArgs; v: string) =
s[a.ra].node = newNode(nkStrLit) s[a.ra].node = newNode(nkStrLit)
s[a.ra].node.strVal = v 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]) = proc setResult*(a: VmArgs; v: seq[string]) =
var s: seq[TFullReg] var s: seq[TFullReg]
move(s, cast[seq[TFullReg]](a.slots)) move(s, cast[seq[TFullReg]](a.slots))

View file

@ -239,7 +239,7 @@ proc loadAny(p: var JsonParser, t: PType,
result = newNode(nkNilLit) result = newNode(nkNilLit)
next(p) next(p)
of jsonInt: of jsonInt:
result = tab[p.getInt] result = tab.getOrDefault(p.getInt)
if result.isNil: if result.isNil:
raiseParseErr(p, "cannot load object with address " & $p.getInt) raiseParseErr(p, "cannot load object with address " & $p.getInt)
next(p) next(p)

View file

@ -1,6 +1,6 @@
# Test high level features # Test high level features
import strutils import strutils, sequtils
echo "Give a list of numbers (separated by spaces): " echo "Give a list of numbers (separated by spaces): "
stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!") stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!")

View file

@ -28,6 +28,8 @@
import import
pcre, strutils, rtarrays pcre, strutils, rtarrays
{.deprecated.}
const const
MaxSubpatterns* = 20 MaxSubpatterns* = 20
## defines the maximum number of subpatterns that can be captured. ## defines the maximum number of subpatterns that can be captured.
@ -46,7 +48,7 @@ type
h: ptr Pcre h: ptr Pcre
e: ptr ExtraData e: ptr ExtraData
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression Regex* = ref RegexDesc ## a compiled regular expression
RegexError* = object of ValueError RegexError* = object of ValueError
## is raised if the pattern is no valid regular expression. ## is raised if the pattern is no valid regular expression.

View file

@ -139,7 +139,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
g.seenIndexTerms = initTable[string, int]() g.seenIndexTerms = initTable[string, int]()
g.msgHandler = msgHandler g.msgHandler = msgHandler
let s = config["split.item.toc"] let s = config.getOrDefault"split.item.toc"
if s != "": g.splitAfter = parseInt(s) if s != "": g.splitAfter = parseInt(s)
for i in low(g.meta)..high(g.meta): g.meta[i] = "" 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. ## previously appeared to give a different identifier value for each.
let refname = n.rstnodeToRefname let refname = n.rstnodeToRefname
if d.seenIndexTerms.hasKey(refname): if d.seenIndexTerms.hasKey(refname):
d.seenIndexTerms[refname] = d.seenIndexTerms[refname] + 1 d.seenIndexTerms[refname] = d.seenIndexTerms.getOrDefault(refname) + 1
else: else:
d.seenIndexTerms[refname] = 1 d.seenIndexTerms[refname] = 1
let id = refname & '_' & $d.seenIndexTerms[refname] let id = refname & '_' & $d.seenIndexTerms.getOrDefault(refname)
var term = "" var term = ""
renderAux(d, n, term) renderAux(d, n, term)
@ -518,7 +518,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string =
sort(titles, cmp) sort(titles, cmp)
for title in titles: for title in titles:
let tocList = generateDocumentationTOC(docs[title]) let tocList = generateDocumentationTOC(docs.getOrDefault(title))
result.add("<ul><li><a href=\"" & result.add("<ul><li><a href=\"" &
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n") 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, dispA(d.target, result,
"""<img src="$1" width="15" """<img src="$1" width="15"
height="17" hspace="2" vspace="2" class="smiley" />""", 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) = proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
## Parses useful fields which can appear before a code block. ## 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 inc d.listingCounter
let id = $d.listingCounter let id = $d.listingCounter
if not params.numberLines: if not params.numberLines:
result = (d.config["doc.listing_start"] % id, result = (d.config.getOrDefault"doc.listing_start" % id,
d.config["doc.listing_end"] % id) d.config.getOrDefault"doc.listing_end" % id)
return return
var codeLines = 1 + code.strip.countLines var codeLines = 1 + code.strip.countLines
@ -856,9 +857,11 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
result.beginTable.add($line & "\n") result.beginTable.add($line & "\n")
line.inc line.inc
codeLines.dec codeLines.dec
result.beginTable.add("</pre></td><td>" & (d.config["doc.listing_start"] % id)) result.beginTable.add("</pre></td><td>" & (
result.endTable = (d.config["doc.listing_end"] % id) & d.config.getOrDefault"doc.listing_start" % id))
"</td></tr></tbody></table>" & (d.config["doc.listing_button"] % 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) = proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
## Renders a code block, appending it to `result`. ## Renders a code block, appending it to `result`.

View file

@ -212,7 +212,7 @@ proc processClient(client: AsyncSocket, address: string,
if request.reqMethod == "post": if request.reqMethod == "post":
# Check for Expect header # Check for Expect header
if request.headers.hasKey("Expect"): 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") await client.sendStatus("100 Continue")
else: else:
await client.sendStatus("417 Expectation Failed") await client.sendStatus("417 Expectation Failed")
@ -221,7 +221,8 @@ proc processClient(client: AsyncSocket, address: string,
# - Check for Content-length header # - Check for Content-length header
if request.headers.hasKey("Content-Length"): if request.headers.hasKey("Content-Length"):
var contentLength = 0 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.") await request.respond(Http400, "Bad Request. Invalid Content-Length.")
continue continue
else: else:
@ -232,16 +233,18 @@ proc processClient(client: AsyncSocket, address: string,
continue continue
case request.reqMethod 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) await callback(request)
else: else:
await request.respond(Http400, "Invalid request method. Got: " & request.reqMethod) await request.respond(Http400, "Invalid request method. Got: " &
request.reqMethod)
# Persistent connections # Persistent connections
if (request.protocol == HttpVer11 and if (request.protocol == HttpVer11 and
request.headers["connection"].normalize != "close") or request.headers.getOrDefault("connection").normalize != "close") or
(request.protocol == HttpVer10 and (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 # In HTTP 1.1 we assume that connection is persistent. Unless connection
# header states otherwise. # header states otherwise.
# In HTTP 1.0 we assume that the connection should not be persistent. # In HTTP 1.0 we assume that the connection should not be persistent.

View file

@ -387,7 +387,7 @@ var
proc getCookie*(name: string): TaintedString = proc getCookie*(name: string): TaintedString =
## Gets a cookie. If no cookie of `name` exists, "" is returned. ## Gets a cookie. If no cookie of `name` exists, "" is returned.
if gcookies == nil: gcookies = parseCookies(getHttpCookie()) if gcookies == nil: gcookies = parseCookies(getHttpCookie())
result = TaintedString(gcookies[name]) result = TaintedString(gcookies.getOrDefault(name))
proc existsCookie*(name: string): bool = proc existsCookie*(name: string): bool =
## Checks if a cookie of `name` exists. ## Checks if a cookie of `name` exists.

View file

@ -11,6 +11,8 @@
## container for a set or a mapping of strings. Based on the excellent paper ## container for a set or a mapping of strings. Based on the excellent paper
## by Adam Langley. ## by Adam Langley.
include "system/inclrtl"
type type
NodeObj[T] = object {.acyclic.} NodeObj[T] = object {.acyclic.}
byte: int ## byte index of the difference 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) var n = rawInsert(c, key)
n.val = val n.val = val
proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = template get[T](c: CritBitTree[T], key: string): T {.immediate.} =
## 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.
let n = rawGet(c, key) let n = rawGet(c, key)
if n != nil: result = n.val 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. ## retrieves the value at ``c[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised. ## If `key` is not in `t`, the ``KeyError`` exception is raised.
let n = rawGet(c, key) get(c, key)
if n != nil: result = n.val
else: raise newException(KeyError, "key not found: " & $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) = proc excl*[T](c: var CritBitTree[T], key: string) =
## removes `key` (and its associated value) from the set `c`. ## removes `key` (and its associated value) from the set `c`.

View file

@ -138,6 +138,8 @@ proc initIntSet*: IntSet =
result.counter = 0 result.counter = 0
result.head = nil result.head = nil
proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil
proc assign*(dest: var IntSet, src: IntSet) = proc assign*(dest: var IntSet, src: IntSet) =
## copies `src` to `dest`. `dest` does not need to be initialized by ## copies `src` to `dest`. `dest` does not need to be initialized by
## `initIntSet`. ## `initIntSet`.

View file

@ -47,7 +47,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
result[i] = itm result[i] = itm
inc(i) 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. ## Returns a new sequence with the items of `s` repeated `n` times.
## ##
## Example: ## Example:
@ -56,15 +56,29 @@ proc repeat*[T](s: seq[T], n: Natural): seq[T] =
## ##
## let ## let
## s = @[1, 2, 3] ## s = @[1, 2, 3]
## total = s.repeat(3) ## total = s.cycle(3)
## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3] ## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
result = newSeq[T](n * s.len) result = newSeq[T](n * s.len)
var o = 0 var o = 0
for x in 1..n: for x in 0..<n:
for e in s: for e in s:
result[o] = e result[o] = e
inc o 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] = proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates. ## 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 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 = iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
## Iterates through a sequence and yields every item that fulfills the ## 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): ## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
## echo($n) ## echo($n)
## # echoes 4, 8, 4 in separate lines ## # echoes 4, 8, 4 in separate lines
for i in countup(0, len(seq1)-1): for i in 0..<seq1.len:
var item = seq1[i] if pred(seq1[i]):
if pred(item): yield 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. ## Returns a new sequence with all the items that fulfilled the predicate.
## ##
## Example: ## 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 ## f2 = filter(colors) do (x: string) -> bool : x.len > 5
## assert f1 == @["red", "black"] ## assert f1 == @["red", "black"]
## assert f2 == @["yellow"] ## 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. ## Keeps the items in the passed sequence if they fulfilled the predicate.
## Same as the ``filter`` proc, but modifies the sequence directly. ## 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): for i in 0 .. <len(seq1):
if pred(seq1[i]): if pred(seq1[i]):
if pos != i: if pos != i:
seq1[pos] = seq1[i] shallowCopy(seq1[pos], seq1[i])
inc(pos) inc(pos)
setLen(seq1, pos) setLen(seq1, pos)
@ -268,7 +358,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
inc(j) 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. ## Returns a new sequence with all the items that fulfilled the predicate.
## ##
## Unlike the `proc` version, the predicate needs to be an expression using ## 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) ## notAcceptable = filterIt(temperatures, it > 50 or it < -10)
## assert acceptable == @[-2.0, 24.5, 44.31] ## assert acceptable == @[-2.0, 24.5, 44.31]
## assert notAcceptable == @[-272.15, 99.9, -113.44] ## 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): for it {.inject.} in items(seq1):
if pred: result.add(it) if pred: result.add(it)
result result
template keepItIf*(varSeq, pred: expr) = template keepItIf*(varSeq: seq, pred: expr) =
## Convenience template around the ``keepIf`` proc to reduce typing. ## Convenience template around the ``keepIf`` proc to reduce typing.
## ##
## Unlike the `proc` version, the predicate needs to be an expression using ## 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] let it {.inject.} = varSeq[i]
if pred: if pred:
if pos != i: if pos != i:
varSeq[pos] = varSeq[i] shallowCopy(varSeq[pos], varSeq[i])
inc(pos) inc(pos)
setLen(varSeq, 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.} = template toSeq*(iter: expr): expr {.immediate.} =
## Transforms any iterator into a sequence. ## Transforms any iterator into a sequence.
@ -320,14 +471,19 @@ template toSeq*(iter: expr): expr {.immediate.} =
## if x mod 2 == 1: ## if x mod 2 == 1:
## result = true) ## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9] ## assert odd_numbers == @[1, 3, 5, 7, 9]
##
## **Note**: Since this is an immediate macro, you cannot always invoke this when compiles(iter.len):
## as ``x.toSeq``, depending on the ``x``. var i = 0
## See `this <manual.html#limitations-of-the-method-call-syntax>`_ var result = newSeq[type(iter)](iter.len)
## for an explanation. for x in iter:
var result {.gensym.}: seq[type(iter)] = @[] result[i] = x
for x in iter: add(result, x) inc i
result result
else:
var result: seq[type(iter)] = @[]
for x in iter:
result.add(x)
result
template foldl*(sequence, operation: expr): expr = template foldl*(sequence, operation: expr): expr =
## Template to fold a sequence from left to right, returning the accumulation. ## 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" assert sequence.len > 0, "Can't fold empty sequences"
var result {.gensym.}: type(sequence[0]) var result {.gensym.}: type(sequence[0])
result = sequence[0] result = sequence[0]
for i in countup(1, sequence.len - 1): for i in 1..<sequence.len:
let let
a {.inject.} = result a {.inject.} = result
b {.inject.} = sequence[i] b {.inject.} = sequence[i]
@ -401,7 +557,7 @@ template foldr*(sequence, operation: expr): expr =
result = operation result = operation
result 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. ## Convenience template around the ``map`` proc to reduce typing.
## ##
## The template injects the ``it`` variable which you can use directly in an ## 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] ## nums = @[1, 2, 3, 4]
## strings = nums.mapIt(string, $(4 * it)) ## strings = nums.mapIt(string, $(4 * it))
## assert strings == @["4", "8", "12", "16"] ## assert strings == @["4", "8", "12", "16"]
## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)``
## template instead.
var result {.gensym.}: seq[typ] = @[] var result {.gensym.}: seq[typ] = @[]
for it {.inject.} in items(seq1): for it {.inject.} in items(seq1):
result.add(op) result.add(op)
result 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 ## 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 ## expression. The expression has to return the same type as the sequence you
@ -428,12 +616,14 @@ template mapIt*(varSeq, op: expr) =
## ##
## .. code-block:: ## .. code-block::
## var nums = @[1, 2, 3, 4] ## var nums = @[1, 2, 3, 4]
## nums.mapIt(it * 3) ## nums.applyIt(it * 3)
## assert nums[0] + nums[3] == 15 ## assert nums[0] + nums[3] == 15
for i in 0 .. <len(varSeq): for i in 0 .. <varSeq.len:
let it {.inject.} = varSeq[i] let it {.inject.} = varSeq[i]
varSeq[i] = op varSeq[i] = op
template newSeqWith*(len: int, init: expr): expr = template newSeqWith*(len: int, init: expr): expr =
## creates a new sequence, calling `init` to initialize each value. Example: ## 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') keepItIf(candidates, it.len == 3 and it[0] == 'b')
assert candidates == @["bar", "baz"] 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 block: # toSeq test
let let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
@ -568,8 +790,8 @@ when isMainModule:
block: # mapIt tests block: # mapIt tests
var var
nums = @[1, 2, 3, 4] nums = @[1, 2, 3, 4]
strings = nums.mapIt(string, $(4 * it)) strings = nums.mapIt($(4 * it))
nums.mapIt(it * 3) nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15 assert nums[0] + nums[3] == 15
block: # distribute tests block: # distribute tests
@ -605,15 +827,19 @@ when isMainModule:
seq2D[0][1] = true seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # repeat tests block: # cycle tests
let let
a = @[1, 2, 3] a = @[1, 2, 3]
b: seq[int] = @[] b: seq[int] = @[]
doAssert a.repeat(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.repeat(0) == @[] doAssert a.cycle(0) == @[]
#doAssert a.repeat(-1) == @[] # will not compile! #doAssert a.cycle(-1) == @[] # will not compile!
doAssert b.repeat(3) == @[] 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): when not defined(testing):
echo "Finished doc tests" echo "Finished doc tests"

View file

@ -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.} = proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
rawGetImpl() 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 ## 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 ## when one overloaded 'hash' and '==' but still needs reference semantics
## for sharing. ## for sharing.
assert s.isValid, "The set needs to be initialized." 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 if index >= 0: result = s.data[index].key
else: raise newException(KeyError, "key not found: " & $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 = proc contains*[A](s: HashSet[A], key: A): bool =
## Returns true iff `key` is in `s`. ## Returns true iff `key` is in `s`.
## ##

View file

@ -68,6 +68,8 @@
import import
hashes, math hashes, math
include "system/inclrtl"
type type
KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B] KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, 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`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
proc `[]`*[A, B](t: Table[A, B], key: A): B = template get(t, key): untyped {.immediate.} =
## 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 =
## retrieves the value at ``t[key]``. The value can be modified. ## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised. ## If `key` is not in `t`, the ``KeyError`` exception is raised.
mixin rawGet
var hc: Hash var hc: Hash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val 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: else:
raise newException(KeyError, "key not found") 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 = 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`. ## iterates over any value in the table `t` that belongs to the given `key`.
var h: Hash = hash(key) and high(t.data) 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): for h in 0..high(t.data):
if isFilled(t.data[h].hcode): yield t.data[h].val if isFilled(t.data[h].hcode): yield t.data[h].val
proc `[]`*[A, B](t: TableRef[A, B], key: A): B = proc `[]`*[A, B](t: TableRef[A, B], key: A): var B {.deprecatedGet.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## default empty value for the type `B` is returned ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## and no exception is raised. One can check with ``hasKey`` whether the key ## the key exists.
## exists.
result = t[][key] 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. ## 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.
t[].mget(key) ## 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 = 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 ## 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 = proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
rawGetImpl() rawGetImpl()
proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B {.deprecatedGet.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## default empty value for the type `B` is returned ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## and no exception is raised. One can check with ``hasKey`` whether the key ## the key exists.
## exists. get(t, key)
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
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. ## 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.
var hc: Hash get(t, key)
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B {.deprecated.} =
else: raise newException(KeyError, "key not found: " & $key) ## 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 = proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
@ -572,17 +597,20 @@ iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
forAllOrderedPairs: forAllOrderedPairs:
yield t.data[h].val yield t.data[h].val
proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): B = proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## default empty value for the type `B` is returned ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## and no exception is raised. One can check with ``hasKey`` whether the key ## the key exists.
## exists.
result = t[][key] 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. ## 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.
result = t[].mget(key) ## 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 = 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 ## 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)) h = nextTry(h, high(t.data))
result = -1 - h # < 0 => MISSING; insert idx = -1 - result result = -1 - h # < 0 => MISSING; insert idx = -1 - result
proc `[]`*[A](t: CountTable[A], key: A): int = template ctget(t, key: untyped): untyped {.immediate.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## 0 is returned. One can check with ``hasKey`` whether the key
## exists.
var index = rawGet(t, key) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val 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 mget*[A](t: var CountTable[A], key: A): var int = 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 `[]`*[A](t: var CountTable[A], key: A): var int {.deprecatedGet.} =
## retrieves the value at ``t[key]``. The value can be modified. ## 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) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val 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 = proc hasKey*[A](t: CountTable[A], key: A): bool =
## returns true iff `key` is in the table `t`. ## 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): for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].val if t.data[h].val != 0: yield t.data[h].val
proc `[]`*[A](t: CountTableRef[A], key: A): int = proc `[]`*[A](t: CountTableRef[A], key: A): var int {.deprecatedGet.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, ## retrieves the value at ``t[key]``. The value can be modified.
## 0 is returned. One can check with ``hasKey`` whether the key ## If `key` is not in `t`, the ``KeyError`` exception is raised.
## exists.
result = t[][key] 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. ## 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.
result = t[].mget(key) ## 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 = proc hasKey*[A](t: CountTableRef[A], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.

View file

@ -8,9 +8,10 @@
# #
## This module implements efficient computations of hash values for diverse ## This module implements efficient computations of hash values for diverse
## Nim types. All the procs are based on these two building blocks: the `!& ## Nim types. All the procs are based on these two building blocks:
## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_ ## - `!& proc <#!&>`_ used to start or mix a hash value, and
## used to *finish* the hash value. If you want to implement hash procs for ## - `!$ 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 ## your custom types you will end up writing the following kind of skeleton of
## code: ## code:
## ##
@ -108,7 +109,7 @@ proc hash*(x: int): Hash {.inline.} =
result = x result = x
proc hash*(x: int64): Hash {.inline.} = proc hash*(x: int64): Hash {.inline.} =
## efficient hashing of integers ## efficient hashing of int64 integers
result = toU32(x) result = toU32(x)
proc hash*(x: char): Hash {.inline.} = proc hash*(x: char): Hash {.inline.} =
@ -126,6 +127,16 @@ proc hash*(x: string): Hash =
h = h !& ord(x[i]) h = h !& ord(x[i])
result = !$h 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 = proc hashIgnoreStyle*(x: string): Hash =
## efficient hashing of strings; style is ignored ## efficient hashing of strings; style is ignored
var h: Hash = 0 var h: Hash = 0
@ -145,6 +156,27 @@ proc hashIgnoreStyle*(x: string): Hash =
result = !$h 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 = proc hashIgnoreCase*(x: string): Hash =
## efficient hashing of strings; case is ignored ## efficient hashing of strings; case is ignored
var h: Hash = 0 var h: Hash = 0
@ -155,7 +187,22 @@ proc hashIgnoreCase*(x: string): Hash =
h = h !& ord(c) h = h !& ord(c)
result = !$h 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.} = proc hash*(x: float): Hash {.inline.} =
## efficient hashing of floats.
var y = x + 1.0 var y = x + 1.0
result = cast[ptr Hash](addr(y))[] result = cast[ptr Hash](addr(y))[]
@ -173,10 +220,29 @@ proc hash*[T: tuple](x: T): Hash =
result = !$result result = !$result
proc hash*[A](x: openArray[A]): Hash = proc hash*[A](x: openArray[A]): Hash =
## efficient hashing of arrays and sequences.
for it in items(x): result = result !& hash(it) for it in items(x): result = result !& hash(it)
result = !$result 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 = proc hash*[A](x: set[A]): Hash =
## efficient hashing of sets.
for it in items(x): result = result !& hash(it) for it in items(x): result = result !& hash(it)
result = !$result 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) )

View file

@ -77,7 +77,7 @@ type
## console ## console
FileLogger* = ref object of Logger ## logger that writes the messages to a file 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 RollingFileLogger* = ref object of FileLogger ## logger that writes the
## messages to a file and ## messages to a file and
@ -92,7 +92,9 @@ type
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} 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 var msgLen = 0
for arg in args: for arg in args:
msgLen += arg.len 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, `$`]) {. method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
raises: [Exception], raises: [Exception],
tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} = tags: [TimeEffect, WriteIOEffect, ReadIOEffect], base.} =
## Override this method in custom loggers. Default implementation does ## Override this method in custom loggers. Default implementation does
## nothing. ## nothing.
discard discard
@ -133,15 +135,17 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
## Logs to the console using ``logger`` only. ## Logs to the console using ``logger`` only.
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
writeLine(stdout, substituteLog(logger.fmtStr, level, args)) writeLine(stdout, substituteLog(logger.fmtStr, level, args))
if level in {lvlError, lvlFatal}: flushFile(stdout)
method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) = method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) =
## Logs to a file using ``logger`` only. ## Logs to a file using ``logger`` only.
if level >= logger.levelThreshold: 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 = proc defaultFilename*(): string =
## Returns the default filename for a logger. ## Returns the default filename for a logger.
var (path, name, ext) = splitFile(getAppFilename()) var (path, name, _) = splitFile(getAppFilename())
result = changeFileExt(path / name, "log") result = changeFileExt(path / name, "log")
proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): ConsoleLogger = 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). ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
new(result) new(result)
result.levelThreshold = levelThreshold result.levelThreshold = levelThreshold
result.f = open(filename, mode, bufSize = bufSize) result.file = open(filename, mode, bufSize = bufSize)
result.fmtStr = fmtStr result.fmtStr = fmtStr
# ------ # ------
proc countLogLines(logger: RollingFileLogger): int = proc countLogLines(logger: RollingFileLogger): int =
result = 0 result = 0
for line in logger.f.lines(): for line in logger.file.lines():
result.inc() result.inc()
proc countFiles(filename: string): int = proc countFiles(filename: string): int =
@ -200,7 +204,7 @@ proc newRollingFileLogger*(filename = defaultFilename(),
result.fmtStr = fmtStr result.fmtStr = fmtStr
result.maxLines = maxLines result.maxLines = maxLines
result.bufSize = bufSize result.bufSize = bufSize
result.f = open(filename, mode, bufSize=result.bufSize) result.file = open(filename, mode, bufSize=result.bufSize)
result.curLine = 0 result.curLine = 0
result.baseName = filename result.baseName = filename
result.baseMode = mode result.baseMode = mode
@ -222,13 +226,14 @@ method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`])
## Logs to a file using rolling ``logger`` only. ## Logs to a file using rolling ``logger`` only.
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
if logger.curLine >= logger.maxLines: if logger.curLine >= logger.maxLines:
logger.f.close() logger.file.close()
rotate(logger) rotate(logger)
logger.logFiles.inc logger.logFiles.inc
logger.curLine = 0 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 logger.curLine.inc
# -------- # --------

View file

@ -176,7 +176,7 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
setPointer(a, nil) setPointer(a, nil)
next(p) next(p)
of jsonInt: of jsonInt:
setPointer(a, t[p.getInt]) setPointer(a, t.getOrDefault(p.getInt))
next(p) next(p)
of jsonArrayStart: of jsonArrayStart:
next(p) next(p)

View file

@ -9,8 +9,6 @@
## Module for computing MD5 checksums. ## Module for computing MD5 checksums.
import unsigned
type type
MD5State = array[0..3, uint32] MD5State = array[0..3, uint32]
MD5Block = array[0..15, uint32] MD5Block = array[0..15, uint32]

View file

@ -499,7 +499,7 @@ proc newMimetypes*(): MimeDB =
proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string = proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext`` ## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
## could not be found. ## could not be found.
result = mimedb.mimes[ext] result = mimedb.mimes.getOrDefault(ext)
if result == "": if result == "":
return default return default

View file

@ -26,8 +26,10 @@ when not declared(getEnv) or defined(nimscript):
## to an environment variable ## to an environment variable
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
## operation to the directory structure ## operation to the directory
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to ## structure
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
## operation to
## the directory structure ## the directory structure
OSErrorCode* = distinct int32 ## Specifies an OS Error Code. OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
@ -63,13 +65,13 @@ when not declared(getEnv) or defined(nimscript):
AltSep* = '/' AltSep* = '/'
## An alternative character used by the operating system to separate ## An alternative character used by the operating system to separate
## pathname components, or the same as `DirSep` if only one separator ## pathname components, or the same as `DirSep` if only one separator
## character exists. This is set to '/' on Windows systems where `DirSep` ## character exists. This is set to '/' on Windows systems
## is a backslash. ## where `DirSep` is a backslash.
PathSep* = ':' PathSep* = ':'
## The character conventionally used by the operating system to separate ## The character conventionally used by the operating system to separate
## search patch components (as in PATH), such as ':' for POSIX or ';' for ## search patch components (as in PATH), such as ':' for POSIX
## Windows. ## or ';' for Windows.
FileSystemCaseSensitive* = true FileSystemCaseSensitive* = true
## true if the file system is case sensitive, false otherwise. Used by ## 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 # MacOS directory separator is a colon ":" which is the only character not
# allowed in filenames. # 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" # E.g. ":kalle:petter" ":kalle" "kalle"
# #
# All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:" # 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, proc joinPath*(parts: varargs[string]): string {.noSideEffect,
rtl, extern: "nos$1OpenArray".} = rtl, extern: "nos$1OpenArray".} =
## The same as `joinPath(head, tail)`, but works with any number of directory ## The same as `joinPath(head, tail)`, but works with any number of
## parts. You need to pass at least one element or the proc will assert in ## directory parts. You need to pass at least one element or the proc
## debug builds and crash on release builds. ## will assert in debug builds and crash on release builds.
result = parts[0] result = parts[0]
for i in 1..high(parts): for i in 1..high(parts):
result = joinPath(result, parts[i]) result = joinPath(result, parts[i])
@ -316,8 +319,8 @@ when not declared(getEnv) or defined(nimscript):
if inclusive: yield path if inclusive: yield path
proc `/../` * (head, tail: string): string {.noSideEffect.} = proc `/../` * (head, tail: string): string {.noSideEffect.} =
## The same as ``parentDir(head) / tail`` unless there is no parent directory. ## The same as ``parentDir(head) / tail`` unless there is no parent
## Then ``head / tail`` is performed instead. ## directory. Then ``head / tail`` is performed instead.
let sepPos = parentDirPos(head) let sepPos = parentDirPos(head)
if sepPos >= 0: if sepPos >= 0:
result = substr(head, 0, sepPos-1) / tail result = substr(head, 0, sepPos-1) / tail
@ -500,7 +503,8 @@ when defined(nimdoc) and not declared(os):
proc existsFile(x: string): bool = discard proc existsFile(x: string): bool = discard
when declared(getEnv) or defined(nimscript): 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. ## Returns the home directory of the current user.
## ##
## This proc is wrapped by the expandTilde proc for the convenience of ## 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")) & "\\" when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
else: return string(getEnv("HOME")) & "/" 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. ## Returns the config directory of the current user for applications.
when defined(windows): return string(getEnv("APPDATA")) & "\\" when defined(windows): return string(getEnv("APPDATA")) & "\\"
else: return string(getEnv("HOME")) & "/.config/" 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 ## Returns the temporary directory of the current user for applications to
## save temporary files in. ## save temporary files in.
when defined(windows): return string(getEnv("TEMP")) & "\\" when defined(windows): return string(getEnv("TEMP")) & "\\"
else: return "/tmp/" 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. ## Expands a path starting with ``~/`` to a full path.
## ##
## If `path` starts with the tilde character and is followed by `/` or `\\` ## 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 ## the getHomeDir() proc, otherwise the input path will be returned without
## modification. ## modification.
## ##
## The behaviour of this proc is the same on the Windows platform despite not ## The behaviour of this proc is the same on the Windows platform despite
## having this convention. Example: ## not having this convention. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## let configFile = expandTilde("~" / "appname.cfg") ## let configFile = expandTilde("~" / "appname.cfg")
@ -549,7 +556,8 @@ when declared(getEnv) or defined(nimscript):
yield substr(s, first, last-1) yield substr(s, first, last-1)
inc(last) 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 ## Searches for `exe` in the current working directory and then
## in directories listed in the ``PATH`` environment variable. ## in directories listed in the ``PATH`` environment variable.
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe` ## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`

View file

@ -145,8 +145,8 @@ proc next*(s: var ScgiState, timeout: int = -1): bool =
L = L * 10 + ord(d) - ord('0') L = L * 10 + ord(d) - ord('0')
recvBuffer(s, L+1) recvBuffer(s, L+1)
s.headers = parseHeaders(s.input, L) s.headers = parseHeaders(s.input, L)
if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected") if s.headers.getOrDefault("SCGI") != "1": raiseScgiError("SCGI Version 1 expected")
L = parseInt(s.headers["CONTENT_LENGTH"]) L = parseInt(s.headers.getOrDefault("CONTENT_LENGTH"))
recvBuffer(s, L) recvBuffer(s, L)
return true return true
@ -221,10 +221,10 @@ proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
case ret case ret
of ReadFullLine: of ReadFullLine:
client.headers = parseHeaders(client.input, client.input.len-1) 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 client.input = "" # For next part
let contentLen = parseInt(client.headers["CONTENT_LENGTH"]) let contentLen = parseInt(client.headers.getOrDefault("CONTENT_LENGTH"))
if contentLen > 0: if contentLen > 0:
client.mode = ClientReadContent client.mode = ClientReadContent
else: else:
@ -232,7 +232,8 @@ proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
checkCloseSocket(client) checkCloseSocket(client)
of ReadPartialLine, ReadDisconnected, ReadNone: return of ReadPartialLine, ReadDisconnected, ReadNone: return
of ClientReadContent: 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: if L > 0:
let ret = recvBufferAsync(client, L) let ret = recvBufferAsync(client, L)
case ret case ret

View file

@ -101,22 +101,32 @@ proc rawGet(t: StringTableRef, key: string): int =
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = - 1 result = - 1
proc `[]`*(t: StringTableRef, key: string): string {.rtl, extern: "nstGet".} = template get(t: StringTableRef, key: string): stmt {.immediate.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned var index = rawGet(t, key)
## and no exception is raised. One can check with ``hasKey`` whether the key if index >= 0: result = t.data[index].val
## exists. else:
when compiles($key):
raise newException(KeyError, "key not found: " & $key)
else:
raise newException(KeyError, "key not found")
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. 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) var index = rawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: result = "" else: result = ""
proc mget*(t: StringTableRef, key: string): var string {.
rtl, extern: "nstTake".} =
## retrieves the location at ``t[key]``. If `key` is not in `t`, the
## ``KeyError`` exception is raised.
var index = rawGet(t, key)
if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key does not exist: " & key)
proc hasKey*(t: StringTableRef, key: string): bool {.rtl, extern: "nst$1".} = proc hasKey*(t: StringTableRef, key: string): bool {.rtl, extern: "nst$1".} =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
@ -152,7 +162,7 @@ proc raiseFormatException(s: string) =
raise e raise e
proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = 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! # hm difficult: assume safety in taint mode here. XXX This is dangerous!
if useEnvironment in flags: result = os.getEnv(key).string if useEnvironment in flags: result = os.getEnv(key).string
else: result = "" else: result = ""
@ -248,7 +258,7 @@ when isMainModule:
assert x["k"] == "v" assert x["k"] == "v"
assert x["11"] == "22" assert x["11"] == "22"
assert x["565"] == "67" assert x["565"] == "67"
x.mget("11") = "23" x["11"] = "23"
assert x["11"] == "23" assert x["11"] == "23"
x.clear(modeCaseInsensitive) x.clear(modeCaseInsensitive)

View file

@ -60,6 +60,132 @@ const
## doAssert "01234".find(invalid) == -1 ## doAssert "01234".find(invalid) == -1
## doAssert "01A34".find(invalid) == 2 ## 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, proc toLower*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerChar".} = rtl, extern: "nsuToLowerChar".} =
## Converts `c` into lower case. ## Converts `c` into lower case.
@ -1526,3 +1652,55 @@ when isMainModule:
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" 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#$"))

View file

@ -150,6 +150,8 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
try: try:
when declared(testSetupIMPLFlag): testSetupIMPL() when declared(testSetupIMPLFlag): testSetupIMPL()
body body
when declared(testTeardownIMPLFlag):
defer: testTeardownIMPL()
except: except:
when not defined(js): when not defined(js):
@ -158,7 +160,6 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
fail() fail()
finally: finally:
when declared(testTeardownIMPLFlag): testTeardownIMPL()
testDone name, testStatusIMPL testDone name, testStatusIMPL
proc checkpoint*(msg: string) = proc checkpoint*(msg: string) =

View file

@ -139,11 +139,16 @@ proc delete*(n: XmlNode, i: Natural) {.noSideEffect.} =
assert n.k == xnElement assert n.k == xnElement
n.s.delete(i) 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 ## returns the `i`'th child of `n` so that it can be modified
assert n.k == xnElement assert n.k == xnElement
result = n.s[i] 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.} = iterator items*(n: XmlNode): XmlNode {.inline.} =
## iterates over any child of `n`. ## iterates over any child of `n`.
assert n.k == xnElement assert n.k == xnElement
@ -152,7 +157,7 @@ iterator items*(n: XmlNode): XmlNode {.inline.} =
iterator mitems*(n: var XmlNode): var XmlNode {.inline.} = iterator mitems*(n: var XmlNode): var XmlNode {.inline.} =
## iterates over any child of `n`. ## iterates over any child of `n`.
assert n.k == xnElement 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.} = proc attrs*(n: XmlNode): XmlAttributes {.inline.} =
## gets the attributes belonging to `n`. ## gets the attributes belonging to `n`.
@ -337,7 +342,7 @@ proc attr*(n: XmlNode, name: string): string =
## Returns "" on failure. ## Returns "" on failure.
assert n.kind == xnElement assert n.kind == xnElement
if n.attrs == nil: return "" if n.attrs == nil: return ""
return n.attrs[name] return n.attrs.getOrDefault(name)
proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) = proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) =
## Iterates over all the children of `n` returning those matching `tag`. ## Iterates over all the children of `n` returning those matching `tag`.

View file

@ -1260,6 +1260,15 @@ const
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
taintMode = compileOption("taintmode") 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: when taintMode:
type TaintedString* = distinct string ## a distinct string type that type TaintedString* = distinct string ## a distinct string type that
## is `tainted`:idx:. It is an alias for ## 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] result = s[L]
setLen(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 {. iterator fields*[T: tuple|object](x: T): RootObj {.
magic: "Fields", noSideEffect.} magic: "Fields", noSideEffect.}
## iterates over every field of `x`. Warning: This really transforms ## iterates over every field of `x`. Warning: This really transforms

View file

@ -51,3 +51,8 @@ when defined(nimlocks):
{.pragma: benign, gcsafe, locks: 0.} {.pragma: benign, gcsafe, locks: 0.}
else: else:
{.pragma: benign, gcsafe.} {.pragma: benign, gcsafe.}
when defined(nimTableGet):
{.pragma: deprecatedGet, deprecated.}
else:
{.pragma: deprecatedGet.}

View file

@ -66,41 +66,34 @@ proc raiseOutOfMem() {.noinline.} =
quit(1) quit(1)
when defined(boehmgc): when defined(boehmgc):
when defined(windows): proc boehmGCinit {.importc: "GC_init", boehmGC.}
const boehmLib = "boehmgc.dll" proc boehmGC_disable {.importc: "GC_disable", boehmGC.}
elif defined(macosx): proc boehmGC_enable {.importc: "GC_enable", boehmGC.}
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 boehmGCincremental {. proc boehmGCincremental {.
importc: "GC_enable_incremental", dynlib: boehmLib.} importc: "GC_enable_incremental", boehmGC.}
proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.} proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.}
proc boehmAlloc(size: int): pointer {. proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.}
importc: "GC_malloc", dynlib: boehmLib.}
proc boehmAllocAtomic(size: int): pointer {. proc boehmAllocAtomic(size: int): pointer {.
importc: "GC_malloc_atomic", dynlib: boehmLib.} importc: "GC_malloc_atomic", boehmGC.}
proc boehmRealloc(p: pointer, size: int): pointer {. proc boehmRealloc(p: pointer, size: int): pointer {.
importc: "GC_realloc", dynlib: boehmLib.} importc: "GC_realloc", boehmGC.}
proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.} 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 ## Return the number of bytes in the heap. Excludes collector private
## data structures. Includes empty blocks and fragmentation loss. ## data structures. Includes empty blocks and fragmentation loss.
## Includes some pages that were allocated but never written. ## 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. ## Return a lower bound on the number of free bytes in the heap.
proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", boehmGC.}
dynlib: boehmLib.}
## Return the number of bytes allocated since the last collection. ## Return the number of bytes allocated since the last collection.
proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", boehmGC.}
dynlib: boehmLib.}
## Return the total number of bytes allocated in this process. ## Return the total number of bytes allocated in this process.
## Never decreases. ## Never decreases.
@ -157,7 +150,9 @@ when defined(boehmgc):
proc setStackBottom(theStackBottom: pointer) = discard proc setStackBottom(theStackBottom: pointer) = discard
proc initGC() = proc initGC() =
when defined(macosx): boehmGCinit() boehmGCinit()
when hasThreadSupport:
boehmGC_allow_register_threads()
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
if ntfNoRefs in typ.flags: result = allocAtomic(size) if ntfNoRefs in typ.flags: result = allocAtomic(size)
@ -204,9 +199,6 @@ elif defined(gogc):
else: else:
const goLib = "libgo.so" 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.} = proc roundup(x, v: int): int {.inline.} =
result = (x + (v-1)) and not (v-1) result = (x + (v-1)) and not (v-1)

View file

@ -304,22 +304,53 @@ type
when not defined(boehmgc) and not hasSharedHeap and not defined(gogc): when not defined(boehmgc) and not hasSharedHeap and not defined(gogc):
proc deallocOsPages() 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.} = template threadProcWrapperBody(closure: expr) {.immediate.} =
when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) when declared(globalsSlot): threadVarSetValue(globalsSlot, closure)
var t = cast[ptr Thread[TArg]](closure) var thrd = cast[ptr Thread[TArg]](closure)
when useStackMaskHack: threadProcWrapStackFrame(thrd)
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()
# Since an unhandled exception terminates the whole process (!), there is # Since an unhandled exception terminates the whole process (!), there is
# no need for a ``try finally`` here, nor would it be correct: The current # 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``! # exception is tried to be re-raised by the code-gen after the ``finally``!
@ -327,7 +358,7 @@ template threadProcWrapperBody(closure: expr) {.immediate.} =
# page! # page!
# mark as not running anymore: # mark as not running anymore:
t.dataFn = nil thrd.dataFn = nil
{.push stack_trace:off.} {.push stack_trace:off.}
when defined(windows): when defined(windows):

View file

@ -79,22 +79,29 @@ All rights reserved.
# Build Status # Build Status
[**Build Waterfall**][waterfall] [**Build Waterfall**][waterfall]
| | Linux | Windows | Mac | | | Linux | Windows | Mac |
| ------ | ----- | ------- | --- | | ------ | ----- | ------- | --- |
| x86 | ![linux-x86][linux-x86-img] | ![windows-x86][windows-x86-img] | ![mac-x86][mac-x86-img] | | x86 | [![linux-x86][linux-x86-img]][linux-x86] | [![windows-x86][windows-x86-img]][windows-x86] |
| x86_64 | ![linux-x86_64][linux-x86_64-img] | ![windows-x86_64][windows-x86_64-img] | ![mac-x86_64][mac-x86_64-img] | | 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]<br/> ![linux-armv6][linux-arm6-img]<br/> ![linux-armv7][linux-arm7-img] | | | | 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-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-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-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-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 [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-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 [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 [mac-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x64-builder
[waterfall]: http://buildbot.nim-lang.org/waterfall [waterfall]: http://buildbot.nim-lang.org/waterfall

View file

@ -24,4 +24,3 @@ var
"D": action4}.toTable "D": action4}.toTable
actionTable["C"]("arg") actionTable["C"]("arg")

View file

@ -1,5 +1,5 @@
import json, tables import json, tables, sequtils
proc run(json_params: TTable) = proc run(json_params: TTable) =
let json_elems = json_params["files"].elems let json_elems = json_params["files"].elems

View file

@ -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]

View file

@ -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"]

View file

@ -60,8 +60,12 @@ block tableTest2:
t["123"] = 1.5 # test overwriting t["123"] = 1.5 # test overwriting
assert t["123"] == 1.5 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(not hasKey(t, "111"))
assert "123" in t assert "123" in t
assert("111" notin t) assert("111" notin t)

View file

@ -60,8 +60,10 @@ block tableTest2:
t["123"] = 1.5 # test overwriting t["123"] = 1.5 # test overwriting
assert t["123"] == 1.5 assert t["123"] == 1.5
assert t["111"] == 0.0 # deleted try:
assert "123" in t echo t["111"] # deleted
except KeyError:
discard
assert(not hasKey(t, "111")) assert(not hasKey(t, "111"))
assert "111" notin t assert "111" notin t

View file

@ -5,6 +5,7 @@ discard """
3''' 3'''
""" """
import sequtils
# https://github.com/Araq/Nim/issues/797 # https://github.com/Araq/Nim/issues/797
proc foo[T](s:T):string = $s proc foo[T](s:T):string = $s

View file

@ -1,4 +1,4 @@
import future import future, sequtils
let x = map(@[1, 2, 3], x => x+10) let x = map(@[1, 2, 3], x => x+10)
assert x == @[11, 12, 13] assert x == @[11, 12, 13]

View file

@ -18,7 +18,7 @@ proc on*(emitter: var EventEmitter, event: string,
if not hasKey(emitter.events, event): if not hasKey(emitter.events, event):
var list: DoublyLinkedList[proc(e: EventArgs) {.nimcall.}] var list: DoublyLinkedList[proc(e: EventArgs) {.nimcall.}]
add(emitter.events, event, list) #if not, add it. 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) = proc initEmitter(emitter: var EventEmitter) =
emitter.events = initTable[string, emitter.events = initTable[string,
@ -30,4 +30,3 @@ var
initEmitter(ee) initEmitter(ee)
ee.on("print", proc(e: EventArgs) = echo("pie")) ee.on("print", proc(e: EventArgs) = echo("pie"))
ee.emit("print", args) ee.emit("print", args)

View file

@ -3,6 +3,9 @@ mode = ScriptMode.Whatif
exec "gcc -v" exec "gcc -v"
# test that ospaths actually compiles:
import ospaths
--forceBuild --forceBuild
task listDirs, "lists every subdirectory": task listDirs, "lists every subdirectory":

View file

@ -5,7 +5,7 @@ yay'''
# Test overloading of procs when used as function pointers # Test overloading of procs when used as function pointers
import strutils import strutils, sequtils
proc parseInt(x: float): int {.noSideEffect.} = discard proc parseInt(x: float): int {.noSideEffect.} = discard
proc parseInt(x: bool): int {.noSideEffect.} = discard proc parseInt(x: bool): int {.noSideEffect.} = discard

View file

@ -5,6 +5,7 @@ discard """
77''' 77'''
""" """
#import math #import math
import sequtils
proc optarg(x:int, y:int = 0):int = x + 3 * y proc optarg(x:int, y:int = 0):int = x + 3 * y
proc singlearg(x:int):int = 20*x proc singlearg(x:int):int = 20*x

141
tests/stdlib/tmget.nim Normal file
View file

@ -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

View file

@ -132,5 +132,5 @@ block:
</Students>""") </Students>""")
for x in d.mitems: for x in d.mitems:
x = <>Student(Name=x.attrs["Name"] & "foo") x = <>Student(Name=x.attrs["Name"] & "foo")
d.mget(1).attrs["Name"] = "bar" d[1].attrs["Name"] = "bar"
echo d echo d

View file

@ -1,4 +1,4 @@
import unittest import unittest, sequtils
proc doThings(spuds: var int): int = proc doThings(spuds: var int): int =

View file

@ -27,6 +27,6 @@ when ATTEMPT == 0:
# bug #1543 # bug #1543
import sequtils import sequtils
(var i = @[""];i).mapIt(it) (var i = @[""];i).applyIt(it)
# now works: # now works:
echo "##", i[0], "##" echo "##", i[0], "##"

View file

@ -8,7 +8,7 @@
import strutils, db_sqlite, os, osproc import strutils, db_sqlite, os, osproc
var db: TDbConn var db: DbConn
proc createDb() = proc createDb() =
db.exec(sql""" db.exec(sql"""
@ -61,7 +61,7 @@ var
proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip
proc getMachine*(db: TDbConn): MachineId = proc getMachine*(db: DbConn): MachineId =
var name = "hostname"() var name = "hostname"()
if name.len == 0: if name.len == 0:
name = when defined(posix): getenv"HOSTNAME".string 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 (?,?,?)", result = db.insertId(sql"insert into Machine(name, os, cpu) values (?,?,?)",
name, system.hostOS, system.hostCPU).MachineId name, system.hostOS, system.hostCPU).MachineId
proc getCommit(db: TDbConn): CommitId = proc getCommit(db: DbConn): CommitId =
const commLen = "commit ".len const commLen = "commit ".len
let hash = "git log -n 1"()[commLen..commLen+10] let hash = "git log -n 1"()[commLen..commLen+10]
let branch = "git symbolic-ref --short HEAD"() let branch = "git symbolic-ref --short HEAD"()

View file

@ -119,12 +119,21 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release -d:useRealtimeGC", cat, actionRun) " -d:release -d:useRealtimeGC", cat, actionRun)
template test(filename: expr): stmt = template testWithoutBoehm(filename: expr): stmt =
testWithoutMs filename testWithoutMs filename
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" --gc:markAndSweep", cat, actionRun) " --gc:markAndSweep", cat, actionRun)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release --gc:markAndSweep", cat, actionRun) " -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 "gcemscripten"
test "growobjcrash" test "growobjcrash"
@ -136,9 +145,9 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
test "gcleak4" test "gcleak4"
# Disabled because it works and takes too long to run: # Disabled because it works and takes too long to run:
#test "gcleak5" #test "gcleak5"
test "weakrefs" testWithoutBoehm "weakrefs"
test "cycleleak" test "cycleleak"
test "closureleak" testWithoutBoehm "closureleak"
testWithoutMs "refarrayleak" testWithoutMs "refarrayleak"
test "stackrefleak" test "stackrefleak"

View file

@ -109,7 +109,7 @@ div.tabContent.hide { display: none; }
proc td(s: string): string = proc td(s: string): string =
result = "<td>" & s.substr(0, 200).xmlEncode & "</td>" 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 var commit = c
for thisCommit in db.rows(sql"select id from [Commit] order by id desc"): for thisCommit in db.rows(sql"select id from [Commit] order by id desc"):
if commit == 0: result = thisCommit[0] if commit == 0: result = thisCommit[0]

View file

@ -65,7 +65,7 @@ proc callCompiler(cmdTemplate, filename, options: string,
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
"options", options, "file", filename.quoteShell]) "options", options, "file", filename.quoteShell])
var p = startProcess(command=c[0], args=c[1.. ^1], var p = startProcess(command=c[0], args=c[1.. ^1],
options={poStdErrToStdOut, poUseShell}) options={poStdErrToStdOut, poUsePath})
let outp = p.outputStream let outp = p.outputStream
var suc = "" var suc = ""
var err = "" var err = ""
@ -215,7 +215,7 @@ proc generatedFile(path, name: string, target: TTarget): string =
proc codegenCheck(test: TTest, check: string, given: var TSpec) = proc codegenCheck(test: TTest, check: string, given: var TSpec) =
try: try:
let (path, name, ext2) = test.name.splitFile let (path, name, _) = test.name.splitFile
let genFile = generatedFile(path, name, test.target) let genFile = generatedFile(path, name, test.target)
let contents = readFile(genFile).string let contents = readFile(genFile).string
if check[0] == '\\': if check[0] == '\\':
@ -307,7 +307,7 @@ proc testSpec(r: var TResults, test: TTest) =
let isJsTarget = test.target == targetJS let isJsTarget = test.target == targetJS
var exeFile: string var exeFile: string
if isJsTarget: if isJsTarget:
let (dir, file, ext) = splitFile(tname) let (dir, file, _) = splitFile(tname)
exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache" exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache"
else: else:
exeFile = changeFileExt(tname, ExeExt) exeFile = changeFileExt(tname, ExeExt)
@ -352,7 +352,7 @@ proc testSpec(r: var TResults, test: TTest) =
proc testNoSpec(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 # 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) inc(r.total)
let given = callCompiler(cmdTemplate, test.name, test.options, test.target) let given = callCompiler(cmdTemplate, test.name, test.options, test.target)
r.addResult(test, "", given.msg, given.err) 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) r.addResult(test, "", given.msg, given.err)
elif test.action == actionRun: elif test.action == actionRun:
let exeFile = changeFileExt(test.name, ExeExt) 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 exitCode != 0: given.err = reExitCodesDiffer
if given.err == reSuccess: inc(r.passed) if given.err == reSuccess: inc(r.passed)

View file

@ -8,7 +8,11 @@ News
Changes affecting backwards compatibility 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 - The ``rawsockets`` module has been renamed to ``nativesockets`` to avoid
confusion with TCP/IP raw sockets, so ``newNativeSocket`` should be used confusion with TCP/IP raw sockets, so ``newNativeSocket`` should be used
instead of ``newRawSocket``. instead of ``newRawSocket``.
@ -68,10 +72,10 @@ News
* ``libeay32.dll``: Split into ``libeay32.dll`` and ``libeay64.dll``. * ``libeay32.dll``: Split into ``libeay32.dll`` and ``libeay64.dll``.
Compile with ``-d:nimOldDLLs`` to make the stdlib use the old DLL names. 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 - Nim VM now treats objects as ``nkObjConstr`` nodes, and not ``nkPar`` nodes
was previously. Macros that generate nkPar nodes when object is expected are as it was previously. Macros that generate ``nkPar`` nodes when object is
likely to break. Macros that expect nkPar nodes to which objects are passed expected are likely to break. Macros that expect ``nkPar`` nodes to which
are likely to break as well. objects are passed are likely to break as well.
- Base methods now need to be annotated with the ``base`` pragma. This makes - Base methods now need to be annotated with the ``base`` pragma. This makes
multi methods less error-prone to use with the effect system. multi methods less error-prone to use with the effect system.
- Nim's parser directive ``#!`` is now ``#?`` in order to produce no conflicts - Nim's parser directive ``#!`` is now ``#?`` in order to produce no conflicts
@ -86,8 +90,14 @@ News
echo f(0, "abc") echo f(0, "abc")
- The ``ftpclient`` module is now deprecated in favour of the - 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 Library Additions
----------------- -----------------

View file

@ -34,8 +34,8 @@ doc: "tools;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.txt"
doc: "nimfix.txt;nimsuggest.txt;nep1.txt;nims.txt" doc: "nimfix.txt;nimsuggest.txt;nep1.txt;nims.txt"
pdf: "manual.txt;lib;tut1;tut2;nimc;niminst;gc" pdf: "manual.txt;lib;tut1;tut2;nimc;niminst;gc"
srcdoc2: "system.nim;system/nimscript;pure/ospaths" srcdoc2: "system.nim;system/nimscript;pure/ospaths"
srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" srcdoc2: "core/macros;pure/marshal;core/typeinfo"
srcdoc2: "impure/re;pure/sockets;pure/typetraits" srcdoc2: "impure/re;pure/typetraits"
srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim" srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim"
srcdoc: "system/threads.nim;system/channels.nim;js/dom" srcdoc: "system/threads.nim;system/channels.nim;js/dom"
srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm" 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/tables;pure/collections/sets;pure/collections/lists"
srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings" srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings"
srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies" srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies"
srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits" srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits"
srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri" srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
srcdoc2: "packages/docutils/rst;packages/docutils/rstast" srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
srcdoc2: "packages/docutils/rstgen;pure/logging;pure/asyncdispatch;pure/asyncnet" 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/asyncfile"
srcdoc2: "pure/md5;pure/rationals" srcdoc2: "pure/md5;pure/rationals"
srcdoc2: "posix/posix" srcdoc2: "posix/posix"