diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index ffab05788f..e4abe11a00 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode = if p.tok.s == "unsigned": isUnsigned = true elif p.tok.s == "signed" or p.tok.s == "int": - nil + discard else: add(x, p.tok.s) getTok(p, nil) @@ -746,7 +746,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = result = declarator(p, a, ident) eat(p, pxParRi, result) else: - nil + discard return parseTypeSuffix(p, a) proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = @@ -1165,7 +1165,7 @@ proc enumSpecifier(p: var TParser): PNode = proc setBaseFlags(n: PNode, base: TNumericalBase) = case base - of base10: nil + of base10: discard of base2: incl(n.flags, nfBase2) of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) @@ -1686,7 +1686,7 @@ proc switchStatement(p: var TParser): PNode = break of "case", "default": break - else: nil + else: discard addSon(result, statement(p)) if sonsLen(result) == 0: # translate empty statement list to Nimrod's ``nil`` statement diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim index 1707b75dbe..84b4c4dfb6 100644 --- a/compiler/c2nim/cpp.nim +++ b/compiler/c2nim/cpp.nim @@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) = m.body.add(tok) of pxDirConc: # just ignore this token: this implements token merging correctly - nil + discard else: m.body.add(p.tok) # we do not want macro expansion here: @@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode = of pxDirectiveParLe, pxDirective: case p.tok.s of "else", "endif", "elif": break - else: nil + else: discard addSon(result, statement(p)) proc eatEndif(p: var TParser) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index af0d657f1c..443d845f63 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -65,6 +65,7 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n", setLen(p.blocks, result + 1) p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 + p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16 proc assignLabel(b: var TBlock): PRope {.inline.} = b.label = con("LA", b.id.toRope) @@ -260,14 +261,22 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = else: internalError(n.info, "genIf()") if sonsLen(n) > 1: fixLabel(p, lend) -proc blockLeaveActions(p: BProc, howMany: int) = - var L = p.nestedTryStmts.len + +proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = + # This is called by return and break stmts. + # When jumping out of try/except/finally stmts, + # we need to pop safe points from try statements, + # execute finally-stmts, and pop exceptions + # from except stmts + + let L = p.nestedTryStmts.len + # danger of endless recursion! we workaround this here by a temp stack var stack: seq[PNode] - newSeq(stack, howMany) - for i in countup(1, howMany): + newSeq(stack, howManyTrys) + for i in countup(1, howManyTrys): stack[i-1] = p.nestedTryStmts[L-i] - setLen(p.nestedTryStmts, L-howMany) + setLen(p.nestedTryStmts, L-howManyTrys) var alreadyPoppedCnt = p.inExceptBlock for tryStmt in items(stack): @@ -276,21 +285,26 @@ proc blockLeaveActions(p: BProc, howMany: int) = dec alreadyPoppedCnt else: linefmt(p, cpsStmts, "#popSafePoint();$n") + # Find finally-stmts for this try-stmt + # and generate a copy of the finally stmts here var finallyStmt = lastSon(tryStmt) if finallyStmt.kind == nkFinally: genStmts(p, finallyStmt.sons[0]) # push old elements again: - for i in countdown(howMany-1, 0): + for i in countdown(howManyTrys-1, 0): p.nestedTryStmts.add(stack[i]) + if gCmd != cmdCompileToCpp: - for i in countdown(p.inExceptBlock-1, 0): + # Pop exceptions that was handled by the + # except-blocks we are in + for i in countdown(howManyExcepts-1, 0): linefmt(p, cpsStmts, "#popCurrentException();$n") proc genReturnStmt(p: BProc, t: PNode) = p.beforeRetNeeded = true genLineDir(p, t) if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) - blockLeaveActions(p, min(1, p.nestedTryStmts.len)) + blockLeaveActions(p, min(1, p.nestedTryStmts.len), p.inExceptBlock) lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", []) proc genComputedGoto(p: BProc; n: PNode) = @@ -450,7 +464,9 @@ proc genBreakStmt(p: BProc, t: PNode) = if idx < 0 or not p.blocks[idx].isLoop: internalError(t.info, "no loop to break") let label = assignLabel(p.blocks[idx]) - blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts) + blockLeaveActions(p, + p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, + p.inExceptBlock - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) lineF(p, cpsStmts, "goto $1;$n", [label]) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 0e11483435..71479abddd 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -58,6 +58,7 @@ type sections*: TCProcSections # the code beloging isLoop*: bool # whether block is a loop nestedTryStmts*: int16 # how many try statements is it nested into + nestedExceptStmts*: int16 # how many except statements is it nested into frameLen*: int16 TCProc{.final.} = object # represents C proc that is currently generated diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim index 67473e71fa..f24b0c420a 100644 --- a/compiler/pas2nim/paslex.nim +++ b/compiler/pas2nim/paslex.nim @@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) = h = h +% ord(c) h = h +% h shl 10 h = h xor (h shr 6) - of '_': nil + of '_': discard else: break inc(pos) h = h +% h shl 3 diff --git a/compiler/pas2nim/pasparse.nim b/compiler/pas2nim/pasparse.nim index 9288963386..a6f8363f61 100644 --- a/compiler/pas2nim/pasparse.nim +++ b/compiler/pas2nim/pasparse.nim @@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind, proc setBaseFlags(n: PNode, base: TNumericalBase) = case base - of base10: nil + of base10: discard of base2: incl(n.flags, nfBase2) of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) @@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = eat(p, pxCurlyDirRi) opNode.ident = getIdent("&") else: - nil + discard of pxMinus: if p.tok.xkind == pxPer: getTok(p) @@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = of pxNeq: opNode.ident = getIdent("!=") else: - nil + discard skipCom(p, opNode) # read sub-expression with higher priority nextop = lowestExprAux(p, v2, opPred) addSon(node, opNode) @@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode = (n.sons[2].kind in {nkCharLit, nkStrLit}): n.sons[0].ident = getIdent("&") # fix operator else: - nil + discard if not (n.kind in {nkEmpty..nkNilLit}): for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) @@ -603,7 +603,7 @@ proc parseStmtList(p: var TParser): PNode = of pxCurlyDirLe, pxStarDirLe: if not isHandledDirective(p): break else: - nil + discard addSon(result, parseStmt(p)) if sonsLen(result) == 1: result = result.sons[0] @@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode = addSon(b, c) addSon(a, b) if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: - nil + discard else: addSon(s, a) addSon(result, s) @@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode = getTok(p) v = newNodeP(nkVarTy, p) else: - nil + discard while true: case p.tok.xkind of pxSymbol: a = createIdentNodeP(p.tok.ident, p) @@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode = proc exSymbol(n: var PNode) = case n.kind of nkPostfix: - nil + discard of nkPragmaExpr: exSymbol(n.sons[0]) of nkIdent, nkAccQuoted: @@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) = for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) of nkIdentDefs: for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) - of nkNilLit, nkEmpty: nil + of nkNilLit, nkEmpty: discard else: internalError(n.info, "fixRecordDef(): " & $n.kind) proc addPragmaToIdent(ident: var PNode, pragma: PNode) = @@ -1191,7 +1191,7 @@ proc parseRecordBody(p: var TParser, result, definition: PNode) = if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) else: internalError(result.info, "anonymous record is not supported") else: - nil + discard opt(p, pxSemicolon) skipCom(p, result) @@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) = proc exSymbols(n: PNode) = case n.kind - of nkEmpty..nkNilLit: nil + of nkEmpty..nkNilLit: discard of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) of nkWhenStmt, nkStmtList: for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) @@ -1410,7 +1410,7 @@ proc exSymbols(n: PNode) = exSymbol(n.sons[i].sons[0]) if n.sons[i].sons[2].kind == nkObjectTy: fixRecordDef(n.sons[i].sons[2]) - else: nil + else: discard proc parseBegin(p: var TParser, result: PNode) = getTok(p) diff --git a/examples/htmlrefs.nim b/examples/htmlrefs.nim index 824c1d8c70..8b668325f0 100644 --- a/examples/htmlrefs.nim +++ b/examples/htmlrefs.nim @@ -36,7 +36,7 @@ block mainLoop: case x.kind of xmlEof: break mainLoop of xmlElementClose: break - else: nil + else: discard x.next() # skip ``xmlElementClose`` # now we have the description for the ``a`` element var desc = "" diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index cdb4a6f492..8541b2ba74 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -import os, oids, tables, strutils +import os, oids, tables, strutils, macros import winlean @@ -23,14 +23,13 @@ import sockets2, net # -- Futures type - PFutureVoid* = ref object of PObject - cbVoid: proc () {.closure.} + PFutureBase* = ref object of PObject + cb: proc () {.closure.} finished: bool - PFuture*[T] = ref object of PFutureVoid + PFuture*[T] = ref object of PFutureBase value: T error: ref EBase - cb: proc (future: PFuture[T]) {.closure.} proc newFuture*[T](): PFuture[T] = ## Creates a new future. @@ -39,42 +38,38 @@ proc newFuture*[T](): PFuture[T] = proc complete*[T](future: PFuture[T], val: T) = ## Completes ``future`` with value ``val``. - assert(not future.finished) + assert(not future.finished, "Future already finished, cannot finish twice.") assert(future.error == nil) future.value = val future.finished = true if future.cb != nil: - future.cb(future) - if future.cbVoid != nil: - future.cbVoid() + future.cb() proc fail*[T](future: PFuture[T], error: ref EBase) = ## Completes ``future`` with ``error``. - assert(not future.finished) + assert(not future.finished, "Future already finished, cannot finish twice.") future.finished = true future.error = error if future.cb != nil: - future.cb(future) + future.cb() + +proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) = + ## Sets the callback proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + ## + ## **Note**: You most likely want the other ``callback`` setter which + ## passes ``future`` as a param to the callback. + future.cb = cb + if future.finished: + future.cb() proc `callback=`*[T](future: PFuture[T], cb: proc (future: PFuture[T]) {.closure.}) = ## Sets the callback proc to be called when the future completes. ## ## If future has already completed then ``cb`` will be called immediately. - future.cb = cb - if future.finished: - future.cb(future) - -proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) = - ## Sets the **void** callback proc to be called when the future completes. - ## - ## If future has already completed then ``cb`` will be called immediately. - ## - ## **Note**: This is used for the ``await`` functionality, you most likely - ## want to use ``callback``. - future.cbVoid = cb - if future.finished: - future.cbVoid() + future.callback = proc () = cb(future) proc read*[T](future: PFuture[T]): T = ## Retrieves the value of ``future``. Future must be finished otherwise @@ -104,10 +99,12 @@ when defined(windows): TCompletionData* = object sock: TSocketHandle - cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.} + cb: proc (sock: TSocketHandle, bytesTransferred: DWORD, + errcode: TOSErrorCode) {.closure.} PDispatcher* = ref object ioPort: THandle + hasHandles: bool TCustomOverlapped = object Internal*: DWORD @@ -129,9 +126,13 @@ when defined(windows): if CreateIOCompletionPort(sock.THandle, p.ioPort, cast[TCompletionKey](sock), 1) == 0: OSError(OSLastError()) + p.hasHandles = true proc poll*(p: PDispatcher, timeout = 500) = ## Waits for completion events and processes them. + if not p.hasHandles: + raise newException(EInvalidValue, "No handles registered in dispatcher.") + let llTimeout = if timeout == -1: winlean.INFINITE else: timeout.int32 @@ -145,16 +146,19 @@ when defined(windows): # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html var customOverlapped = cast[PCustomOverlapped](lpOverlapped) if res: + # This is useful for ensuring the reliability of the overlapped struct. assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle - customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1)) + customOverlapped.data.cb(customOverlapped.data.sock, + lpNumberOfBytesTransferred, TOSErrorCode(-1)) dealloc(customOverlapped) else: let errCode = OSLastError() if lpOverlapped != nil: assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + customOverlapped.data.cb(customOverlapped.data.sock, + lpNumberOfBytesTransferred, errCode) dealloc(customOverlapped) - customOverlapped.data.cb(customOverlapped.data.sock, errCode) else: if errCode.int32 == WAIT_TIMEOUT: # Timed out @@ -252,11 +256,12 @@ when defined(windows): # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - retFuture.complete(0) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, @@ -265,7 +270,9 @@ when defined(windows): # Request to connect completed immediately. success = true retFuture.complete(0) - dealloc(ol) + # 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``. break else: lastError = OSLastError() @@ -283,7 +290,8 @@ when defined(windows): retFuture.fail(newException(EOS, osErrorMsg(lastError))) return retFuture - proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] = + proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, + flags: int = 0): PFuture[string] = ## Reads ``size`` bytes from ``socket``. Returned future will complete once ## all of the requested data is read. @@ -293,31 +301,50 @@ when defined(windows): dataBuf.buf = newString(size) dataBuf.len = size - var bytesReceived, flags: DWord + var bytesReceived: DWord + var flagsio = flags.dword var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - var data = newString(size) - copyMem(addr data[0], addr dataBuf.buf[0], size) - retFuture.complete($data) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + if bytesCount == 0 and dataBuf.buf[0] == '\0': + retFuture.complete("") + else: + var data = newString(size) + copyMem(addr data[0], addr dataBuf.buf[0], size) + retFuture.complete($data) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) - + let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, - addr flags, cast[POverlapped](ol), nil) + addr flagsio, cast[POverlapped](ol), nil) if ret == -1: let err = OSLastError() if err.int32 != ERROR_IO_PENDING: retFuture.fail(newException(EOS, osErrorMsg(err))) dealloc(ol) + 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 immediatelly 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("") + # 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. var data = newString(size) copyMem(addr data[0], addr dataBuf.buf[0], size) retFuture.complete($data) - dealloc(ol) + # 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*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = @@ -332,11 +359,12 @@ when defined(windows): var bytesReceived, flags: DWord var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - retFuture.complete(0) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, @@ -348,7 +376,9 @@ when defined(windows): dealloc(ol) else: retFuture.complete(0) - dealloc(ol) + # 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 acceptAddr*(p: PDispatcher, socket: TSocketHandle): @@ -390,11 +420,12 @@ when defined(windows): var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - completeAccept() - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + completeAccept() + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx @@ -411,7 +442,9 @@ when defined(windows): dealloc(ol) else: completeAccept() - dealloc(ol) + # 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 @@ -434,6 +467,199 @@ when defined(windows): else: # TODO: Selectors. +# -- Await Macro + +template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} = + proc cbName {.closure.} = + if not varNameIterSym.finished: + var next = varNameIterSym() + if next == nil: + assert retFutureSym.finished, "Async procedure's return Future was not finished." + else: + next.callback = cbName + +template createVar(futSymName: string, asyncProc: PNimrodNode, + valueReceiver: expr) {.immediate, dirty.} = + # TODO: Used template here due to bug #926 + result = newNimNode(nnkStmtList) + var futSym = newIdentNode(futSymName) #genSym(nskVar, "future") + result.add newVarStmt(futSym, asyncProc) # -> var future = y + result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future + valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future.read + +proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = + result = node + case node.kind + of nnkReturnStmt: + result = newNimNode(nnkStmtList) + result.add newCall(newIdentNode("complete"), retFutureSym, + if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0]) + result.add newNimNode(nnkYieldStmt).add(newNilLit()) + of nnkCommand: + if node[0].ident == !"await": + case node[1].kind + of nnkIdent: + # await x + result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x + of nnkCall: + # await foo(p, x) + var futureValue: PNimrodNode + createVar("future" & $node[1][0].toStrLit, node[1], futureValue) + result.add futureValue + else: + error("Invalid node kind in 'await', got: " & $node[1].kind) + elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and + node[1][0].ident == !"await": + # foo await x + var newCommand = node + createVar("future" & $node[0].ident, node[1][0], newCommand[1]) + result.add newCommand + + of nnkVarSection, nnkLetSection: + case node[0][2].kind + of nnkCommand: + if node[0][2][0].ident == !"await": + # var x = await y + var newVarSection = node # TODO: Should this use copyNimNode? + createVar("future" & $node[0][0].ident, node[0][2][1], + newVarSection[0][2]) + result.add newVarSection + else: discard + of nnkAsgn: + case node[1].kind + of nnkCommand: + if node[1][0].ident == !"await": + # x = await y + var newAsgn = node + createVar("future" & $node[0].ident, node[1][1], newAsgn[1]) + result.add newAsgn + else: discard + of nnkDiscardStmt: + # discard await x + if node[0][0].ident == !"await": + var dummy = newNimNode(nnkStmtList) + createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy) + else: discard + + for i in 0 .. var retFuture = newFuture[T]() + var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture") + outerProcBody.add( + newVarStmt(retFutureSym, + newCall( + newNimNode(nnkBracketExpr).add( + newIdentNode("newFuture"), + prc[3][0][1])))) # Get type from return type of this proc. + + # -> iterator nameIter(): PFutureBase {.closure.} = + # -> var result: T + # -> + # -> complete(retFuture, result) + var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter") + var procBody = prc[6].processBody(retFutureSym) + procBody.insert(0, newNimNode(nnkVarSection).add( + newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T + procBody.add( + newCall(newIdentNode("complete"), + retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) + + var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], + procBody, nnkIteratorDef) + closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure")) + outerProcBody.add(closureIterator) + + # -> var nameIterVar = nameIter + # -> var first = nameIterVar() + var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar") + var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym) + outerProcBody.add varNameIter + var varFirstSym = genSym(nskVar, "first") + var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym)) + outerProcBody.add varFirst + + # -> createCb(cb, nameIter, retFuture) + var cbName = newIdentNode("cb") + var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym) + outerProcBody.add procCb + + # -> first.callback = cb + outerProcBody.add newAssignment( + newDotExpr(varFirstSym, newIdentNode("callback")), + cbName) + + # -> return retFuture + outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym) + + result = prc + + # Remove the 'async' pragma. + for i in 0 .. 0 and c == "\L": + discard await p.recv(socket, 1) + addNLIfEmpty() + return + elif c == "\L": + addNLIfEmpty() + return + add(result.string, c) when isMainModule: @@ -442,39 +668,65 @@ when isMainModule: #sock.setBlocking false p.register(sock) - when true: - var f = p.connect(sock, "irc.freenode.org", TPort(6667)) - f.callback = - proc (future: PFuture[int]) = - echo("Connected in future!") - echo(future.read) - for i in 0 .. 50: - var recvF = p.recv(sock, 10) - recvF.callback = - proc (future: PFuture[string]) = - echo("Read: ", future.read) + when false: + # Await tests + proc main(p: PDispatcher): PFuture[int] {.async.} = + discard await p.connect(sock, "irc.freenode.net", TPort(6667)) + while true: + var line = await p.recvLine(sock) + echo("Line is: ", line.repr) + if line == "": + echo "Disconnected" + break + + proc peekTest(p: PDispatcher): PFuture[int] {.async.} = + discard await p.connect(sock, "localhost", TPort(6667)) + while true: + var line = await p.recv(sock, 1, MSG_PEEK) + var line2 = await p.recv(sock, 1) + echo(line.repr) + echo(line2.repr) + echo("---") + if line2 == "": break + sleep(500) + + var f = main(p) + else: + when false: - sock.bindAddr(TPort(6667)) - sock.listen() - proc onAccept(future: PFuture[TSocketHandle]) = - echo "Accepted" - var t = p.send(future.read, "test\c\L") - t.callback = + var f = p.connect(sock, "irc.freenode.org", TPort(6667)) + f.callback = proc (future: PFuture[int]) = + echo("Connected in future!") echo(future.read) - + for i in 0 .. 50: + var recvF = p.recv(sock, 10) + recvF.callback = + proc (future: PFuture[string]) = + echo("Read: ", future.read) + + else: + + sock.bindAddr(TPort(6667)) + sock.listen() + proc onAccept(future: PFuture[TSocketHandle]) = + echo "Accepted" + var t = p.send(future.read, "test\c\L") + t.callback = + proc (future: PFuture[int]) = + echo(future.read) + + var f = p.accept(sock) + f.callback = onAccept + var f = p.accept(sock) f.callback = onAccept - - var f = p.accept(sock) - f.callback = onAccept while true: p.poll() - echo "polled" diff --git a/lib/pure/times.nim b/lib/pure/times.nim index de6c4e4fa2..2fce235e2d 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string = "November", "December"] return lookup[m] +proc format_token(info: TTimeInfo, token: string, buf: var string) = + ## Helper of the format proc to parse individual tokens. + ## + ## Pass the found token in the user input string, and the buffer where the + ## final string is being built. This has to be a var value because certain + ## formatting tokens require modifying the previous characters. + case token + of "d": + buf.add($info.monthday) + of "dd": + if info.monthday < 10: + buf.add("0") + buf.add($info.monthday) + of "ddd": + buf.add(($info.weekday)[0 .. 2]) + of "dddd": + buf.add($info.weekday) + of "h": + buf.add($(if info.hour > 12: info.hour - 12 else: info.hour)) + of "hh": + let amerHour = if info.hour > 12: info.hour - 12 else: info.hour + if amerHour < 10: + buf.add('0') + buf.add($amerHour) + of "H": + buf.add($info.hour) + of "HH": + if info.hour < 10: + buf.add('0') + buf.add($info.hour) + of "m": + buf.add($info.minute) + of "mm": + if info.minute < 10: + buf.add('0') + buf.add($info.minute) + of "M": + buf.add($(int(info.month)+1)) + of "MM": + if info.month < mOct: + buf.add('0') + buf.add($(int(info.month)+1)) + of "MMM": + buf.add(($info.month)[0..2]) + of "MMMM": + buf.add($info.month) + of "s": + buf.add($info.second) + of "ss": + if info.second < 10: + buf.add('0') + buf.add($info.second) + of "t": + if info.hour >= 12: + buf.add('P') + else: buf.add('A') + of "tt": + if info.hour >= 12: + buf.add("PM") + else: buf.add("AM") + of "y": + var fr = ($info.year).len()-1 + if fr < 0: fr = 0 + buf.add(($info.year)[fr .. ($info.year).len()-1]) + of "yy": + var fr = ($info.year).len()-2 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyy": + var fr = ($info.year).len()-3 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyyy": + var fr = ($info.year).len()-4 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyyyy": + var fr = ($info.year).len()-5 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear + buf.add(fyear) + of "z": + let hrs = (info.timezone div 60) div 60 + buf.add($hrs) + of "zz": + let hrs = (info.timezone div 60) div 60 + + buf.add($hrs) + if hrs.abs < 10: + var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0)) + buf.insert("0", atIndex) + of "zzz": + let hrs = (info.timezone div 60) div 60 + + buf.add($hrs & ":00") + if hrs.abs < 10: + var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0)) + buf.insert("0", atIndex) + of "ZZZ": + buf.add(info.tzname) + of "": + discard + else: + raise newException(EInvalidValue, "Invalid format string: " & token) + + proc format*(info: TTimeInfo, f: string): string = ## This function formats `info` as specified by `f`. The following format ## specifiers are available: @@ -591,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string = ## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST`` ## ========== ================================================================================= ================================================ ## - ## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``. - ## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,`` + ## Other strings can be inserted by putting them in ``''``. For example + ## ``hh'->'mm`` will give ``01->56``. The following characters can be + ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` + ## ``,``. However you don't need to necessarily separate format specifiers, a + ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. result = "" var i = 0 @@ -600,112 +716,8 @@ proc format*(info: TTimeInfo, f: string): string = while true: case f[i] of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': - case currentF - of "d": - result.add($info.monthday) - of "dd": - if info.monthday < 10: - result.add("0") - result.add($info.monthday) - of "ddd": - result.add(($info.weekday)[0 .. 2]) - of "dddd": - result.add($info.weekday) - of "h": - result.add($(if info.hour > 12: info.hour - 12 else: info.hour)) - of "hh": - let amerHour = if info.hour > 12: info.hour - 12 else: info.hour - if amerHour < 10: - result.add('0') - result.add($amerHour) - of "H": - result.add($info.hour) - of "HH": - if info.hour < 10: - result.add('0') - result.add($info.hour) - of "m": - result.add($info.minute) - of "mm": - if info.minute < 10: - result.add('0') - result.add($info.minute) - of "M": - result.add($(int(info.month)+1)) - of "MM": - if info.month < mOct: - result.add('0') - result.add($(int(info.month)+1)) - of "MMM": - result.add(($info.month)[0..2]) - of "MMMM": - result.add($info.month) - of "s": - result.add($info.second) - of "ss": - if info.second < 10: - result.add('0') - result.add($info.second) - of "t": - if info.hour >= 12: - result.add('P') - else: result.add('A') - of "tt": - if info.hour >= 12: - result.add("PM") - else: result.add("AM") - of "y": - var fr = ($info.year).len()-1 - if fr < 0: fr = 0 - result.add(($info.year)[fr .. ($info.year).len()-1]) - of "yy": - var fr = ($info.year).len()-2 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear - result.add(fyear) - of "yyy": - var fr = ($info.year).len()-3 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear - result.add(fyear) - of "yyyy": - var fr = ($info.year).len()-4 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear - result.add(fyear) - of "yyyyy": - var fr = ($info.year).len()-5 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear - result.add(fyear) - of "z": - let hrs = (info.timezone div 60) div 60 - result.add($hrs) - of "zz": - let hrs = (info.timezone div 60) div 60 - - result.add($hrs) - if hrs.abs < 10: - var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0)) - result.insert("0", atIndex) - of "zzz": - let hrs = (info.timezone div 60) div 60 - - result.add($hrs & ":00") - if hrs.abs < 10: - var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0)) - result.insert("0", atIndex) - of "ZZZ": - result.add(info.tzname) - of "": - discard - else: - raise newException(EInvalidValue, "Invalid format string: " & currentF) - + format_token(info, currentF, result) + currentF = "" if f[i] == '\0': break @@ -716,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string = inc(i) else: result.add(f[i]) - else: currentF.add(f[i]) + else: + # Check if the letter being added matches previous accumulated buffer. + if currentF.len < 1 or currentF[high(currentF)] == f[i]: + currentF.add(f[i]) + else: + format_token(info, currentF, result) + dec(i) # Move position back to re-process the character separately. + currentF = "" + inc(i) {.pop.} @@ -727,11 +747,15 @@ when isMainModule: 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" assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC" + + assert t.format("yyyyMMddhhmmss") == "20380119031407" var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 6c8fa48823..74ef9c9ec9 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -199,14 +199,14 @@ else: importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} - proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. + proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {. importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} proc removeDirectoryA*(lpPathName: cstring): int32 {. importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} - proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. + proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {. importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} when useWinUnicode: @@ -304,7 +304,7 @@ else: dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} - proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, + proc copyFileA*(lpExistingFileName, lpNewFileName: CString, bFailIfExists: cint): cint {. importc: "CopyFileA", stdcall, dynlib: "kernel32".} diff --git a/tests/ambsym/mambsym1.nim b/tests/ambsym/mambsym1.nim index cf8ac52421..d9d57b5e51 100644 --- a/tests/ambsym/mambsym1.nim +++ b/tests/ambsym/mambsym1.nim @@ -7,4 +7,4 @@ type proc ha() = var x: TExport # no error - nil + discard diff --git a/tests/ambsym/mambsys1.nim b/tests/ambsym/mambsys1.nim index 5472b5ae4e..04f9561d30 100644 --- a/tests/ambsym/mambsys1.nim +++ b/tests/ambsym/mambsys1.nim @@ -4,4 +4,4 @@ type TExport* = enum x, y, z proc foo*(x: int) = - nil + discard diff --git a/tests/ambsym/mambsys2.nim b/tests/ambsym/mambsys2.nim index 395425b868..d59706865c 100644 --- a/tests/ambsym/mambsys2.nim +++ b/tests/ambsym/mambsys2.nim @@ -1,4 +1,4 @@ type TExport* = enum x, y, z # exactly the same type! -proc foo*(x: int) = nil +proc foo*(x: int) = discard diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim new file mode 100644 index 0000000000..bcaffc2875 --- /dev/null +++ b/tests/async/tasyncawait.nim @@ -0,0 +1,64 @@ +discard """ + file: "tasyncawait.nim" + cmd: "nimrod cc --hints:on $# $#" + output: "5000" +""" +import asyncio2, sockets2, net, strutils + +var disp = newDispatcher() +var msgCount = 0 + +const + swarmSize = 50 + messagesToSend = 100 + +var clientCount = 0 + +proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} = + for i in 0 .. 2..4 diff --git a/tests/typerel/typalias.nim b/tests/typerel/typalias.nim index ba9f38ed90..40ff067655 100644 --- a/tests/typerel/typalias.nim +++ b/tests/typerel/typalias.nim @@ -9,7 +9,7 @@ proc init: TYourObj = result.y = -1 proc f(x: var TYourObj) = - nil + discard var m: TMyObj = init() f(m) diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index b20e86a68f..28d92402e8 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -249,7 +249,7 @@ proc walker(dir: string) = of pcDir: if optRecursive in options: walker(path) - else: nil + else: discard if existsFile(dir): processFile(dir) proc writeHelp() =