Better param passing. Implemented send, readLine and support for

documentation stubs.
This commit is contained in:
Dominik Picheta 2013-07-21 13:58:54 +01:00
commit d15e67e020
2 changed files with 100 additions and 45 deletions

View file

@ -133,7 +133,7 @@ type
SockUDPBound
TRequestKind* = enum
reqNil, reqReg, reqRead, reqWrite, reqReadLine, reqAccept
reqNil, reqReg, reqRead, reqWrite, reqReadLine, reqAccept, reqConnect
PRequest* = ref object
socket*: PAsyncSocket
@ -154,10 +154,12 @@ type
toWrite*: string ## Request
written: int ## Internal data
of reqReadLine:
line*: string ## Response
line*: string ## Response
of reqAccept:
client*: PAsyncSocket ## Response
of reqConnect:
address*: string ## Request
PWorker* = ref object
worker: iterator (x: PRequest): PRequest {.closure.}
x: PRequest
@ -820,6 +822,8 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool =
else:
let toWrite = worker.lastReq.toWrite[written .. -1]
doSend(toWrite)
of reqConnect:
#execReq
of reqReg:
assert false, "reqReg should have been processed already"
of reqNil:
@ -834,6 +838,17 @@ proc len*(disp: PDispatcher): int =
# ---- Async macro
proc createRequestNode(varName,
reqArgs: string): PNimrodNode {.compiletime.} =
result = newNimNode(nnkStmtList)
var reqObj = parseExpr(
"""var $# = PRequest($#)""" %
[varName, reqArgs])
result.add reqObj
result.add parseExpr("yield $#" % [varName])
# Check for exception
result.add parseStmt("if $1.hasException: raise $1.exc" % [varName])
proc toYieldVar(n: PNimrodNode): seq[PNimrodNode] {.compiletime.} =
## Transforms a var/let section
## E.g:
@ -843,57 +858,50 @@ proc toYieldVar(n: PNimrodNode): seq[PNimrodNode] {.compiletime.} =
expectLen(n[0], 3) # IdentDefs
let insideAwait = n[0][2][1]
let reqCall = $insideAwait[0].ident
case reqCall
expectLen(insideAwait, 2)
let sockName = $insideAwait[1].ident
case reqCall.normalize
of "accept":
expectLen(insideAwait, 2)
let sockName = $insideAwait[1].ident
let acceptReqVar = "acceptReq"
# TODO: Random var names which do not conflict. or wait for gensym?
var reqObj = parseExpr(
"""var $# = PRequest(socket: $#, kind: reqAccept,
client: nil)""" %
[acceptReqVar, sockName])
result.add reqObj
result.add parseExpr("yield $#" % [acceptReqVar])
# Check for exception
result.add parseStmt("if $1.hasException: raise $1.exc" % [acceptReqVar])
result.add createRequestNode(acceptReqVar,
"socket: $#, kind: reqAccept, client: nil" % sockName)
case n.kind
of nnkLetSection:
result.add parseExpr("let $# = $#.client" % [$nameIdent, acceptReqVar])
of nnkVarSection:
result.add parseExpr("var $# = $#.client" % [$nameIdent, acceptReqVar])
else: error "Bad node kind in toYieldVar"
of "readline":
let readReqVar = "readLineReq"
# TODO: Random var names which do not conflict. or wait for gensym?
result.add createRequestNode(readReqVar,
"socket: $#, kind: reqReadLine, line: \"\"" % sockName)
case n.kind
of nnkLetSection:
result.add parseExpr("let $# = $#.line" % [$nameIdent, readReqVar])
of nnkVarSection:
result.add parseExpr("var $# = $#.line" % [$nameIdent, readReqVar])
else: error "Bad node kind in toYieldVar"
else:
error(reqCall & " is not a valid async call")
const typeDef =
"""
type
PArgObject = ref object of TObject
P$#ArgObject = ref object of TObject
"""
proc transformCallWithArg(call: PNimrodNode): PNimrodNode {.compiletime.} =
result = parseStmt(typeDef)
var RecList = newNimNode(nnkRecList)
for i in 1 .. call[1].len-1:
var typeOf = newNimNode(nnkTypeOfExpr).add(newNimNode(nnkPar))
typeOf[0].add(call[1][i])
case call[1][i].kind
of nnkIdent:
RecList.add newIdentDefs(call[1][i], typeOf)
of nnkLiterals:
RecList.add newIdentDefs(newIdentNode("dummy" & $i), typeOf)
else: assert false
result = newNimNode(nnkStmtList)
result[0][0][2][0][2] = RecList
result.add parseExpr("var argsToPass: PArgObject")
result.add parseExpr("var argsToPass: P$#ArgObject" % [$call[1][0].ident])
result.add parseExpr("new argsToPass")
for i in 1 .. call[1].len-1:
case call[1][i].kind
of nnkIdent:
result.add parseExpr("argsToPass.$1 = $1" % [$call[1][i].ident])
of nnkLiterals:
of nnkLiterals, nnkIdent:
let dotExpr = newDotExpr(newIdentNode("argsToPass"),
newIdentNode("dummy" & $i))
result.add newAssignment(dotExpr, call[1][i])
@ -904,8 +912,13 @@ proc toYieldCall(n: PNimrodNode): seq[PNimrodNode] {.compileTime.} =
if $n[0].ident != "await": error "'await' expected"
result = @[]
let callIdent = $n[1][0].ident
case callIdent
of "send": error("TODO")
case callIdent.normalize
of "send":
let socketName = $n[1][1].ident
let toWrite = n[1][2]
result.add createRequestNode("sendReq",
"socket: $#, kind: reqWrite, toWrite: $#" %
[socketName, $(toWrite.toStrLit)])
else:
result.add(transformCallWithArg(n))
# reqRegister
@ -945,29 +958,53 @@ proc transform(n: PNimrodNode): PNimrodNode {.compiletime.} =
else:
result.add(son)
proc transformArgs(formalParams: PNimrodNode): PNimrodNode {.compiletime.} =
proc transformArgs(procName: string,
formalParams: PNimrodNode): PNimrodNode {.compiletime.} =
## Transforms formal params into a typedef with a dummy type
## ``ref object of TObject``. The PRequest.param is then casted to it,
## and immutable vars are defined as specified the proc's params.
## ``ref object of TObject``. This is inserted above the proc definition.
expectKind(formalParams, nnkFormalParams)
result = parseStmt(typeDef)
result = parseStmt(typeDef % procName)
var RecList = newNimNode(nnkRecList)
for i in 1 .. formalParams.len-1:
expectKind(formalParams[i], nnkIdentDefs)
RecList.add(formalParams[i])
RecList.add(newIdentDefs(newIdentNode("dummy" & $i), formalParams[i][1]))
# TODO: Add comment with the original param name?
result[0][0][2][0][2] = RecList
result.add(parseExpr("let passedInParams = cast[PArgObject](x.param)"))
proc declareArgsInBody(procName: string,
formalParams: PNimrodNode): PNimrodNode {.compiletime.} =
## Creates a local immutable var by casting the PRequest.param.
## Immutable vars are then defined as specified in the proc's params.
result = newNimNode(nnkStmtList)
result.add(parseExpr("let passedInParams = P$#ArgObject(x.param)" % procName))
# Fields take the form ``dummy<i>``.
for i in 1 .. formalParams.len-1:
expectKind(formalParams[i], nnkIdentDefs)
result.add(parseExpr("let $1: $2 = passedInParams.$1" %
[$formalParams[i][0].ident, $formalParams[i][1].ident]))
result.add(parseExpr("let $1: $2 = passedInParams.$3" %
[$formalParams[i][0].ident, $formalParams[i][1].ident,
"dummy" & $i]))
proc isDocumentation(n: PNimrodNode): bool {.compiletime.} =
## Determines whether this proc def is a docs stub.
result = true
for i in 0 .. n[6].len-1:
if n[6][i].kind != nnkCommentStmt:
return false
macro async*(n: stmt): stmt {.immediate.} =
expectKind(n, nnkProcDef)
#echo(treeRepr(n))
if n.isDocumentation():
# Documentation stub?
# TODO: Give it an async tag?
# TODO: Doc strings are not generated in doc2
result = n
result[6] = parseStmt("nil")
return
#echo("-------------")
result = newNimNode(nnkIteratorDef)
for i in 0 .. n.len-1:
@ -989,21 +1026,35 @@ macro async*(n: stmt): stmt {.immediate.} =
result[6] = newNimNode(nnkStmtList)
# Transform any params passed to the proc into a typedef.
# Declare variables based on the params that the async proc takes.
if n[3].len > 1:
let transfArgs = transformArgs(n[3])
let args = declareArgsInBody($n[0].ident, n[3])
result[6].add(transfArgs)
result[6].add(args)
# Body
var body = transform(n[6])
result[6].add(body)
# Add typedef above the proc def for parameters.
if n[3].len > 1:
let procDef = copyNimTree(result)
result = newNimNode(nnkStmtList)
result.add(transformArgs($n[0].ident, n[3]))
result.add procDef
#echo treeRepr(result)
echo result.toStrLit().strVal
# ---- Async macro end
proc send*(socket: PAsyncSocket, text: string) {.async.} =
## Sends ``text`` to ``socket`` asynchronously.
proc accept*(socket: PAsyncSocket): PAsyncSocket {.async.} =
## Accepts a client connecting to a server socket asynchronously.
## Returns that client.
when isMainModule:
proc testConnect(s: PAsyncSocket, no: int) =

View file

@ -2,7 +2,11 @@ import sockets, asyncio, strutils
proc processRequest(client: PAsyncSocket, test: string) {.async.} =
assert test == "ahha"
assert client != nil
echo(test)
echo("Test = ", test)
let line = await(readLine(client))
echo("Read: ", line)
await send(client, "Goodbye.\c\L")
client.close()
proc processServer() {.async.} =