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

This commit is contained in:
Aman Gupta
2015-10-13 15:25:40 -07:00
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');
</script>
""" % [config["doc.googleAnalytics"]]
""" % [config.getOrDefault"doc.googleAnalytics"]
else:
result.analytics = ""

View File

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

View File

@@ -15,7 +15,7 @@ import
wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, jsgen, json, nversion,
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists
docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists
from magicsys import systemModule, resetSysTypes

View File

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

View File

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

View File

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

View File

@@ -309,6 +309,10 @@ proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
findUsages(info, s)
elif gIdeCmd == ideDef:
findDefinition(info, s)
elif gIdeCmd == ideDus and s != nil:
if isTracked(info, s.name.s.len):
suggestResult(symToSuggest(s, isLocal=false, $ideDef))
findUsages(info, s)
proc markUsed(info: TLineInfo; s: PSym) =
incl(s.flags, sfUsed)
@@ -366,7 +370,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
suggestCall(c, a, n, outputs)
dec(c.compilesContextId)
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
if outputs > 0 and gIdeCmd notin {ideUse, ideDus}: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n)

View File

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

View File

@@ -30,6 +30,14 @@ proc setResult*(a: VmArgs; v: string) =
s[a.ra].node = newNode(nkStrLit)
s[a.ra].node.strVal = v
proc setResult*(a: VmArgs; n: PNode) =
var s: seq[TFullReg]
move(s, cast[seq[TFullReg]](a.slots))
if s[a.ra].kind != rkNode:
myreset(s[a.ra])
s[a.ra].kind = rkNode
s[a.ra].node = n
proc setResult*(a: VmArgs; v: seq[string]) =
var s: seq[TFullReg]
move(s, cast[seq[TFullReg]](a.slots))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,8 @@
## container for a set or a mapping of strings. Based on the excellent paper
## by Adam Langley.
include "system/inclrtl"
type
NodeObj[T] = object {.acyclic.}
byte: int ## byte index of the difference
@@ -140,20 +142,32 @@ proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
var n = rawInsert(c, key)
n.val = val
proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} =
## retrieves the value at ``c[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
template get[T](c: CritBitTree[T], key: string): T {.immediate.} =
let n = rawGet(c, key)
if n != nil: result = n.val
else:
when compiles($key):
raise newException(KeyError, "key not found: " & $key)
else:
raise newException(KeyError, "key not found")
proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline.} =
proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline, deprecatedGet.} =
## retrieves the value at ``c[key]``. If `key` is not in `t`, the
## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## the key exists.
get(c, key)
proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline,
deprecatedGet.} =
## retrieves the value at ``c[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
let n = rawGet(c, key)
if n != nil: result = n.val
else: raise newException(KeyError, "key not found: " & $key)
get(c, key)
proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline, deprecated.} =
## retrieves the value at ``c[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
## Use ```[]``` instead.
get(c, key)
proc excl*[T](c: var CritBitTree[T], key: string) =
## removes `key` (and its associated value) from the set `c`.

View File

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

View File

@@ -47,7 +47,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
result[i] = itm
inc(i)
proc repeat*[T](s: seq[T], n: Natural): seq[T] =
proc cycle*[T](s: seq[T], n: Natural): seq[T] =
## Returns a new sequence with the items of `s` repeated `n` times.
##
## Example:
@@ -56,15 +56,29 @@ proc repeat*[T](s: seq[T], n: Natural): seq[T] =
##
## let
## s = @[1, 2, 3]
## total = s.repeat(3)
## total = s.cycle(3)
## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
result = newSeq[T](n * s.len)
var o = 0
for x in 1..n:
for x in 0..<n:
for e in s:
result[o] = e
inc o
proc repeat*[T](x: T, n: Natural): seq[T] =
## Returns a new sequence with the item `x` repeated `n` times.
##
## Example:
##
## .. code-block:
##
## let
## total = repeat(5, 3)
## assert total == @[5, 5, 5]
result = newSeq[T](n)
for i in 0..<n:
result[i] = x
proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates.
##
@@ -169,6 +183,77 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
first = last
proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}):
seq[S]{.inline.} =
## Returns a new sequence with the results of `op` applied to every item in
## `data`.
##
## Since the input is not modified you can use this version of ``map`` to
## transform the type of the elements in the input sequence. Example:
##
## .. code-block:: nim
## let
## a = @[1, 2, 3, 4]
## b = map(a, proc(x: int): string = $x)
## assert b == @["1", "2", "3", "4"]
newSeq(result, data.len)
for i in 0..data.len-1: result[i] = op(data[i])
proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.})
{.deprecated.} =
## Applies `op` to every item in `data` modifying it directly.
##
## Note that this version of ``map`` requires your input and output types to
## be the same, since they are modified in-place. Example:
##
## .. code-block:: nim
## var a = @["1", "2", "3", "4"]
## echo repr(a)
## # --> ["1", "2", "3", "4"]
## map(a, proc(x: var string) = x &= "42")
## echo repr(a)
## # --> ["142", "242", "342", "442"]
## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead.
for i in 0..data.len-1: op(data[i])
proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.})
{.inline.} =
## Applies `op` to every item in `data` modifying it directly.
##
## Note that this requires your input and output types to
## be the same, since they are modified in-place.
## The parameter function takes a ``var T`` type parameter.
## Example:
##
## .. code-block:: nim
## var a = @["1", "2", "3", "4"]
## echo repr(a)
## # --> ["1", "2", "3", "4"]
## map(a, proc(x: var string) = x &= "42")
## echo repr(a)
## # --> ["142", "242", "342", "442"]
##
for i in 0..data.len-1: op(data[i])
proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.})
{.inline.} =
## Applies `op` to every item in `data` modifying it directly.
##
## Note that this requires your input and output types to
## be the same, since they are modified in-place.
## The parameter function takes and returns a ``T`` type variable.
## Example:
##
## .. code-block:: nim
## var a = @["1", "2", "3", "4"]
## echo repr(a)
## # --> ["1", "2", "3", "4"]
## map(a, proc(x: string): string = x & "42")
## echo repr(a)
## # --> ["142", "242", "342", "442"]
##
for i in 0..data.len-1: data[i] = op(data[i])
iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
## Iterates through a sequence and yields every item that fulfills the
@@ -181,11 +266,12 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
## echo($n)
## # echoes 4, 8, 4 in separate lines
for i in countup(0, len(seq1)-1):
var item = seq1[i]
if pred(item): yield seq1[i]
for i in 0..<seq1.len:
if pred(seq1[i]):
yield seq1[i]
proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T]
{.inline.} =
## Returns a new sequence with all the items that fulfilled the predicate.
##
## Example:
@@ -197,9 +283,13 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
## f2 = filter(colors) do (x: string) -> bool : x.len > 5
## assert f1 == @["red", "black"]
## assert f2 == @["yellow"]
accumulateResult(filter(seq1, pred))
result = newSeq[T]()
for i in 0..<seq1.len:
if pred(seq1[i]):
result.add(seq1[i])
proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) =
proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.})
{.inline.} =
## Keeps the items in the passed sequence if they fulfilled the predicate.
## Same as the ``filter`` proc, but modifies the sequence directly.
##
@@ -213,7 +303,7 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) =
for i in 0 .. <len(seq1):
if pred(seq1[i]):
if pos != i:
seq1[pos] = seq1[i]
shallowCopy(seq1[pos], seq1[i])
inc(pos)
setLen(seq1, pos)
@@ -268,7 +358,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
inc(j)
template filterIt*(seq1, pred: expr): expr {.immediate.} =
template filterIt*(seq1, pred: expr): expr =
## Returns a new sequence with all the items that fulfilled the predicate.
##
## Unlike the `proc` version, the predicate needs to be an expression using
@@ -282,12 +372,12 @@ template filterIt*(seq1, pred: expr): expr {.immediate.} =
## notAcceptable = filterIt(temperatures, it > 50 or it < -10)
## assert acceptable == @[-2.0, 24.5, 44.31]
## assert notAcceptable == @[-272.15, 99.9, -113.44]
var result {.gensym.}: type(seq1) = @[]
var result {.gensym.} = newSeq[type(seq1[0])]()
for it {.inject.} in items(seq1):
if pred: result.add(it)
result
template keepItIf*(varSeq, pred: expr) =
template keepItIf*(varSeq: seq, pred: expr) =
## Convenience template around the ``keepIf`` proc to reduce typing.
##
## Unlike the `proc` version, the predicate needs to be an expression using
@@ -303,10 +393,71 @@ template keepItIf*(varSeq, pred: expr) =
let it {.inject.} = varSeq[i]
if pred:
if pos != i:
varSeq[pos] = varSeq[i]
shallowCopy(varSeq[pos], varSeq[i])
inc(pos)
setLen(varSeq, pos)
proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
## Iterates through a sequence and checks if every item fulfills the
## predicate.
##
## Example:
##
## .. code-block::
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## assert all(numbers, proc (x: int): bool = return x < 10) == true
## assert all(numbers, proc (x: int): bool = return x < 9) == false
for i in seq1:
if not pred(i):
return false
return true
template allIt*(seq1, pred: expr): bool {.immediate.} =
## Checks if every item fulfills the predicate.
##
## Example:
##
## .. code-block::
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## assert allIt(numbers, it < 10) == true
## assert allIt(numbers, it < 9) == false
var result {.gensym.} = true
for it {.inject.} in items(seq1):
if not pred:
result = false
break
result
proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
## Iterates through a sequence and checks if some item fulfills the
## predicate.
##
## Example:
##
## .. code-block::
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## assert any(numbers, proc (x: int): bool = return x > 8) == true
## assert any(numbers, proc (x: int): bool = return x > 9) == false
for i in seq1:
if pred(i):
return true
return false
template anyIt*(seq1, pred: expr): bool {.immediate.} =
## Checks if some item fulfills the predicate.
##
## Example:
##
## .. code-block::
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## assert anyIt(numbers, it > 8) == true
## assert anyIt(numbers, it > 9) == false
var result {.gensym.} = false
for it {.inject.} in items(seq1):
if pred:
result = true
break
result
template toSeq*(iter: expr): expr {.immediate.} =
## Transforms any iterator into a sequence.
@@ -320,14 +471,19 @@ template toSeq*(iter: expr): expr {.immediate.} =
## if x mod 2 == 1:
## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9]
##
## **Note**: Since this is an immediate macro, you cannot always invoke this
## as ``x.toSeq``, depending on the ``x``.
## See `this <manual.html#limitations-of-the-method-call-syntax>`_
## for an explanation.
var result {.gensym.}: seq[type(iter)] = @[]
for x in iter: add(result, x)
result
when compiles(iter.len):
var i = 0
var result = newSeq[type(iter)](iter.len)
for x in iter:
result[i] = x
inc i
result
else:
var result: seq[type(iter)] = @[]
for x in iter:
result.add(x)
result
template foldl*(sequence, operation: expr): expr =
## Template to fold a sequence from left to right, returning the accumulation.
@@ -358,7 +514,7 @@ template foldl*(sequence, operation: expr): expr =
assert sequence.len > 0, "Can't fold empty sequences"
var result {.gensym.}: type(sequence[0])
result = sequence[0]
for i in countup(1, sequence.len - 1):
for i in 1..<sequence.len:
let
a {.inject.} = result
b {.inject.} = sequence[i]
@@ -401,7 +557,7 @@ template foldr*(sequence, operation: expr): expr =
result = operation
result
template mapIt*(seq1, typ, op: expr): expr =
template mapIt*(seq1, typ, op: expr): expr {.deprecated.}=
## Convenience template around the ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
@@ -414,13 +570,45 @@ template mapIt*(seq1, typ, op: expr): expr =
## nums = @[1, 2, 3, 4]
## strings = nums.mapIt(string, $(4 * it))
## assert strings == @["4", "8", "12", "16"]
## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)``
## template instead.
var result {.gensym.}: seq[typ] = @[]
for it {.inject.} in items(seq1):
result.add(op)
result
template mapIt*(varSeq, op: expr) =
## Convenience template around the mutable ``map`` proc to reduce typing.
template mapIt*(seq1, op: expr): expr =
## Convenience template around the ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. Example:
##
## .. code-block::
## let
## nums = @[1, 2, 3, 4]
## strings = nums.mapIt($(4 * it))
## assert strings == @["4", "8", "12", "16"]
type outType = type((
block:
var it{.inject.}: type(items(seq1));
op))
var result: seq[outType]
when compiles(seq1.len):
let s = seq1
var i = 0
result = newSeq[outType](s.len)
for it {.inject.} in s:
result[i] = op
i += 1
else:
result = @[]
for it {.inject.} in seq1:
result.add(op)
result
template applyIt*(varSeq, op: expr) =
## Convenience template around the mutable ``apply`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. The expression has to return the same type as the sequence you
@@ -428,12 +616,14 @@ template mapIt*(varSeq, op: expr) =
##
## .. code-block::
## var nums = @[1, 2, 3, 4]
## nums.mapIt(it * 3)
## nums.applyIt(it * 3)
## assert nums[0] + nums[3] == 15
for i in 0 .. <len(varSeq):
for i in 0 .. <varSeq.len:
let it {.inject.} = varSeq[i]
varSeq[i] = op
template newSeqWith*(len: int, init: expr): expr =
## creates a new sequence, calling `init` to initialize each value. Example:
##
@@ -513,6 +703,38 @@ when isMainModule:
keepItIf(candidates, it.len == 3 and it[0] == 'b')
assert candidates == @["bar", "baz"]
block: # any
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert any(numbers, proc (x: int): bool = return x > 8) == true
assert any(numbers, proc (x: int): bool = return x > 9) == false
assert any(len0seq, proc (x: int): bool = return true) == false
block: # anyIt
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert anyIt(numbers, it > 8) == true
assert anyIt(numbers, it > 9) == false
assert anyIt(len0seq, true) == false
block: # all
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert all(numbers, proc (x: int): bool = return x < 10) == true
assert all(numbers, proc (x: int): bool = return x < 9) == false
assert all(len0seq, proc (x: int): bool = return false) == true
block: # allIt
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert allIt(numbers, it < 10) == true
assert allIt(numbers, it < 9) == false
assert allIt(len0seq, false) == true
block: # toSeq test
let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
@@ -568,8 +790,8 @@ when isMainModule:
block: # mapIt tests
var
nums = @[1, 2, 3, 4]
strings = nums.mapIt(string, $(4 * it))
nums.mapIt(it * 3)
strings = nums.mapIt($(4 * it))
nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15
block: # distribute tests
@@ -605,15 +827,19 @@ when isMainModule:
seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # repeat tests
block: # cycle tests
let
a = @[1, 2, 3]
b: seq[int] = @[]
doAssert a.repeat(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.repeat(0) == @[]
#doAssert a.repeat(-1) == @[] # will not compile!
doAssert b.repeat(3) == @[]
doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.cycle(0) == @[]
#doAssert a.cycle(-1) == @[] # will not compile!
doAssert b.cycle(3) == @[]
block: # repeat tests
assert repeat(10, 5) == @[10, 10, 10, 10, 10]
assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
when not defined(testing):
echo "Finished doc tests"

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.} =
rawGetImpl()
proc mget*[A](s: var HashSet[A], key: A): var A =
proc `[]`*[A](s: var HashSet[A], key: A): var A =
## returns the element that is actually stored in 's' which has the same
## value as 'key' or raises the ``EInvalidKey`` exception. This is useful
## value as 'key' or raises the ``KeyError`` exception. This is useful
## when one overloaded 'hash' and '==' but still needs reference semantics
## for sharing.
assert s.isValid, "The set needs to be initialized."
@@ -165,6 +165,13 @@ proc mget*[A](s: var HashSet[A], key: A): var A =
if index >= 0: result = s.data[index].key
else: raise newException(KeyError, "key not found: " & $key)
proc mget*[A](s: var HashSet[A], key: A): var A {.deprecated.} =
## returns the element that is actually stored in 's' which has the same
## value as 'key' or raises the ``KeyError`` exception. This is useful
## when one overloaded 'hash' and '==' but still needs reference semantics
## for sharing. Use ```[]``` instead.
s[key]
proc contains*[A](s: HashSet[A], key: A): bool =
## Returns true iff `key` is in `s`.
##

View File

@@ -68,6 +68,8 @@
import
hashes, math
include "system/inclrtl"
type
KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
@@ -96,18 +98,10 @@ proc len*[A, B](t: Table[A, B]): int =
## returns the number of keys in `t`.
result = t.counter
proc `[]`*[A, B](t: Table[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
proc mget*[A, B](t: var Table[A, B], key: A): var B =
template get(t, key): untyped {.immediate.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
mixin rawGet
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
@@ -117,6 +111,31 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
else:
raise newException(KeyError, "key not found")
template getOrDefaultImpl(t, key): untyped {.immediate.} =
mixin rawGet
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
proc `[]`*[A, B](t: Table[A, B], key: A): B {.deprecatedGet.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## the key exists.
get(t, key)
proc `[]`*[A, B](t: var Table[A, B], key: A): var B {.deprecatedGet.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
get(t, key)
proc mget*[A, B](t: var Table[A, B], key: A): var B {.deprecated.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised. Use ```[]```
## instead.
get(t, key)
proc getOrDefault*[A, B](t: Table[A, B], key: A): B = getOrDefaultImpl(t, key)
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`.
var h: Hash = hash(key) and high(t.data)
@@ -276,17 +295,19 @@ iterator mvalues*[A, B](t: TableRef[A, B]): var B =
for h in 0..high(t.data):
if isFilled(t.data[h].hcode): yield t.data[h].val
proc `[]`*[A, B](t: TableRef[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
proc `[]`*[A, B](t: TableRef[A, B], key: A): var B {.deprecatedGet.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## the key exists.
result = t[][key]
proc mget*[A, B](t: TableRef[A, B], key: A): var B =
proc mget*[A, B](t: TableRef[A, B], key: A): var B {.deprecated.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
t[].mget(key)
## If `key` is not in `t`, the ``KeyError`` exception is raised.
## Use ```[]``` instead.
t[][key]
proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = getOrDefault(t[], key)
proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
@@ -399,22 +420,26 @@ proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
rawGetImpl()
proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B {.deprecatedGet.} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## the key exists.
get(t, key)
proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B =
proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B{.deprecatedGet.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key)
## If `key` is not in `t`, the ``KeyError`` exception is raised.
get(t, key)
proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B {.deprecated.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
## Use ```[]``` instead.
get(t, key)
proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
getOrDefaultImpl(t, key)
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`.
@@ -572,17 +597,20 @@ iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
forAllOrderedPairs:
yield t.data[h].val
proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): B =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
## retrieves the value at ``t[key]``. If `key` is not in `t`, the
## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
## the key exists.
result = t[][key]
proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B =
proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B {.deprecated.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
result = t[].mget(key)
## If `key` is not in `t`, the ``KeyError`` exception is raised.
## Use ```[]``` instead.
result = t[][key]
proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
getOrDefault(t[], key)
proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
@@ -683,19 +711,35 @@ proc rawGet[A](t: CountTable[A], key: A): int =
h = nextTry(h, high(t.data))
result = -1 - h # < 0 => MISSING; insert idx = -1 - result
proc `[]`*[A](t: CountTable[A], key: A): int =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## 0 is returned. One can check with ``hasKey`` whether the key
## exists.
template ctget(t, key: untyped): untyped {.immediate.} =
var index = rawGet(t, key)
if index >= 0: result = t.data[index].val
else:
when compiles($key):
raise newException(KeyError, "key not found: " & $key)
else:
raise newException(KeyError, "key not found")
proc 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.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
ctget(t, key)
proc mget*[A](t: var CountTable[A], key: A): var int {.deprecated.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
## Use ```[]``` instead.
ctget(t, key)
proc getOrDefault*[A](t: CountTable[A], key: A): int =
var index = rawGet(t, key)
if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key)
proc hasKey*[A](t: CountTable[A], key: A): bool =
## returns true iff `key` is in the table `t`.
@@ -831,16 +875,19 @@ iterator mvalues*[A](t: CountTableRef[A]): var int =
for h in 0..high(t.data):
if t.data[h].val != 0: yield t.data[h].val
proc `[]`*[A](t: CountTableRef[A], key: A): int =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## 0 is returned. One can check with ``hasKey`` whether the key
## exists.
proc `[]`*[A](t: CountTableRef[A], key: A): var int {.deprecatedGet.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
result = t[][key]
proc mget*[A](t: CountTableRef[A], key: A): var int =
proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
result = t[].mget(key)
## If `key` is not in `t`, the ``KeyError`` exception is raised.
## Use ```[]``` instead.
result = t[][key]
proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
getOrDefaultImpl(t, key)
proc hasKey*[A](t: CountTableRef[A], key: A): bool =
## returns true iff `key` is in the table `t`.

View File

@@ -8,9 +8,10 @@
#
## This module implements efficient computations of hash values for diverse
## Nim types. All the procs are based on these two building blocks: the `!&
## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_
## used to *finish* the hash value. If you want to implement hash procs for
## Nim types. All the procs are based on these two building blocks:
## - `!& proc <#!&>`_ used to start or mix a hash value, and
## - `!$ proc <#!$>`_ used to *finish* the hash value.
## If you want to implement hash procs for
## your custom types you will end up writing the following kind of skeleton of
## code:
##
@@ -108,7 +109,7 @@ proc hash*(x: int): Hash {.inline.} =
result = x
proc hash*(x: int64): Hash {.inline.} =
## efficient hashing of integers
## efficient hashing of int64 integers
result = toU32(x)
proc hash*(x: char): Hash {.inline.} =
@@ -126,6 +127,16 @@ proc hash*(x: string): Hash =
h = h !& ord(x[i])
result = !$h
proc hash*(sBuf: string, sPos, ePos: int): Hash =
## efficient hashing of a string buffer, from starting
## position `sPos` to ending position `ePos`
##
## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)``
var h: Hash = 0
for i in sPos..ePos:
h = h !& ord(sBuf[i])
result = !$h
proc hashIgnoreStyle*(x: string): Hash =
## efficient hashing of strings; style is ignored
var h: Hash = 0
@@ -145,6 +156,27 @@ proc hashIgnoreStyle*(x: string): Hash =
result = !$h
proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash =
## efficient hashing of a string buffer, from starting
## position `sPos` to ending position `ePos`; style is ignored
##
## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent
## to ``hashIgnoreStyle(myBuf)``
var h: Hash = 0
var i = sPos
while i <= ePos:
var c = sBuf[i]
if c == '_':
inc(i)
elif isMagicIdentSeparatorRune(cstring(sBuf), i):
inc(i, magicIdentSeparatorRuneByteWidth)
else:
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
inc(i)
result = !$h
proc hashIgnoreCase*(x: string): Hash =
## efficient hashing of strings; case is ignored
var h: Hash = 0
@@ -155,7 +187,22 @@ proc hashIgnoreCase*(x: string): Hash =
h = h !& ord(c)
result = !$h
proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
## efficient hashing of a string buffer, from starting
## position `sPos` to ending position `ePos`; case is ignored
##
## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent
## to ``hashIgnoreCase(myBuf)``
var h: Hash = 0
for i in sPos..ePos:
var c = sBuf[i]
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
result = !$h
proc hash*(x: float): Hash {.inline.} =
## efficient hashing of floats.
var y = x + 1.0
result = cast[ptr Hash](addr(y))[]
@@ -173,10 +220,29 @@ proc hash*[T: tuple](x: T): Hash =
result = !$result
proc hash*[A](x: openArray[A]): Hash =
## efficient hashing of arrays and sequences.
for it in items(x): result = result !& hash(it)
result = !$result
proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
## efficient hashing of portions of arrays and sequences.
##
## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)``
for i in sPos..ePos:
result = result !& hash(aBuf[i])
result = !$result
proc hash*[A](x: set[A]): Hash =
## efficient hashing of sets.
for it in items(x): result = result !& hash(it)
result = !$result
when isMainModule:
doAssert( hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) )
doAssert( hashIgnoreCase("aa bb aaaa1234") == hash("aa bb aaaa1234") )
doAssert( hashIgnoreStyle("aa bb aaaa1234") == hashIgnoreCase("aa bb aaaa1234") )
let xx = @['H','e','l','l','o']
let ss = "Hello"
doAssert( hash(xx) == hash(ss) )
doAssert( hash(xx) == hash(xx, 0, xx.high) )
doAssert( hash(ss) == hash(ss, 0, ss.high) )

View File

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

View File

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

View File

@@ -9,8 +9,6 @@
## Module for computing MD5 checksums.
import unsigned
type
MD5State = array[0..3, 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 =
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
## could not be found.
result = mimedb.mimes[ext]
result = mimedb.mimes.getOrDefault(ext)
if result == "":
return default

View File

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

View File

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

View File

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

View File

@@ -60,6 +60,132 @@ const
## doAssert "01234".find(invalid) == -1
## doAssert "01A34".find(invalid) == 2
proc isAlpha*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaChar".}=
## Checks whether or not `c` is alphabetical.
##
## This checks a-z, A-Z ASCII characters only.
return c in Letters
proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaNumericChar".}=
## Checks whether or not `c` is alphanumeric.
##
## This checks a-z, A-Z, 0-9 ASCII characters only.
return c in Letters or c in Digits
proc isDigit*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsDigitChar".}=
## Checks whether or not `c` is a number.
##
## This checks 0-9 ASCII characters only.
return c in Digits
proc isSpace*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsSpaceChar".}=
## Checks whether or not `c` is a whitespace character.
return c in Whitespace
proc isLower*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsLowerChar".}=
## Checks whether or not `c` is a lower case character.
##
## This checks ASCII characters only.
return c in {'a'..'z'}
proc isUpper*(c: char): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsUpperChar".}=
## Checks whether or not `c` is an upper case character.
##
## This checks ASCII characters only.
return c in {'A'..'Z'}
proc isAlpha*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaStr".}=
## Checks whether or not `s` is alphabetical.
##
## This checks a-z, A-Z ASCII characters only.
## Returns true if all characters in `s` are
## alphabetic and there is at least one character
## in `s`.
if s.len() == 0:
return false
result = true
for c in s:
result = c.isAlpha() and result
proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaNumericStr".}=
## Checks whether or not `s` is alphanumeric.
##
## This checks a-z, A-Z, 0-9 ASCII characters only.
## Returns true if all characters in `s` are
## alpanumeric and there is at least one character
## in `s`.
if s.len() == 0:
return false
result = true
for c in s:
result = c.isAlphaNumeric() and result
proc isDigit*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsDigitStr".}=
## Checks whether or not `s` is a numeric value.
##
## This checks 0-9 ASCII characters only.
## Returns true if all characters in `s` are
## numeric and there is at least one character
## in `s`.
if s.len() == 0:
return false
result = true
for c in s:
result = c.isDigit() and result
proc isSpace*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsSpaceStr".}=
## Checks whether or not `s` is completely whitespace.
##
## Returns true if all characters in `s` are whitespace
## characters and there is at least one character in `s`.
if s.len() == 0:
return false
result = true
for c in s:
result = c.isSpace() and result
proc isLower*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsLowerStr".}=
## Checks whether or not `s` contains all lower case characters.
##
## This checks ASCII characters only.
## Returns true if all characters in `s` are lower case
## and there is at least one character in `s`.
if s.len() == 0:
return false
result = true
for c in s:
result = c.isLower() and result
proc isUpper*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsUpperStr".}=
## Checks whether or not `s` contains all upper case characters.
##
## This checks ASCII characters only.
## Returns true if all characters in `s` are upper case
## and there is at least one character in `s`.
if s.len() == 0:
return false
result = true
for c in s:
result = c.isUpper() and result
proc toLower*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerChar".} =
## Converts `c` into lower case.
@@ -1526,3 +1652,55 @@ when isMainModule:
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
doAssert isAlpha('r')
doAssert isAlpha('A')
doAssert(not isAlpha('$'))
doAssert isAlpha("Rasp")
doAssert isAlpha("Args")
doAssert(not isAlpha("$Tomato"))
doAssert isAlphaNumeric('3')
doAssert isAlphaNumeric('R')
doAssert(not isAlphaNumeric('!'))
doAssert isAlphaNumeric("34ABc")
doAssert isAlphaNumeric("Rad")
doAssert isAlphaNumeric("1234")
doAssert(not isAlphaNumeric("@nose"))
doAssert isDigit('3')
doAssert(not isDigit('a'))
doAssert(not isDigit('%'))
doAssert isDigit("12533")
doAssert(not isDigit("12.33"))
doAssert(not isDigit("A45b"))
doAssert isSpace('\t')
doAssert isSpace('\l')
doAssert(not isSpace('A'))
doAssert isSpace("\t\l \v\r\f")
doAssert isSpace(" ")
doAssert(not isSpace("ABc \td"))
doAssert isLower('a')
doAssert isLower('z')
doAssert(not isLower('A'))
doAssert(not isLower('5'))
doAssert(not isLower('&'))
doAssert isLower("abcd")
doAssert(not isLower("abCD"))
doAssert(not isLower("33aa"))
doAssert isUpper('A')
doAssert(not isUpper('b'))
doAssert(not isUpper('5'))
doAssert(not isUpper('%'))
doAssert isUpper("ABC")
doAssert(not isUpper("AAcc"))
doAssert(not isUpper("A#$"))

View File

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

View File

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

View File

@@ -1260,6 +1260,15 @@ const
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
taintMode = compileOption("taintmode")
when defined(boehmgc):
when defined(windows):
const boehmLib = "boehmgc.dll"
elif defined(macosx):
const boehmLib = "libgc.dylib"
else:
const boehmLib = "libgc.so.1"
{.pragma: boehmGC, noconv, dynlib: boehmLib.}
when taintMode:
type TaintedString* = distinct string ## a distinct string type that
## is `tainted`:idx:. It is an alias for
@@ -2160,53 +2169,6 @@ proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
result = s[L]
setLen(s, L)
proc each*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] {.
deprecated.} =
## The well-known ``map`` operation from functional programming. Applies
## `op` to every item in `data` and returns the result as a sequence.
##
## **Deprecated since version 0.9:** Use the ``map`` proc instead.
newSeq(result, data.len)
for i in 0..data.len-1: result[i] = op(data[i])
proc each*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) {.
deprecated.} =
## The well-known ``map`` operation from functional programming. Applies
## `op` to every item in `data` modifying it directly.
##
## **Deprecated since version 0.9:** Use the ``map`` proc instead.
for i in 0..data.len-1: op(data[i])
proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] =
## Returns a new sequence with the results of `op` applied to every item in
## `data`.
##
## Since the input is not modified you can use this version of ``map`` to
## transform the type of the elements in the input sequence. Example:
##
## .. code-block:: nim
## let
## a = @[1, 2, 3, 4]
## b = map(a, proc(x: int): string = $x)
## assert b == @["1", "2", "3", "4"]
newSeq(result, data.len)
for i in 0..data.len-1: result[i] = op(data[i])
proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) =
## Applies `op` to every item in `data` modifying it directly.
##
## Note that this version of ``map`` requires your input and output types to
## be the same, since they are modified in-place. Example:
##
## .. code-block:: nim
## var a = @["1", "2", "3", "4"]
## echo repr(a)
## # --> ["1", "2", "3", "4"]
## map(a, proc(x: var string) = x &= "42")
## echo repr(a)
## # --> ["142", "242", "342", "442"]
for i in 0..data.len-1: op(data[i])
iterator fields*[T: tuple|object](x: T): RootObj {.
magic: "Fields", noSideEffect.}
## iterates over every field of `x`. Warning: This really transforms

View File

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

View File

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

View File

@@ -304,22 +304,53 @@ type
when not defined(boehmgc) and not hasSharedHeap and not defined(gogc):
proc deallocOsPages()
when defined(boehmgc):
type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer)
{.importc: "GC_call_with_stack_base", boehmGC.}
proc boehmGC_register_my_thread(sb: pointer)
{.importc: "GC_register_my_thread", boehmGC.}
proc boehmGC_unregister_my_thread()
{.importc: "GC_unregister_my_thread", boehmGC.}
proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv.} =
boehmGC_register_my_thread(sb)
let thrd = cast[ptr Thread[TArg]](thrd)
when TArg is void:
thrd.dataFn()
else:
thrd.dataFn(thrd.data)
boehmGC_unregister_my_thread()
else:
proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) =
when TArg is void:
thrd.dataFn()
else:
thrd.dataFn(thrd.data)
proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
when defined(boehmgc):
boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
elif not defined(nogc) and not defined(gogc):
var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} =
threadProcWrapDispatch[TArg]
when not hasSharedHeap:
# init the GC for refc/markandsweep
setStackBottom(addr(p))
initGC()
when declared(registerThread):
thrd.stackBottom = addr(thrd)
registerThread(thrd)
p(thrd)
when declared(registerThread): unregisterThread(thrd)
when declared(deallocOsPages): deallocOsPages()
else:
threadProcWrapDispatch(thrd)
template threadProcWrapperBody(closure: expr) {.immediate.} =
when declared(globalsSlot): threadVarSetValue(globalsSlot, closure)
var t = cast[ptr Thread[TArg]](closure)
when useStackMaskHack:
var tls: ThreadLocalStorage
when not defined(boehmgc) and not defined(gogc) and not defined(nogc) and not hasSharedHeap:
# init the GC for this thread:
setStackBottom(addr(t))
initGC()
when declared(registerThread):
t.stackBottom = addr(t)
registerThread(t)
when TArg is void: t.dataFn()
else: t.dataFn(t.data)
when declared(registerThread): unregisterThread(t)
when declared(deallocOsPages): deallocOsPages()
var thrd = cast[ptr Thread[TArg]](closure)
threadProcWrapStackFrame(thrd)
# Since an unhandled exception terminates the whole process (!), there is
# no need for a ``try finally`` here, nor would it be correct: The current
# exception is tried to be re-raised by the code-gen after the ``finally``!
@@ -327,7 +358,7 @@ template threadProcWrapperBody(closure: expr) {.immediate.} =
# page!
# mark as not running anymore:
t.dataFn = nil
thrd.dataFn = nil
{.push stack_trace:off.}
when defined(windows):

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import json, tables
import json, tables, sequtils
proc run(json_params: TTable) =
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
assert t["123"] == 1.5
assert t["111"] == 0.0 # deleted
try:
echo t["111"] # deleted
except KeyError:
discard
assert(not hasKey(t, "111"))
assert "123" in t
assert("111" notin t)

View File

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

View File

@@ -5,6 +5,7 @@ discard """
3'''
"""
import sequtils
# https://github.com/Araq/Nim/issues/797
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)
assert x == @[11, 12, 13]

View File

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

View File

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

View File

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

View File

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

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>""")
for x in d.mitems:
x = <>Student(Name=x.attrs["Name"] & "foo")
d.mget(1).attrs["Name"] = "bar"
d[1].attrs["Name"] = "bar"
echo d

View File

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

View File

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

View File

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

View File

@@ -119,12 +119,21 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
testSpec r, makeTest("tests/gc" / filename, options &
" -d:release -d:useRealtimeGC", cat, actionRun)
template test(filename: expr): stmt =
template testWithoutBoehm(filename: expr): stmt =
testWithoutMs filename
testSpec r, makeTest("tests/gc" / filename, options &
" --gc:markAndSweep", cat, actionRun)
testSpec r, makeTest("tests/gc" / filename, options &
" -d:release --gc:markAndSweep", cat, actionRun)
template test(filename: expr): stmt =
testWithoutBoehm filename
when not defined(windows):
# AR: cannot find any boehm.dll on the net, right now, so disabled
# for windows:
testSpec r, makeTest("tests/gc" / filename, options &
" --gc:boehm", cat, actionRun)
testSpec r, makeTest("tests/gc" / filename, options &
" -d:release --gc:boehm", cat, actionRun)
test "gcemscripten"
test "growobjcrash"
@@ -136,9 +145,9 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
test "gcleak4"
# Disabled because it works and takes too long to run:
#test "gcleak5"
test "weakrefs"
testWithoutBoehm "weakrefs"
test "cycleleak"
test "closureleak"
testWithoutBoehm "closureleak"
testWithoutMs "refarrayleak"
test "stackrefleak"

View File

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

View File

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

View File

@@ -8,7 +8,11 @@ News
Changes affecting backwards compatibility
-----------------------------------------
- ``tables.[]``, ``strtabs.[]``, ``critbits.[]`` **now raise**
the ``KeyError`` **exception when the key does not exist**! Use the
new ``getOrDefault`` instead to get the old behaviour. Compile all your
code with ``-d:nimTableGet`` to get a listing of where your code
uses ``[]``!
- The ``rawsockets`` module has been renamed to ``nativesockets`` to avoid
confusion with TCP/IP raw sockets, so ``newNativeSocket`` should be used
instead of ``newRawSocket``.
@@ -68,10 +72,10 @@ News
* ``libeay32.dll``: Split into ``libeay32.dll`` and ``libeay64.dll``.
Compile with ``-d:nimOldDLLs`` to make the stdlib use the old DLL names.
- Nim VM now treats objects as nkObjConstr nodes, and not nkPar nodes as it
was previously. Macros that generate nkPar nodes when object is expected are
likely to break. Macros that expect nkPar nodes to which objects are passed
are likely to break as well.
- Nim VM now treats objects as ``nkObjConstr`` nodes, and not ``nkPar`` nodes
as it was previously. Macros that generate ``nkPar`` nodes when object is
expected are likely to break. Macros that expect ``nkPar`` nodes to which
objects are passed are likely to break as well.
- Base methods now need to be annotated with the ``base`` pragma. This makes
multi methods less error-prone to use with the effect system.
- Nim's parser directive ``#!`` is now ``#?`` in order to produce no conflicts
@@ -86,8 +90,14 @@ News
echo f(0, "abc")
- The ``ftpclient`` module is now deprecated in favour of the
``asyncdispatch`` module.
``asyncftpclient`` module.
- In sequtils.nim renamed ``repeat`` function to ``cycle`` (concatenating
a sequence by itself the given times), and also introduced ``repeat``,
which repeats an element the given times.
- The function ``map`` is moved to sequtils.nim. The inplace ``map`` version
is renamed to ``apply``.
- The template ``mapIt`` now doesn't require the result's type parameter.
Also the inplace ``mapIt`` is renamed to ``apply``.
Library Additions
-----------------

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"
pdf: "manual.txt;lib;tut1;tut2;nimc;niminst;gc"
srcdoc2: "system.nim;system/nimscript;pure/ospaths"
srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned"
srcdoc2: "impure/re;pure/sockets;pure/typetraits"
srcdoc2: "core/macros;pure/marshal;core/typeinfo"
srcdoc2: "impure/re;pure/typetraits"
srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim"
srcdoc: "system/threads.nim;system/channels.nim;js/dom"
srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm"
@@ -52,12 +52,13 @@ srcdoc2: "pure/json;pure/base64;pure/scgi"
srcdoc2: "pure/collections/tables;pure/collections/sets;pure/collections/lists"
srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings"
srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies"
srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits"
srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits"
srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
srcdoc2: "packages/docutils/rstgen;pure/logging;pure/asyncdispatch;pure/asyncnet"
srcdoc2: "pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future"
srcdoc2: "deprecated/pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future"
srcdoc2: "deprecated/pure/ftpclient"
srcdoc2: "pure/asyncfile"
srcdoc2: "pure/md5;pure/rationals"
srcdoc2: "posix/posix"