Implemented default arguments for async procs.

This commit is contained in:
Dominik Picheta 2013-07-28 14:41:09 +01:00
commit 426c2d6c01
2 changed files with 35 additions and 28 deletions

View file

@ -840,6 +840,7 @@ proc len*(disp: PDispatcher): int =
proc createRequestNode(varName, proc createRequestNode(varName,
reqArgs: string, sym: var PNimrodNode): PNimrodNode {.compiletime.} = reqArgs: string, sym: var PNimrodNode): PNimrodNode {.compiletime.} =
## Creates a constructor for a PRequest object, which will be then yielded.
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
# TODO: Using gensym here causes segfaults because hasException is not # TODO: Using gensym here causes segfaults because hasException is not
# initialised. # initialised.
@ -905,33 +906,19 @@ const typeDef =
proc transformCallWithArg(call: PNimrodNode, proc transformCallWithArg(call: PNimrodNode,
sym: var PNimrodNode): PNimrodNode {.compiletime.} = sym: var PNimrodNode): PNimrodNode {.compiletime.} =
## Transforms an async await call of a user-defined proc into a
## ``reqReg`` yield.
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
sym = gensym(nskVar, "argsToPass") sym = gensym(nskVar, "argsToPass")
# var argsToPass: P$#ArgObject
result.add newNimNode(nnkVarSection).add(
newNimNode(nnkIdentDefs).add(sym,
newIdentNode("P$#ArgObject" % [$call[1][0].ident]),
newNimNode(nnkEmpty)))
result.add newCall("new", sym)
for i in 1 .. call[1].len-1:
case call[1][i].kind
of nnkLiterals, nnkIdent:
let dotExpr = newDotExpr(sym,
newIdentNode("dummy" & $i))
result.add newAssignment(dotExpr, call[1][i])
else: assert false
# Add in a call to a pre-generated proc stub so that the compiler verifies # Add in a call to a pre-generated proc stub so that the compiler verifies
# the params for us :) # the params for us :)
# TODO: Make sure this doesn't actually get called, maybe inline it? # TODO: Inline this maybe?
var args: seq[PNimrodNode] = @[] var args: seq[PNimrodNode] = @[]
for i in 1 .. call[1].len-1: for i in 1 .. call[1].len-1:
args.add(call[1][i]) args.add(call[1][i])
result.add newCall(call[1][0], args) result.add newVarStmt(sym, newCall(call[1][0], args))
proc toYieldCall(n: PNimrodNode): seq[PNimrodNode] {.compileTime.} = proc toYieldCall(n: PNimrodNode): seq[PNimrodNode] {.compileTime.} =
## Transforms a call/command ## Transforms a call/command
@ -958,6 +945,11 @@ proc toYieldCall(n: PNimrodNode): seq[PNimrodNode] {.compileTime.} =
result.add yie result.add yie
proc transform(n: PNimrodNode): PNimrodNode {.compiletime.} = proc transform(n: PNimrodNode): PNimrodNode {.compiletime.} =
## Transforms body.
## Specifically it does the following:
##
## * Looks for 'await' and transforms it into a yield.
## * Handles arguments correctly.
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
expectKind(n, nnkStmtList) expectKind(n, nnkStmtList)
for i in 0 .. n.len-1: for i in 0 .. n.len-1:
@ -1029,10 +1021,22 @@ proc isDocumentation(n: PNimrodNode): bool {.compiletime.} =
proc createVerificationProc(procName: PNimrodNode, proc createVerificationProc(procName: PNimrodNode,
formalParams: PNimrodNode): PNimrodNode {.compiletime.} = formalParams: PNimrodNode): PNimrodNode {.compiletime.} =
# TODO: Export this stub if our async proc is exported? # TODO: Export this stub if our async proc is exported?
var kids: seq[PNimrodNode] = @[] # Generate list of parameters for the proc. First param is the return type.
for i in 0 .. formalParams.len-1: var params: seq[PNimrodNode] = @[newIdentNode("P$#ArgObject" % $procName.ident)]
kids.add(formalParams[i]) for i in 1 .. formalParams.len-1:
result = newProc(procName, kids) params.add(formalParams[i])
# Generate body. We construct the ArgObject here, this is done so that
# default variables of the async proc can be captured.
var body = newNimNode(nnkStmtList)
body.add newCall("new", newIdentNode("result"))
for i in 1 .. formalParams.len-1:
let dotExpr = newDotExpr(newIdentNode("result"),
newIdentNode("dummy" & $i))
body.add newAssignment(dotExpr, formalParams[i][0])
result = newProc(procName, params, body)
macro async*(n: stmt): stmt {.immediate.} = macro async*(n: stmt): stmt {.immediate.} =
expectKind(n, nnkProcDef) expectKind(n, nnkProcDef)
@ -1045,7 +1049,6 @@ macro async*(n: stmt): stmt {.immediate.} =
result[6] = parseStmt("nil") result[6] = parseStmt("nil")
return return
#echo("-------------")
result = newNimNode(nnkIteratorDef) result = newNimNode(nnkIteratorDef)
for i in 0 .. n.len-1: for i in 0 .. n.len-1:
result.add(copyNimTree(n[i])) result.add(copyNimTree(n[i]))
@ -1061,18 +1064,20 @@ macro async*(n: stmt): stmt {.immediate.} =
formalParams.add(params) formalParams.add(params)
result[3] = formalParams result[3] = formalParams
# Pragma # Closure pragma
result[4].add(newIdentNode(!"closure")) result[4].add(newIdentNode(!"closure"))
# Body
result[6] = newNimNode(nnkStmtList) result[6] = newNimNode(nnkStmtList)
# Declare variables based on the params that the async proc takes. # Declare variables based on the params that the async proc takes.
# i.e. extract them from the PRequest object.
if n[3].len > 1: if n[3].len > 1:
let args = declareArgsInBody($n[0].ident, n[3]) let args = declareArgsInBody($n[0].ident, n[3])
result[6].add(args) result[6].add(args)
# Body # Transform body
var body = transform(n[6]) var body = transform(n[6])
result[6].add(body) result[6].add(body)
@ -1081,7 +1086,9 @@ macro async*(n: stmt): stmt {.immediate.} =
let procDef = copyNimTree(result) let procDef = copyNimTree(result)
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
result.add(transformArgs($n[0].ident, n[3])) result.add(transformArgs($n[0].ident, n[3]))
# Generate a proc stub to verify that the user passes the correct params. # Generate a proc to verify that the user passes the correct params.
# The proc also constructs the ArgObject, this is so that default params
# can be captured into the ArgObject.
result.add createVerificationProc(n[0], n[3]) result.add createVerificationProc(n[0], n[3])
result.add procDef result.add procDef

View file

@ -1,6 +1,6 @@
import sockets, asyncio, strutils import sockets, asyncio, strutils
proc processRequest(client: PAsyncSocket, test: string, closeSock: bool) {.async.} = proc processRequest(client: PAsyncSocket, test: string, closeSock: bool = true) {.async.} =
assert test == "ahha" assert test == "ahha"
assert client != nil assert client != nil
echo("Test = ", test) echo("Test = ", test)
@ -25,7 +25,7 @@ proc processServer() {.async.} =
let client: PAsyncSocket = await(accept(sock)) let client: PAsyncSocket = await(accept(sock))
assert client != nil assert client != nil
await processRequest(client, "ahha", false) await processRequest(client, "ahha", false)
await processRequest(client, "ahha", true) await processRequest(client, "ahha")
var disp = newDispatcher(false) var disp = newDispatcher(false)
disp.register(processServer, nil) disp.register(processServer, nil)