diff --git a/chronos.nim b/chronos.nim index 53e39e6..661781c 100644 --- a/chronos.nim +++ b/chronos.nim @@ -6,5 +6,5 @@ # Apache License, version 2.0, (LICENSE-APACHEv2) # MIT license (LICENSE-MIT) import chronos/[asyncloop, asyncfutures2, asyncsync, handles, transport, - timer] -export asyncloop, asyncfutures2, asyncsync, handles, transport, timer + timer, version] +export asyncloop, asyncfutures2, asyncsync, handles, transport, timer, version diff --git a/chronos/asyncmacro2.nim b/chronos/asyncmacro2.nim index b208971..46bef06 100644 --- a/chronos/asyncmacro2.nim +++ b/chronos/asyncmacro2.nim @@ -66,7 +66,7 @@ template createCb(retFutureSym, iteratorNameSym, #{.pop.} template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver, - rootReceiver: untyped, fromNode: NimNode) = + rootReceiver: untyped, fromNode: NimNode, isawait: bool) = ## Params: ## futureVarNode: The NimNode which is a symbol identifying the Future[T] ## variable to yield. @@ -75,16 +75,22 @@ template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver, ## future's value. ## ## rootReceiver: ??? TODO - # -> yield future - result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode) - # -> future.read - valueReceiver = newDotExpr(futureVarNode, newIdentNode("read")) - result.add rootReceiver + if isawait: + # -> yield future + result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode) + # -> future.read + valueReceiver = newDotExpr(futureVarNode, newIdentNode("read")) + result.add rootReceiver + else: + # -> yield future + result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode) + valueReceiver = futureVarNode + result.add rootReceiver template createVar(result: var NimNode, futSymName: string, asyncProc: NimNode, valueReceiver, rootReceiver, retFutSym: untyped, - fromNode: NimNode) = + fromNode: NimNode, isawait: bool) = result = newNimNode(nnkStmtList, fromNode) var futSym = genSym(nskVar, "future") result.add newVarStmt(futSym, asyncProc) # -> var future = y @@ -96,7 +102,7 @@ template createVar(result: var NimNode, futSymName: string, ), newCall(newIdentNode("FutureBase"), copyNimNode(futSym)) ) - useVar(result, futSym, valueReceiver, rootReceiver, fromNode) + useVar(result, futSym, valueReceiver, rootReceiver, fromNode, isawait) proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode): NimNode {.compileTime.} = @@ -144,7 +150,8 @@ proc processBody(node, retFutureSym: NimNode, result.add newNimNode(nnkReturnStmt, node).add(newNilLit()) return # Don't process the children of this return stmt of nnkCommand, nnkCall: - if node[0].kind == nnkIdent and node[0].eqIdent("await"): + if node[0].kind == nnkIdent and + (node[0].eqIdent("await") or node[0].eqIdent("awaitne")): case node[1].kind of nnkIdent, nnkInfix, nnkDotExpr, nnkCall, nnkCommand: # await x @@ -153,41 +160,48 @@ proc processBody(node, retFutureSym: NimNode, # await foo p, x var futureValue: NimNode result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue, - futureValue, retFutureSym, node) + futureValue, retFutureSym, node, + node[0].eqIdent("await")) else: error("Invalid node kind in 'await', got: " & $node[1].kind) elif node.len > 1 and node[1].kind == nnkCommand and - node[1][0].kind == nnkIdent and node[1][0].eqIdent("await"): + node[1][0].kind == nnkIdent and + (node[1][0].eqIdent("await") or node[1][0].eqIdent("awaitne")): # foo await x var newCommand = node result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1], - newCommand, retFutureSym, node) + newCommand, retFutureSym, node, + node[1][0].eqIdent("await")) of nnkVarSection, nnkLetSection: case node[0][2].kind of nnkCommand: - if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"): + if node[0][2][0].kind == nnkIdent and + (node[0][2][0].eqIdent("await") or node[0][2][0].eqIdent("awaitne")): # var x = await y var newVarSection = node # TODO: Should this use copyNimNode? result.createVar("future" & node[0][0].strVal, node[0][2][1], - newVarSection[0][2], newVarSection, retFutureSym, node) + newVarSection[0][2], newVarSection, retFutureSym, node, + node[0][2][0].eqIdent("await")) else: discard of nnkAsgn: case node[1].kind of nnkCommand: - if node[1][0].eqIdent("await"): + if node[1][0].eqIdent("await") or node[1][0].eqIdent("awaitne"): # x = await y var newAsgn = node result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], - newAsgn, retFutureSym, node) + newAsgn, retFutureSym, node, + node[1][0].eqIdent("await")) else: discard of nnkDiscardStmt: # discard await x if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and - node[0][0].eqIdent("await"): + (node[0][0].eqIdent("await") or node[0][0].eqIdent("awaitne")): var newDiscard = node result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], - newDiscard[0], newDiscard, retFutureSym, node) + newDiscard[0], newDiscard, retFutureSym, node, + node[0][0].eqIdent("await")) else: discard for i in 0 ..< result.len: diff --git a/tests/testall.nim b/tests/testall.nim index 710afee..cc3bb9e 100644 --- a/tests/testall.nim +++ b/tests/testall.nim @@ -5,6 +5,6 @@ # Licensed under either of # Apache License, version 2.0, (LICENSE-APACHEv2) # MIT license (LICENSE-MIT) -import testsync, testsoon, testtime, testfut, testsignal, testaddress, - testdatagram, teststream, testserver, testbugs, testnet, +import testmacro, testsync, testsoon, testtime, testfut, testsignal, + testaddress, testdatagram, teststream, testserver, testbugs, testnet, testasyncstream diff --git a/tests/testmacro.nim b/tests/testmacro.nim new file mode 100644 index 0000000..ac50983 --- /dev/null +++ b/tests/testmacro.nim @@ -0,0 +1,80 @@ +# Chronos Test Suite +# (c) Copyright 2018-Present +# Status Research & Development GmbH +# +# Licensed under either of +# Apache License, version 2.0, (LICENSE-APACHEv2) +# MIT license (LICENSE-MIT) +import unittest +import ../chronos + +proc asyncRetValue(n: int): Future[int] {.async.} = + await sleepAsync(n.milliseconds) + result = n * 10 + +proc asyncRetVoid(n: int) {.async.} = + await sleepAsync(n.milliseconds) + +proc asyncRetExceptionValue(n: int): Future[int] {.async.} = + await sleepAsync(n.milliseconds) + result = n * 10 + if true: + raise newException(ValueError, "Test exception") + +proc asyncRetExceptionVoid(n: int) {.async.} = + await sleepAsync(n.milliseconds) + if true: + raise newException(ValueError, "Test exception") + +proc testAwait(): Future[bool] {.async.} = + var res: int + await asyncRetVoid(100) + res = await asyncRetValue(100) + if res != 1000: + return false + if (await asyncRetValue(100)) != 1000: + return false + try: + await asyncRetExceptionVoid(100) + return false + except ValueError: + discard + res = 0 + try: + var res = await asyncRetExceptionValue(100) + return false + except ValueError: + discard + if res != 0: + return false + return true + +proc testAwaitne(): Future[bool] {.async.} = + var res1: Future[void] + var res2: Future[int] + + res1 = awaitne asyncRetVoid(100) + res2 = awaitne asyncRetValue(100) + if res1.failed(): + return false + if res2.read() != 1000: + return false + + res1 = awaitne asyncRetExceptionVoid(100) + if not(res1.failed()): + return false + + res2 = awaitne asyncRetExceptionValue(100) + try: + var res = res2.read() + return false + except ValueError: + discard + + return true + +suite "Macro transformations test suite": + test "`await` command test": + check waitFor(testAwait()) == true + test "`awaitne` command test": + check waitFor(testAwaitne()) == true