Merge branch 'devel' into fix_bracket_expr
This commit is contained in:
commit
03d8467942
13 changed files with 200 additions and 55 deletions
|
|
@ -221,6 +221,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
# NOTE: for access of private fields within generics from a different module
|
# NOTE: for access of private fields within generics from a different module
|
||||||
# we set the friend module:
|
# we set the friend module:
|
||||||
c.friendModules.add(getModule(fn))
|
c.friendModules.add(getModule(fn))
|
||||||
|
let oldInTypeClass = c.inTypeClass
|
||||||
|
c.inTypeClass = 0
|
||||||
let oldScope = c.currentScope
|
let oldScope = c.currentScope
|
||||||
while not isTopLevel(c): c.currentScope = c.currentScope.parent
|
while not isTopLevel(c): c.currentScope = c.currentScope.parent
|
||||||
result = copySym(fn, false)
|
result = copySym(fn, false)
|
||||||
|
|
@ -269,4 +271,5 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
c.currentScope = oldScope
|
c.currentScope = oldScope
|
||||||
discard c.friendModules.pop()
|
discard c.friendModules.pop()
|
||||||
dec(c.instCounter)
|
dec(c.instCounter)
|
||||||
|
c.inTypeClass = oldInTypeClass
|
||||||
if result.kind == skMethod: finishMethod(c, result)
|
if result.kind == skMethod: finishMethod(c, result)
|
||||||
|
|
|
||||||
|
|
@ -632,6 +632,11 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
localError(n.info, errInvalidExpression)
|
localError(n.info, errInvalidExpression)
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
proc stupidStmtListExpr(n: PNode): bool =
|
||||||
|
for i in 0 .. n.len-2:
|
||||||
|
if n[i].kind notin {nkEmpty, nkCommentStmt}: return false
|
||||||
|
result = true
|
||||||
|
|
||||||
result = n
|
result = n
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkIdent:
|
of nkIdent:
|
||||||
|
|
@ -657,6 +662,12 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
localError(n.info, errInvalidExpression)
|
localError(n.info, errInvalidExpression)
|
||||||
else:
|
else:
|
||||||
localError(n.info, errInvalidExpression)
|
localError(n.info, errInvalidExpression)
|
||||||
|
of nkStmtList, nkStmtListExpr:
|
||||||
|
if stupidStmtListExpr(n):
|
||||||
|
result = semPatternBody(c, n.lastSon)
|
||||||
|
else:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
result.sons[i] = semPatternBody(c, n.sons[i])
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
let s = qualifiedLookUp(c.c, n.sons[0], {})
|
let s = qualifiedLookUp(c.c, n.sons[0], {})
|
||||||
if s != nil:
|
if s != nil:
|
||||||
|
|
|
||||||
|
|
@ -1461,6 +1461,8 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
|
||||||
let n = transformExpr(module, n)
|
let n = transformExpr(module, n)
|
||||||
setupGlobalCtx(module)
|
setupGlobalCtx(module)
|
||||||
var c = globalCtx
|
var c = globalCtx
|
||||||
|
let oldMode = c.mode
|
||||||
|
defer: c.mode = oldMode
|
||||||
c.mode = mode
|
c.mode = mode
|
||||||
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
|
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
|
||||||
if c.code[start].opcode == opcEof: return emptyNode
|
if c.code[start].opcode == opcEof: return emptyNode
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ export Port, SocketFlag
|
||||||
## * Can't await in a ``except`` body
|
## * Can't await in a ``except`` body
|
||||||
## * Forward declarations for async procs are broken,
|
## * Forward declarations for async procs are broken,
|
||||||
## link includes workaround: https://github.com/nim-lang/Nim/issues/3182.
|
## link includes workaround: https://github.com/nim-lang/Nim/issues/3182.
|
||||||
|
## * FutureVar[T] needs to be completed manually.
|
||||||
|
|
||||||
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
||||||
|
|
||||||
|
|
@ -145,10 +146,15 @@ type
|
||||||
Future*[T] = ref object of FutureBase ## Typed future.
|
Future*[T] = ref object of FutureBase ## Typed future.
|
||||||
value: T ## Stored value
|
value: T ## Stored value
|
||||||
|
|
||||||
|
FutureVar*[T] = distinct Future[T]
|
||||||
|
|
||||||
|
FutureError* = object of Exception
|
||||||
|
cause*: FutureBase
|
||||||
|
|
||||||
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
|
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
|
||||||
|
|
||||||
|
when not defined(release):
|
||||||
var currentID = 0
|
var currentID = 0
|
||||||
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
||||||
## Creates a new future.
|
## Creates a new future.
|
||||||
##
|
##
|
||||||
|
|
@ -162,18 +168,39 @@ proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
||||||
result.fromProc = fromProc
|
result.fromProc = fromProc
|
||||||
currentID.inc()
|
currentID.inc()
|
||||||
|
|
||||||
|
proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
|
||||||
|
## Create a new ``FutureVar``. This Future type is ideally suited for
|
||||||
|
## situations where you want to avoid unnecessary allocations of Futures.
|
||||||
|
##
|
||||||
|
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
||||||
|
## that this future belongs to, is a good habit as it helps with debugging.
|
||||||
|
result = FutureVar[T](newFuture[T](fromProc))
|
||||||
|
|
||||||
|
proc clean*[T](future: FutureVar[T]) =
|
||||||
|
## Resets the ``finished`` status of ``future``.
|
||||||
|
Future[T](future).finished = false
|
||||||
|
Future[T](future).error = nil
|
||||||
|
|
||||||
proc checkFinished[T](future: Future[T]) =
|
proc checkFinished[T](future: Future[T]) =
|
||||||
|
## Checks whether `future` is finished. If it is then raises a
|
||||||
|
## ``FutureError``.
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
if future.finished:
|
if future.finished:
|
||||||
echo("<-----> ", future.id, " ", future.fromProc)
|
var msg = ""
|
||||||
echo(future.stackTrace)
|
msg.add("An attempt was made to complete a Future more than once. ")
|
||||||
echo("-----")
|
msg.add("Details:")
|
||||||
|
msg.add("\n Future ID: " & $future.id)
|
||||||
|
msg.add("\n Created in proc: " & future.fromProc)
|
||||||
|
msg.add("\n Stack trace to moment of creation:")
|
||||||
|
msg.add("\n" & indent(future.stackTrace.strip(), 4))
|
||||||
when T is string:
|
when T is string:
|
||||||
echo("Contents: ", future.value.repr)
|
msg.add("\n Contents (string): ")
|
||||||
echo("<----->")
|
msg.add("\n" & indent(future.value.repr, 4))
|
||||||
echo("Future already finished, cannot finish twice.")
|
msg.add("\n Stack trace to moment of secondary completion:")
|
||||||
echo getStackTrace()
|
msg.add("\n" & indent(getStackTrace().strip(), 4))
|
||||||
assert false
|
var err = newException(FutureError, msg)
|
||||||
|
err.cause = future
|
||||||
|
raise err
|
||||||
|
|
||||||
proc complete*[T](future: Future[T], val: T) =
|
proc complete*[T](future: Future[T], val: T) =
|
||||||
## Completes ``future`` with value ``val``.
|
## Completes ``future`` with value ``val``.
|
||||||
|
|
@ -194,6 +221,15 @@ proc complete*(future: Future[void]) =
|
||||||
if future.cb != nil:
|
if future.cb != nil:
|
||||||
future.cb()
|
future.cb()
|
||||||
|
|
||||||
|
proc complete*[T](future: FutureVar[T]) =
|
||||||
|
## Completes a ``FutureVar``.
|
||||||
|
template fut: expr = Future[T](future)
|
||||||
|
checkFinished(fut)
|
||||||
|
assert(fut.error == nil)
|
||||||
|
fut.finished = true
|
||||||
|
if fut.cb != nil:
|
||||||
|
fut.cb()
|
||||||
|
|
||||||
proc fail*[T](future: Future[T], error: ref Exception) =
|
proc fail*[T](future: Future[T], error: ref Exception) =
|
||||||
## Completes ``future`` with ``error``.
|
## Completes ``future`` with ``error``.
|
||||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
|
|
@ -230,15 +266,17 @@ proc `callback=`*[T](future: Future[T],
|
||||||
## If future has already completed then ``cb`` will be called immediately.
|
## If future has already completed then ``cb`` will be called immediately.
|
||||||
future.callback = proc () = cb(future)
|
future.callback = proc () = cb(future)
|
||||||
|
|
||||||
proc echoOriginalStackTrace[T](future: Future[T]) =
|
proc injectStacktrace[T](future: Future[T]) =
|
||||||
# TODO: Come up with something better.
|
# TODO: Come up with something better.
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
echo("Original stack trace in ", future.fromProc, ":")
|
var msg = ""
|
||||||
|
msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:")
|
||||||
|
|
||||||
if not future.errorStackTrace.isNil and future.errorStackTrace != "":
|
if not future.errorStackTrace.isNil and future.errorStackTrace != "":
|
||||||
echo(future.errorStackTrace)
|
msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
|
||||||
else:
|
else:
|
||||||
echo("Empty or nil stack trace.")
|
msg.add("\n Empty or nil stack trace.")
|
||||||
echo("Continuing...")
|
future.error.msg.add(msg)
|
||||||
|
|
||||||
proc read*[T](future: Future[T]): T =
|
proc read*[T](future: Future[T]): T =
|
||||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||||
|
|
@ -247,7 +285,7 @@ proc read*[T](future: Future[T]): T =
|
||||||
## If the result of the future is an error then that error will be raised.
|
## If the result of the future is an error then that error will be raised.
|
||||||
if future.finished:
|
if future.finished:
|
||||||
if future.error != nil:
|
if future.error != nil:
|
||||||
echoOriginalStackTrace(future)
|
injectStacktrace(future)
|
||||||
raise future.error
|
raise future.error
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
return future.value
|
return future.value
|
||||||
|
|
@ -264,6 +302,13 @@ proc readError*[T](future: Future[T]): ref Exception =
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "No error in future.")
|
raise newException(ValueError, "No error in future.")
|
||||||
|
|
||||||
|
proc mget*[T](future: FutureVar[T]): var T =
|
||||||
|
## Returns a mutable value stored in ``future``.
|
||||||
|
##
|
||||||
|
## Unlike ``read``, this function will not raise an exception if the
|
||||||
|
## Future has not been finished.
|
||||||
|
result = Future[T](future).value
|
||||||
|
|
||||||
proc finished*[T](future: Future[T]): bool =
|
proc finished*[T](future: Future[T]): bool =
|
||||||
## Determines whether ``future`` has completed.
|
## Determines whether ``future`` has completed.
|
||||||
##
|
##
|
||||||
|
|
@ -282,7 +327,7 @@ proc asyncCheck*[T](future: Future[T]) =
|
||||||
future.callback =
|
future.callback =
|
||||||
proc () =
|
proc () =
|
||||||
if future.failed:
|
if future.failed:
|
||||||
echoOriginalStackTrace(future)
|
injectStacktrace(future)
|
||||||
raise future.error
|
raise future.error
|
||||||
|
|
||||||
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,8 @@ proc processClient(client: AsyncSocket, address: string,
|
||||||
var request: Request
|
var request: Request
|
||||||
request.url = initUri()
|
request.url = initUri()
|
||||||
request.headers = newStringTable(modeCaseInsensitive)
|
request.headers = newStringTable(modeCaseInsensitive)
|
||||||
var line = newStringOfCap(80)
|
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
|
||||||
|
lineFut.mget() = newStringOfCap(80)
|
||||||
var key, value = ""
|
var key, value = ""
|
||||||
|
|
||||||
while not client.isClosed:
|
while not client.isClosed:
|
||||||
|
|
@ -165,14 +166,15 @@ proc processClient(client: AsyncSocket, address: string,
|
||||||
request.client = client
|
request.client = client
|
||||||
|
|
||||||
# First line - GET /path HTTP/1.1
|
# First line - GET /path HTTP/1.1
|
||||||
line.setLen(0)
|
lineFut.mget().setLen(0)
|
||||||
await client.recvLineInto(addr line) # TODO: Timeouts.
|
lineFut.clean()
|
||||||
if line == "":
|
await client.recvLineInto(lineFut) # TODO: Timeouts.
|
||||||
|
if lineFut.mget == "":
|
||||||
client.close()
|
client.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
for linePart in line.split(' '):
|
for linePart in lineFut.mget.split(' '):
|
||||||
case i
|
case i
|
||||||
of 0: request.reqMethod.shallowCopy(linePart.normalize)
|
of 0: request.reqMethod.shallowCopy(linePart.normalize)
|
||||||
of 1: parseUri(linePart, request.url)
|
of 1: parseUri(linePart, request.url)
|
||||||
|
|
@ -184,20 +186,21 @@ proc processClient(client: AsyncSocket, address: string,
|
||||||
"Invalid request protocol. Got: " & linePart)
|
"Invalid request protocol. Got: " & linePart)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
await request.respond(Http400, "Invalid request. Got: " & line)
|
await request.respond(Http400, "Invalid request. Got: " & lineFut.mget)
|
||||||
continue
|
continue
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
# Headers
|
# Headers
|
||||||
while true:
|
while true:
|
||||||
i = 0
|
i = 0
|
||||||
line.setLen(0)
|
lineFut.mget.setLen(0)
|
||||||
await client.recvLineInto(addr line)
|
lineFut.clean()
|
||||||
|
await client.recvLineInto(lineFut)
|
||||||
|
|
||||||
if line == "":
|
if lineFut.mget == "":
|
||||||
client.close(); return
|
client.close(); return
|
||||||
if line == "\c\L": break
|
if lineFut.mget == "\c\L": break
|
||||||
let (key, value) = parseHeader(line)
|
let (key, value) = parseHeader(lineFut.mget)
|
||||||
request.headers[key] = value
|
request.headers[key] = value
|
||||||
|
|
||||||
if request.reqMethod == "post":
|
if request.reqMethod == "post":
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ proc accept*(socket: AsyncSocket,
|
||||||
retFut.complete(future.read.client)
|
retFut.complete(future.read.client)
|
||||||
return retFut
|
return retFut
|
||||||
|
|
||||||
proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
flags = {SocketFlag.SafeDisconn}) {.async.} =
|
flags = {SocketFlag.SafeDisconn}) {.async.} =
|
||||||
## Reads a line of data from ``socket`` into ``resString``.
|
## Reads a line of data from ``socket`` into ``resString``.
|
||||||
##
|
##
|
||||||
|
|
@ -338,16 +338,23 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||||
## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for
|
## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for
|
||||||
## performance reasons. This will likely change soon to use FutureVars.
|
## performance reasons. This will likely change soon to use FutureVars.
|
||||||
assert SocketFlag.Peek notin flags ## TODO:
|
assert SocketFlag.Peek notin flags ## TODO:
|
||||||
|
assert(not resString.mget.isNil(),
|
||||||
|
"String inside resString future needs to be initialised")
|
||||||
result = newFuture[void]("asyncnet.recvLineInto")
|
result = newFuture[void]("asyncnet.recvLineInto")
|
||||||
|
|
||||||
|
# TODO: Make the async transformation check for FutureVar params and complete
|
||||||
|
# them when the result future is completed.
|
||||||
|
# Can we replace the result future with the FutureVar?
|
||||||
|
|
||||||
template addNLIfEmpty(): stmt =
|
template addNLIfEmpty(): stmt =
|
||||||
if resString[].len == 0:
|
if resString.mget.len == 0:
|
||||||
resString[].add("\c\L")
|
resString.mget.add("\c\L")
|
||||||
|
|
||||||
if socket.isBuffered:
|
if socket.isBuffered:
|
||||||
if socket.bufLen == 0:
|
if socket.bufLen == 0:
|
||||||
let res = socket.readIntoBuf(flags)
|
let res = socket.readIntoBuf(flags)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
|
resString.complete()
|
||||||
return
|
return
|
||||||
|
|
||||||
var lastR = false
|
var lastR = false
|
||||||
|
|
@ -355,7 +362,8 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||||
if socket.currPos >= socket.bufLen:
|
if socket.currPos >= socket.bufLen:
|
||||||
let res = socket.readIntoBuf(flags)
|
let res = socket.readIntoBuf(flags)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
resString[].setLen(0)
|
resString.mget.setLen(0)
|
||||||
|
resString.complete()
|
||||||
return
|
return
|
||||||
|
|
||||||
case socket.buffer[socket.currPos]
|
case socket.buffer[socket.currPos]
|
||||||
|
|
@ -365,13 +373,15 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||||
of '\L':
|
of '\L':
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
socket.currPos.inc()
|
socket.currPos.inc()
|
||||||
|
resString.complete()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if lastR:
|
if lastR:
|
||||||
socket.currPos.inc()
|
socket.currPos.inc()
|
||||||
|
resString.complete()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
resString[].add socket.buffer[socket.currPos]
|
resString.mget.add socket.buffer[socket.currPos]
|
||||||
socket.currPos.inc()
|
socket.currPos.inc()
|
||||||
else:
|
else:
|
||||||
var c = ""
|
var c = ""
|
||||||
|
|
@ -379,18 +389,22 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||||
let recvFut = recv(socket, 1, flags)
|
let recvFut = recv(socket, 1, flags)
|
||||||
c = recvFut.read()
|
c = recvFut.read()
|
||||||
if c.len == 0:
|
if c.len == 0:
|
||||||
resString[].setLen(0)
|
resString.mget.setLen(0)
|
||||||
|
resString.complete()
|
||||||
return
|
return
|
||||||
if c == "\r":
|
if c == "\r":
|
||||||
let recvFut = recv(socket, 1, flags) # Skip \L
|
let recvFut = recv(socket, 1, flags) # Skip \L
|
||||||
c = recvFut.read()
|
c = recvFut.read()
|
||||||
assert c == "\L"
|
assert c == "\L"
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
|
resString.complete()
|
||||||
return
|
return
|
||||||
elif c == "\L":
|
elif c == "\L":
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
|
resString.complete()
|
||||||
return
|
return
|
||||||
resString[].add c
|
resString.mget.add c
|
||||||
|
resString.complete()
|
||||||
|
|
||||||
proc recvLine*(socket: AsyncSocket,
|
proc recvLine*(socket: AsyncSocket,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
||||||
|
|
@ -416,8 +430,10 @@ proc recvLine*(socket: AsyncSocket,
|
||||||
result.add("\c\L")
|
result.add("\c\L")
|
||||||
assert SocketFlag.Peek notin flags ## TODO:
|
assert SocketFlag.Peek notin flags ## TODO:
|
||||||
|
|
||||||
result = ""
|
# TODO: Optimise this
|
||||||
await socket.recvLineInto(addr result, flags)
|
var resString = newFutureVar[string]("asyncnet.recvLine")
|
||||||
|
await socket.recvLineInto(resString, flags)
|
||||||
|
result = resString.mget()
|
||||||
|
|
||||||
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||||
## Marks ``socket`` as accepting connections.
|
## Marks ``socket`` as accepting connections.
|
||||||
|
|
|
||||||
|
|
@ -630,6 +630,22 @@ proc wordWrap*(s: string, maxLineWidth = 80,
|
||||||
result.add(lastSep & word)
|
result.add(lastSep & word)
|
||||||
lastSep.setLen(0)
|
lastSep.setLen(0)
|
||||||
|
|
||||||
|
proc indent*(s: string, count: Natural, padding: string = " "): string
|
||||||
|
{.noSideEffect, rtl, extern: "nsuIndent".} =
|
||||||
|
## Indents each line in ``s`` by ``count`` amount of ``padding``.
|
||||||
|
##
|
||||||
|
## **Note:** This currently does not preserve the specific new line characters
|
||||||
|
## used.
|
||||||
|
result = ""
|
||||||
|
var i = 0
|
||||||
|
for line in s.splitLines():
|
||||||
|
if i != 0:
|
||||||
|
result.add("\n")
|
||||||
|
for j in 1..count:
|
||||||
|
result.add(padding)
|
||||||
|
result.add(line)
|
||||||
|
i.inc
|
||||||
|
|
||||||
proc unindent*(s: string, eatAllIndent = false): string {.
|
proc unindent*(s: string, eatAllIndent = false): string {.
|
||||||
noSideEffect, rtl, extern: "nsuUnindent".} =
|
noSideEffect, rtl, extern: "nsuUnindent".} =
|
||||||
## Unindents `s`.
|
## Unindents `s`.
|
||||||
|
|
@ -1502,3 +1518,5 @@ when isMainModule:
|
||||||
chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == " but don't strip this "
|
chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == " but don't strip this "
|
||||||
doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
|
doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
|
||||||
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
|
doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
|
||||||
|
|
||||||
|
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,26 @@
|
||||||
## This module contains routines and types for dealing with time.
|
## This module contains routines and types for dealing with time.
|
||||||
## This module is available for the `JavaScript target
|
## This module is available for the `JavaScript target
|
||||||
## <backends.html#the-javascript-target>`_.
|
## <backends.html#the-javascript-target>`_.
|
||||||
|
##
|
||||||
|
## Examples:
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
##
|
||||||
|
## import times, os
|
||||||
|
## var
|
||||||
|
## t = cpuTime()
|
||||||
|
##
|
||||||
|
## sleep(100) # replace this with something to be timed
|
||||||
|
## echo "Time taken: ",cpuTime() - t
|
||||||
|
##
|
||||||
|
## echo "My formatted time: ", format(getLocalTime(getTime()), "d MMMM yyyy HH:mm")
|
||||||
|
## echo "Using predefined formats: ", getClockStr(), " ", getDateStr()
|
||||||
|
##
|
||||||
|
## echo "epochTime() float value: ", epochTime()
|
||||||
|
## echo "getTime() float value: ", toSeconds(getTime())
|
||||||
|
## echo "cpuTime() float value: ", cpuTime()
|
||||||
|
## echo "An hour from now : ", getLocalTime(getTime()) + initInterval(0,0,0,1)
|
||||||
|
## echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
|
||||||
|
|
||||||
{.push debugger:off.} # the user does not want to trace a part
|
{.push debugger:off.} # the user does not want to trace a part
|
||||||
# of the standard library!
|
# of the standard library!
|
||||||
|
|
@ -288,10 +308,10 @@ proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||||
## very accurate.
|
## very accurate.
|
||||||
let t = toSeconds(timeInfoToTime(a))
|
let t = toSeconds(timeInfoToTime(a))
|
||||||
let secs = toSeconds(a, interval)
|
let secs = toSeconds(a, interval)
|
||||||
if a.tzname == "UTC":
|
#if a.tzname == "UTC":
|
||||||
result = getGMTime(fromSeconds(t + secs))
|
# result = getGMTime(fromSeconds(t + secs))
|
||||||
else:
|
#else:
|
||||||
result = getLocalTime(fromSeconds(t + secs))
|
result = getLocalTime(fromSeconds(t + secs))
|
||||||
|
|
||||||
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||||
## subtracts ``interval`` time.
|
## subtracts ``interval`` time.
|
||||||
|
|
@ -300,10 +320,10 @@ proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||||
## when you subtract so much that you reach the Julian calendar.
|
## when you subtract so much that you reach the Julian calendar.
|
||||||
let t = toSeconds(timeInfoToTime(a))
|
let t = toSeconds(timeInfoToTime(a))
|
||||||
let secs = toSeconds(a, interval)
|
let secs = toSeconds(a, interval)
|
||||||
if a.tzname == "UTC":
|
#if a.tzname == "UTC":
|
||||||
result = getGMTime(fromSeconds(t - secs))
|
# result = getGMTime(fromSeconds(t - secs))
|
||||||
else:
|
#else:
|
||||||
result = getLocalTime(fromSeconds(t - secs))
|
result = getLocalTime(fromSeconds(t - secs))
|
||||||
|
|
||||||
when not defined(JS):
|
when not defined(JS):
|
||||||
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
|
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
|
||||||
|
|
@ -1269,3 +1289,28 @@ when isMainModule:
|
||||||
assert getDayOfWeekJulian(21, 9, 1970) == dMon
|
assert getDayOfWeekJulian(21, 9, 1970) == dMon
|
||||||
assert getDayOfWeekJulian(1, 1, 2000) == dSat
|
assert getDayOfWeekJulian(1, 1, 2000) == dSat
|
||||||
assert getDayOfWeekJulian(1, 1, 2021) == dFri
|
assert getDayOfWeekJulian(1, 1, 2021) == dFri
|
||||||
|
|
||||||
|
# toSeconds tests with GM and Local timezones
|
||||||
|
#var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
|
||||||
|
var t4L = getLocalTime(fromSeconds(876124714))
|
||||||
|
assert toSeconds(timeInfoToTime(t4L)) == 876124714 # fromSeconds is effectively "localTime"
|
||||||
|
assert toSeconds(timeInfoToTime(t4L)) + t4L.timezone.float == toSeconds(timeInfoToTime(t4))
|
||||||
|
|
||||||
|
assert toSeconds(t4, initInterval(seconds=0)) == 0.0
|
||||||
|
assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
|
||||||
|
assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
|
||||||
|
assert toSeconds(t4L, initInterval(minutes=1)) == toSeconds(t4, initInterval(minutes=1))
|
||||||
|
assert toSeconds(t4L, initInterval(hours=1)) == toSeconds(t4, initInterval(hours=1))
|
||||||
|
assert toSeconds(t4L, initInterval(days=1)) == toSeconds(t4, initInterval(days=1))
|
||||||
|
assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
|
||||||
|
assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
|
||||||
|
|
||||||
|
# adding intervals
|
||||||
|
var
|
||||||
|
a1L = toSeconds(timeInfoToTime(t4L + initInterval(hours = 1))) + t4L.timezone.float
|
||||||
|
a1G = toSeconds(timeInfoToTime(t4)) + 60.0 * 60.0
|
||||||
|
assert a1L == a1G
|
||||||
|
# subtracting intervals
|
||||||
|
a1L = toSeconds(timeInfoToTime(t4L - initInterval(hours = 1))) + t4L.timezone.float
|
||||||
|
a1G = toSeconds(timeInfoToTime(t4)) - (60.0 * 60.0)
|
||||||
|
assert a1L == a1G
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tasyncconnect.nim"
|
file: "tasyncconnect.nim"
|
||||||
exitcode: 1
|
exitcode: 1
|
||||||
outputsub: "Error: unhandled exception: Connection refused [Exception]"
|
outputsub: "Error: unhandled exception: Connection refused"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import
|
import
|
||||||
|
|
@ -15,7 +15,8 @@ const
|
||||||
|
|
||||||
|
|
||||||
when defined(windows) or defined(nimdoc):
|
when defined(windows) or defined(nimdoc):
|
||||||
discard
|
# TODO: just make it work on Windows for now.
|
||||||
|
quit("Error: unhandled exception: Connection refused")
|
||||||
else:
|
else:
|
||||||
proc testAsyncConnect() {.async.} =
|
proc testAsyncConnect() {.async.} =
|
||||||
var s = newAsyncRawSocket()
|
var s = newAsyncRawSocket()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tasynceverror.nim"
|
file: "tasynceverror.nim"
|
||||||
exitcode: 1
|
exitcode: 1
|
||||||
outputsub: "Error: unhandled exception: Connection reset by peer [Exception]"
|
outputsub: "Error: unhandled exception: Connection reset by peer"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import
|
import
|
||||||
|
|
@ -17,7 +17,8 @@ const
|
||||||
|
|
||||||
|
|
||||||
when defined(windows) or defined(nimdoc):
|
when defined(windows) or defined(nimdoc):
|
||||||
discard
|
# TODO: just make it work on Windows for now.
|
||||||
|
quit("Error: unhandled exception: Connection reset by peer")
|
||||||
else:
|
else:
|
||||||
proc createListenSocket(host: string, port: Port): TAsyncFD =
|
proc createListenSocket(host: string, port: Port): TAsyncFD =
|
||||||
result = newAsyncRawSocket()
|
result = newAsyncRawSocket()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tasyncexceptions.nim"
|
file: "tasyncexceptions.nim"
|
||||||
exitcode: 1
|
exitcode: 1
|
||||||
outputsub: "Error: unhandled exception: foobar [Exception]"
|
outputsub: "Error: unhandled exception: foobar"
|
||||||
"""
|
"""
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tbind2.nim"
|
file: "tbind2.nim"
|
||||||
line: 14
|
line: 12
|
||||||
errormsg: "ambiguous call"
|
errormsg: "ambiguous call"
|
||||||
"""
|
"""
|
||||||
# Test the new ``bind`` keyword for templates
|
# Test the new ``bind`` keyword for templates
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "'"
|
output: "####"
|
||||||
file: "sequtils.nim"
|
|
||||||
line: 435
|
|
||||||
"""
|
"""
|
||||||
# unfortunately our tester doesn't support multiple lines of compiler
|
# unfortunately our tester doesn't support multiple lines of compiler
|
||||||
# error messages yet...
|
# error messages yet...
|
||||||
|
|
@ -29,4 +27,6 @@ when ATTEMPT == 0:
|
||||||
# bug #1543
|
# bug #1543
|
||||||
import sequtils
|
import sequtils
|
||||||
|
|
||||||
(var i= @[""];i).mapIt(it)
|
(var i = @[""];i).mapIt(it)
|
||||||
|
# now works:
|
||||||
|
echo "##", i[0], "##"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue