Await is now supported in try statements.
This commit is contained in:
parent
543687f345
commit
a21289f5d5
1 changed files with 79 additions and 26 deletions
|
|
@ -37,10 +37,10 @@ type
|
||||||
PFutureBase* = ref object of PObject
|
PFutureBase* = ref object of PObject
|
||||||
cb: proc () {.closure,gcsafe.}
|
cb: proc () {.closure,gcsafe.}
|
||||||
finished: bool
|
finished: bool
|
||||||
|
error*: ref EBase
|
||||||
|
|
||||||
PFuture*[T] = ref object of PFutureBase
|
PFuture*[T] = ref object of PFutureBase
|
||||||
value: T
|
value: T
|
||||||
error*: ref EBase # TODO: This shouldn't be necessary, generics bug?
|
|
||||||
|
|
||||||
proc newFuture*[T](): PFuture[T] =
|
proc newFuture*[T](): PFuture[T] =
|
||||||
## Creates a new future.
|
## Creates a new future.
|
||||||
|
|
@ -114,7 +114,7 @@ proc finished*[T](future: PFuture[T]): bool =
|
||||||
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
|
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
|
||||||
future.finished
|
future.finished
|
||||||
|
|
||||||
proc failed*[T](future: PFuture[T]): bool =
|
proc failed*(future: PFutureBase): bool =
|
||||||
## Determines whether ``future`` completed with an error.
|
## Determines whether ``future`` completed with an error.
|
||||||
future.error != nil
|
future.error != nil
|
||||||
|
|
||||||
|
|
@ -764,25 +764,56 @@ proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] =
|
||||||
template createCb*(retFutureSym, iteratorNameSym: expr): stmt {.immediate.} =
|
template createCb*(retFutureSym, iteratorNameSym: expr): stmt {.immediate.} =
|
||||||
var nameIterVar = iteratorNameSym
|
var nameIterVar = iteratorNameSym
|
||||||
proc cb {.closure,gcsafe.} =
|
proc cb {.closure,gcsafe.} =
|
||||||
if not nameIterVar.finished:
|
try:
|
||||||
var next = nameIterVar()
|
if not nameIterVar.finished:
|
||||||
if next == nil:
|
var next = nameIterVar()
|
||||||
assert retFutureSym.finished, "Async procedure's return Future was not finished."
|
if next == nil:
|
||||||
else:
|
assert retFutureSym.finished, "Async procedure's return Future was not finished."
|
||||||
next.callback = cb
|
else:
|
||||||
|
next.callback = cb
|
||||||
|
except:
|
||||||
|
retFutureSym.fail(getCurrentException())
|
||||||
cb()
|
cb()
|
||||||
|
|
||||||
|
proc generateExceptionCheck(futSym,
|
||||||
|
exceptBranch, rootReceiver: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
if exceptBranch == nil:
|
||||||
|
result = rootReceiver
|
||||||
|
else:
|
||||||
|
if exceptBranch[0].kind == nnkStmtList:
|
||||||
|
result = newIfStmt(
|
||||||
|
(newDotExpr(futSym, newIdentNode("failed")),
|
||||||
|
exceptBranch[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
expectKind(exceptBranch[1], nnkStmtList)
|
||||||
|
result = newIfStmt(
|
||||||
|
(newDotExpr(futSym, newIdentNode("failed")),
|
||||||
|
newIfStmt(
|
||||||
|
(infix(newDotExpr(futSym, newIdentNode("error")), "of", exceptBranch[0]),
|
||||||
|
exceptBranch[1])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
let elseNode = newNimNode(nnkElse)
|
||||||
|
elseNode.add newNimNode(nnkStmtList)
|
||||||
|
elseNode[0].add rootReceiver
|
||||||
|
result.add elseNode
|
||||||
|
|
||||||
template createVar(futSymName: string, asyncProc: PNimrodNode,
|
template createVar(futSymName: string, asyncProc: PNimrodNode,
|
||||||
valueReceiver: expr) {.immediate, dirty.} =
|
valueReceiver, rootReceiver: expr) {.immediate, dirty.} =
|
||||||
# TODO: Used template here due to bug #926
|
|
||||||
result = newNimNode(nnkStmtList)
|
result = newNimNode(nnkStmtList)
|
||||||
var futSym = genSym(nskVar, "future")
|
var futSym = genSym(nskVar, "future")
|
||||||
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
|
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
|
||||||
result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x>
|
result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x>
|
||||||
valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
|
valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
|
||||||
|
result.add generateExceptionCheck(futSym, exceptBranch, rootReceiver)
|
||||||
|
|
||||||
proc processBody(node, retFutureSym: PNimrodNode,
|
proc processBody(node, retFutureSym: PNimrodNode,
|
||||||
subtypeName: string): PNimrodNode {.compileTime.} =
|
subtypeName: string,
|
||||||
|
exceptBranch: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
#echo(node.treeRepr)
|
||||||
result = node
|
result = node
|
||||||
case node.kind
|
case node.kind
|
||||||
of nnkReturnStmt:
|
of nnkReturnStmt:
|
||||||
|
|
@ -795,7 +826,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
|
||||||
result.add newCall(newIdentNode("complete"), retFutureSym)
|
result.add newCall(newIdentNode("complete"), retFutureSym)
|
||||||
else:
|
else:
|
||||||
result.add newCall(newIdentNode("complete"), retFutureSym,
|
result.add newCall(newIdentNode("complete"), retFutureSym,
|
||||||
node[0].processBody(retFutureSym, subtypeName))
|
node[0].processBody(retFutureSym, subtypeName, exceptBranch))
|
||||||
|
|
||||||
result.add newNimNode(nnkReturnStmt).add(newNilLit())
|
result.add newNimNode(nnkReturnStmt).add(newNilLit())
|
||||||
return # Don't process the children of this return stmt
|
return # Don't process the children of this return stmt
|
||||||
|
|
@ -808,16 +839,16 @@ proc processBody(node, retFutureSym: PNimrodNode,
|
||||||
of nnkCall:
|
of nnkCall:
|
||||||
# await foo(p, x)
|
# await foo(p, x)
|
||||||
var futureValue: PNimrodNode
|
var futureValue: PNimrodNode
|
||||||
createVar("future" & $node[1][0].toStrLit, node[1], futureValue)
|
createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
|
||||||
result.add futureValue
|
futureValue)
|
||||||
else:
|
else:
|
||||||
error("Invalid node kind in 'await', got: " & $node[1].kind)
|
error("Invalid node kind in 'await', got: " & $node[1].kind)
|
||||||
elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and
|
elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and
|
||||||
node[1][0].ident == !"await":
|
node[1][0].ident == !"await":
|
||||||
# foo await x
|
# foo await x
|
||||||
var newCommand = node
|
var newCommand = node
|
||||||
createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1])
|
createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
|
||||||
result.add newCommand
|
newCommand)
|
||||||
|
|
||||||
of nnkVarSection, nnkLetSection:
|
of nnkVarSection, nnkLetSection:
|
||||||
case node[0][2].kind
|
case node[0][2].kind
|
||||||
|
|
@ -826,8 +857,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
|
||||||
# var x = await y
|
# var x = await y
|
||||||
var newVarSection = node # TODO: Should this use copyNimNode?
|
var newVarSection = node # TODO: Should this use copyNimNode?
|
||||||
createVar("future" & $node[0][0].ident, node[0][2][1],
|
createVar("future" & $node[0][0].ident, node[0][2][1],
|
||||||
newVarSection[0][2])
|
newVarSection[0][2], newVarSection)
|
||||||
result.add newVarSection
|
|
||||||
else: discard
|
else: discard
|
||||||
of nnkAsgn:
|
of nnkAsgn:
|
||||||
case node[1].kind
|
case node[1].kind
|
||||||
|
|
@ -835,19 +865,42 @@ proc processBody(node, retFutureSym: PNimrodNode,
|
||||||
if node[1][0].ident == !"await":
|
if node[1][0].ident == !"await":
|
||||||
# x = await y
|
# x = await y
|
||||||
var newAsgn = node
|
var newAsgn = node
|
||||||
createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1])
|
createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn)
|
||||||
result.add newAsgn
|
|
||||||
else: discard
|
else: discard
|
||||||
of nnkDiscardStmt:
|
of nnkDiscardStmt:
|
||||||
# discard await x
|
# discard await x
|
||||||
if node[0][0].kind == nnkIdent and node[0][0].ident == !"await":
|
if node[0][0].kind == nnkIdent and node[0][0].ident == !"await":
|
||||||
var dummy = newNimNode(nnkStmtList)
|
var newDiscard = node
|
||||||
createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy)
|
createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
|
||||||
|
newDiscard[0], newDiscard)
|
||||||
|
of nnkTryStmt:
|
||||||
|
# try: await x; except: ...
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
proc processForTry(n: PNimrodNode, i: var int,
|
||||||
|
res: PNimrodNode): bool {.compileTime.} =
|
||||||
|
result = false
|
||||||
|
while i < n[0].len:
|
||||||
|
var processed = processBody(n[0][i], retFutureSym, subtypeName, n[1])
|
||||||
|
if processed.kind != n[0][i].kind or processed.len != n[0][i].len:
|
||||||
|
expectKind(processed, nnkStmtList)
|
||||||
|
expectKind(processed[2][1], nnkElse)
|
||||||
|
i.inc
|
||||||
|
discard processForTry(n, i, processed[2][1][0])
|
||||||
|
res.add processed
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
res.add n[0][i]
|
||||||
|
i.inc
|
||||||
|
var i = 0
|
||||||
|
if not processForTry(node, i, result):
|
||||||
|
var temp = node
|
||||||
|
temp[0] = result
|
||||||
|
result = temp
|
||||||
|
return
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
for i in 0 .. <result.len:
|
for i in 0 .. <result.len:
|
||||||
result[i] = processBody(result[i], retFutureSym, subtypeName)
|
result[i] = processBody(result[i], retFutureSym, subtypeName, exceptBranch)
|
||||||
#echo(treeRepr(result))
|
|
||||||
|
|
||||||
proc getName(node: PNimrodNode): string {.compileTime.} =
|
proc getName(node: PNimrodNode): string {.compileTime.} =
|
||||||
case node.kind
|
case node.kind
|
||||||
|
|
@ -894,7 +947,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
# -> <proc_body>
|
# -> <proc_body>
|
||||||
# -> complete(retFuture, result)
|
# -> complete(retFuture, result)
|
||||||
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
|
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
|
||||||
var procBody = prc[6].processBody(retFutureSym, subtypeName)
|
var procBody = prc[6].processBody(retFutureSym, subtypeName, nil)
|
||||||
if subtypeName != "void":
|
if subtypeName != "void":
|
||||||
procBody.insert(0, newNimNode(nnkVarSection).add(
|
procBody.insert(0, newNimNode(nnkVarSection).add(
|
||||||
newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
|
newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue