merged devel into epc
This commit is contained in:
commit
f5cca89610
183 changed files with 5295 additions and 4043 deletions
|
|
@ -221,7 +221,7 @@ proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn,
|
|||
setupTask()
|
||||
schedule()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var
|
||||
a: TActorPool[int, void]
|
||||
createActorPool(a)
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ proc reverse*[T](a: var openArray[T]) =
|
|||
proc reversed*[T](a: openArray[T], first, last: Natural): seq[T] =
|
||||
## returns the reverse of the array `a[first..last]`.
|
||||
result = newSeq[T](last - first + 1)
|
||||
var x = first
|
||||
var y = last
|
||||
var x = first.int
|
||||
var y = last.int
|
||||
while x <= last:
|
||||
result[x] = a[y]
|
||||
dec(y)
|
||||
|
|
|
|||
|
|
@ -634,6 +634,93 @@ when defined(windows) or defined(nimdoc):
|
|||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
|
||||
## at least be of that size. Returned future will complete once all the
|
||||
## data requested is read, a part of the data has been read, or the socket
|
||||
## has disconnected in which case the future will complete with a value of
|
||||
## ``0``.
|
||||
##
|
||||
## **Warning**: The ``Peek`` socket flag is not supported on Windows.
|
||||
|
||||
|
||||
# Things to note:
|
||||
# * When WSARecv completes immediately then ``bytesReceived`` is very
|
||||
# unreliable.
|
||||
# * Still need to implement message-oriented socket disconnection,
|
||||
# '\0' in the message currently signifies a socket disconnect. Who
|
||||
# knows what will happen when someone sends that to our socket.
|
||||
verifyPresence(socket)
|
||||
assert SocketFlag.Peek notin flags, "Peek not supported on Windows."
|
||||
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
#buf[] = '\0'
|
||||
var dataBuf: TWSABuf
|
||||
dataBuf.buf = buf
|
||||
dataBuf.len = size
|
||||
|
||||
var bytesReceived: Dword
|
||||
var flagsio = flags.toOSFlags().Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.complete(bytesCount)
|
||||
else:
|
||||
if flags.isDisconnectionError(errcode):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
if dataBuf.buf != nil:
|
||||
dataBuf.buf = nil
|
||||
)
|
||||
|
||||
let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||
addr flagsio, cast[POVERLAPPED](ol), nil)
|
||||
if ret == -1:
|
||||
let err = osLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
if dataBuf.buf != nil:
|
||||
dataBuf.buf = nil
|
||||
GC_unref(ol)
|
||||
if flags.isDisconnectionError(err):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
|
||||
# We have to ensure that the buffer is empty because WSARecv will tell
|
||||
# us immediately when it was disconnected, even when there is still
|
||||
# data in the buffer.
|
||||
# We want to give the user as much data as we can. So we only return
|
||||
# the empty string (which signals a disconnection) when there is
|
||||
# nothing left to read.
|
||||
retFuture.complete(0)
|
||||
# TODO: "For message-oriented sockets, where a zero byte message is often
|
||||
# allowable, a failure with an error code of WSAEDISCON is used to
|
||||
# indicate graceful closure."
|
||||
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
|
||||
else:
|
||||
# Request to read completed immediately.
|
||||
# From my tests bytesReceived isn't reliable.
|
||||
let realSize =
|
||||
if bytesReceived == 0:
|
||||
size
|
||||
else:
|
||||
bytesReceived
|
||||
assert realSize <= size
|
||||
retFuture.complete(realSize)
|
||||
# We don't deallocate ``ol`` here because even though this completed
|
||||
# immediately poll will still be notified about its completion and it will
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||
|
|
@ -983,6 +1070,30 @@ else:
|
|||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
result = true
|
||||
let res = recv(sock.SocketHandle, buf, size.cint,
|
||||
flags.toOSFlags())
|
||||
if res < 0:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||
if flags.isDisconnectionError(lastError):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
retFuture.complete(res)
|
||||
# TODO: The following causes a massive slowdown.
|
||||
#if not cb(socket):
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
var retFuture = newFuture[void]("send")
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ proc newAsyncFtpClient*(address: string, port = Port(21),
|
|||
result.dsockConnected = false
|
||||
result.csock = newAsyncSocket()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
|
||||
proc main(ftp: AsyncFtpClient) {.async.} =
|
||||
await ftp.connect()
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@
|
|||
## proc cb(req: Request) {.async.} =
|
||||
## await req.respond(Http200, "Hello World")
|
||||
##
|
||||
## asyncCheck server.serve(Port(8080), cb)
|
||||
## runForever()
|
||||
## waitFor server.serve(Port(8080), cb)
|
||||
|
||||
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||
type
|
||||
|
|
@ -109,22 +108,19 @@ proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] =
|
|||
addHeaders(msg, headers)
|
||||
return req.client.send(msg)
|
||||
|
||||
proc respond*(req: Request, code: HttpCode,
|
||||
content: string, headers = newStringTable()) {.async.} =
|
||||
proc respond*(req: Request, code: HttpCode, content: string,
|
||||
headers: StringTableRef = nil): Future[void] =
|
||||
## Responds to the request with the specified ``HttpCode``, headers and
|
||||
## content.
|
||||
##
|
||||
## This procedure will **not** close the client socket.
|
||||
var customHeaders = headers
|
||||
customHeaders["Content-Length"] = $content.len
|
||||
var msg = "HTTP/1.1 " & $code & "\c\L"
|
||||
msg.addHeaders(customHeaders)
|
||||
await req.client.send(msg & "\c\L" & content)
|
||||
|
||||
proc newRequest(): Request =
|
||||
result.headers = newStringTable(modeCaseInsensitive)
|
||||
result.hostname = ""
|
||||
result.body = ""
|
||||
if headers != nil:
|
||||
msg.addHeaders(headers)
|
||||
msg.add("Content-Length: " & $content.len & "\c\L\c\L")
|
||||
msg.add(content)
|
||||
result = req.client.send(msg)
|
||||
|
||||
proc parseHeader(line: string): tuple[key, value: string] =
|
||||
var i = 0
|
||||
|
|
@ -149,59 +145,65 @@ proc sendStatus(client: AsyncSocket, status: string): Future[void] =
|
|||
proc processClient(client: AsyncSocket, address: string,
|
||||
callback: proc (request: Request):
|
||||
Future[void] {.closure, gcsafe.}) {.async.} =
|
||||
var request: Request
|
||||
request.url = initUri()
|
||||
request.headers = newStringTable(modeCaseInsensitive)
|
||||
var line = newStringOfCap(80)
|
||||
var key, value = ""
|
||||
|
||||
while not client.isClosed:
|
||||
# GET /path HTTP/1.1
|
||||
# Header: val
|
||||
# \n
|
||||
var request = newRequest()
|
||||
request.hostname = address
|
||||
request.headers.clear(modeCaseInsensitive)
|
||||
request.hostname.shallowCopy(address)
|
||||
assert client != nil
|
||||
request.client = client
|
||||
|
||||
# First line - GET /path HTTP/1.1
|
||||
let line = await client.recvLine() # TODO: Timeouts.
|
||||
line.setLen(0)
|
||||
await client.recvLineInto(addr line) # TODO: Timeouts.
|
||||
if line == "":
|
||||
client.close()
|
||||
return
|
||||
let lineParts = line.split(' ')
|
||||
if lineParts.len != 3:
|
||||
await request.respond(Http400, "Invalid request. Got: " & line)
|
||||
continue
|
||||
|
||||
let reqMethod = lineParts[0]
|
||||
let path = lineParts[1]
|
||||
let protocol = lineParts[2]
|
||||
var i = 0
|
||||
for linePart in line.split(' '):
|
||||
case i
|
||||
of 0: request.reqMethod.shallowCopy(linePart.normalize)
|
||||
of 1: parseUri(linePart, request.url)
|
||||
of 2:
|
||||
try:
|
||||
request.protocol = parseProtocol(linePart)
|
||||
except ValueError:
|
||||
asyncCheck request.respond(Http400,
|
||||
"Invalid request protocol. Got: " & linePart)
|
||||
continue
|
||||
else:
|
||||
await request.respond(Http400, "Invalid request. Got: " & line)
|
||||
continue
|
||||
inc i
|
||||
|
||||
# Headers
|
||||
var i = 0
|
||||
while true:
|
||||
i = 0
|
||||
let headerLine = await client.recvLine()
|
||||
if headerLine == "":
|
||||
line.setLen(0)
|
||||
await client.recvLineInto(addr line)
|
||||
|
||||
if line == "":
|
||||
client.close(); return
|
||||
if headerLine == "\c\L": break
|
||||
# TODO: Compiler crash
|
||||
#let (key, value) = parseHeader(headerLine)
|
||||
let kv = parseHeader(headerLine)
|
||||
request.headers[kv.key] = kv.value
|
||||
if line == "\c\L": break
|
||||
let (key, value) = parseHeader(line)
|
||||
request.headers[key] = value
|
||||
|
||||
request.reqMethod = reqMethod
|
||||
request.url = parseUri(path)
|
||||
try:
|
||||
request.protocol = protocol.parseProtocol()
|
||||
except ValueError:
|
||||
asyncCheck request.respond(Http400, "Invalid request protocol. Got: " &
|
||||
protocol)
|
||||
continue
|
||||
|
||||
if reqMethod.normalize == "post":
|
||||
if request.reqMethod == "post":
|
||||
# Check for Expect header
|
||||
if request.headers.hasKey("Expect"):
|
||||
if request.headers["Expect"].toLower == "100-continue":
|
||||
await client.sendStatus("100 Continue")
|
||||
else:
|
||||
await client.sendStatus("417 Expectation Failed")
|
||||
|
||||
|
||||
# Read the body
|
||||
# - Check for Content-length header
|
||||
if request.headers.hasKey("Content-Length"):
|
||||
|
|
@ -215,11 +217,11 @@ proc processClient(client: AsyncSocket, address: string,
|
|||
await request.respond(Http400, "Bad Request. No Content-Length.")
|
||||
continue
|
||||
|
||||
case reqMethod.normalize
|
||||
case request.reqMethod
|
||||
of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch":
|
||||
await callback(request)
|
||||
else:
|
||||
await request.respond(Http400, "Invalid request method. Got: " & reqMethod)
|
||||
await request.respond(Http400, "Invalid request method. Got: " & request.reqMethod)
|
||||
|
||||
# Persistent connections
|
||||
if (request.protocol == HttpVer11 and
|
||||
|
|
@ -247,7 +249,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
|
|||
server.socket.setSockOpt(OptReuseAddr, true)
|
||||
server.socket.bindAddr(port, address)
|
||||
server.socket.listen()
|
||||
|
||||
|
||||
while true:
|
||||
# TODO: Causes compiler crash.
|
||||
#var (address, client) = await server.socket.acceptAddr()
|
||||
|
|
@ -260,7 +262,7 @@ proc close*(server: AsyncHttpServer) =
|
|||
## Terminates the async http server instance.
|
||||
server.socket.close()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
var server = newAsyncHttpServer()
|
||||
proc cb(req: Request) {.async.} =
|
||||
|
|
|
|||
|
|
@ -660,7 +660,7 @@ proc len*(disp: Dispatcher): int =
|
|||
## Retrieves the amount of delegates in ``disp``.
|
||||
return disp.delegates.len
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
|
||||
proc testConnect(s: AsyncSocket, no: int) =
|
||||
echo("Connected! " & $no)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
##
|
||||
## Chat server
|
||||
## ^^^^^^^^^^^
|
||||
##
|
||||
##
|
||||
## The following example demonstrates a simple chat server.
|
||||
##
|
||||
## .. code-block::nim
|
||||
|
|
@ -182,26 +182,30 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
|
|||
sslSetConnectState(socket.sslHandle)
|
||||
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
||||
|
||||
proc readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): Future[int] {.async.} =
|
||||
template readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): int =
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that
|
||||
## this is a template and not a proc.
|
||||
var res = 0
|
||||
if socket.isSsl:
|
||||
when defined(ssl):
|
||||
# SSL mode.
|
||||
sslLoop(socket, flags,
|
||||
sslRead(socket.sslHandle, buf, size.cint))
|
||||
result = opResult
|
||||
res = opResult
|
||||
else:
|
||||
var data = await recv(socket.fd.TAsyncFD, size, flags)
|
||||
if data.len != 0:
|
||||
copyMem(buf, addr data[0], data.len)
|
||||
var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags)
|
||||
yield recvIntoFut
|
||||
# Not in SSL mode.
|
||||
result = data.len
|
||||
res = recvIntoFut.read()
|
||||
res
|
||||
|
||||
proc readIntoBuf(socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): Future[int] {.async.} =
|
||||
result = await readInto(addr socket.buffer[0], BufferSize, socket, flags)
|
||||
template readIntoBuf(socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): int =
|
||||
var size = readInto(addr socket.buffer[0], BufferSize, socket, flags)
|
||||
socket.currPos = 0
|
||||
socket.bufLen = result
|
||||
socket.bufLen = size
|
||||
size
|
||||
|
||||
proc recv*(socket: AsyncSocket, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
||||
|
|
@ -222,10 +226,11 @@ proc recv*(socket: AsyncSocket, size: int,
|
|||
## to be read then the future will complete with a value of ``""``.
|
||||
if socket.isBuffered:
|
||||
result = newString(size)
|
||||
shallow(result)
|
||||
let originalBufPos = socket.currPos
|
||||
|
||||
if socket.bufLen == 0:
|
||||
let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
let res = socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
if res == 0:
|
||||
result.setLen(0)
|
||||
return
|
||||
|
|
@ -236,7 +241,7 @@ proc recv*(socket: AsyncSocket, size: int,
|
|||
if SocketFlag.Peek in flags:
|
||||
# We don't want to get another buffer if we're peeking.
|
||||
break
|
||||
let res = await socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
let res = socket.readIntoBuf(flags - {SocketFlag.Peek})
|
||||
if res == 0:
|
||||
break
|
||||
|
||||
|
|
@ -251,7 +256,7 @@ proc recv*(socket: AsyncSocket, size: int,
|
|||
result.setLen(read)
|
||||
else:
|
||||
result = newString(size)
|
||||
let read = await readInto(addr result[0], size, socket, flags)
|
||||
let read = readInto(addr result[0], size, socket, flags)
|
||||
result.setLen(read)
|
||||
|
||||
proc send*(socket: AsyncSocket, data: string,
|
||||
|
|
@ -302,15 +307,14 @@ proc accept*(socket: AsyncSocket,
|
|||
retFut.complete(future.read.client)
|
||||
return retFut
|
||||
|
||||
proc recvLine*(socket: AsyncSocket,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
||||
## Reads a line of data from ``socket``. Returned future will complete once
|
||||
## a full line is read or an error occurs.
|
||||
proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||
flags = {SocketFlag.SafeDisconn}) {.async.} =
|
||||
## Reads a line of data from ``socket`` into ``resString``.
|
||||
##
|
||||
## If a full line is read ``\r\L`` is not
|
||||
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||
## will be set to it.
|
||||
##
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||
##
|
||||
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||
|
|
@ -318,27 +322,32 @@ proc recvLine*(socket: AsyncSocket,
|
|||
## The partial line **will be lost**.
|
||||
##
|
||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||
##
|
||||
## **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")
|
||||
##
|
||||
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
|
||||
## protocol uses ``\r\L`` to delimit a new line.
|
||||
##
|
||||
## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for
|
||||
## performance reasons. This will likely change soon to use FutureVars.
|
||||
assert SocketFlag.Peek notin flags ## TODO:
|
||||
result = newFuture[void]("asyncnet.recvLineInto")
|
||||
|
||||
template addNLIfEmpty(): stmt =
|
||||
if resString[].len == 0:
|
||||
resString[].add("\c\L")
|
||||
|
||||
if socket.isBuffered:
|
||||
result = ""
|
||||
if socket.bufLen == 0:
|
||||
let res = await socket.readIntoBuf(flags)
|
||||
let res = socket.readIntoBuf(flags)
|
||||
if res == 0:
|
||||
return
|
||||
|
||||
var lastR = false
|
||||
while true:
|
||||
if socket.currPos >= socket.bufLen:
|
||||
let res = await socket.readIntoBuf(flags)
|
||||
let res = socket.readIntoBuf(flags)
|
||||
if res == 0:
|
||||
result = ""
|
||||
break
|
||||
resString[].setLen(0)
|
||||
return
|
||||
|
||||
case socket.buffer[socket.currPos]
|
||||
of '\r':
|
||||
|
|
@ -353,24 +362,53 @@ proc recvLine*(socket: AsyncSocket,
|
|||
socket.currPos.inc()
|
||||
return
|
||||
else:
|
||||
result.add socket.buffer[socket.currPos]
|
||||
resString[].add socket.buffer[socket.currPos]
|
||||
socket.currPos.inc()
|
||||
else:
|
||||
result = ""
|
||||
var c = ""
|
||||
while true:
|
||||
c = await recv(socket, 1, flags)
|
||||
let recvFut = recv(socket, 1, flags)
|
||||
c = recvFut.read()
|
||||
if c.len == 0:
|
||||
return ""
|
||||
resString[].setLen(0)
|
||||
return
|
||||
if c == "\r":
|
||||
c = await recv(socket, 1, flags) # Skip \L
|
||||
let recvFut = recv(socket, 1, flags) # Skip \L
|
||||
c = recvFut.read()
|
||||
assert c == "\L"
|
||||
addNLIfEmpty()
|
||||
return
|
||||
elif c == "\L":
|
||||
addNLIfEmpty()
|
||||
return
|
||||
add(result.string, c)
|
||||
resString[].add c
|
||||
|
||||
proc recvLine*(socket: AsyncSocket,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
||||
## Reads a line of data from ``socket``. Returned future will complete once
|
||||
## a full line is read or an error occurs.
|
||||
##
|
||||
## If a full line is read ``\r\L`` is not
|
||||
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||
## will be set to it.
|
||||
##
|
||||
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||
##
|
||||
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||
## is read) then line will be set to ``""``.
|
||||
## The partial line **will be lost**.
|
||||
##
|
||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||
##
|
||||
## **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:
|
||||
|
||||
result = ""
|
||||
await socket.recvLineInto(addr result, flags)
|
||||
|
||||
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
|
|
@ -458,7 +496,7 @@ proc isClosed*(socket: AsyncSocket): bool =
|
|||
## Determines whether the socket has been closed.
|
||||
return socket.closed
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
type
|
||||
TestCases = enum
|
||||
HighClient, LowClient, LowServer
|
||||
|
|
@ -500,11 +538,11 @@ when isMainModule:
|
|||
proc (future: Future[void]) =
|
||||
echo("Send")
|
||||
client.close()
|
||||
|
||||
|
||||
var f = accept(sock)
|
||||
f.callback = onAccept
|
||||
|
||||
|
||||
var f = accept(sock)
|
||||
f.callback = onAccept
|
||||
runForever()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,33 +16,33 @@ import times
|
|||
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
|
||||
## the translation part of matrix is ignored. The coordinate system used is
|
||||
## right handed, because its compatible with 2d coordinate system (rotation around
|
||||
## zaxis equals 2d rotation).
|
||||
## zaxis equals 2d rotation).
|
||||
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented
|
||||
## for vectors and scalars.
|
||||
##
|
||||
##
|
||||
## Quick start example:
|
||||
##
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
##
|
||||
## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
|
||||
##
|
||||
##
|
||||
## # Create a 3d point at (100,150,200) and a vector (5,2,3)
|
||||
##
|
||||
## var pt:TPoint3d=point3d(100.0,150.0,200.0)
|
||||
##
|
||||
##
|
||||
## var pt:TPoint3d=point3d(100.0,150.0,200.0)
|
||||
##
|
||||
## var vec:TVector3d=vector3d(5.0,2.0,3.0)
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
##
|
||||
## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
##
|
||||
## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
|
||||
type
|
||||
type
|
||||
TMatrix3d* =object
|
||||
## Implements a row major 3d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
|
|
@ -53,12 +53,12 @@ type
|
|||
## [ tx ty tz tw ]
|
||||
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
|
||||
TPoint3d* = object
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
## an `x` , `y` and `z` coordinate.
|
||||
x*,y*,z*:float
|
||||
TVector3d* = object
|
||||
## Implements a 3d **direction vector** stored as
|
||||
## an `x` , `y` and `z` coordinate. Direction vector means,
|
||||
TVector3d* = object
|
||||
## Implements a 3d **direction vector** stored as
|
||||
## an `x` , `y` and `z` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*,z*:float
|
||||
|
|
@ -67,7 +67,7 @@ type
|
|||
|
||||
# Some forward declarations
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d {.noInit.}
|
||||
## Creates a new 4x4 3d transformation matrix.
|
||||
## Creates a new 4x4 3d transformation matrix.
|
||||
## `ax` , `ay` , `az` is the local x axis.
|
||||
## `bx` , `by` , `bz` is the local y axis.
|
||||
## `cx` , `cy` , `cz` is the local z axis.
|
||||
|
|
@ -76,7 +76,7 @@ proc vector3d*(x,y,z:float):TVector3d {.noInit,inline.}
|
|||
## Returns a new 3d vector (`x`,`y`,`z`)
|
||||
proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.}
|
||||
## Returns a new 4d point (`x`,`y`,`z`)
|
||||
proc tryNormalize*(v:var TVector3d):bool
|
||||
proc tryNormalize*(v:var TVector3d):bool
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
|
|
@ -85,7 +85,7 @@ proc tryNormalize*(v:var TVector3d):bool
|
|||
|
||||
let
|
||||
IDMATRIX*:TMatrix3d=matrix3d(
|
||||
1.0,0.0,0.0,0.0,
|
||||
1.0,0.0,0.0,0.0,
|
||||
0.0,1.0,0.0,0.0,
|
||||
0.0,0.0,1.0,0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
|
|
@ -114,20 +114,20 @@ proc safeArccos(v:float):float=
|
|||
## due to rounding issues
|
||||
return arccos(clamp(v,-1.0,1.0))
|
||||
|
||||
template makeBinOpVector(s:expr)=
|
||||
template makeBinOpVector(s:expr)=
|
||||
## implements binary operators + , - , * and / for vectors
|
||||
proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
|
||||
proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} =
|
||||
proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} =
|
||||
vector3d(s(a.x,b),s(a.y,b),s(a.z,b))
|
||||
proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
## implements inplace binary operators += , -= , /= and *= for vectors
|
||||
proc s*(a:var TVector3d,b:TVector3d) {.inline.} =
|
||||
proc s*(a:var TVector3d,b:TVector3d) {.inline.} =
|
||||
s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z)
|
||||
proc s*(a:var TVector3d,b:float) {.inline.} =
|
||||
proc s*(a:var TVector3d,b:float) {.inline.} =
|
||||
s(a.x,b) ; s(a.y,b) ; s(a.z,b)
|
||||
|
||||
|
||||
|
|
@ -188,20 +188,20 @@ proc scale*(s:float):TMatrix3d {.noInit.} =
|
|||
|
||||
proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
## Returns a new scaling matrix using, `org` as scale origin.
|
||||
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
|
||||
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
|
||||
org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0)
|
||||
|
||||
proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x,y and z.
|
||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1)
|
||||
|
||||
|
||||
proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x,y and z.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1)
|
||||
|
||||
|
||||
proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1)
|
||||
|
|
@ -235,7 +235,7 @@ proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}=
|
|||
uvomc=normax.x*normax.y*omc
|
||||
uwomc=normax.x*normax.z*omc
|
||||
vwomc=normax.y*normax.z*omc
|
||||
|
||||
|
||||
result.setElements(
|
||||
u2+(1.0-u2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
|
||||
uvomc-wsi, v2+(1.0-v2)*cs, vwomc+usi, 0.0,
|
||||
|
|
@ -248,11 +248,11 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
|||
|
||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||
# for how this is computed
|
||||
|
||||
|
||||
var normax=axis
|
||||
if not normax.tryNormalize: #simplifies matrix computation below a lot
|
||||
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
||||
|
||||
|
||||
let
|
||||
u=normax.x
|
||||
v=normax.y
|
||||
|
|
@ -272,7 +272,7 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
|||
uvomc=normax.x*normax.y*omc
|
||||
uwomc=normax.x*normax.z*omc
|
||||
vwomc=normax.y*normax.z*omc
|
||||
|
||||
|
||||
result.setElements(
|
||||
u2+(v2+w2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
|
||||
uvomc-wsi, v2+(u2+w2)*cs, vwomc+usi, 0.0,
|
||||
|
|
@ -305,7 +305,7 @@ proc rotateY*(angle:float):TMatrix3d {.noInit.}=
|
|||
0,1,0,0,
|
||||
s,0,c,0,
|
||||
0,0,0,1)
|
||||
|
||||
|
||||
proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the z-axis with `angle` radians,
|
||||
## which is also called a 'yaw' matrix.
|
||||
|
|
@ -317,19 +317,19 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
|||
-s,c,0,0,
|
||||
0,0,1,0,
|
||||
0,0,0,1)
|
||||
|
||||
|
||||
proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a sphere into an ellipsoid.
|
||||
## `tol` is used as tolerance for both equal length comparison
|
||||
## `tol` is used as tolerance for both equal length comparison
|
||||
## and perpendicular comparison.
|
||||
|
||||
|
||||
#dot product=0 means perpendicular coord. system, check xaxis vs yaxis and xaxis vs zaxis
|
||||
if abs(m.ax*m.bx+m.ay*m.by+m.az*m.bz)<=tol and # x vs y
|
||||
abs(m.ax*m.cx+m.ay*m.cy+m.az*m.cz)<=tol and #x vs z
|
||||
abs(m.bx*m.cx+m.by*m.cy+m.bz*m.cz)<=tol: #y vs z
|
||||
|
||||
|
||||
#subtract squared lengths of axes to check if uniform scaling:
|
||||
let
|
||||
sqxlen=(m.ax*m.ax+m.ay*m.ay+m.az*m.az)
|
||||
|
|
@ -340,16 +340,16 @@ proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
|||
return false
|
||||
|
||||
|
||||
|
||||
|
||||
proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
||||
## and passes through origo. `planeperp` does not need to be normalized.
|
||||
|
||||
|
||||
# https://en.wikipedia.org/wiki/Transformation_matrix
|
||||
var n=planeperp
|
||||
if not n.tryNormalize:
|
||||
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
||||
|
||||
|
||||
let
|
||||
a=n.x
|
||||
b=n.y
|
||||
|
|
@ -357,7 +357,7 @@ proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
|||
ab=a*b
|
||||
ac=a*c
|
||||
bc=b*c
|
||||
|
||||
|
||||
result.setElements(
|
||||
1-2*a*a , -2*ab,-2*ac,0,
|
||||
-2*ab , 1-2*b*b, -2*bc, 0,
|
||||
|
|
@ -376,7 +376,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
|||
var n=planeperp
|
||||
if not n.tryNormalize:
|
||||
raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
|
||||
|
||||
|
||||
let
|
||||
a=n.x
|
||||
b=n.y
|
||||
|
|
@ -390,7 +390,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
|||
tx=org.x
|
||||
ty=org.y
|
||||
tz=org.z
|
||||
|
||||
|
||||
result.setElements(
|
||||
1-2*aa , -2*ab,-2*ac,0,
|
||||
-2*ab , 1-2*bb, -2*bc, 0,
|
||||
|
|
@ -402,8 +402,8 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
|||
|
||||
proc determinant*(m:TMatrix3d):float=
|
||||
## Computes the determinant of matrix `m`.
|
||||
|
||||
# This computation is gotten from ratsimp(optimize(determinant(m)))
|
||||
|
||||
# This computation is gotten from ratsimp(optimize(determinant(m)))
|
||||
# in maxima CAS
|
||||
let
|
||||
O1=m.cx*m.tw-m.cw*m.tx
|
||||
|
|
@ -423,10 +423,10 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
|||
## Computes the inverse of matrix `m`. If the matrix
|
||||
## determinant is zero, thus not invertible, a EDivByZero
|
||||
## will be raised.
|
||||
|
||||
|
||||
# this computation comes from optimize(invert(m)) in maxima CAS
|
||||
|
||||
let
|
||||
|
||||
let
|
||||
det=m.determinant
|
||||
O2=m.cy*m.tw-m.cw*m.ty
|
||||
O3=m.cz*m.tw-m.cw*m.tz
|
||||
|
|
@ -464,7 +464,7 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
|||
proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
return
|
||||
abs(m1.ax-m2.ax)<=tol and
|
||||
abs(m1.ay-m2.ay)<=tol and
|
||||
abs(m1.az-m2.az)<=tol and
|
||||
|
|
@ -486,11 +486,11 @@ proc `=~`*(m1,m2:TMatrix3d):bool=
|
|||
## Checks if `m1` and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
|
||||
proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
## Returns the transpose of `m`
|
||||
result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw)
|
||||
|
||||
|
||||
proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
## Gets the local x axis of `m`
|
||||
result.x=m.ax
|
||||
|
|
@ -509,26 +509,26 @@ proc getZAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
|||
result.y=m.cy
|
||||
result.z=m.cz
|
||||
|
||||
|
||||
|
||||
proc `$`*(m:TMatrix3d):string=
|
||||
## String representation of `m`
|
||||
return rtos(m.ax) & "," & rtos(m.ay) & "," &rtos(m.az) & "," & rtos(m.aw) &
|
||||
"\n" & rtos(m.bx) & "," & rtos(m.by) & "," &rtos(m.bz) & "," & rtos(m.bw) &
|
||||
"\n" & rtos(m.cx) & "," & rtos(m.cy) & "," &rtos(m.cz) & "," & rtos(m.cw) &
|
||||
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," &rtos(m.tz) & "," & rtos(m.tw)
|
||||
|
||||
return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) &
|
||||
"\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) &
|
||||
"\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) &
|
||||
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw)
|
||||
|
||||
proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
||||
## Applies transformation `m` onto `x` , `y` , `z` , optionally
|
||||
## using the translation part of the matrix.
|
||||
let
|
||||
let
|
||||
oldx=x
|
||||
oldy=y
|
||||
oldz=z
|
||||
|
||||
|
||||
x=m.cx*oldz+m.bx*oldy+m.ax*oldx
|
||||
y=m.cy*oldz+m.by*oldy+m.ay*oldx
|
||||
z=m.cz*oldz+m.bz*oldy+m.az*oldx
|
||||
|
||||
|
||||
if translate:
|
||||
x+=m.tx
|
||||
y+=m.ty
|
||||
|
|
@ -552,13 +552,13 @@ proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
|
|||
## an arbitrary vector of the requested length is returned.
|
||||
|
||||
let fac=newlen/v.len
|
||||
|
||||
|
||||
if newlen==0.0:
|
||||
v.x=0.0
|
||||
v.y=0.0
|
||||
v.z=0.0
|
||||
return
|
||||
|
||||
|
||||
if fac==Inf or fac==NegInf:
|
||||
#to short for float accuracy
|
||||
#do as good as possible:
|
||||
|
|
@ -588,7 +588,7 @@ proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
|
|||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
|
||||
# | AX AY AZ AW |
|
||||
# | X Y Z 1 | * | BX BY BZ BW |
|
||||
# | CX CY CZ CW |
|
||||
|
|
@ -605,12 +605,12 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
|||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
|
||||
# | AX AY AZ AW |
|
||||
# | X Y Z 1 | * | BX BY BZ BW |
|
||||
# | CX CY CZ CW |
|
||||
# | 0 0 0 1 |
|
||||
|
||||
|
||||
let
|
||||
newx=m.cx*v.z+m.bx*v.y+m.ax*v.x
|
||||
newy=m.cy*v.z+m.by*v.y+m.ay*v.x
|
||||
|
|
@ -620,38 +620,38 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
|||
|
||||
proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
||||
## Applies a normal direction transformation `m` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## (determinant=0), an EDivByZero will be raised.
|
||||
|
||||
# transforming a normal is done by transforming
|
||||
# by the transpose of the inverse of the original matrix
|
||||
|
||||
|
||||
# Major reason this simple function is here is that this function can be optimized in the future,
|
||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||
v&=transpose(inverse(m))
|
||||
|
||||
|
||||
proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
||||
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
||||
## the translational part of the matrix. Transforming a vector ignores the
|
||||
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
||||
## the translational part of the matrix. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
|
||||
|
||||
# Major reason this simple function is here is that this function can be optimized in the future,
|
||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||
v&=m.inverse
|
||||
|
||||
|
||||
proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
||||
## Applies an inverse normal direction transformation `m` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
|
||||
|
||||
# see vector2d:s equivalent for a deeper look how/why this works
|
||||
vec&=m.transpose
|
||||
|
||||
proc tryNormalize*(v:var TVector3d):bool=
|
||||
proc tryNormalize*(v:var TVector3d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
|
|
@ -663,26 +663,26 @@ proc tryNormalize*(v:var TVector3d):bool=
|
|||
v.x/=mag
|
||||
v.y/=mag
|
||||
v.z/=mag
|
||||
|
||||
|
||||
return true
|
||||
|
||||
proc normalize*(v:var TVector3d) {.inline.}=
|
||||
proc normalize*(v:var TVector3d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||
## Rotates `vec` in place, with `angle` radians over `axis`, which passes
|
||||
## Rotates `vec` in place, with `angle` radians over `axis`, which passes
|
||||
## through origo.
|
||||
|
||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||
# for how this is computed
|
||||
|
||||
|
||||
var normax=axis
|
||||
if not normax.tryNormalize:
|
||||
raise newException(DivByZeroError,"Cannot rotate around zero length axis")
|
||||
|
||||
|
||||
let
|
||||
cs=cos(angle)
|
||||
si=sin(angle)
|
||||
|
|
@ -694,11 +694,11 @@ proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
|||
y=vec.y
|
||||
z=vec.z
|
||||
uxyzomc=(u*x+v*y+w*z)*omc
|
||||
|
||||
|
||||
vec.x=u*uxyzomc+x*cs+(v*z-w*y)*si
|
||||
vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
|
||||
vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
|
||||
|
||||
|
||||
proc scale*(v:var TVector3d,s:float)=
|
||||
## Scales the vector in place with factor `s`
|
||||
v.x*=s
|
||||
|
|
@ -713,12 +713,12 @@ proc stretch*(v:var TVector3d,sx,sy,sz:float)=
|
|||
|
||||
proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||
## Computes the mirrored vector of `v` over the plane
|
||||
## that has `planeperp` as normal direction.
|
||||
## that has `planeperp` as normal direction.
|
||||
## `planeperp` does not need to be normalized.
|
||||
|
||||
|
||||
var n=planeperp
|
||||
n.normalize
|
||||
|
||||
|
||||
let
|
||||
x=v.x
|
||||
y=v.y
|
||||
|
|
@ -729,7 +729,7 @@ proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
|||
ac=a*c
|
||||
ab=a*b
|
||||
bc=b*c
|
||||
|
||||
|
||||
v.x= -2*(ac*z+ab*y+a*a*x)+x
|
||||
v.y= -2*(bc*z+b*b*y+ab*x)+y
|
||||
v.z= -2*(c*c*z+bc*y+ac*x)+z
|
||||
|
|
@ -740,7 +740,7 @@ proc `-` *(v:TVector3d):TVector3d=
|
|||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
result.z= -v.z
|
||||
|
||||
|
||||
# declare templated binary operators
|
||||
makeBinOpVector(`+`)
|
||||
makeBinOpVector(`-`)
|
||||
|
|
@ -752,7 +752,7 @@ makeBinOpAssignVector(`*=`)
|
|||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
proc dot*(v1,v2:TVector3d):float {.inline.}=
|
||||
## Computes the dot product of two vectors.
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
|
||||
|
||||
|
|
@ -769,12 +769,12 @@ proc cross*(v1,v2:TVector3d):TVector3d {.inline.}=
|
|||
proc equals*(v1,v2:TVector3d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol
|
||||
|
||||
|
||||
proc `=~` *(v1,v2:TVector3d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
|
||||
proc angleTo*(v1,v2:TVector3d):float=
|
||||
## Returns the smallest angle between v1 and v2,
|
||||
## which is in range 0-PI
|
||||
|
|
@ -801,7 +801,7 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
|||
ay=cross(norm,ax)
|
||||
ay.normalize()
|
||||
az=cross(ax,ay)
|
||||
|
||||
|
||||
result.setElements(
|
||||
ax.x,ax.y,ax.z,0.0,
|
||||
ay.x,ay.y,ay.z,0.0,
|
||||
|
|
@ -811,20 +811,20 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
|||
proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
## an arbitrary normalized vector `v1` is returned.
|
||||
var
|
||||
vmag1=v1.len
|
||||
vmag2=v2.len
|
||||
|
||||
# zero length vector equals arbitrary vector, just change
|
||||
|
||||
# zero length vector equals arbitrary vector, just change
|
||||
# magnitude to one to avoid zero division
|
||||
if vmag1==0.0:
|
||||
if vmag1==0.0:
|
||||
if vmag2==0: #both are zero length return any normalized vector
|
||||
return XAXIS
|
||||
vmag1=1.0
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
let
|
||||
x1=v1.x/vmag1
|
||||
y1=v1.y/vmag1
|
||||
|
|
@ -832,14 +832,14 @@ proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
|||
x2=v2.x/vmag2
|
||||
y2=v2.y/vmag2
|
||||
z2=v2.z/vmag2
|
||||
|
||||
|
||||
result.x=(x1 + x2) * 0.5
|
||||
result.y=(y1 + y2) * 0.5
|
||||
result.z=(z1 + z2) * 0.5
|
||||
|
||||
|
||||
if not result.tryNormalize():
|
||||
# This can happen if vectors are colinear. In this special case
|
||||
# there are actually inifinitely many bisectors, we select just
|
||||
# there are actually inifinitely many bisectors, we select just
|
||||
# one of them.
|
||||
result=v1.cross(XAXIS)
|
||||
if result.sqrLen<1.0e-9:
|
||||
|
|
@ -857,14 +857,14 @@ proc point3d*(x,y,z:float):TPoint3d=
|
|||
result.x=x
|
||||
result.y=y
|
||||
result.z=z
|
||||
|
||||
|
||||
proc sqrDist*(a,b:TPoint3d):float=
|
||||
## Computes the squared distance between `a`and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
let dz=b.z-a.z
|
||||
result=dx*dx+dy*dy+dz*dz
|
||||
|
||||
|
||||
proc dist*(a,b:TPoint3d):float {.inline.}=
|
||||
## Computes the absolute distance between `a`and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
|
|
@ -876,7 +876,7 @@ proc `$` *(p:TPoint3d):string=
|
|||
result.add(rtos(p.y))
|
||||
result.add(",")
|
||||
result.add(rtos(p.z))
|
||||
|
||||
|
||||
proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d=
|
||||
## Concatenates a point `p` with a transform `m`,
|
||||
## resulting in a new, transformed point.
|
||||
|
|
@ -893,18 +893,18 @@ proc `&=` *(p:var TPoint3d,m:TMatrix3d)=
|
|||
p.x=m.cx*z+m.bx*y+m.ax*x+m.tx
|
||||
p.y=m.cy*z+m.by*y+m.ay*x+m.ty
|
||||
p.z=m.cz*z+m.bz*y+m.az*x+m.tz
|
||||
|
||||
|
||||
proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
||||
## Applies the inverse of transformation `m` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
|
||||
|
||||
# can possibly be more optimized in the future so use this function when possible
|
||||
p&=inverse(m)
|
||||
|
||||
|
||||
proc `+`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
|
|
@ -917,7 +917,7 @@ proc `+=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
|||
p.z+=v.z
|
||||
|
||||
proc `-`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
|
|
@ -933,37 +933,37 @@ proc `-=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
|||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
p.z-=v.z
|
||||
p.z-=v.z
|
||||
|
||||
proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:TPoint3d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## passing through origo.
|
||||
|
||||
|
||||
var v=vector3d(p.x,p.y,p.z)
|
||||
v.rotate(rad,axis) # reuse this code here since doing the same thing and quite complicated
|
||||
p.x=v.x
|
||||
p.y=v.y
|
||||
p.z=v.z
|
||||
|
||||
|
||||
proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## passing through `org`
|
||||
|
||||
|
||||
# see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
|
||||
# for how this is computed
|
||||
|
||||
|
||||
var normax=axis
|
||||
normax.normalize
|
||||
|
||||
|
||||
let
|
||||
cs=cos(angle)
|
||||
omc=1.0-cs
|
||||
|
|
@ -987,17 +987,17 @@ proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
|||
bv=b*v
|
||||
cw=c*w
|
||||
uxmvymwz=ux-vy-wz
|
||||
|
||||
|
||||
p.x=(a*(vv+ww)-u*(bv+cw-uxmvymwz))*omc + x*cs + (b*w+v*z-c*v-w*y)*si
|
||||
p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si
|
||||
p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si
|
||||
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
p.z*=fac
|
||||
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
|
|
@ -1005,7 +1005,7 @@ proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
|||
p.z=(p.z - org.z) * fac + org.z
|
||||
|
||||
proc stretch*(p:var TPoint3d,facx,facy,facz:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` , `facy` , `facz` times
|
||||
## Scales a point in place non uniformly `facx` , `facy` , `facz` times
|
||||
## with world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
|
|
@ -1017,7 +1017,7 @@ proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}=
|
|||
p.x=(p.x - org.x) * facx + org.x
|
||||
p.y=(p.y - org.y) * facy + org.y
|
||||
p.z=(p.z - org.z) * facz + org.z
|
||||
|
||||
|
||||
|
||||
proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}=
|
||||
## Translates a point `dx` , `dy` , `dz` in place.
|
||||
|
|
@ -1033,7 +1033,7 @@ proc move*(p:var TPoint3d,v:TVector3d){.inline.}=
|
|||
|
||||
proc area*(a,b,c:TPoint3d):float {.inline.}=
|
||||
## Computes the area of the triangle thru points `a` , `b` and `c`
|
||||
|
||||
|
||||
# The area of a planar 3d quadliteral is the magnitude of the cross
|
||||
# product of two edge vectors. Taking this time 0.5 gives the triangle area.
|
||||
return cross(b-a,c-a).len*0.5
|
||||
|
|
|
|||
|
|
@ -525,7 +525,7 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
|||
|
||||
|
||||
#Tests ----------------------------
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
import locks, times, mersenne
|
||||
|
||||
const
|
||||
|
|
|
|||
|
|
@ -286,18 +286,19 @@ proc `$`*[T](c: CritBitTree[T]): string =
|
|||
result.add("}")
|
||||
|
||||
when isMainModule:
|
||||
import sequtils
|
||||
|
||||
var r: CritBitTree[void]
|
||||
r.incl "abc"
|
||||
r.incl "xyz"
|
||||
r.incl "def"
|
||||
r.incl "definition"
|
||||
r.incl "prefix"
|
||||
|
||||
doAssert r.contains"def"
|
||||
#r.del "def"
|
||||
|
||||
for w in r.items:
|
||||
echo w
|
||||
|
||||
for w in r.itemsWithPrefix("de"):
|
||||
echo w
|
||||
r.excl "def"
|
||||
|
||||
assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
|
||||
|
||||
assert toSeq(r.itemsWithPrefix("de")) == @["definition"]
|
||||
|
|
|
|||
|
|
@ -198,14 +198,21 @@ proc empty*(s: IntSet): bool {.inline, deprecated.} =
|
|||
result = s.counter == 0
|
||||
|
||||
when isMainModule:
|
||||
import sequtils, algorithm
|
||||
|
||||
var x = initIntSet()
|
||||
x.incl(1)
|
||||
x.incl(2)
|
||||
x.incl(7)
|
||||
x.incl(1056)
|
||||
for e in items(x): echo e
|
||||
|
||||
var y: TIntSet
|
||||
var xs = toSeq(items(x))
|
||||
xs.sort(cmp[int])
|
||||
assert xs == @[1, 2, 7, 1056]
|
||||
|
||||
var y: IntSet
|
||||
assign(y, x)
|
||||
for e in items(y): echo e
|
||||
var ys = toSeq(items(y))
|
||||
ys.sort(cmp[int])
|
||||
assert ys == @[1, 2, 7, 1056]
|
||||
|
||||
|
|
|
|||
|
|
@ -492,9 +492,8 @@ when isMainModule:
|
|||
|
||||
block: # filter iterator test
|
||||
let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
||||
for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
||||
echo($n)
|
||||
# echoes 4, 8, 4 in separate lines
|
||||
assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
|
||||
@[4, 8, 4]
|
||||
|
||||
block: # keepIf test
|
||||
var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
||||
|
|
@ -616,4 +615,5 @@ when isMainModule:
|
|||
#doAssert a.repeat(-1) == @[] # will not compile!
|
||||
doAssert b.repeat(3) == @[]
|
||||
|
||||
echo "Finished doc tests"
|
||||
when not defined(testing):
|
||||
echo "Finished doc tests"
|
||||
|
|
|
|||
|
|
@ -970,6 +970,7 @@ when isMainModule and not defined(release):
|
|||
if s <= i or mustRehash(s, i):
|
||||
echo "performance issue: rightSize() will not elide enlarge() at ", i
|
||||
|
||||
echo "Micro tests run successfully."
|
||||
when not defined(testing):
|
||||
echo "Micro tests run successfully."
|
||||
|
||||
testModule()
|
||||
|
|
|
|||
|
|
@ -819,15 +819,18 @@ proc enlarge[A](t: var CountTable[A]) =
|
|||
swap(t.data, n)
|
||||
|
||||
proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
|
||||
## puts a (key, value)-pair into `t`. `val` has to be positive.
|
||||
## puts a (key, value)-pair into `t`.
|
||||
assert val > 0
|
||||
var h = rawGet(t, key)
|
||||
if h >= 0:
|
||||
t.data[h].val = val
|
||||
else:
|
||||
h = -1 - h
|
||||
t.data[h].key = key
|
||||
t.data[h].val = val
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t, t.data, key, val)
|
||||
inc(t.counter)
|
||||
#h = -1 - h
|
||||
#t.data[h].key = key
|
||||
#t.data[h].val = val
|
||||
|
||||
proc initCountTable*[A](initialSize=64): CountTable[A] =
|
||||
## creates a new count table that is empty.
|
||||
|
|
@ -984,6 +987,22 @@ proc sort*[A](t: CountTableRef[A]) =
|
|||
## `t` in the sorted order.
|
||||
t[].sort
|
||||
|
||||
proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
|
||||
## merges the second table into the first one
|
||||
for key, value in t:
|
||||
s.inc(key, value)
|
||||
|
||||
proc merge*[A](s, t: CountTable[A]): CountTable[A] =
|
||||
## merges the two tables into a new one
|
||||
result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
|
||||
for table in @[s, t]:
|
||||
for key, value in table:
|
||||
result.inc(key, value)
|
||||
|
||||
proc merge*[A](s, t: CountTableRef[A]) =
|
||||
## merges the second table into the first one
|
||||
s[].merge(t[])
|
||||
|
||||
when isMainModule:
|
||||
type
|
||||
Person = object
|
||||
|
|
@ -1012,3 +1031,48 @@ when isMainModule:
|
|||
s2[p2] = 45_000
|
||||
s3[p1] = 30_000
|
||||
s3[p2] = 45_000
|
||||
|
||||
var
|
||||
t1 = initCountTable[string]()
|
||||
t2 = initCountTable[string]()
|
||||
t1.inc("foo")
|
||||
t1.inc("bar", 2)
|
||||
t1.inc("baz", 3)
|
||||
t2.inc("foo", 4)
|
||||
t2.inc("bar")
|
||||
t2.inc("baz", 11)
|
||||
merge(t1, t2)
|
||||
assert(t1["foo"] == 5)
|
||||
assert(t1["bar"] == 3)
|
||||
assert(t1["baz"] == 14)
|
||||
|
||||
let
|
||||
t1r = newCountTable[string]()
|
||||
t2r = newCountTable[string]()
|
||||
t1r.inc("foo")
|
||||
t1r.inc("bar", 2)
|
||||
t1r.inc("baz", 3)
|
||||
t2r.inc("foo", 4)
|
||||
t2r.inc("bar")
|
||||
t2r.inc("baz", 11)
|
||||
merge(t1r, t2r)
|
||||
assert(t1r["foo"] == 5)
|
||||
assert(t1r["bar"] == 3)
|
||||
assert(t1r["baz"] == 14)
|
||||
|
||||
var
|
||||
t1l = initCountTable[string]()
|
||||
t2l = initCountTable[string]()
|
||||
t1l.inc("foo")
|
||||
t1l.inc("bar", 2)
|
||||
t1l.inc("baz", 3)
|
||||
t2l.inc("foo", 4)
|
||||
t2l.inc("bar")
|
||||
t2l.inc("baz", 11)
|
||||
let
|
||||
t1merging = t1l
|
||||
t2merging = t2l
|
||||
let merged = merge(t1merging, t2merging)
|
||||
assert(merged["foo"] == 5)
|
||||
assert(merged["bar"] == 3)
|
||||
assert(merged["baz"] == 14)
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
|||
result = doNothing
|
||||
inc s.calls
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc busyLoop() =
|
||||
while true:
|
||||
discard random(80)
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) =
|
|||
minPoolSize = size
|
||||
|
||||
proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) =
|
||||
## sets the minimal thread pool size. The default value of this
|
||||
## sets the maximal thread pool size. The default value of this
|
||||
## is ``MaxThreadPoolSize``.
|
||||
maxPoolSize = size
|
||||
if currentPoolSize > maxPoolSize:
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ proc setCookie*(key, value: string, expires: TimeInfo,
|
|||
when isMainModule:
|
||||
var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
|
||||
|
||||
echo(setCookie("test", "value", tim.getGMTime()))
|
||||
let cookie = setCookie("test", "value", tim.getGMTime())
|
||||
when not defined(testing):
|
||||
echo cookie
|
||||
let start = "Set-Cookie: test=value; Expires="
|
||||
assert cookie[0..start.high] == start
|
||||
|
||||
echo parseCookies("uid=1; kp=2")
|
||||
let table = parseCookies("uid=1; kp=2")
|
||||
assert table["uid"] == "1"
|
||||
assert table["kp"] == "2"
|
||||
|
|
|
|||
|
|
@ -451,7 +451,7 @@ proc convert*(s: string, destEncoding = "UTF-8",
|
|||
finally:
|
||||
close(c)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
let
|
||||
orig = "öäüß"
|
||||
cp1252 = convert(orig, "CP1252", "UTF-8")
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ proc register*(d: Dispatcher, monitor: FSMonitor,
|
|||
var deleg = toDelegate(monitor)
|
||||
d.register(deleg)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
var disp = newDispatcher()
|
||||
var monitor = newMonitor()
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ proc register*(d: Dispatcher, ftp: AsyncFTPClient): Delegate {.discardable.} =
|
|||
ftp.disp = d
|
||||
return ftp.disp.register(ftp.csock)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
var d = newDispatcher()
|
||||
let hev =
|
||||
|
|
@ -629,7 +629,7 @@ when isMainModule:
|
|||
if not d.poll(): break
|
||||
main()
|
||||
|
||||
when isMainModule and false:
|
||||
when not defined(testing) and isMainModule:
|
||||
var ftp = ftpClient("example.com", user = "foo", pass = "bar")
|
||||
ftp.connect()
|
||||
echo ftp.pwd()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
## The ``gentabs`` module implements an efficient hash table that is a
|
||||
## key-value mapping. The keys are required to be strings, but the values
|
||||
## may be any Nim or user defined type. This module supports matching
|
||||
## may be any Nim or user defined type. This module supports matching
|
||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||
|
||||
{.deprecated.}
|
||||
|
|
@ -22,7 +22,7 @@ type
|
|||
modeCaseSensitive, ## case sensitive matching of keys
|
||||
modeCaseInsensitive, ## case insensitive matching of keys
|
||||
modeStyleInsensitive ## style sensitive matching of keys
|
||||
|
||||
|
||||
TGenKeyValuePair[T] = tuple[key: string, val: T]
|
||||
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]]
|
||||
TGenTable*[T] = object of RootObj
|
||||
|
|
@ -83,7 +83,7 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
|||
h = nextTry(h, high(tbl.data))
|
||||
result = - 1
|
||||
|
||||
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
||||
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
||||
key: string, val: T) =
|
||||
var h: THash
|
||||
h = myhash(tbl, key) and high(data)
|
||||
|
|
@ -96,7 +96,7 @@ proc enlarge[T](tbl: PGenTable[T]) =
|
|||
var n: TGenKeyValuePairSeq[T]
|
||||
newSeq(n, len(tbl.data) * growthFactor)
|
||||
for i in countup(0, high(tbl.data)):
|
||||
if not isNil(tbl.data[i].key):
|
||||
if not isNil(tbl.data[i].key):
|
||||
rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
|
||||
swap(tbl.data, n)
|
||||
|
||||
|
|
@ -141,20 +141,20 @@ when isMainModule:
|
|||
assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table.
|
||||
for k,v in pairs(x): # make sure the 'pairs' iterator works
|
||||
assert(x[k]==v)
|
||||
|
||||
|
||||
#
|
||||
# Verify a table of user-defined types
|
||||
#
|
||||
type
|
||||
TMyType = tuple[first, second: string] # a pair of strings
|
||||
|
||||
|
||||
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each
|
||||
# value is TMyType tuple
|
||||
|
||||
|
||||
#var junk: TMyType = ("OK", "Here")
|
||||
|
||||
|
||||
#echo junk.first, " ", junk.second
|
||||
|
||||
|
||||
y["Hello"] = ("Hello", "World")
|
||||
y["Goodbye"] = ("Goodbye", "Everyone")
|
||||
#y["Hello"] = TMyType( ("Hello", "World") )
|
||||
|
|
@ -163,31 +163,44 @@ when isMainModule:
|
|||
assert( not isNil(y["Hello"].first) )
|
||||
assert( y["Hello"].first == "Hello" )
|
||||
assert( y["Hello"].second == "World" )
|
||||
|
||||
|
||||
#
|
||||
# Verify table of tables
|
||||
#
|
||||
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
||||
var z: PGenTable[ PGenTable[int] ] # hash table where each value is
|
||||
# a hash table of ints
|
||||
|
||||
|
||||
z = newGenTable[PGenTable[int]](modeCaseInsensitive)
|
||||
z["first"] = newGenTable[int](modeCaseInsensitive)
|
||||
z["first"]["one"] = 1
|
||||
z["first"]["two"] = 2
|
||||
z["first"]["three"] = 3
|
||||
|
||||
|
||||
z["second"] = newGenTable[int](modeCaseInsensitive)
|
||||
z["second"]["red"] = 10
|
||||
z["second"]["blue"] = 20
|
||||
|
||||
|
||||
assert(len(z) == 2) # length of outer table
|
||||
assert(len(z["first"]) == 3) # length of "first" table
|
||||
assert(len(z["second"]) == 2) # length of "second" table
|
||||
assert( z["first"]["one"] == 1) # retrieve from first inner table
|
||||
assert( z["second"]["red"] == 10) # retrieve from second inner table
|
||||
|
||||
for k,v in pairs(z):
|
||||
echo( "$# ($#) ->" % [k,$len(v)] )
|
||||
#for k2,v2 in pairs(v):
|
||||
# echo( " $# <-> $#" % [k2,$v2] )
|
||||
echo()
|
||||
|
||||
when false:
|
||||
# disabled: depends on hash order:
|
||||
var output = ""
|
||||
for k, v in pairs(z):
|
||||
output.add( "$# ($#) ->\L" % [k,$len(v)] )
|
||||
for k2,v2 in pairs(v):
|
||||
output.add( " $# <-> $#\L" % [k2,$v2] )
|
||||
|
||||
let expected = unindent """
|
||||
first (3) ->
|
||||
two <-> 2
|
||||
three <-> 3
|
||||
one <-> 1
|
||||
second (2) ->
|
||||
red <-> 10
|
||||
blue <-> 20
|
||||
"""
|
||||
assert output == expected
|
||||
|
|
|
|||
|
|
@ -37,29 +37,29 @@
|
|||
## h = h !& hash(x.bar)
|
||||
## result = !$h
|
||||
|
||||
import
|
||||
import
|
||||
strutils
|
||||
|
||||
type
|
||||
THash* = int ## a hash value; hash tables using these values should
|
||||
type
|
||||
THash* = int ## a hash value; hash tables using these values should
|
||||
## always have a size of a power of two and can use the ``and``
|
||||
## operator instead of ``mod`` for truncation of the hash value.
|
||||
|
||||
proc `!&`*(h: THash, val: int): THash {.inline.} =
|
||||
proc `!&`*(h: THash, val: int): THash {.inline.} =
|
||||
## mixes a hash value `h` with `val` to produce a new hash value. This is
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% val
|
||||
result = result +% result shl 10
|
||||
result = result xor (result shr 6)
|
||||
|
||||
proc `!$`*(h: THash): THash {.inline.} =
|
||||
proc `!$`*(h: THash): THash {.inline.} =
|
||||
## finishes the computation of the hash value. This is
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% h shl 3
|
||||
result = result xor (result shr 11)
|
||||
result = result +% result shl 15
|
||||
|
||||
proc hashData*(data: pointer, size: int): THash =
|
||||
proc hashData*(data: pointer, size: int): THash =
|
||||
## hashes an array of bytes of size `size`
|
||||
var h: THash = 0
|
||||
when defined(js):
|
||||
|
|
@ -69,7 +69,7 @@ proc hashData*(data: pointer, size: int): THash =
|
|||
var p = cast[cstring](data)
|
||||
var i = 0
|
||||
var s = size
|
||||
while s > 0:
|
||||
while s > 0:
|
||||
h = h !& ord(p[i])
|
||||
inc(i)
|
||||
dec(s)
|
||||
|
|
@ -78,7 +78,7 @@ proc hashData*(data: pointer, size: int): THash =
|
|||
when defined(js):
|
||||
var objectID = 0
|
||||
|
||||
proc hash*(x: pointer): THash {.inline.} =
|
||||
proc hash*(x: pointer): THash {.inline.} =
|
||||
## efficient hashing of pointers
|
||||
when defined(js):
|
||||
asm """
|
||||
|
|
@ -93,7 +93,7 @@ proc hash*(x: pointer): THash {.inline.} =
|
|||
"""
|
||||
else:
|
||||
result = (cast[THash](x)) shr 3 # skip the alignment
|
||||
|
||||
|
||||
when not defined(booting):
|
||||
proc hash*[T: proc](x: T): THash {.inline.} =
|
||||
## efficient hashing of proc vars; closures are supported too.
|
||||
|
|
@ -101,58 +101,65 @@ when not defined(booting):
|
|||
result = hash(rawProc(x)) !& hash(rawEnv(x))
|
||||
else:
|
||||
result = hash(pointer(x))
|
||||
|
||||
proc hash*(x: int): THash {.inline.} =
|
||||
|
||||
proc hash*(x: int): THash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = x
|
||||
|
||||
proc hash*(x: int64): THash {.inline.} =
|
||||
proc hash*(x: int64): THash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = toU32(x)
|
||||
|
||||
proc hash*(x: char): THash {.inline.} =
|
||||
proc hash*(x: char): THash {.inline.} =
|
||||
## efficient hashing of characters
|
||||
result = ord(x)
|
||||
|
||||
proc hash*(x: string): THash =
|
||||
proc hash*(x: string): THash =
|
||||
## efficient hashing of strings
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
for i in 0..x.len-1:
|
||||
h = h !& ord(x[i])
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreStyle*(x: string): THash =
|
||||
|
||||
proc hashIgnoreStyle*(x: string): THash =
|
||||
## efficient hashing of strings; style is ignored
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
for i in 0..x.len-1:
|
||||
var c = x[i]
|
||||
if c == '_':
|
||||
if c == '_':
|
||||
continue # skip _
|
||||
if c in {'A'..'Z'}:
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreCase*(x: string): THash =
|
||||
proc hashIgnoreCase*(x: string): THash =
|
||||
## efficient hashing of strings; case is ignored
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
for i in 0..x.len-1:
|
||||
var c = x[i]
|
||||
if c in {'A'..'Z'}:
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
result = !$h
|
||||
|
||||
proc hash*[T: tuple](x: T): THash =
|
||||
## efficient hashing of tuples.
|
||||
for f in fields(x):
|
||||
result = result !& hash(f)
|
||||
result = !$result
|
||||
|
||||
proc hash*(x: float): THash {.inline.} =
|
||||
var y = x + 1.0
|
||||
result = cast[ptr THash](addr(y))[]
|
||||
|
||||
|
||||
# Forward declarations before methods that hash containers. This allows
|
||||
# containers to contain other containers
|
||||
proc hash*[A](x: openArray[A]): THash
|
||||
proc hash*[A](x: set[A]): THash
|
||||
|
||||
|
||||
proc hash*[T: tuple](x: T): THash =
|
||||
## efficient hashing of tuples.
|
||||
for f in fields(x):
|
||||
result = result !& hash(f)
|
||||
result = !$result
|
||||
|
||||
proc hash*[A](x: openArray[A]): THash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
|
@ -160,3 +167,4 @@ proc hash*[A](x: openArray[A]): THash =
|
|||
proc hash*[A](x: set[A]): THash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
||||
|
|
|
|||
|
|
@ -483,7 +483,8 @@ macro `var`*(e: expr): expr {.immediate.} =
|
|||
result = xmlCheckedTag(e, "var", commonAttr)
|
||||
|
||||
when isMainModule:
|
||||
var nim = "Nim"
|
||||
echo h1(a(href="http://nim-lang.org", nim))
|
||||
echo form(action="test", `accept-charset` = "Content-Type")
|
||||
|
||||
let nim = "Nim"
|
||||
assert h1(a(href="http://nim-lang.org", nim)) ==
|
||||
"""<h1><a href="http://nim-lang.org">Nim</a></h1>"""
|
||||
assert form(action="test", `accept-charset` = "Content-Type") ==
|
||||
"""<form action="test" accept-charset="Content-Type"></form>"""
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ proc loadHtml*(path: string): XmlNode =
|
|||
var errors: seq[string] = @[]
|
||||
result = loadHtml(path, errors)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
import os
|
||||
|
||||
var errors: seq[string] = @[]
|
||||
|
|
|
|||
|
|
@ -819,7 +819,7 @@ proc get*(client: AsyncHttpClient, url: string): Future[Response] {.async.} =
|
|||
result = await client.request(redirectTo, httpGET)
|
||||
lastUrl = redirectTo
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
when true:
|
||||
# Async
|
||||
proc main() {.async.} =
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ proc close*(h: PAsyncHTTPServer) =
|
|||
## Closes the ``PAsyncHTTPServer``.
|
||||
h.asyncSocket.close()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var counter = 0
|
||||
|
||||
var s: TServer
|
||||
|
|
|
|||
|
|
@ -808,22 +808,25 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) =
|
|||
return
|
||||
obj.fields.add((key, val))
|
||||
|
||||
proc `{}`*(node: JsonNode, key: string): JsonNode =
|
||||
## Transverses the node and gets the given value. If any of the
|
||||
## names does not exist, returns nil
|
||||
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
|
||||
result = node
|
||||
if isNil(node): return nil
|
||||
result = result[key]
|
||||
for key in keys:
|
||||
if isNil(result) or result.kind!=JObject:
|
||||
return nil
|
||||
result=result[key]
|
||||
|
||||
proc `{}=`*(node: JsonNode, names: varargs[string], value: JsonNode) =
|
||||
## Transverses the node and tries to set the value at the given location
|
||||
## to `value` If any of the names are missing, they are added
|
||||
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
|
||||
var node = node
|
||||
for i in 0..(names.len-2):
|
||||
if isNil(node[names[i]]):
|
||||
node[names[i]] = newJObject()
|
||||
node = node[names[i]]
|
||||
node[names[names.len-1]] = value
|
||||
for i in 0..(keys.len-2):
|
||||
if isNil(node[keys[i]]):
|
||||
node[keys[i]] = newJObject()
|
||||
node = node[keys[i]]
|
||||
node[keys[keys.len-1]] = value
|
||||
|
||||
proc delete*(obj: JsonNode, key: string) =
|
||||
## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs.
|
||||
|
|
@ -1150,19 +1153,22 @@ when false:
|
|||
when isMainModule:
|
||||
#var node = parse("{ \"test\": null }")
|
||||
#echo(node.existsKey("test56"))
|
||||
|
||||
var parsed = parseFile("tests/testdata/jsontest.json")
|
||||
var parsed2 = parseFile("tests/testdata/jsontest2.json")
|
||||
echo(parsed)
|
||||
echo()
|
||||
echo(pretty(parsed, 2))
|
||||
echo()
|
||||
echo(parsed["keyÄÖöoßß"])
|
||||
echo()
|
||||
echo(pretty(parsed2))
|
||||
try:
|
||||
echo(parsed["key2"][12123])
|
||||
raise newException(ValueError, "That line was expected to fail")
|
||||
except IndexError: echo()
|
||||
|
||||
when not defined(testing):
|
||||
echo(parsed)
|
||||
echo()
|
||||
echo(pretty(parsed, 2))
|
||||
echo()
|
||||
echo(parsed["keyÄÖöoßß"])
|
||||
echo()
|
||||
echo(pretty(parsed2))
|
||||
try:
|
||||
echo(parsed["key2"][12123])
|
||||
raise newException(ValueError, "That line was expected to fail")
|
||||
except IndexError: echo()
|
||||
|
||||
let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }"""
|
||||
# nil passthrough
|
||||
|
|
@ -1186,9 +1192,17 @@ when isMainModule:
|
|||
except:
|
||||
assert(false, "EInvalidIndex thrown for valid index")
|
||||
|
||||
assert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}")
|
||||
assert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
|
||||
assert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
|
||||
assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
|
||||
assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
|
||||
assert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found")
|
||||
assert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil")
|
||||
|
||||
# Generator:
|
||||
var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}]
|
||||
assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
||||
assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
||||
|
||||
var j2 = %*
|
||||
[
|
||||
|
|
@ -1216,12 +1230,13 @@ when isMainModule:
|
|||
}
|
||||
]
|
||||
assert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
|
||||
|
||||
discard """
|
||||
while true:
|
||||
var json = stdin.readLine()
|
||||
var node = parse(json)
|
||||
echo(node)
|
||||
echo()
|
||||
echo()
|
||||
"""
|
||||
|
||||
when not defined(testing):
|
||||
discard """
|
||||
while true:
|
||||
var json = stdin.readLine()
|
||||
var node = parse(json)
|
||||
echo(node)
|
||||
echo()
|
||||
echo()
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ proc getLogFilter*(): Level =
|
|||
|
||||
# --------------
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var L = newConsoleLogger()
|
||||
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
## type than its compiletime type:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## type
|
||||
##
|
||||
## type
|
||||
## TA = object
|
||||
## TB = object of TA
|
||||
## f: int
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
## new(b)
|
||||
## a = b
|
||||
## echo($$a[]) # produces "{}", not "{f: 0}"
|
||||
##
|
||||
## **Note**: The ``to`` and ``$$`` operations are available at compile-time!
|
||||
|
||||
import streams, typeinfo, json, intsets, tables
|
||||
|
||||
|
|
@ -38,7 +40,12 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
|||
case a.kind
|
||||
of akNone: assert false
|
||||
of akBool: s.write($getBool(a))
|
||||
of akChar: s.write(escapeJson($getChar(a)))
|
||||
of akChar:
|
||||
let ch = getChar(a)
|
||||
if ch < '\128':
|
||||
s.write(escapeJson($ch))
|
||||
else:
|
||||
s.write($int(ch))
|
||||
of akArray, akSequence:
|
||||
if a.kind == akSequence and isNil(a): s.write("null")
|
||||
else:
|
||||
|
|
@ -92,7 +99,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
|||
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
case a.kind
|
||||
of akNone: assert false
|
||||
of akBool:
|
||||
of akBool:
|
||||
case p.kind
|
||||
of jsonFalse: setBiggestInt(a, 0)
|
||||
of jsonTrue: setBiggestInt(a, 1)
|
||||
|
|
@ -105,8 +112,12 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
setBiggestInt(a, ord(x[0]))
|
||||
next(p)
|
||||
return
|
||||
elif p.kind == jsonInt:
|
||||
setBiggestInt(a, getInt(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "string of length 1 expected for a char")
|
||||
of akEnum:
|
||||
of akEnum:
|
||||
if p.kind == jsonString:
|
||||
setBiggestInt(a, getEnumOrdinal(a, p.str))
|
||||
next(p)
|
||||
|
|
@ -122,7 +133,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of akSequence:
|
||||
case p.kind
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
next(p)
|
||||
|
|
@ -143,7 +154,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||
next(p)
|
||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||
if p.kind != jsonString:
|
||||
if p.kind != jsonString:
|
||||
raiseParseErr(p, "string expected for a field name")
|
||||
var fieldName = p.str
|
||||
next(p)
|
||||
|
|
@ -160,7 +171,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of akPtr, akRef:
|
||||
case p.kind
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
next(p)
|
||||
|
|
@ -170,7 +181,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
of jsonArrayStart:
|
||||
next(p)
|
||||
if a.kind == akRef: invokeNew(a)
|
||||
else: setPointer(a, alloc0(a.baseTypeSize))
|
||||
else: setPointer(a, alloc0(a.baseTypeSize))
|
||||
if p.kind == jsonInt:
|
||||
t[p.getInt] = getPointer(a)
|
||||
next(p)
|
||||
|
|
@ -179,8 +190,8 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||
else: raiseParseErr(p, "int for pointer type expected")
|
||||
of akProc, akPointer, akCString:
|
||||
case p.kind
|
||||
of akProc, akPointer, akCString:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
next(p)
|
||||
|
|
@ -189,7 +200,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
next(p)
|
||||
else: raiseParseErr(p, "int for pointer type expected")
|
||||
of akString:
|
||||
case p.kind
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
setPointer(a, nil)
|
||||
next(p)
|
||||
|
|
@ -197,7 +208,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
|||
setString(a, p.str)
|
||||
next(p)
|
||||
else: raiseParseErr(p, "string expected")
|
||||
of akInt..akInt64, akUInt..akUInt64:
|
||||
of akInt..akInt64, akUInt..akUInt64:
|
||||
if p.kind == jsonInt:
|
||||
setBiggestInt(a, getInt(p))
|
||||
next(p)
|
||||
|
|
@ -243,22 +254,22 @@ proc to*[T](data: string): T =
|
|||
## reads data and transforms it to a ``T``.
|
||||
var tab = initTable[BiggestInt, pointer]()
|
||||
loadAny(newStringStream(data), toAny(result), tab)
|
||||
|
||||
when isMainModule:
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
template testit(x: expr) = echo($$to[type(x)]($$x))
|
||||
|
||||
var x: array[0..4, array[0..4, string]] = [
|
||||
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
||||
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
||||
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
||||
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
|
||||
["test", "1", "2", "3", "4"]]
|
||||
testit(x)
|
||||
var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
|
||||
testit(test2)
|
||||
|
||||
|
||||
type
|
||||
TE = enum
|
||||
blah, blah2
|
||||
|
||||
|
||||
TestObj = object
|
||||
test, asd: int
|
||||
case test2: TE
|
||||
|
|
@ -266,7 +277,7 @@ when isMainModule:
|
|||
help: string
|
||||
else:
|
||||
nil
|
||||
|
||||
|
||||
PNode = ref TNode
|
||||
TNode = object
|
||||
next, prev: PNode
|
||||
|
|
@ -294,7 +305,7 @@ when isMainModule:
|
|||
test4.a = "ref string test: A"
|
||||
test4.b = "ref string test: B"
|
||||
testit(test4)
|
||||
|
||||
|
||||
var test5 = @[(0,1),(2,3),(4,5)]
|
||||
testit(test5)
|
||||
|
||||
|
|
@ -305,7 +316,7 @@ when isMainModule:
|
|||
echo($$test7)
|
||||
testit(test7)
|
||||
|
||||
type
|
||||
type
|
||||
TA {.inheritable.} = object
|
||||
TB = object of TA
|
||||
f: int
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ proc fac*(n: int): int {.noSideEffect.} =
|
|||
proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
|
||||
## returns true, if `x` is a power of two, false otherwise.
|
||||
## Zero and negative numbers are not a power of two.
|
||||
return (x != 0) and ((x and (x - 1)) == 0)
|
||||
return (x > 0) and ((x and (x - 1)) == 0)
|
||||
|
||||
proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
|
||||
## returns `x` rounded up to the nearest power of two.
|
||||
|
|
@ -114,18 +114,23 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
|
|||
## If `x` is empty, 0 is returned.
|
||||
for i in items(x): result = result + i
|
||||
|
||||
proc mean*(x: openArray[float]): float {.noSideEffect.} =
|
||||
## computes the mean of the elements in `x`.
|
||||
## If `x` is empty, NaN is returned.
|
||||
result = sum(x) / toFloat(len(x))
|
||||
template toFloat(f: float): float = f
|
||||
|
||||
proc variance*(x: openArray[float]): float {.noSideEffect.} =
|
||||
proc mean*[T](x: openArray[T]): float {.noSideEffect.} =
|
||||
## computes the mean of the elements in `x`, which are first converted to floats.
|
||||
## If `x` is empty, NaN is returned.
|
||||
## ``toFloat(x: T): float`` must be defined.
|
||||
for i in items(x): result = result + toFloat(i)
|
||||
result = result / toFloat(len(x))
|
||||
|
||||
proc variance*[T](x: openArray[T]): float {.noSideEffect.} =
|
||||
## computes the variance of the elements in `x`.
|
||||
## If `x` is empty, NaN is returned.
|
||||
## ``toFloat(x: T): float`` must be defined.
|
||||
result = 0.0
|
||||
var m = mean(x)
|
||||
for i in 0 .. high(x):
|
||||
var diff = x[i] - m
|
||||
for i in items(x):
|
||||
var diff = toFloat(i) - m
|
||||
result = result + diff*diff
|
||||
result = result / toFloat(len(x))
|
||||
|
||||
|
|
@ -372,7 +377,9 @@ when isMainModule and not defined(JS):
|
|||
randomize(seed)
|
||||
for i in 0..SIZE-1:
|
||||
assert buf[i] == random(high(int)), "non deterministic random seeding"
|
||||
echo "random values equal after reseeding"
|
||||
|
||||
when not defined(testing):
|
||||
echo "random values equal after reseeding"
|
||||
|
||||
# Check for no side effect annotation
|
||||
proc mySqrt(num: float): float {.noSideEffect.} =
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ proc getNum*(m: var MersenneTwister): int =
|
|||
return int(y)
|
||||
|
||||
# Test
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var mt = newMersenneTwister(2525)
|
||||
|
||||
for i in 0..99:
|
||||
|
|
|
|||
|
|
@ -518,5 +518,5 @@ proc register*(mimedb: var MimeDB, ext: string, mimetype: string) =
|
|||
|
||||
when isMainModule:
|
||||
var m = newMimetypes()
|
||||
echo m.getMimetype("mp4")
|
||||
echo m.getExt("text/html")
|
||||
assert m.getMimetype("mp4") == "video/mp4"
|
||||
assert m.getExt("text/html") == "html"
|
||||
|
|
|
|||
|
|
@ -88,6 +88,6 @@ proc generatedTime*(oid: Oid): Time =
|
|||
bigEndian32(addr(tmp), addr(dummy))
|
||||
result = Time(tmp)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
let xo = genOid()
|
||||
echo xo.generatedTime
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
|
|||
proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
|
||||
## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to
|
||||
## the process. On Windows ``kill()`` is simply an alias for ``terminate()``.
|
||||
|
||||
|
||||
proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].}
|
||||
## Returns true iff the process `p` is still running. Returns immediately.
|
||||
|
||||
|
|
@ -666,7 +666,7 @@ elif not defined(useNimRtl):
|
|||
data.workingDir = workingDir
|
||||
|
||||
|
||||
when declared(posix_spawn) and not defined(useFork) and
|
||||
when declared(posix_spawn) and not defined(useFork) and
|
||||
not defined(useClone) and not defined(linux):
|
||||
pid = startProcessAuxSpawn(data)
|
||||
else:
|
||||
|
|
@ -823,7 +823,7 @@ elif not defined(useNimRtl):
|
|||
discard execvp(data.sysCommand, data.sysArgs)
|
||||
else:
|
||||
when defined(uClibc):
|
||||
# uClibc environment (OpenWrt included) doesn't have the full execvpe
|
||||
# uClibc environment (OpenWrt included) doesn't have the full execvpe
|
||||
discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
|
||||
else:
|
||||
discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv)
|
||||
|
|
@ -864,9 +864,9 @@ elif not defined(useNimRtl):
|
|||
raiseOsError(osLastError())
|
||||
|
||||
proc kill(p: Process) =
|
||||
if kill(p.id, SIGKILL) != 0'i32:
|
||||
if kill(p.id, SIGKILL) != 0'i32:
|
||||
raiseOsError(osLastError())
|
||||
|
||||
|
||||
proc waitForExit(p: Process, timeout: int = -1): int =
|
||||
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
|
||||
# ``waitPid`` fails if the process is not running anymore. But then
|
||||
|
|
@ -883,7 +883,7 @@ elif not defined(useNimRtl):
|
|||
var ret = waitpid(p.id, p.exitCode, WNOHANG)
|
||||
var b = ret == int(p.id)
|
||||
if b: result = -1
|
||||
if p.exitCode == -3: result = -1
|
||||
if not WIFEXITED(p.exitCode): result = -1
|
||||
else: result = p.exitCode.int shr 8
|
||||
|
||||
proc createStream(stream: var Stream, handle: var FileHandle,
|
||||
|
|
@ -907,7 +907,7 @@ elif not defined(useNimRtl):
|
|||
createStream(p.errStream, p.errHandle, fmRead)
|
||||
return p.errStream
|
||||
|
||||
proc csystem(cmd: cstring): cint {.nodecl, importc: "system",
|
||||
proc csystem(cmd: cstring): cint {.nodecl, importc: "system",
|
||||
header: "<stdlib.h>".}
|
||||
|
||||
proc execCmd(command: string): int =
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ proc close*(my: var CsvParser) {.inline.} =
|
|||
## closes the parser `my` and its associated input stream.
|
||||
lexbase.close(my)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
import os
|
||||
var s = newFileStream(paramStr(1), fmRead)
|
||||
if s == nil: quit("cannot open the file" & paramStr(1))
|
||||
|
|
|
|||
|
|
@ -1330,7 +1330,7 @@ proc renderSQL*(n: SqlNode): string =
|
|||
result = ""
|
||||
ra(n, result, 0)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
echo(renderSQL(parseSQL(newStringStream("""
|
||||
CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
|
||||
CREATE TABLE holidays (
|
||||
|
|
|
|||
|
|
@ -323,8 +323,12 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
|
|||
i = j
|
||||
|
||||
when isMainModule:
|
||||
for k, v in interpolatedFragments("$test{} $this is ${an{ example}} "):
|
||||
echo "(", k, ", \"", v, "\")"
|
||||
import sequtils
|
||||
let input = "$test{} $this is ${an{ example}} "
|
||||
let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
|
||||
(ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
|
||||
assert toSeq(interpolatedFragments(input)) == expected
|
||||
|
||||
var value = 0
|
||||
discard parseHex("0x38", value)
|
||||
assert value == 56
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ proc next*(my: var XmlParser) =
|
|||
my.kind = xmlError
|
||||
my.state = stateNormal
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
import os
|
||||
var s = newFileStream(paramStr(1), fmRead)
|
||||
if s == nil: quit("cannot open the file" & paramStr(1))
|
||||
|
|
|
|||
1770
lib/pure/pegs.nimfix
1770
lib/pure/pegs.nimfix
File diff suppressed because it is too large
Load diff
|
|
@ -1080,7 +1080,7 @@ proc assertListsIdentical(listA, listB: seq[string]) =
|
|||
assert(item == listB[i])
|
||||
i = i + 1
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
when false:
|
||||
var r = open()
|
||||
|
||||
|
|
|
|||
|
|
@ -44,16 +44,13 @@ proc decimalToRoman*(number: range[1..3_999]): string =
|
|||
("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
|
||||
("V", 5), ("IV", 4), ("I", 1)]
|
||||
result = ""
|
||||
var decVal = number
|
||||
var decVal: int = number
|
||||
for key, val in items(romanComposites):
|
||||
while decVal >= val:
|
||||
dec(decVal, val)
|
||||
result.add(key)
|
||||
|
||||
when isMainModule:
|
||||
import math
|
||||
randomize()
|
||||
for i in 1 .. 100:
|
||||
var rnd = 1 + random(3990)
|
||||
assert rnd == rnd.decimalToRoman.romanToDecimal
|
||||
for i in 1 .. 3_999:
|
||||
assert i == i.decimalToRoman.romanToDecimal
|
||||
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ proc contains*(s: Selector, key: SelectorKey): bool =
|
|||
TReadyInfo: ReadyInfo, PSelector: Selector].}
|
||||
|
||||
|
||||
when isMainModule and not defined(nimdoc):
|
||||
when not defined(testing) and isMainModule and not defined(nimdoc):
|
||||
# Select()
|
||||
import sockets
|
||||
type
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ proc close*(smtp: AsyncSmtp) {.async.} =
|
|||
await smtp.sock.send("QUIT\c\L")
|
||||
smtp.sock.close()
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
#var msg = createMessage("Test subject!",
|
||||
# "Hello, my name is dom96.\n What\'s yours?", @["dominik@localhost"])
|
||||
#echo(msg)
|
||||
|
|
|
|||
|
|
@ -168,6 +168,12 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {.
|
|||
result.counter = 0
|
||||
newSeq(result.data, startSize)
|
||||
|
||||
proc clear*(s: StringTableRef, mode: StringTableMode) =
|
||||
## resets a string table to be empty again.
|
||||
s.mode = mode
|
||||
s.counter = 0
|
||||
s.data.setLen(startSize)
|
||||
|
||||
proc newStringTable*(keyValuePairs: varargs[string],
|
||||
mode: StringTableMode): StringTableRef {.
|
||||
rtl, extern: "nst$1WithPairs".} =
|
||||
|
|
@ -227,7 +233,7 @@ proc `$`*(t: StringTableRef): string {.rtl, extern: "nstDollar".} =
|
|||
result = "{:}"
|
||||
else:
|
||||
result = "{"
|
||||
for key, val in pairs(t):
|
||||
for key, val in pairs(t):
|
||||
if result.len > 1: result.add(", ")
|
||||
result.add(key)
|
||||
result.add(": ")
|
||||
|
|
|
|||
|
|
@ -169,14 +169,12 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
|
|||
|
||||
{.pop.}
|
||||
|
||||
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
|
||||
rtl, extern: "nsuStrip".} =
|
||||
## Strips whitespace from `s` and returns the resulting string.
|
||||
proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string
|
||||
{.noSideEffect, rtl, extern: "nsuStrip".} =
|
||||
## Strips `chars` from `s` and returns the resulting string.
|
||||
##
|
||||
## If `leading` is true, leading whitespace is stripped.
|
||||
## If `trailing` is true, trailing whitespace is stripped.
|
||||
const
|
||||
chars: set[char] = Whitespace
|
||||
## If `leading` is true, leading `chars` are stripped.
|
||||
## If `trailing` is true, trailing `chars` are stripped.
|
||||
var
|
||||
first = 0
|
||||
last = len(s)-1
|
||||
|
|
@ -1402,14 +1400,21 @@ when isMainModule:
|
|||
doAssert align("a", 0) == "a"
|
||||
doAssert align("1232", 6) == " 1232"
|
||||
doAssert align("1232", 6, '#') == "##1232"
|
||||
echo wordWrap(""" this is a long text -- muchlongerthan10chars and here
|
||||
it goes""", 10, false)
|
||||
|
||||
let
|
||||
inp = """ this is a long text -- muchlongerthan10chars and here
|
||||
it goes"""
|
||||
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
|
||||
doAssert wordWrap(inp, 10, false) == outp
|
||||
|
||||
doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
|
||||
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11"
|
||||
doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) in
|
||||
["1.0e-11", "1.0e-011"]
|
||||
|
||||
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
|
||||
echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB"
|
||||
echo formatSize(1'i64 shl 31)
|
||||
when not defined(testing):
|
||||
echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB"
|
||||
echo formatSize(1'i64 shl 31)
|
||||
|
||||
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
|
||||
"The cat eats fish."
|
||||
|
|
@ -1426,3 +1431,11 @@ when isMainModule:
|
|||
doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
|
||||
doAssert count("foofoofoo", 'f') == 3
|
||||
doAssert count("foofoofoobar", {'f','b'}) == 4
|
||||
|
||||
doAssert strip(" foofoofoo ") == "foofoofoo"
|
||||
doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
|
||||
doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
|
||||
doAssert strip("stripme but don't strip this stripme",
|
||||
chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == " but don't strip this "
|
||||
doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
|
||||
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
|
||||
|
|
|
|||
|
|
@ -386,8 +386,13 @@ when isMainModule:
|
|||
longishA,
|
||||
longish)"""
|
||||
|
||||
echo "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
|
||||
"fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"]
|
||||
assert "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
|
||||
"fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] ==
|
||||
strutils.unindent """
|
||||
type TMyEnum* = enum
|
||||
fieldA, fieldB,
|
||||
FiledClkad, fieldD,
|
||||
fieldE, longishFieldName"""
|
||||
|
||||
doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
|
||||
|
||||
|
|
@ -395,7 +400,12 @@ when isMainModule:
|
|||
|
||||
doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
|
||||
|
||||
echo subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
|
||||
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
|
||||
assert subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
|
||||
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] ==
|
||||
strutils.unindent """
|
||||
type
|
||||
TEnum = enum
|
||||
fieldNameA, fieldNameB, fieldNameC,
|
||||
fieldNameD"""
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,12 +51,13 @@ else:
|
|||
proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
|
||||
var mode: Termios
|
||||
discard fd.tcgetattr(addr mode)
|
||||
mode.iflag = mode.iflag and not Tcflag(BRKINT or ICRNL or INPCK or ISTRIP or IXON)
|
||||
mode.oflag = mode.oflag and not Tcflag(OPOST)
|
||||
mode.cflag = (mode.cflag and not Tcflag(CSIZE or PARENB)) or CS8
|
||||
mode.lflag = mode.lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG)
|
||||
mode.cc[VMIN] = 1.cuchar
|
||||
mode.cc[VTIME] = 0.cuchar
|
||||
mode.c_iflag = mode.c_iflag and not Tcflag(BRKINT or ICRNL or INPCK or
|
||||
ISTRIP or IXON)
|
||||
mode.c_oflag = mode.c_oflag and not Tcflag(OPOST)
|
||||
mode.c_cflag = (mode.c_cflag and not Tcflag(CSIZE or PARENB)) or CS8
|
||||
mode.c_lflag = mode.c_lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG)
|
||||
mode.c_cc[VMIN] = 1.cuchar
|
||||
mode.c_cc[VTIME] = 0.cuchar
|
||||
discard fd.tcsetattr(time, addr mode)
|
||||
|
||||
proc setCursorPos*(x, y: int) =
|
||||
|
|
@ -375,7 +376,7 @@ when not defined(windows):
|
|||
result = stdin.readChar()
|
||||
discard fd.tcsetattr(TCSADRAIN, addr oldMode)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
system.addQuitProc(resetAttributes)
|
||||
write(stdout, "never mind")
|
||||
eraseLine()
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ type
|
|||
## in the range 0 to 23.
|
||||
monthday*: range[1..31] ## The day of the month, in the range 1 to 31.
|
||||
month*: Month ## The current month.
|
||||
year*: range[-10_000..10_000] ## The current year.
|
||||
year*: int ## The current year.
|
||||
weekday*: WeekDay ## The current day of the week.
|
||||
yearday*: range[0..365] ## The number of days since January 1,
|
||||
## in the range 0 to 365.
|
||||
|
|
@ -379,7 +379,7 @@ when not defined(JS):
|
|||
result.hour = t.hour
|
||||
result.monthday = t.monthday
|
||||
result.month = ord(t.month)
|
||||
result.year = t.year - 1900
|
||||
result.year = cint(t.year - 1900)
|
||||
result.weekday = weekDays[t.weekday]
|
||||
result.yearday = t.yearday
|
||||
result.isdst = if t.isDST: 1 else: 0
|
||||
|
|
@ -764,7 +764,7 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
|
|||
info.monthday = value[j..j+1].parseInt()
|
||||
j += 2
|
||||
of "ddd":
|
||||
case value[j..j+2].toLower():
|
||||
case value[j..j+2].toLower()
|
||||
of "sun":
|
||||
info.weekday = dSun
|
||||
of "mon":
|
||||
|
|
@ -1036,8 +1036,6 @@ when isMainModule:
|
|||
# Tue 19 Jan 03:14:07 GMT 2038
|
||||
|
||||
var t = getGMTime(fromSeconds(2147483647))
|
||||
echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
|
||||
echo t.format("ddd ddMMMhhmmssZZZyyyy")
|
||||
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
|
||||
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
|
||||
|
||||
|
|
@ -1070,46 +1068,48 @@ when isMainModule:
|
|||
var f = "dddd at hh:mmtt on MMM d, yyyy"
|
||||
assert($s.parse(f) == "Tue Dec 15 09:04:00 2015")
|
||||
# ANSIC = "Mon Jan _2 15:04:05 2006"
|
||||
s = "Mon Jan 2 15:04:05 2006"
|
||||
f = "ddd MMM d HH:mm:ss yyyy"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
s = "Thu Jan 12 15:04:05 2006"
|
||||
f = "ddd MMM dd HH:mm:ss yyyy"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
|
||||
s = "Mon Jan 2 15:04:05 MST 2006"
|
||||
f = "ddd MMM d HH:mm:ss ZZZ yyyy"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
s = "Thu Jan 12 15:04:05 MST 2006"
|
||||
f = "ddd MMM dd HH:mm:ss ZZZ yyyy"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
|
||||
s = "Mon Jan 02 15:04:05 -07:00 2006"
|
||||
s = "Thu Jan 12 15:04:05 -07:00 2006"
|
||||
f = "ddd MMM dd HH:mm:ss zzz yyyy"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC822 = "02 Jan 06 15:04 MST"
|
||||
s = "02 Jan 06 15:04 MST"
|
||||
s = "12 Jan 16 15:04 MST"
|
||||
f = "dd MMM yy HH:mm ZZZ"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:00 2006")
|
||||
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
|
||||
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
|
||||
s = "02 Jan 06 15:04 -07:00"
|
||||
s = "12 Jan 16 15:04 -07:00"
|
||||
f = "dd MMM yy HH:mm zzz"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:00 2006")
|
||||
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
|
||||
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
|
||||
s = "Monday, 02-Jan-06 15:04:05 MST"
|
||||
s = "Monday, 12-Jan-06 15:04:05 MST"
|
||||
f = "dddd, dd-MMM-yy HH:mm:ss ZZZ"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
s = "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
s = "Thu, 12 Jan 2006 15:04:05 MST"
|
||||
f = "ddd, dd MMM yyyy HH:mm:ss ZZZ"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
|
||||
s = "Mon, 02 Jan 2006 15:04:05 -07:00"
|
||||
s = "Thu, 12 Jan 2006 15:04:05 -07:00"
|
||||
f = "ddd, dd MMM yyyy HH:mm:ss zzz"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC3339 = "2006-01-02T15:04:05Z07:00"
|
||||
s = "2006-01-02T15:04:05Z-07:00"
|
||||
s = "2006-01-12T15:04:05Z-07:00"
|
||||
f = "yyyy-MM-ddTHH:mm:ssZzzz"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
|
||||
s = "2006-01-02T15:04:05.999999999Z-07:00"
|
||||
s = "2006-01-12T15:04:05.999999999Z-07:00"
|
||||
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
|
||||
assert($s.parse(f) == "Mon Jan 2 15:04:05 2006")
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# Kitchen = "3:04PM"
|
||||
s = "3:04PM"
|
||||
f = "h:mmtt"
|
||||
echo "Kitchen: " & $s.parse(f)
|
||||
assert "15:04:00" in $s.parse(f)
|
||||
when not defined(testing):
|
||||
echo "Kitchen: " & $s.parse(f)
|
||||
|
|
|
|||
|
|
@ -105,6 +105,31 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
|||
result = Rune(ord(s[i]))
|
||||
when doInc: inc(i)
|
||||
|
||||
proc validateUtf8*(s: string): int =
|
||||
## returns the position of the invalid byte in ``s`` if the string ``s`` does
|
||||
## not hold valid UTF-8 data. Otherwise -1 is returned.
|
||||
var i = 0
|
||||
let L = s.len
|
||||
while i < L:
|
||||
if ord(s[i]) <=% 127:
|
||||
inc(i)
|
||||
elif ord(s[i]) shr 5 == 0b110:
|
||||
if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
|
||||
else: return i
|
||||
elif ord(s[i]) shr 4 == 0b1110:
|
||||
if i+2 < L and ord(s[i+1]) shr 6 == 0b10 and ord(s[i+2]) shr 6 == 0b10:
|
||||
inc i, 3
|
||||
else: return i
|
||||
elif ord(s[i]) shr 3 == 0b11110:
|
||||
if i+3 < L and ord(s[i+1]) shr 6 == 0b10 and
|
||||
ord(s[i+2]) shr 6 == 0b10 and
|
||||
ord(s[i+3]) shr 6 == 0b10:
|
||||
inc i, 4
|
||||
else: return i
|
||||
else:
|
||||
return i
|
||||
return -1
|
||||
|
||||
proc runeAt*(s: string, i: Natural): Rune =
|
||||
## returns the unicode character in `s` at byte index `i`
|
||||
fastRuneAt(s, i, result, false)
|
||||
|
|
|
|||
|
|
@ -70,5 +70,5 @@ proc unidecode*(s: string): string =
|
|||
|
||||
when isMainModule:
|
||||
loadUnidecodeTable("lib/pure/unidecode/unidecode.dat")
|
||||
echo unidecode("Äußerst")
|
||||
assert unidecode("Äußerst") == "Ausserst"
|
||||
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ proc parseAuthority(authority: string, result: var Uri) =
|
|||
while true:
|
||||
case authority[i]
|
||||
of '@':
|
||||
result.password = result.port
|
||||
result.port = ""
|
||||
result.username = result.hostname
|
||||
result.hostname = ""
|
||||
swap result.password, result.port
|
||||
result.port.setLen(0)
|
||||
swap result.username, result.hostname
|
||||
result.hostname.setLen(0)
|
||||
inPort = false
|
||||
of ':':
|
||||
inPort = true
|
||||
|
|
@ -75,7 +75,7 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
|
|||
# The 'mailto' scheme's PATH actually contains the hostname/username
|
||||
if result.scheme.toLower == "mailto":
|
||||
parseAuthority(result.path, result)
|
||||
result.path = ""
|
||||
result.path.setLen(0)
|
||||
|
||||
if uri[i] == '?':
|
||||
i.inc # Skip '?'
|
||||
|
|
@ -85,13 +85,21 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
|
|||
i.inc # Skip '#'
|
||||
i.inc parseUntil(uri, result.anchor, {}, i)
|
||||
|
||||
proc initUri(): Uri =
|
||||
proc initUri*(): Uri =
|
||||
## Initializes a URI.
|
||||
result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
|
||||
path: "", query: "", anchor: "")
|
||||
|
||||
proc parseUri*(uri: string): Uri =
|
||||
## Parses a URI.
|
||||
result = initUri()
|
||||
proc resetUri(uri: var Uri) =
|
||||
for f in uri.fields:
|
||||
when f is string:
|
||||
f.setLen(0)
|
||||
else:
|
||||
f = false
|
||||
|
||||
proc parseUri*(uri: string, result: var Uri) =
|
||||
## Parses a URI. The `result` variable will be cleared before.
|
||||
resetUri(result)
|
||||
|
||||
var i = 0
|
||||
|
||||
|
|
@ -105,7 +113,7 @@ proc parseUri*(uri: string): Uri =
|
|||
if uri[i] != ':':
|
||||
# Assume this is a reference URI (relative URI)
|
||||
i = 0
|
||||
result.scheme = ""
|
||||
result.scheme.setLen(0)
|
||||
parsePath(uri, i, result)
|
||||
return
|
||||
i.inc # Skip ':'
|
||||
|
|
@ -124,6 +132,11 @@ proc parseUri*(uri: string): Uri =
|
|||
# Path
|
||||
parsePath(uri, i, result)
|
||||
|
||||
proc parseUri*(uri: string): Uri =
|
||||
## Parses a URI and returns it.
|
||||
result = initUri()
|
||||
parseUri(uri, result)
|
||||
|
||||
proc removeDotSegments(path: string): string =
|
||||
var collection: seq[string] = @[]
|
||||
let endsWithSlash = path[path.len-1] == '/'
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ proc loadXMLFile*(path: string): PDocument =
|
|||
return loadXMLStream(s)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
var xml = loadXMLFile("nim/xmldom/test.xml")
|
||||
#echo(xml.getElementsByTagName("m:test2")[0].namespaceURI)
|
||||
#echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ proc loadXml*(path: string): XmlNode =
|
|||
result = loadXml(path, errors)
|
||||
if errors.len > 0: raiseInvalidXml(errors)
|
||||
|
||||
when isMainModule:
|
||||
when not defined(testing) and isMainModule:
|
||||
import os
|
||||
|
||||
var errors: seq[string] = @[]
|
||||
|
|
|
|||
|
|
@ -353,5 +353,6 @@ proc findAll*(n: XmlNode, tag: string): seq[XmlNode] =
|
|||
findAll(n, tag, result)
|
||||
|
||||
when isMainModule:
|
||||
assert """<a href="http://nim-lang.org">Nim rules.</a>""" ==
|
||||
let link = "http://nim-lang.org"
|
||||
assert """<a href="""" & escape(link) & """">Nim rules.</a>""" ==
|
||||
$(<>a(href="http://nim-lang.org", newText("Nim rules.")))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue