mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-01 02:42:05 +00:00
Merge branch 'devel' of github.com:nim-lang/Nim into devel
This commit is contained in:
@@ -654,7 +654,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
|
||||
else:
|
||||
result = cppName & "<"
|
||||
for i in 1 .. typ.len-2:
|
||||
if i > 1: result.add(", ")
|
||||
if i > 1: result.add(" COMMA ")
|
||||
result.add(getTypeDescAux(m, typ.sons[i], check))
|
||||
result.add("> ")
|
||||
# always call for sideeffects:
|
||||
|
||||
@@ -129,7 +129,7 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
|
||||
result = bindOrCheck(c, p.sons[2].sym, arglist)
|
||||
|
||||
proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||
# hidden conversions (?)
|
||||
let n = skipHidden(n)
|
||||
if nfNoRewrite in n.flags:
|
||||
result = false
|
||||
elif isPatternParam(c, p):
|
||||
|
||||
@@ -414,8 +414,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||
result = newTransNode(nkChckRange, n, 3)
|
||||
dest = skipTypes(n.typ, abstractVar)
|
||||
result[0] = transform(c, n.sons[1])
|
||||
result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), source).PTransNode
|
||||
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
|
||||
result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), dest).PTransNode
|
||||
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), dest).PTransNode
|
||||
of tyFloat..tyFloat128:
|
||||
# XXX int64 -> float conversion?
|
||||
if skipTypes(n.typ, abstractVar).kind == tyRange:
|
||||
|
||||
@@ -1448,6 +1448,18 @@ proc skipConv*(n: PNode): PNode =
|
||||
result = n.sons[1]
|
||||
else: discard
|
||||
|
||||
proc skipHidden*(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
if result.sons[1].typ.classify == result.typ.classify:
|
||||
result = result.sons[1]
|
||||
else: break
|
||||
of nkHiddenDeref, nkHiddenAddr:
|
||||
result = result.sons[0]
|
||||
else: break
|
||||
|
||||
proc skipConvTakeType*(n: PNode): PNode =
|
||||
result = n.skipConv
|
||||
result.typ = n.typ
|
||||
|
||||
@@ -796,17 +796,17 @@ proc infix*(a: NimNode; op: string;
|
||||
proc unpackPostfix*(node: NimNode): tuple[node: NimNode; op: string] {.
|
||||
compileTime.} =
|
||||
node.expectKind nnkPostfix
|
||||
result = (node[0], $node[1])
|
||||
result = (node[1], $node[0])
|
||||
|
||||
proc unpackPrefix*(node: NimNode): tuple[node: NimNode; op: string] {.
|
||||
compileTime.} =
|
||||
node.expectKind nnkPrefix
|
||||
result = (node[0], $node[1])
|
||||
result = (node[1], $node[0])
|
||||
|
||||
proc unpackInfix*(node: NimNode): tuple[left: NimNode; op: string;
|
||||
right: NimNode] {.compileTime.} =
|
||||
assert node.kind == nnkInfix
|
||||
result = (node[0], $node[1], node[2])
|
||||
result = (node[1], $node[0], node[2])
|
||||
|
||||
proc copy*(node: NimNode): NimNode {.compileTime.} =
|
||||
## An alias for copyNimTree().
|
||||
|
||||
@@ -105,7 +105,8 @@ else:
|
||||
proc readLineFromStdin*(prompt: string): TaintedString {.
|
||||
tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
var buffer = linenoise.readLine(prompt)
|
||||
if isNil(buffer): quit(0)
|
||||
if isNil(buffer):
|
||||
raise newException(IOError, "Linenoise returned nil")
|
||||
result = TaintedString($buffer)
|
||||
if result.string.len > 0:
|
||||
historyAdd(buffer)
|
||||
@@ -114,12 +115,12 @@ else:
|
||||
proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
|
||||
tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
var buffer = linenoise.readLine(prompt)
|
||||
if isNil(buffer): quit(0)
|
||||
if isNil(buffer):
|
||||
raise newException(IOError, "Linenoise returned nil")
|
||||
line = TaintedString($buffer)
|
||||
if line.string.len > 0:
|
||||
historyAdd(buffer)
|
||||
linenoise.free(buffer)
|
||||
# XXX how to determine CTRL+D?
|
||||
result = true
|
||||
|
||||
proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
## This module provides an easy to use sockets-style
|
||||
## nim interface to the OpenSSL library.
|
||||
##
|
||||
## **Warning:** This module is deprecated, use the SSL procedures defined in
|
||||
## the ``net`` module instead.
|
||||
|
||||
{.deprecated.}
|
||||
|
||||
|
||||
@@ -222,6 +222,8 @@ __clang__
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
#define COMMA ,
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
@@ -2627,3 +2627,17 @@ proc utimes*(path: cstring, times: ptr array [2, Timeval]): int {.
|
||||
## Returns zero on success.
|
||||
##
|
||||
## For more information read http://www.unix.com/man-page/posix/3/utimes/.
|
||||
|
||||
proc handle_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {.importc: "signal", header: "<signal.h>".}
|
||||
|
||||
template onSignal*(signals: varargs[cint], body: untyped): stmt =
|
||||
## Setup code to be executed when Unix signals are received. Example:
|
||||
## from posix import SIGINT, SIGTERM
|
||||
## onSignal(SIGINT, SIGTERM):
|
||||
## echo "bye"
|
||||
|
||||
for s in signals:
|
||||
handle_signal(s,
|
||||
proc (sig: cint) {.noconv.} =
|
||||
body
|
||||
)
|
||||
|
||||
@@ -1316,6 +1316,23 @@ proc generateExceptionCheck(futSym,
|
||||
)
|
||||
result.add elseNode
|
||||
|
||||
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
|
||||
rootReceiver: expr, fromNode: NimNode) =
|
||||
## Params:
|
||||
## futureVarNode: The NimNode which is a symbol identifying the Future[T]
|
||||
## variable to yield.
|
||||
## fromNode: Used for better debug information (to give context).
|
||||
## valueReceiver: The node which defines an expression that retrieves the
|
||||
## future's value.
|
||||
##
|
||||
## rootReceiver: ??? TODO
|
||||
# -> yield future<x>
|
||||
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
|
||||
# -> future<x>.read
|
||||
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
|
||||
result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
|
||||
fromNode)
|
||||
|
||||
template createVar(result: var NimNode, futSymName: string,
|
||||
asyncProc: NimNode,
|
||||
valueReceiver, rootReceiver: expr,
|
||||
@@ -1323,9 +1340,7 @@ template createVar(result: var NimNode, futSymName: string,
|
||||
result = newNimNode(nnkStmtList, fromNode)
|
||||
var futSym = genSym(nskVar, "future")
|
||||
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
|
||||
result.add newNimNode(nnkYieldStmt, fromNode).add(futSym) # -> yield future<x>
|
||||
valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
|
||||
result.add generateExceptionCheck(futSym, tryStmt, rootReceiver, fromNode)
|
||||
useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
|
||||
|
||||
proc processBody(node, retFutureSym: NimNode,
|
||||
subTypeIsVoid: bool,
|
||||
@@ -1352,7 +1367,11 @@ proc processBody(node, retFutureSym: NimNode,
|
||||
case node[1].kind
|
||||
of nnkIdent, nnkInfix:
|
||||
# await x
|
||||
result = newNimNode(nnkYieldStmt, node).add(node[1]) # -> yield x
|
||||
result = newNimNode(nnkStmtList, node)
|
||||
var futureValue: NimNode
|
||||
result.useVar(node[1], futureValue, futureValue, node)
|
||||
# -> yield x
|
||||
# -> x.read()
|
||||
of nnkCall, nnkCommand:
|
||||
# await foo(p, x)
|
||||
var futureValue: NimNode
|
||||
@@ -1550,6 +1569,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
||||
for i in 0 .. <result[4].len:
|
||||
if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
|
||||
result[4].del(i)
|
||||
result[4] = newEmptyNode()
|
||||
if subtypeIsVoid:
|
||||
# Add discardable pragma.
|
||||
if returnType.kind == nnkEmpty:
|
||||
@@ -1559,7 +1579,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
||||
result[6] = outerProcBody
|
||||
|
||||
#echo(treeRepr(result))
|
||||
#if prc[0].getName == "hubConnectionLoop":
|
||||
#if prc[0].getName == "g":
|
||||
# echo(toStrLit(result))
|
||||
|
||||
macro async*(prc: stmt): stmt {.immediate.} =
|
||||
|
||||
@@ -162,7 +162,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
# Request completed immediately.
|
||||
var bytesRead: DWord
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POverlapped](ol)[], bytesRead, false.WinBool)
|
||||
cast[POverlapped](ol), bytesRead, false.WinBool)
|
||||
if not overlappedRes.bool:
|
||||
let err = osLastError()
|
||||
if err.int32 == ERROR_HANDLE_EOF:
|
||||
@@ -282,7 +282,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
# Request completed immediately.
|
||||
var bytesWritten: DWord
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POverlapped](ol)[], bytesWritten, false.WinBool)
|
||||
cast[POverlapped](ol), bytesWritten, false.WinBool)
|
||||
if not overlappedRes.bool:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
else:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## asynchronous dispatcher defined in the ``asyncdispatch`` module.
|
||||
##
|
||||
## SSL
|
||||
## ---
|
||||
## ----
|
||||
##
|
||||
## SSL can be enabled by compiling with the ``-d:ssl`` flag.
|
||||
##
|
||||
@@ -62,7 +62,9 @@ import os
|
||||
|
||||
export SOBool
|
||||
|
||||
when defined(ssl):
|
||||
const defineSsl = defined(ssl) or defined(nimdoc)
|
||||
|
||||
when defineSsl:
|
||||
import openssl
|
||||
|
||||
type
|
||||
@@ -79,7 +81,7 @@ type
|
||||
of false: nil
|
||||
case isSsl: bool
|
||||
of true:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
sslHandle: SslPtr
|
||||
sslContext: SslContext
|
||||
bioIn: BIO
|
||||
@@ -125,7 +127,7 @@ proc newAsyncSocket*(domain, sockType, protocol: cint,
|
||||
Domain(domain), SockType(sockType),
|
||||
Protocol(protocol), buffered)
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
proc getSslError(handle: SslPtr, err: cint): cint =
|
||||
assert err < 0
|
||||
var ret = SSLGetError(handle, err.cint)
|
||||
@@ -186,7 +188,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
|
||||
## or an error occurs.
|
||||
await connect(socket.fd.AsyncFD, address, port, socket.domain)
|
||||
if socket.isSsl:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
let flags = {SocketFlag.SafeDisconn}
|
||||
sslSetConnectState(socket.sslHandle)
|
||||
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
||||
@@ -197,7 +199,7 @@ template readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
## this is a template and not a proc.
|
||||
var res = 0
|
||||
if socket.isSsl:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
# SSL mode.
|
||||
sslLoop(socket, flags,
|
||||
sslRead(socket.sslHandle, buf, size.cint))
|
||||
@@ -274,7 +276,7 @@ proc send*(socket: AsyncSocket, data: string,
|
||||
## data has been sent.
|
||||
assert socket != nil
|
||||
if socket.isSsl:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
var copy = data
|
||||
sslLoop(socket, flags,
|
||||
sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
|
||||
@@ -426,9 +428,6 @@ proc recvLine*(socket: AsyncSocket,
|
||||
##
|
||||
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
|
||||
## uses ``\r\L`` to delimit a new line.
|
||||
template addNLIfEmpty(): stmt =
|
||||
if result.len == 0:
|
||||
result.add("\c\L")
|
||||
assert SocketFlag.Peek notin flags ## TODO:
|
||||
|
||||
# TODO: Optimise this
|
||||
@@ -456,7 +455,8 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
|
||||
of AF_INET6: realaddr = "::"
|
||||
of AF_INET: realaddr = "0.0.0.0"
|
||||
else:
|
||||
raiseOSError("Unknown socket address family and no address specified to bindAddr")
|
||||
raise newException(ValueError,
|
||||
"Unknown socket address family and no address specified to bindAddr")
|
||||
|
||||
var aiList = getAddrInfo(realaddr, port, socket.domain)
|
||||
if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
|
||||
@@ -468,7 +468,7 @@ proc close*(socket: AsyncSocket) =
|
||||
## Closes the socket.
|
||||
defer:
|
||||
socket.fd.AsyncFD.closeSocket()
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
let res = SslShutdown(socket.sslHandle)
|
||||
SSLFree(socket.sslHandle)
|
||||
@@ -478,7 +478,7 @@ proc close*(socket: AsyncSocket) =
|
||||
raiseSslError()
|
||||
socket.closed = true # TODO: Add extra debugging checks for this.
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) =
|
||||
## Wraps a socket in an SSL context. This function effectively turns
|
||||
## ``socket`` into an SSL socket.
|
||||
|
||||
@@ -90,6 +90,8 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
if r != result.len:
|
||||
setLen(result, r)
|
||||
#assert(r == result.len)
|
||||
discard
|
||||
|
||||
@@ -162,4 +164,3 @@ when isMainModule:
|
||||
"asure.", longText]
|
||||
for t in items(tests):
|
||||
assert decode(encode(t)) == t
|
||||
|
||||
|
||||
@@ -663,6 +663,27 @@ proc sort*[A, B](t: OrderedTableRef[A, B],
|
||||
## contrast to the `sort` for count tables).
|
||||
t[].sort(cmp)
|
||||
|
||||
proc del*[A, B](t: var OrderedTable[A, B], key: A) =
|
||||
## deletes `key` from ordered hash table `t`. O(n) comlexity.
|
||||
var prev = -1
|
||||
let hc = hash(key)
|
||||
forAllOrderedPairs:
|
||||
if t.data[h].hcode == hc:
|
||||
if t.first == h:
|
||||
t.first = t.data[h].next
|
||||
else:
|
||||
t.data[prev].next = t.data[h].next
|
||||
var zeroValue : type(t.data[h])
|
||||
t.data[h] = zeroValue
|
||||
dec t.counter
|
||||
break
|
||||
else:
|
||||
prev = h
|
||||
|
||||
proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
|
||||
## deletes `key` from ordered hash table `t`. O(n) comlexity.
|
||||
t[].del(key)
|
||||
|
||||
# ------------------------------ count tables -------------------------------
|
||||
|
||||
type
|
||||
@@ -984,6 +1005,26 @@ when isMainModule:
|
||||
s3[p1] = 30_000
|
||||
s3[p2] = 45_000
|
||||
|
||||
block: # Ordered table should preserve order after deletion
|
||||
var
|
||||
s4 = initOrderedTable[int, int]()
|
||||
s4[1] = 1
|
||||
s4[2] = 2
|
||||
s4[3] = 3
|
||||
|
||||
var prev = 0
|
||||
for i in s4.values:
|
||||
doAssert(prev < i)
|
||||
prev = i
|
||||
|
||||
s4.del(2)
|
||||
doAssert(2 notin s4)
|
||||
doAssert(s4.len == 2)
|
||||
prev = 0
|
||||
for i in s4.values:
|
||||
doAssert(prev < i)
|
||||
prev = i
|
||||
|
||||
var
|
||||
t1 = initCountTable[string]()
|
||||
t2 = initCountTable[string]()
|
||||
|
||||
@@ -335,7 +335,7 @@ proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
|
||||
var m = newMimetypes()
|
||||
for name, file in xs.items:
|
||||
var contentType: string
|
||||
let (dir, fName, ext) = splitFile(file)
|
||||
let (_, fName, ext) = splitFile(file)
|
||||
if ext.len > 0:
|
||||
contentType = m.getMimetype(ext[1..ext.high], nil)
|
||||
p.add(name, readFile(file), fName & ext, contentType)
|
||||
@@ -627,7 +627,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||
result.userAgent = defUserAgent
|
||||
result.maxRedirects = maxRedirects
|
||||
when defined(ssl):
|
||||
result.sslContext = net.SslContext(sslContext)
|
||||
result.sslContext = sslContext
|
||||
|
||||
proc close*(client: AsyncHttpClient) =
|
||||
## Closes any connections held by the HTTP client.
|
||||
|
||||
@@ -56,6 +56,11 @@ import
|
||||
export
|
||||
tables.`$`
|
||||
|
||||
when defined(nimJsonGet):
|
||||
{.pragma: deprecatedGet, deprecated.}
|
||||
else:
|
||||
{.pragma: deprecatedGet.}
|
||||
|
||||
type
|
||||
JsonEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||
jsonError, ## an error occurred during parsing
|
||||
@@ -799,16 +804,23 @@ proc len*(n: JsonNode): int =
|
||||
of JObject: result = n.fields.len
|
||||
else: discard
|
||||
|
||||
proc `[]`*(node: JsonNode, name: string): JsonNode {.inline.} =
|
||||
proc `[]`*(node: JsonNode, name: string): JsonNode {.inline, deprecatedGet.} =
|
||||
## Gets a field from a `JObject`, which must not be nil.
|
||||
## If the value at `name` does not exist, returns nil
|
||||
## If the value at `name` does not exist, raises KeyError.
|
||||
##
|
||||
## **Note:** The behaviour of this procedure changed in version 0.14.0. To
|
||||
## get a list of usages and to restore the old behaviour of this procedure,
|
||||
## compile with the ``-d:nimJsonGet`` flag.
|
||||
assert(not isNil(node))
|
||||
assert(node.kind == JObject)
|
||||
result = node.fields.getOrDefault(name)
|
||||
when defined(nimJsonGet):
|
||||
if not node.fields.hasKey(name): return nil
|
||||
result = node.fields[name]
|
||||
|
||||
proc `[]`*(node: JsonNode, index: int): JsonNode {.inline.} =
|
||||
## Gets the node at `index` in an Array. Result is undefined if `index`
|
||||
## is out of bounds
|
||||
## is out of bounds, but as long as array bound checks are enabled it will
|
||||
## result in an exception.
|
||||
assert(not isNil(node))
|
||||
assert(node.kind == JArray)
|
||||
return node.elems[index]
|
||||
@@ -838,20 +850,28 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
|
||||
|
||||
proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
|
||||
## Traverses the node and gets the given value. If any of the
|
||||
## keys do not exist, returns nil. Also returns nil if one of the
|
||||
## intermediate data structures is not an object
|
||||
## keys do not exist, returns ``nil``. Also returns ``nil`` if one of the
|
||||
## intermediate data structures is not an object.
|
||||
result = node
|
||||
for key in keys:
|
||||
if isNil(result) or result.kind!=JObject:
|
||||
if isNil(result) or result.kind != JObject:
|
||||
return nil
|
||||
result=result[key]
|
||||
result = result.fields.getOrDefault(key)
|
||||
|
||||
proc getOrDefault*(node: JsonNode, key: string): JsonNode =
|
||||
## Gets a field from a `node`. If `node` is nil or not an object or
|
||||
## value at `key` does not exist, returns nil
|
||||
if not isNil(node) and node.kind == JObject:
|
||||
result = node.fields.getOrDefault(key)
|
||||
|
||||
template simpleGetOrDefault*{`{}`(node, [key])}(node: JsonNode, key: string): JsonNode = node.getOrDefault(key)
|
||||
|
||||
proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) =
|
||||
## Traverses the node and tries to set the value at the given location
|
||||
## to `value` If any of the keys are missing, they are added
|
||||
## to ``value``. If any of the keys are missing, they are added.
|
||||
var node = node
|
||||
for i in 0..(keys.len-2):
|
||||
if isNil(node[keys[i]]):
|
||||
if not node.hasKey(keys[i]):
|
||||
node[keys[i]] = newJObject()
|
||||
node = node[keys[i]]
|
||||
node[keys[keys.len-1]] = value
|
||||
@@ -1217,16 +1237,6 @@ when false:
|
||||
# To get that we shall use, obj["json"]
|
||||
|
||||
when isMainModule:
|
||||
when not defined(js):
|
||||
var parsed = parseFile("tests/testdata/jsontest.json")
|
||||
|
||||
try:
|
||||
discard parsed["key2"][12123]
|
||||
doAssert(false)
|
||||
except IndexError: doAssert(true)
|
||||
|
||||
var parsed2 = parseFile("tests/testdata/jsontest2.json")
|
||||
doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
|
||||
|
||||
let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}"""
|
||||
# nil passthrough
|
||||
@@ -1314,3 +1324,19 @@ when isMainModule:
|
||||
|
||||
var j4 = %*{"test": nil}
|
||||
doAssert j4 == %{"test": newJNull()}
|
||||
|
||||
echo("99% of tests finished. Going to try loading file.")
|
||||
|
||||
# Test loading of file.
|
||||
when not defined(js):
|
||||
var parsed = parseFile("tests/testdata/jsontest.json")
|
||||
|
||||
try:
|
||||
discard parsed["key2"][12123]
|
||||
doAssert(false)
|
||||
except IndexError: doAssert(true)
|
||||
|
||||
var parsed2 = parseFile("tests/testdata/jsontest2.json")
|
||||
doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
|
||||
|
||||
echo("Tests succeeded!")
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#
|
||||
|
||||
## This module contains various string matchers for email addresses, etc.
|
||||
##
|
||||
## **Warning:** This module is deprecated since version 0.14.0.
|
||||
{.deprecated.}
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
{.push debugger:off .} # the user does not want to trace a part
|
||||
|
||||
104
lib/pure/net.nim
104
lib/pure/net.nim
@@ -8,19 +8,76 @@
|
||||
#
|
||||
|
||||
## This module implements a high-level cross-platform sockets interface.
|
||||
## The procedures implemented in this module are primarily for blocking sockets.
|
||||
## For asynchronous non-blocking sockets use the ``asyncnet`` module together
|
||||
## with the ``asyncdispatch`` module.
|
||||
##
|
||||
## The first thing you will always need to do in order to start using sockets,
|
||||
## is to create a new instance of the ``Socket`` type using the ``newSocket``
|
||||
## procedure.
|
||||
##
|
||||
## SSL
|
||||
## ====
|
||||
##
|
||||
## In order to use the SSL procedures defined in this module, you will need to
|
||||
## compile your application with the ``-d:ssl`` flag.
|
||||
##
|
||||
## Examples
|
||||
## ========
|
||||
##
|
||||
## Connecting to a server
|
||||
## ----------------------
|
||||
##
|
||||
## After you create a socket with the ``newSocket`` procedure, you can easily
|
||||
## connect it to a server running at a known hostname (or IP address) and port.
|
||||
## To do so over TCP, use the example below.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var socket = newSocket()
|
||||
## socket.connect("google.com", Port(80))
|
||||
##
|
||||
## UDP is a connectionless protocol, so UDP sockets don't have to explicitly
|
||||
## call the ``connect`` procedure. They can simply start sending data
|
||||
## immediately.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var socket = newSocket()
|
||||
## socket.sendTo("192.168.0.1", Port(27960), "status\n")
|
||||
##
|
||||
## Creating a server
|
||||
## -----------------
|
||||
##
|
||||
## After you create a socket with the ``newSocket`` procedure, you can create a
|
||||
## TCP server by calling the ``bindAddr`` and ``listen`` procedures.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var socket = newSocket()
|
||||
## socket.bindAddr(Port(1234))
|
||||
## socket.listen()
|
||||
##
|
||||
## You can then begin accepting connections using the ``accept`` procedure.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var client = newSocket()
|
||||
## var address = ""
|
||||
## while true:
|
||||
## socket.acceptAddr(client, address)
|
||||
## echo("Client connected from: ", address)
|
||||
##
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
import nativesockets, os, strutils, parseutils, times
|
||||
export Port, `$`, `==`
|
||||
|
||||
const useWinVersion = defined(Windows) or defined(nimdoc)
|
||||
const defineSsl = defined(ssl) or defined(nimdoc)
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
import openssl
|
||||
|
||||
# Note: The enumerations are mapped to Window's constants.
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
type
|
||||
SslError* = object of Exception
|
||||
|
||||
@@ -54,7 +111,7 @@ type
|
||||
currPos: int # current index in buffer
|
||||
bufLen: int # current length of buffer
|
||||
of false: nil
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
case isSsl: bool
|
||||
of true:
|
||||
sslHandle: SSLPtr
|
||||
@@ -160,7 +217,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
|
||||
raiseOSError(osLastError())
|
||||
result = newSocket(fd, domain, sockType, protocol, buffered)
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
CRYPTO_malloc_init()
|
||||
SslLibraryInit()
|
||||
SslLoadErrorStrings()
|
||||
@@ -301,7 +358,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
|
||||
## error was caused by no data being available to be read.
|
||||
##
|
||||
## If ``err`` is not lower than 0 no exception will be raised.
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
if err <= 0:
|
||||
var ret = SSLGetError(socket.sslHandle, err.cint)
|
||||
@@ -334,7 +391,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
|
||||
raiseSSLError()
|
||||
else: raiseSSLError("Unknown Error")
|
||||
|
||||
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||
if err == -1 and not (when defineSsl: socket.isSSL else: false):
|
||||
var lastE = if lastError.int == -1: getSocketError(socket) else: lastError
|
||||
if async:
|
||||
when useWinVersion:
|
||||
@@ -414,7 +471,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||
client.isBuffered = server.isBuffered
|
||||
|
||||
# Handle SSL.
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if server.isSSL:
|
||||
# We must wrap the client sock in a ssl context.
|
||||
|
||||
@@ -425,7 +482,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||
# Client socket is set above.
|
||||
address = $inet_ntoa(sockAddress.sin_addr)
|
||||
|
||||
when false: #defined(ssl):
|
||||
when false: #defineSsl:
|
||||
proc acceptAddrSSL*(server: Socket, client: var Socket,
|
||||
address: var string): SSLAcceptResult {.
|
||||
tags: [ReadIOEffect].} =
|
||||
@@ -444,7 +501,7 @@ when false: #defined(ssl):
|
||||
## ``AcceptNoClient`` will be returned when no client is currently attempting
|
||||
## to connect.
|
||||
template doHandshake(): stmt =
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if server.isSSL:
|
||||
client.setBlocking(false)
|
||||
# We must wrap the client sock in a ssl context.
|
||||
@@ -495,7 +552,7 @@ proc accept*(server: Socket, client: var Socket,
|
||||
proc close*(socket: Socket) =
|
||||
## Closes a socket.
|
||||
try:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
ErrClearError()
|
||||
# As we are closing the underlying socket immediately afterwards,
|
||||
@@ -547,8 +604,9 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
|
||||
var valuei = cint(if value: 1 else: 0)
|
||||
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
||||
|
||||
when defined(ssl):
|
||||
proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||
when defineSsl:
|
||||
proc handshake*(socket: Socket): bool
|
||||
{.tags: [ReadIOEffect, WriteIOEffect], deprecated.} =
|
||||
## This proc needs to be called on a socket after it connects. This is
|
||||
## only applicable when using ``connectAsync``.
|
||||
## This proc performs the SSL handshake.
|
||||
@@ -557,6 +615,8 @@ when defined(ssl):
|
||||
## ``True`` whenever handshake completed successfully.
|
||||
##
|
||||
## A ESSL error is raised on any other errors.
|
||||
##
|
||||
## **Note:** This procedure is deprecated since version 0.14.0.
|
||||
result = true
|
||||
if socket.isSSL:
|
||||
var ret = SSLConnect(socket.sslHandle)
|
||||
@@ -594,7 +654,7 @@ proc hasDataBuffered*(s: Socket): bool =
|
||||
if s.isBuffered:
|
||||
result = s.bufLen > 0 and s.currPos != s.bufLen
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if s.isSSL and not result:
|
||||
result = s.sslHasPeekChar
|
||||
|
||||
@@ -608,7 +668,7 @@ proc select(readfd: Socket, timeout = 500): int =
|
||||
|
||||
proc readIntoBuf(socket: Socket, flags: int32): int =
|
||||
result = 0
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
result = SSLRead(socket.sslHandle, addr(socket.buffer), int(socket.buffer.high))
|
||||
else:
|
||||
@@ -658,7 +718,7 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
|
||||
|
||||
result = read
|
||||
else:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
if socket.sslHasPeekChar:
|
||||
copyMem(data, addr(socket.sslPeekChar), 1)
|
||||
@@ -696,7 +756,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
|
||||
if timeout - int(waited * 1000.0) < 1:
|
||||
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
if socket.hasDataBuffered:
|
||||
# sslPeekChar is present.
|
||||
@@ -764,7 +824,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
|
||||
|
||||
c = socket.buffer[socket.currPos]
|
||||
else:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
if not socket.sslHasPeekChar:
|
||||
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
|
||||
@@ -872,7 +932,7 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
|
||||
##
|
||||
## **Note**: This is a low-level version of ``send``. You likely should use
|
||||
## the version below.
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
return SSLWrite(socket.sslHandle, cast[cstring](data), size)
|
||||
|
||||
@@ -943,7 +1003,7 @@ proc sendTo*(socket: Socket, address: string, port: Port,
|
||||
|
||||
proc isSsl*(socket: Socket): bool =
|
||||
## Determines whether ``socket`` is a SSL socket.
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
result = socket.isSSL
|
||||
else:
|
||||
result = false
|
||||
@@ -1253,7 +1313,7 @@ proc connect*(socket: Socket, address: string,
|
||||
dealloc(aiList)
|
||||
if not success: raiseOSError(lastError)
|
||||
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
# RFC3546 for SNI specifies that IP addresses are not allowed.
|
||||
if not isIpAddress(address):
|
||||
@@ -1314,8 +1374,10 @@ proc connect*(socket: Socket, address: string, port = Port(0),
|
||||
if selectWrite(s, timeout) != 1:
|
||||
raise newException(TimeoutError, "Call to 'connect' timed out.")
|
||||
else:
|
||||
when defined(ssl):
|
||||
when defineSsl:
|
||||
if socket.isSSL:
|
||||
socket.fd.setBlocking(true)
|
||||
{.warning[Deprecated]: off.}
|
||||
doAssert socket.handshake()
|
||||
{.warning[Deprecated]: on.}
|
||||
socket.fd.setBlocking(true)
|
||||
|
||||
@@ -129,7 +129,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
|
||||
if additionalInfo.len == 0:
|
||||
e.msg = osErrorMsg(errorCode)
|
||||
else:
|
||||
e.msg = additionalInfo & " " & osErrorMsg(errorCode)
|
||||
e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
|
||||
if e.msg == "":
|
||||
e.msg = "unknown OS error"
|
||||
raise e
|
||||
@@ -1452,7 +1452,7 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr =
|
||||
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
|
||||
## or a 'Stat' structure on posix
|
||||
when defined(Windows):
|
||||
template toTime(e): expr = winTimeToUnixTime(rdFileTime(e))
|
||||
template toTime(e: FILETIME): expr {.gensym.} = winTimeToUnixTime(rdFileTime(e)) # local templates default to bind semantics
|
||||
template merge(a, b): expr = a or (b shl 32)
|
||||
formalInfo.id.device = rawInfo.dwVolumeSerialNumber
|
||||
formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
|
||||
|
||||
@@ -784,8 +784,7 @@ proc indent*(s: string, count: Natural, padding: string = " "): string
|
||||
{.noSideEffect, rtl, extern: "nsuIndent".} =
|
||||
## Indents each line in ``s`` by ``count`` amount of ``padding``.
|
||||
##
|
||||
## **Note:** This currently does not preserve the specific new line characters
|
||||
## used.
|
||||
## **Note:** This does not preserve the new line characters used in ``s``.
|
||||
result = ""
|
||||
var i = 0
|
||||
for line in s.splitLines():
|
||||
@@ -796,32 +795,39 @@ proc indent*(s: string, count: Natural, padding: string = " "): string
|
||||
result.add(line)
|
||||
i.inc
|
||||
|
||||
proc unindent*(s: string, eatAllIndent = false): string {.
|
||||
noSideEffect, rtl, extern: "nsuUnindent".} =
|
||||
## Unindents `s`.
|
||||
result = newStringOfCap(s.len)
|
||||
proc unindent*(s: string, count: Natural, padding: string = " "): string
|
||||
{.noSideEffect, rtl, extern: "nsuUnindent".} =
|
||||
## Unindents each line in ``s`` by ``count`` amount of ``padding``.
|
||||
##
|
||||
## **Note:** This does not preserve the new line characters used in ``s``.
|
||||
result = ""
|
||||
var i = 0
|
||||
var pattern = true
|
||||
var indent = 0
|
||||
while s[i] == ' ': inc i
|
||||
var level = if i == 0: -1 else: i
|
||||
while i < s.len:
|
||||
if s[i] == ' ':
|
||||
if i > 0 and s[i-1] in {'\l', '\c'}:
|
||||
pattern = true
|
||||
indent = 0
|
||||
if pattern:
|
||||
inc(indent)
|
||||
if indent > level and not eatAllIndent:
|
||||
result.add(s[i])
|
||||
if level < 0: level = indent
|
||||
else:
|
||||
# a space somewhere: do not delete
|
||||
result.add(s[i])
|
||||
else:
|
||||
pattern = false
|
||||
result.add(s[i])
|
||||
inc i
|
||||
for line in s.splitLines():
|
||||
if i != 0:
|
||||
result.add("\n")
|
||||
var indentCount = 0
|
||||
for j in 0..<count.int:
|
||||
indentCount.inc
|
||||
if line[j .. j + <padding.len] != padding:
|
||||
indentCount = j
|
||||
break
|
||||
result.add(line[indentCount*padding.len .. ^1])
|
||||
i.inc
|
||||
|
||||
proc unindent*(s: string): string
|
||||
{.noSideEffect, rtl, extern: "nsuUnindentAll".} =
|
||||
## Removes all indentation composed of whitespace from each line in ``s``.
|
||||
##
|
||||
## For example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## const x = """
|
||||
## Hello
|
||||
## There
|
||||
## """.unindent()
|
||||
##
|
||||
## doAssert x == "Hello\nThere\n"
|
||||
unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish.
|
||||
|
||||
proc startsWith*(s, prefix: string): bool {.noSideEffect,
|
||||
rtl, extern: "nsuStartsWith".} =
|
||||
@@ -1743,3 +1749,33 @@ when isMainModule:
|
||||
doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
|
||||
doAssert join([1, 2, 3]) == "123"
|
||||
doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
|
||||
|
||||
doAssert """~~!!foo
|
||||
~~!!bar
|
||||
~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
|
||||
|
||||
doAssert """~~!!foo
|
||||
~~!!bar
|
||||
~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
|
||||
doAssert """~~foo
|
||||
~~ bar
|
||||
~~ baz""".unindent(4, "~") == "foo\n bar\n baz"
|
||||
doAssert """foo
|
||||
bar
|
||||
baz
|
||||
""".unindent(4) == "foo\nbar\nbaz\n"
|
||||
doAssert """foo
|
||||
bar
|
||||
baz
|
||||
""".unindent(2) == "foo\n bar\n baz\n"
|
||||
doAssert """foo
|
||||
bar
|
||||
baz
|
||||
""".unindent(100) == "foo\nbar\nbaz\n"
|
||||
|
||||
doAssert """foo
|
||||
foo
|
||||
bar
|
||||
""".unindent() == "foo\nfoo\nbar\n"
|
||||
|
||||
echo("strutils tests passed")
|
||||
|
||||
@@ -390,11 +390,11 @@ when isMainModule:
|
||||
|
||||
doAssert(("type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
|
||||
"fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"]).replace(" \n", "\n") ==
|
||||
strutils.unindent """
|
||||
strutils.unindent("""
|
||||
type MyEnum* = enum
|
||||
fieldA, fieldB,
|
||||
FiledClkad, fieldD,
|
||||
fieldE, longishFieldName""")
|
||||
fieldE, longishFieldName""", 6))
|
||||
|
||||
doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
|
||||
|
||||
@@ -404,8 +404,8 @@ when isMainModule:
|
||||
|
||||
doAssert((subex("type\n Enum = enum\n $', '40c'\n '{..}") % [
|
||||
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]).replace(" \n", "\n") ==
|
||||
strutils.unindent """
|
||||
strutils.unindent("""
|
||||
type
|
||||
Enum = enum
|
||||
fieldNameA, fieldNameB, fieldNameC,
|
||||
fieldNameD""")
|
||||
fieldNameD""", 6))
|
||||
|
||||
@@ -182,7 +182,16 @@ proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.}
|
||||
## converts the calendar time `t` to broken-down time representation,
|
||||
## expressed in Coordinated Universal Time (UTC).
|
||||
|
||||
proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], benign.}
|
||||
proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], benign, deprecated.}
|
||||
## converts a broken-down time structure to
|
||||
## calendar time representation. The function ignores the specified
|
||||
## contents of the structure members `weekday` and `yearday` and recomputes
|
||||
## them from the other information in the broken-down time structure.
|
||||
##
|
||||
## **Warning:** This procedure is deprecated since version 0.14.0.
|
||||
## Use ``toTime`` instead.
|
||||
|
||||
proc toTime*(timeInfo: TimeInfo): Time {.tags: [], benign.}
|
||||
## converts a broken-down time structure to
|
||||
## calendar time representation. The function ignores the specified
|
||||
## contents of the structure members `weekday` and `yearday` and recomputes
|
||||
@@ -356,7 +365,7 @@ proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||
##
|
||||
## **Note:** This has been only briefly tested and it may not be
|
||||
## very accurate.
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let t = toSeconds(toTime(a))
|
||||
let secs = toSeconds(a, interval)
|
||||
result = getLocalTime(fromSeconds(t + secs))
|
||||
|
||||
@@ -365,7 +374,7 @@ proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||
##
|
||||
## **Note:** This has been only briefly tested, it is inaccurate especially
|
||||
## when you subtract so much that you reach the Julian calendar.
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let t = toSeconds(toTime(a))
|
||||
var intval: TimeInterval
|
||||
intval.milliseconds = - interval.milliseconds
|
||||
intval.seconds = - interval.seconds
|
||||
@@ -517,6 +526,11 @@ when not defined(JS):
|
||||
# because the header of mktime is broken in my version of libc
|
||||
return mktime(timeInfoToTM(cTimeInfo))
|
||||
|
||||
proc toTime(timeInfo: TimeInfo): Time =
|
||||
var cTimeInfo = timeInfo # for C++ we have to make a copy,
|
||||
# because the header of mktime is broken in my version of libc
|
||||
return mktime(timeInfoToTM(cTimeInfo))
|
||||
|
||||
proc toStringTillNL(p: cstring): string =
|
||||
result = ""
|
||||
var i = 0
|
||||
@@ -618,7 +632,16 @@ elif defined(JS):
|
||||
result.setFullYear(timeInfo.year)
|
||||
result.setDate(timeInfo.monthday)
|
||||
|
||||
proc `$`(timeInfo: TimeInfo): string = return $(timeInfoToTime(timeInfo))
|
||||
proc toTime*(timeInfo: TimeInfo): Time =
|
||||
result = internGetTime()
|
||||
result.setSeconds(timeInfo.second)
|
||||
result.setMinutes(timeInfo.minute)
|
||||
result.setHours(timeInfo.hour)
|
||||
result.setMonth(ord(timeInfo.month))
|
||||
result.setFullYear(timeInfo.year)
|
||||
result.setDate(timeInfo.monthday)
|
||||
|
||||
proc `$`(timeInfo: TimeInfo): string = return $(toTime(timeInfo))
|
||||
proc `$`(time: Time): string = return $time.toLocaleString()
|
||||
|
||||
proc `-` (a, b: Time): int64 =
|
||||
@@ -708,24 +731,24 @@ proc years*(y: int): TimeInterval {.inline.} =
|
||||
|
||||
proc `+=`*(t: var Time, ti: TimeInterval) =
|
||||
## modifies `t` by adding the interval `ti`
|
||||
t = timeInfoToTime(getLocalTime(t) + ti)
|
||||
t = toTime(getLocalTime(t) + ti)
|
||||
|
||||
proc `+`*(t: Time, ti: TimeInterval): Time =
|
||||
## adds the interval `ti` to Time `t`
|
||||
## by converting to localTime, adding the interval, and converting back
|
||||
##
|
||||
## ``echo getTime() + 1.day``
|
||||
result = timeInfoToTime(getLocalTime(t) + ti)
|
||||
result = toTime(getLocalTime(t) + ti)
|
||||
|
||||
proc `-=`*(t: var Time, ti: TimeInterval) =
|
||||
## modifies `t` by subtracting the interval `ti`
|
||||
t = timeInfoToTime(getLocalTime(t) - ti)
|
||||
t = toTime(getLocalTime(t) - ti)
|
||||
|
||||
proc `-`*(t: Time, ti: TimeInterval): Time =
|
||||
## adds the interval `ti` to Time `t`
|
||||
##
|
||||
## ``echo getTime() - 1.day``
|
||||
result = timeInfoToTime(getLocalTime(t) - ti)
|
||||
result = toTime(getLocalTime(t) - ti)
|
||||
|
||||
proc formatToken(info: TimeInfo, token: string, buf: var string) =
|
||||
## Helper of the format proc to parse individual tokens.
|
||||
@@ -1193,7 +1216,7 @@ proc parse*(value, layout: string): TimeInfo =
|
||||
parseToken(info, token, value, j)
|
||||
token = ""
|
||||
# Reset weekday as it might not have been provided and the default may be wrong
|
||||
info.weekday = getLocalTime(timeInfoToTime(info)).weekday
|
||||
info.weekday = getLocalTime(toTime(info)).weekday
|
||||
return info
|
||||
|
||||
# Leap year calculations are adapted from:
|
||||
@@ -1204,7 +1227,7 @@ proc parse*(value, layout: string): TimeInfo =
|
||||
proc countLeapYears*(yearSpan: int): int =
|
||||
## Returns the number of leap years spanned by a given number of years.
|
||||
##
|
||||
## Note: for leap years, start date is assumed to be 1 AD.
|
||||
## **Note:** For leap years, start date is assumed to be 1 AD.
|
||||
## counts the number of leap years up to January 1st of a given year.
|
||||
## Keep in mind that if specified year is a leap year, the leap day
|
||||
## has not happened before January 1st of that year.
|
||||
@@ -1239,13 +1262,14 @@ proc getDayOfWeek*(day, month, year: int): WeekDay =
|
||||
y = year - a
|
||||
m = month + (12*a) - 2
|
||||
d = (day + y + (y div 4) - (y div 100) + (y div 400) + (31*m) div 12) mod 7
|
||||
# The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct
|
||||
# for the WeekDay type.
|
||||
# The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc.
|
||||
# so we must correct for the WeekDay type.
|
||||
if d == 0: return dSun
|
||||
result = (d-1).WeekDay
|
||||
|
||||
proc getDayOfWeekJulian*(day, month, year: int): WeekDay =
|
||||
## Returns the day of the week enum from day, month and year, according to the Julian calendar.
|
||||
## Returns the day of the week enum from day, month and year,
|
||||
## according to the Julian calendar.
|
||||
# Day & month start from one.
|
||||
let
|
||||
a = (14 - month) div 12
|
||||
@@ -1254,8 +1278,11 @@ proc getDayOfWeekJulian*(day, month, year: int): WeekDay =
|
||||
d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7
|
||||
result = d.WeekDay
|
||||
|
||||
proc timeToTimeInfo*(t: Time): TimeInfo =
|
||||
proc timeToTimeInfo*(t: Time): TimeInfo {.deprecated.} =
|
||||
## Converts a Time to TimeInfo.
|
||||
##
|
||||
## **Warning:** This procedure is deprecated since version 0.14.0.
|
||||
## Use ``getLocalTime`` or ``getGMTime`` instead.
|
||||
let
|
||||
secs = t.toSeconds().int
|
||||
daysSinceEpoch = secs div secondsInDay
|
||||
@@ -1286,34 +1313,21 @@ proc timeToTimeInfo*(t: Time): TimeInfo =
|
||||
s = daySeconds mod secondsInMin
|
||||
result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s)
|
||||
|
||||
proc timeToTimeInterval*(t: Time): TimeInterval =
|
||||
proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
|
||||
## Converts a Time to a TimeInterval.
|
||||
|
||||
let
|
||||
secs = t.toSeconds().int
|
||||
daysSinceEpoch = secs div secondsInDay
|
||||
(yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch)
|
||||
daySeconds = secs mod secondsInDay
|
||||
|
||||
result.years = yearsSinceEpoch + epochStartYear
|
||||
|
||||
var
|
||||
mon = mJan
|
||||
days = daysRemaining
|
||||
daysInMonth = getDaysInMonth(mon, result.years)
|
||||
|
||||
# calculate month and day remainder
|
||||
while days > daysInMonth and mon <= mDec:
|
||||
days -= daysInMonth
|
||||
mon.inc
|
||||
daysInMonth = getDaysInMonth(mon, result.years)
|
||||
|
||||
result.months = mon.int + 1 # month is 1 indexed int
|
||||
result.days = days
|
||||
result.hours = daySeconds div secondsInHour + 1
|
||||
result.minutes = (daySeconds mod secondsInHour) div secondsInMin
|
||||
result.seconds = daySeconds mod secondsInMin
|
||||
##
|
||||
## **Warning:** This procedure is deprecated since version 0.14.0.
|
||||
## Use ``toTimeInterval`` instead.
|
||||
# Milliseconds not available from Time
|
||||
var tInfo = t.getLocalTime()
|
||||
initInterval(0, tInfo.second, tInfo.minute, tInfo.hour, tInfo.weekday.ord, tInfo.month.ord, tInfo.year)
|
||||
|
||||
proc toTimeInterval*(t: Time): TimeInterval =
|
||||
## Converts a Time to a TimeInterval.
|
||||
# Milliseconds not available from Time
|
||||
var tInfo = t.getLocalTime()
|
||||
initInterval(0, tInfo.second, tInfo.minute, tInfo.hour, tInfo.weekday.ord, tInfo.month.ord, tInfo.year)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
# this is testing non-exported function
|
||||
|
||||
@@ -28,6 +28,9 @@ template mulThreshold(x): expr {.immediate.} = x * 2
|
||||
|
||||
when defined(memProfiler):
|
||||
proc nimProfile(requestedSize: int)
|
||||
|
||||
when hasThreadSupport:
|
||||
import sharedlist
|
||||
|
||||
type
|
||||
WalkOp = enum
|
||||
|
||||
@@ -769,7 +769,7 @@ proc getQueuedCompletionStatus*(CompletionPort: Handle,
|
||||
dwMilliseconds: DWORD): WINBOOL{.stdcall,
|
||||
dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
|
||||
|
||||
proc getOverlappedResult*(hFile: Handle, lpOverlapped: OVERLAPPED,
|
||||
proc getOverlappedResult*(hFile: Handle, lpOverlapped: POVERLAPPED,
|
||||
lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL): WINBOOL{.
|
||||
stdcall, dynlib: "kernel32", importc: "GetOverlappedResult".}
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSS
|
||||
|
||||
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
|
||||
|
||||
when not useWinVersion:
|
||||
when not useWinVersion and not defined(macosx):
|
||||
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
|
||||
dynlib: DLLUtilName, importc.}
|
||||
|
||||
@@ -532,7 +532,7 @@ proc md5_File* (file: string): string {.raises: [IOError,Exception].} =
|
||||
|
||||
result = hexStr(buf)
|
||||
|
||||
proc md5_Str* (str:string): string {.raises:[IOError].} =
|
||||
proc md5_Str*(str:string): string =
|
||||
##Generate MD5 hash for a string. Result is a 32 character
|
||||
#hex string with lowercase characters
|
||||
var
|
||||
|
||||
104
readme.md
104
readme.md
@@ -1,18 +1,9 @@
|
||||
# Nim Compiler
|
||||
|
||||
[](https://webchat.freenode.net/?channels=nim)
|
||||
[](http://forum.nim-lang.org)
|
||||
[](http://stackoverflow.com/questions/tagged/nim?sort=newest&pageSize=15)
|
||||
[](https://twitter.com/nim_lang)
|
||||
|
||||
[](https://travis-ci.org/nim-lang/Nim)
|
||||
|
||||
[](https://gratipay.com/nim/)
|
||||
[](https://www.bountysource.com/teams/nim)
|
||||
|
||||
# <img src="https://raw.githubusercontent.com/nim-lang/assets/master/Art/logo-crown.png" width="36"> Nim [](https://travis-ci.org/nim-lang/Nim)
|
||||
|
||||
This repo contains the Nim compiler, Nim's stdlib, tools and
|
||||
documentation.
|
||||
documentation. For more information about Nim, including downloads
|
||||
and documentation for the latest release, check out
|
||||
[Nim's website](http://nim-lang.org).
|
||||
|
||||
## Compiling
|
||||
Compiling the Nim compiler is quite straightforward. Because
|
||||
@@ -48,14 +39,15 @@ $ bin/nim c koch
|
||||
$ ./koch boot -d:release
|
||||
```
|
||||
|
||||
``koch install [dir]`` may then be used to install Nim, but lots of things
|
||||
don't work then so don't do that. Add it to your PATH instead. More ``koch``
|
||||
related options are documented in [doc/koch.txt](doc/koch.txt).
|
||||
You should then add the ``bin`` directory to your PATH, to make it easily
|
||||
executable on your system.
|
||||
|
||||
The above steps can be performed on Windows in a similar fashion, the
|
||||
``build.bat`` and ``build64.bat`` (for x86_64 systems) are provided to be used
|
||||
instead of ``build.sh``.
|
||||
|
||||
The ``koch`` tool is the Nim build tool, more ``koch`` related options are
|
||||
documented in [doc/koch.txt](doc/koch.txt).
|
||||
|
||||
## Nimble
|
||||
[Nimble](https://github.com/nim-lang/nimble) is Nim's package manager. For the
|
||||
@@ -66,12 +58,80 @@ the easiest way of installing Nimble is via:
|
||||
$ nim e install_nimble.nims
|
||||
```
|
||||
|
||||
## Getting help
|
||||
A [forum](http://forum.nim-lang.org/) is available if you have any
|
||||
questions, and you can also get help in the IRC channel on
|
||||
[Freenode](irc://irc.freenode.net/nim) in #nim. If you ask questions on
|
||||
[StackOverflow use the nim
|
||||
tag](http://stackoverflow.com/questions/tagged/nim).
|
||||
**Warning:** If you install Nimble this way, you will not be able to use binary
|
||||
Nimble packages or update Nimble easily.
|
||||
The [Nimble readme](https://github.com/nim-lang/nimble#installation)
|
||||
provides thorough instructions on how to install Nimble, so that this isn't a
|
||||
problem.
|
||||
|
||||
## Community
|
||||
[](https://webchat.freenode.net/?channels=nim)
|
||||
[](http://forum.nim-lang.org)
|
||||
[](http://stackoverflow.com/questions/tagged/nim?sort=newest&pageSize=15)
|
||||
[](https://twitter.com/nim_lang)
|
||||
|
||||
* The [forum](http://forum.nim-lang.org/) - the best place to ask questions and to discuss Nim.
|
||||
* [IRC (Freenode#nim)](https://webchat.freenode.net/?channels=nim) - the best place to discuss
|
||||
Nim in real-time, this is also where most development decision get made!
|
||||
* [Stackoverflow](http://stackoverflow.com/questions/tagged/nim)
|
||||
|
||||
## Contributing
|
||||
|
||||
[](https://gratipay.com/nim/)
|
||||
[](https://www.bountysource.com/teams/nim)
|
||||
|
||||
We welcome everyone's contributions to Nim. No matter how small or large
|
||||
the contribution is, anything from small spelling fixes to large modules
|
||||
intended to be included in the standard library are accepted. Before
|
||||
you get started, you should know the following about this repositories
|
||||
structure:
|
||||
|
||||
* ``bin/``, ``build/`` - these directories are empty, but are used when Nim is built.
|
||||
* ``compiler/`` - the compiler source code, all the Nim source code files in this
|
||||
directory implement the compiler. This also includes nimfix, and plugins
|
||||
which live in ``compiler/nimfix`` and ``compiler/plugins``
|
||||
respectively. Nimsuggest used to live in the ``compiler`` directory also,
|
||||
but was moved to https://github.com/nim-lang/nimsuggest.
|
||||
* ``config/`` - the configuration for the compiler and documentation generator.
|
||||
* ``doc/`` - the documentation files in reStructuredText format.
|
||||
* ``lib/`` - where the standard library lives.
|
||||
* ``pure/`` - modules in the standard library written in pure Nim.
|
||||
* ``impure/`` - modules in the standard library written in pure Nim which
|
||||
depend on libraries written in other languages.
|
||||
* ``wrappers/`` - modules which wrap libraries written in other languages.
|
||||
* ``tests/`` - contains tests for the compiler and standard library, organised by
|
||||
category.
|
||||
* ``tools/`` - the tools including ``niminst`` and ``nimweb``, most of these are invoked
|
||||
via ``koch``.
|
||||
* ``web/`` - the Nim website (http://nim-lang.org).
|
||||
* ``koch.nim`` - tool used to bootstrap Nim, generate C sources, build the website, documentation
|
||||
and more.
|
||||
|
||||
Most importantly, the ``koch`` tool can be used to run the test suite. To do so compile it first
|
||||
by executing ``nim c koch``, then execute ``./koch tests``. The test suite takes a while to run,
|
||||
but you can run specific tests by specifying a category to run, for example ``./koch tests cat async``.
|
||||
|
||||
Make sure that the tests all pass before
|
||||
[submitting your pull request](https://help.github.com/articles/using-pull-requests/).
|
||||
If you're short on time, you can
|
||||
just run the tests specific to your change. Just run the category which corresponds to the change
|
||||
you've made. When you create your pull request, Travis CI will verify that all the tests pass
|
||||
anyway.
|
||||
|
||||
If you're looking for things to do, take a look at our
|
||||
[issue tracker](https://github.com/nim-lang/Nim/issues). There is always plenty of issues
|
||||
labelled [``Easy``](https://github.com/nim-lang/Nim/labels/Easy), these should be a good
|
||||
starting point if this is your first contribution to Nim.
|
||||
|
||||
You can also help with the development of Nim by making donations. You can do so
|
||||
in many ways:
|
||||
|
||||
* [Gratipay](https://gratipay.com/nim/)
|
||||
* [Bountysource](https://www.bountysource.com/teams/nim)
|
||||
* Bitcoin - 1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ
|
||||
|
||||
Finally, if you have any questions feel free to submit a question on the issue tracker,
|
||||
on the [Nim forum](http://forum.nim-lang.org), or on IRC.
|
||||
|
||||
## License
|
||||
The compiler and the standard library are licensed under the MIT license,
|
||||
|
||||
@@ -24,7 +24,7 @@ proc main() {.async.} =
|
||||
var file = openAsync(fn, fmAppend)
|
||||
await file.write("\ntest2")
|
||||
let errorTest = file.readAll()
|
||||
await errorTest
|
||||
echo await errorTest
|
||||
doAssert errorTest.failed
|
||||
file.close()
|
||||
file = openAsync(fn, fmRead)
|
||||
|
||||
9
tests/cpp/ttemplatetype.nim
Normal file
9
tests/cpp/ttemplatetype.nim
Normal file
@@ -0,0 +1,9 @@
|
||||
type
|
||||
Map {.importcpp: "std::map", header: "<map>".} [T,U] = object
|
||||
|
||||
proc cInitMap(T: typedesc, U: typedesc): Map[T,U] {.importcpp: "std::map<'*1,'*2>()", nodecl.}
|
||||
|
||||
proc initMap[T, U](): Map[T, U] =
|
||||
result = cInitMap(T, U)
|
||||
|
||||
var x: Map[cstring, cint] = initMap[cstring, cint]()
|
||||
51
tests/js/tclosures.nim
Normal file
51
tests/js/tclosures.nim
Normal file
@@ -0,0 +1,51 @@
|
||||
discard """
|
||||
action: run
|
||||
"""
|
||||
|
||||
import math, strutils
|
||||
const consolePrefix = "jsCallbacks"
|
||||
|
||||
asm """
|
||||
var callback = []
|
||||
function regCallback (fn) { callback.push (fn); }
|
||||
function runCallbacks () {
|
||||
var result = "\n"
|
||||
var n = 0
|
||||
for (var fn in callback) {
|
||||
n += 1
|
||||
result += "("+String (n)+")"
|
||||
result += callback [fn] ()
|
||||
result += "\n"
|
||||
}
|
||||
return result
|
||||
}
|
||||
function print (text) { console.log (text); }
|
||||
"""
|
||||
|
||||
proc consoleprint (str:cstring): void {.importc: "print", noDecl.}
|
||||
proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join (a, " ")]
|
||||
|
||||
type CallbackProc {.importc.} = proc () : cstring
|
||||
|
||||
proc regCallback (fn:CallbackProc) {.importc.}
|
||||
proc runCallbacks ():cstring {.importc.}
|
||||
|
||||
proc `*` (s:string, n:Natural) : string = s.repeat (n)
|
||||
|
||||
proc outer (i:Natural) : (string, int) =
|
||||
let c = $char (random (93) + 33)
|
||||
let n = random (40)
|
||||
let s = c * n
|
||||
proc inner () : cstring = ("[$1]" % $n) & s & " <--"
|
||||
regCallback (inner)
|
||||
return (s, n)
|
||||
|
||||
var expected = "\n"
|
||||
for i in 1 .. 10:
|
||||
let (s, n) = outer (i)
|
||||
expected &= ("($1)[$2]" % [$i, $n]) & s & " <--"
|
||||
expected &= "\n"
|
||||
|
||||
let results = runCallbacks ()
|
||||
|
||||
doAssert(expected == results)
|
||||
@@ -107,6 +107,15 @@ proc specDefaults*(result: var TSpec) =
|
||||
result.tline = 0
|
||||
result.tcolumn = 0
|
||||
|
||||
proc parseTargets*(value: string): set[TTarget] =
|
||||
for v in value.normalize.split:
|
||||
case v
|
||||
of "c": result.incl(targetC)
|
||||
of "cpp", "c++": result.incl(targetCpp)
|
||||
of "objc": result.incl(targetObjC)
|
||||
of "js": result.incl(targetJS)
|
||||
else: echo "target ignored: " & v
|
||||
|
||||
proc parseSpec*(filename: string): TSpec =
|
||||
specDefaults(result)
|
||||
result.file = filename
|
||||
|
||||
@@ -33,6 +33,7 @@ Options:
|
||||
--print also print results to the console
|
||||
--failing only show failing/ignored tests
|
||||
--pedantic return non-zero status code if there are failures
|
||||
--targets:"c c++ js objc" run tests for specified targets (default: all)
|
||||
""" % resultsFile
|
||||
|
||||
type
|
||||
@@ -60,6 +61,8 @@ let
|
||||
pegSuccess = peg"'Hint: operation successful'.*"
|
||||
pegOfInterest = pegLineError / pegOtherError
|
||||
|
||||
var targets = {low(TTarget)..high(TTarget)}
|
||||
|
||||
proc callCompiler(cmdTemplate, filename, options: string,
|
||||
target: TTarget): TSpec =
|
||||
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
||||
@@ -275,6 +278,11 @@ proc analyzeAndConsolidateOutput(s: string): string =
|
||||
|
||||
proc testSpec(r: var TResults, test: TTest) =
|
||||
# major entry point for a single test
|
||||
if test.target notin targets:
|
||||
r.addResult(test, "", "", reIgnored)
|
||||
inc(r.skipped)
|
||||
return
|
||||
|
||||
let tname = test.name.addFileExt(".nim")
|
||||
inc(r.total)
|
||||
var expected: TSpec
|
||||
@@ -336,7 +344,7 @@ proc testSpec(r: var TResults, test: TTest) =
|
||||
reExitCodesDiffer)
|
||||
return
|
||||
|
||||
if bufB != expectedOut:
|
||||
if bufB != expectedOut and expected.action != actionRunNoSpec:
|
||||
if not (expected.substr and expectedOut in bufB):
|
||||
given.err = reOutputsDiffer
|
||||
r.addResult(test, expected.outp, bufB, reOutputsDiffer)
|
||||
@@ -394,6 +402,7 @@ proc main() =
|
||||
var optPrintResults = false
|
||||
var optFailing = false
|
||||
var optPedantic = false
|
||||
|
||||
var p = initOptParser()
|
||||
p.next()
|
||||
while p.kind == cmdLongoption:
|
||||
@@ -401,6 +410,7 @@ proc main() =
|
||||
of "print", "verbose": optPrintResults = true
|
||||
of "failing": optFailing = true
|
||||
of "pedantic": optPedantic = true
|
||||
of "targets": targets = parseTargets(p.val.string)
|
||||
else: quit Usage
|
||||
p.next()
|
||||
if p.kind != cmdArgument: quit Usage
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
discard """
|
||||
output: '''48
|
||||
hel'''
|
||||
hel
|
||||
lo'''
|
||||
"""
|
||||
|
||||
template optZero{x+x}(x: int): int = x*3
|
||||
@@ -15,3 +16,8 @@ s[0] = "hello"
|
||||
s[0] = substr(s[0], 0, 2)
|
||||
|
||||
echo s[0]
|
||||
|
||||
# Test varargs matching
|
||||
proc someVarargProc(k: varargs[string]) = doAssert(false) # this should not get called
|
||||
template someVarargProcSingleArg{someVarargProc([a])}(a: string) = echo a
|
||||
someVarargProc("lo")
|
||||
|
||||
@@ -6,13 +6,23 @@
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>$c.projectTitle</title>
|
||||
<link rel="stylesheet" type="text/css" href="assets/style.css" />
|
||||
<link rel="stylesheet" type="text/css" href="assets/style.css?t=2221" />
|
||||
<link rel="shortcut icon" href="assets/images/favicon.ico">
|
||||
#if len(rss) > 0:
|
||||
<link href="$rss" title="Recent changes" type="application/atom+xml" rel="alternate">
|
||||
#end if
|
||||
</head>
|
||||
<body>
|
||||
<div id="bountysource">
|
||||
<a href="https://salt.bountysource.com/teams/nim">
|
||||
<div class="page-layout" style="padding: 2pt 2pt 2pt 30pt">
|
||||
<img src="assets/bountysource/bountysource.png" style="width: 20px; float: left;">
|
||||
<span style="margin-left: 10pt; float: left; margin-top: 2pt;">Fund Nim and help us develop it further!</span>
|
||||
<img src="https://api.bountysource.com/badge/team?team_id=19072&style=raised" style="margin-top: 2pt; margin-left: 10pt"/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<header id="head">
|
||||
<div class="page-layout tall">
|
||||
<div id="head-logo"></div>
|
||||
|
||||
@@ -563,3 +563,14 @@ pre .end { background:url("images/tabEnd.png") no-repeat left bottom; }
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
border-spacing: 0px; }
|
||||
|
||||
#bountysource {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background-color: #19975d;
|
||||
}
|
||||
|
||||
#bountysource a, #bountysource a:visited, #bountysource a:hover {
|
||||
color: #1a1a1a;
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,15 @@ Changes affecting backwards compatibility
|
||||
- Procedures in ``mersenne.nim`` (Mersenne Twister implementation) no longer
|
||||
accept and produce ``int`` values which have platform-dependent size -
|
||||
they use ``uint32`` instead.
|
||||
- The ``strutils.unindent`` procedure has been rewritten. Its parameters now
|
||||
match the parameters of ``strutils.indent``. See issue [#4037](https://github.com/nim-lang/Nim/issues/4037)
|
||||
for more details.
|
||||
- The ``matchers`` module has been deprecated. See issue [#2446](https://github.com/nim-lang/Nim/issues/2446)
|
||||
for more details.
|
||||
- The ``json.[]`` no longer returns ``nil`` when a key is not found. Instead it
|
||||
raises a ``KeyError`` exception. You can compile with the ``-d:nimJsonGet``
|
||||
flag to get a list of usages of ``[]``, as well as to restore the operator's
|
||||
previous behaviour.
|
||||
|
||||
|
||||
Library Additions
|
||||
|
||||
Reference in New Issue
Block a user