Merge branch 'devel' into fix_bracket_expr

This commit is contained in:
Araq
2015-09-12 20:42:27 +02:00
13 changed files with 200 additions and 55 deletions

View File

@@ -221,6 +221,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
# NOTE: for access of private fields within generics from a different module
# we set the friend module:
c.friendModules.add(getModule(fn))
let oldInTypeClass = c.inTypeClass
c.inTypeClass = 0
let oldScope = c.currentScope
while not isTopLevel(c): c.currentScope = c.currentScope.parent
result = copySym(fn, false)
@@ -269,4 +271,5 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
c.currentScope = oldScope
discard c.friendModules.pop()
dec(c.instCounter)
c.inTypeClass = oldInTypeClass
if result.kind == skMethod: finishMethod(c, result)

View File

@@ -632,6 +632,11 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
localError(n.info, errInvalidExpression)
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
case n.kind
of nkIdent:
@@ -657,6 +662,12 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
localError(n.info, errInvalidExpression)
else:
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:
let s = qualifiedLookUp(c.c, n.sons[0], {})
if s != nil:

View File

@@ -1461,6 +1461,8 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
let n = transformExpr(module, n)
setupGlobalCtx(module)
var c = globalCtx
let oldMode = c.mode
defer: c.mode = oldMode
c.mode = mode
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
if c.code[start].opcode == opcEof: return emptyNode

View File

@@ -126,6 +126,7 @@ export Port, SocketFlag
## * Can't await in a ``except`` body
## * Forward declarations for async procs are broken,
## 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
@@ -145,10 +146,15 @@ type
Future*[T] = ref object of FutureBase ## Typed future.
value: T ## Stored value
FutureVar*[T] = distinct Future[T]
FutureError* = object of Exception
cause*: FutureBase
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
var currentID = 0
when not defined(release):
var currentID = 0
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
## Creates a new future.
##
@@ -162,18 +168,39 @@ proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
result.fromProc = fromProc
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]) =
## Checks whether `future` is finished. If it is then raises a
## ``FutureError``.
when not defined(release):
if future.finished:
echo("<-----> ", future.id, " ", future.fromProc)
echo(future.stackTrace)
echo("-----")
var msg = ""
msg.add("An attempt was made to complete a Future more than once. ")
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:
echo("Contents: ", future.value.repr)
echo("<----->")
echo("Future already finished, cannot finish twice.")
echo getStackTrace()
assert false
msg.add("\n Contents (string): ")
msg.add("\n" & indent(future.value.repr, 4))
msg.add("\n Stack trace to moment of secondary completion:")
msg.add("\n" & indent(getStackTrace().strip(), 4))
var err = newException(FutureError, msg)
err.cause = future
raise err
proc complete*[T](future: Future[T], val: T) =
## Completes ``future`` with value ``val``.
@@ -194,6 +221,15 @@ proc complete*(future: Future[void]) =
if future.cb != nil:
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) =
## Completes ``future`` with ``error``.
#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.
future.callback = proc () = cb(future)
proc echoOriginalStackTrace[T](future: Future[T]) =
proc injectStacktrace[T](future: Future[T]) =
# TODO: Come up with something better.
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 != "":
echo(future.errorStackTrace)
msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
else:
echo("Empty or nil stack trace.")
echo("Continuing...")
msg.add("\n Empty or nil stack trace.")
future.error.msg.add(msg)
proc read*[T](future: Future[T]): T =
## 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 future.finished:
if future.error != nil:
echoOriginalStackTrace(future)
injectStacktrace(future)
raise future.error
when T isnot void:
return future.value
@@ -264,6 +302,13 @@ proc readError*[T](future: Future[T]): ref Exception =
else:
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 =
## Determines whether ``future`` has completed.
##
@@ -282,7 +327,7 @@ proc asyncCheck*[T](future: Future[T]) =
future.callback =
proc () =
if future.failed:
echoOriginalStackTrace(future)
injectStacktrace(future)
raise future.error
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =

View File

@@ -151,7 +151,8 @@ proc processClient(client: AsyncSocket, address: string,
var request: Request
request.url = initUri()
request.headers = newStringTable(modeCaseInsensitive)
var line = newStringOfCap(80)
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)
var key, value = ""
while not client.isClosed:
@@ -165,14 +166,15 @@ proc processClient(client: AsyncSocket, address: string,
request.client = client
# First line - GET /path HTTP/1.1
line.setLen(0)
await client.recvLineInto(addr line) # TODO: Timeouts.
if line == "":
lineFut.mget().setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut) # TODO: Timeouts.
if lineFut.mget == "":
client.close()
return
var i = 0
for linePart in line.split(' '):
for linePart in lineFut.mget.split(' '):
case i
of 0: request.reqMethod.shallowCopy(linePart.normalize)
of 1: parseUri(linePart, request.url)
@@ -184,20 +186,21 @@ proc processClient(client: AsyncSocket, address: string,
"Invalid request protocol. Got: " & linePart)
continue
else:
await request.respond(Http400, "Invalid request. Got: " & line)
await request.respond(Http400, "Invalid request. Got: " & lineFut.mget)
continue
inc i
# Headers
while true:
i = 0
line.setLen(0)
await client.recvLineInto(addr line)
lineFut.mget.setLen(0)
lineFut.clean()
await client.recvLineInto(lineFut)
if line == "":
if lineFut.mget == "":
client.close(); return
if line == "\c\L": break
let (key, value) = parseHeader(line)
if lineFut.mget == "\c\L": break
let (key, value) = parseHeader(lineFut.mget)
request.headers[key] = value
if request.reqMethod == "post":

View File

@@ -316,7 +316,7 @@ proc accept*(socket: AsyncSocket,
retFut.complete(future.read.client)
return retFut
proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
flags = {SocketFlag.SafeDisconn}) {.async.} =
## 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
## performance reasons. This will likely change soon to use FutureVars.
assert SocketFlag.Peek notin flags ## TODO:
assert(not resString.mget.isNil(),
"String inside resString future needs to be initialised")
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 =
if resString[].len == 0:
resString[].add("\c\L")
if resString.mget.len == 0:
resString.mget.add("\c\L")
if socket.isBuffered:
if socket.bufLen == 0:
let res = socket.readIntoBuf(flags)
if res == 0:
resString.complete()
return
var lastR = false
@@ -355,7 +362,8 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
if socket.currPos >= socket.bufLen:
let res = socket.readIntoBuf(flags)
if res == 0:
resString[].setLen(0)
resString.mget.setLen(0)
resString.complete()
return
case socket.buffer[socket.currPos]
@@ -365,13 +373,15 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
of '\L':
addNLIfEmpty()
socket.currPos.inc()
resString.complete()
return
else:
if lastR:
socket.currPos.inc()
resString.complete()
return
else:
resString[].add socket.buffer[socket.currPos]
resString.mget.add socket.buffer[socket.currPos]
socket.currPos.inc()
else:
var c = ""
@@ -379,18 +389,22 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
let recvFut = recv(socket, 1, flags)
c = recvFut.read()
if c.len == 0:
resString[].setLen(0)
resString.mget.setLen(0)
resString.complete()
return
if c == "\r":
let recvFut = recv(socket, 1, flags) # Skip \L
c = recvFut.read()
assert c == "\L"
addNLIfEmpty()
resString.complete()
return
elif c == "\L":
addNLIfEmpty()
resString.complete()
return
resString[].add c
resString.mget.add c
resString.complete()
proc recvLine*(socket: AsyncSocket,
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
@@ -416,8 +430,10 @@ proc recvLine*(socket: AsyncSocket,
result.add("\c\L")
assert SocketFlag.Peek notin flags ## TODO:
result = ""
await socket.recvLineInto(addr result, flags)
# TODO: Optimise this
var resString = newFutureVar[string]("asyncnet.recvLine")
await socket.recvLineInto(resString, flags)
result = resString.mget()
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections.

View File

@@ -630,6 +630,22 @@ proc wordWrap*(s: string, maxLineWidth = 80,
result.add(lastSep & word)
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 {.
noSideEffect, rtl, extern: "nsuUnindent".} =
## Unindents `s`.
@@ -1502,3 +1518,5 @@ when isMainModule:
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"
doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"

View File

@@ -11,6 +11,26 @@
## This module contains routines and types for dealing with time.
## This module is available for 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
# of the standard library!
@@ -288,10 +308,10 @@ proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## very accurate.
let t = toSeconds(timeInfoToTime(a))
let secs = toSeconds(a, interval)
if a.tzname == "UTC":
result = getGMTime(fromSeconds(t + secs))
else:
result = getLocalTime(fromSeconds(t + secs))
#if a.tzname == "UTC":
# result = getGMTime(fromSeconds(t + secs))
#else:
result = getLocalTime(fromSeconds(t + secs))
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## subtracts ``interval`` time.
@@ -300,10 +320,10 @@ proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## when you subtract so much that you reach the Julian calendar.
let t = toSeconds(timeInfoToTime(a))
let secs = toSeconds(a, interval)
if a.tzname == "UTC":
result = getGMTime(fromSeconds(t - secs))
else:
result = getLocalTime(fromSeconds(t - secs))
#if a.tzname == "UTC":
# result = getGMTime(fromSeconds(t - secs))
#else:
result = getLocalTime(fromSeconds(t - secs))
when not defined(JS):
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
@@ -1269,3 +1289,28 @@ when isMainModule:
assert getDayOfWeekJulian(21, 9, 1970) == dMon
assert getDayOfWeekJulian(1, 1, 2000) == dSat
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

View File

@@ -1,7 +1,7 @@
discard """
file: "tasyncconnect.nim"
exitcode: 1
outputsub: "Error: unhandled exception: Connection refused [Exception]"
outputsub: "Error: unhandled exception: Connection refused"
"""
import
@@ -15,7 +15,8 @@ const
when defined(windows) or defined(nimdoc):
discard
# TODO: just make it work on Windows for now.
quit("Error: unhandled exception: Connection refused")
else:
proc testAsyncConnect() {.async.} =
var s = newAsyncRawSocket()

View File

@@ -1,7 +1,7 @@
discard """
file: "tasynceverror.nim"
exitcode: 1
outputsub: "Error: unhandled exception: Connection reset by peer [Exception]"
outputsub: "Error: unhandled exception: Connection reset by peer"
"""
import
@@ -17,7 +17,8 @@ const
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:
proc createListenSocket(host: string, port: Port): TAsyncFD =
result = newAsyncRawSocket()

View File

@@ -1,7 +1,7 @@
discard """
file: "tasyncexceptions.nim"
exitcode: 1
outputsub: "Error: unhandled exception: foobar [Exception]"
outputsub: "Error: unhandled exception: foobar"
"""
import asyncdispatch

View File

@@ -1,6 +1,6 @@
discard """
file: "tbind2.nim"
line: 14
line: 12
errormsg: "ambiguous call"
"""
# Test the new ``bind`` keyword for templates

View File

@@ -1,7 +1,5 @@
discard """
errormsg: "'"
file: "sequtils.nim"
line: 435
output: "####"
"""
# unfortunately our tester doesn't support multiple lines of compiler
# error messages yet...
@@ -29,4 +27,6 @@ when ATTEMPT == 0:
# bug #1543
import sequtils
(var i= @[""];i).mapIt(it)
(var i = @[""];i).mapIt(it)
# now works:
echo "##", i[0], "##"