Better param passing. Implemented send, readLine and support for
documentation stubs.
This commit is contained in:
parent
84ea209fe1
commit
d15e67e020
2 changed files with 100 additions and 45 deletions
|
|
@ -133,7 +133,7 @@ type
|
||||||
SockUDPBound
|
SockUDPBound
|
||||||
|
|
||||||
TRequestKind* = enum
|
TRequestKind* = enum
|
||||||
reqNil, reqReg, reqRead, reqWrite, reqReadLine, reqAccept
|
reqNil, reqReg, reqRead, reqWrite, reqReadLine, reqAccept, reqConnect
|
||||||
|
|
||||||
PRequest* = ref object
|
PRequest* = ref object
|
||||||
socket*: PAsyncSocket
|
socket*: PAsyncSocket
|
||||||
|
|
@ -154,9 +154,11 @@ type
|
||||||
toWrite*: string ## Request
|
toWrite*: string ## Request
|
||||||
written: int ## Internal data
|
written: int ## Internal data
|
||||||
of reqReadLine:
|
of reqReadLine:
|
||||||
line*: string ## Response
|
line*: string ## Response
|
||||||
of reqAccept:
|
of reqAccept:
|
||||||
client*: PAsyncSocket ## Response
|
client*: PAsyncSocket ## Response
|
||||||
|
of reqConnect:
|
||||||
|
address*: string ## Request
|
||||||
|
|
||||||
PWorker* = ref object
|
PWorker* = ref object
|
||||||
worker: iterator (x: PRequest): PRequest {.closure.}
|
worker: iterator (x: PRequest): PRequest {.closure.}
|
||||||
|
|
@ -820,6 +822,8 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool =
|
||||||
else:
|
else:
|
||||||
let toWrite = worker.lastReq.toWrite[written .. -1]
|
let toWrite = worker.lastReq.toWrite[written .. -1]
|
||||||
doSend(toWrite)
|
doSend(toWrite)
|
||||||
|
of reqConnect:
|
||||||
|
#execReq
|
||||||
of reqReg:
|
of reqReg:
|
||||||
assert false, "reqReg should have been processed already"
|
assert false, "reqReg should have been processed already"
|
||||||
of reqNil:
|
of reqNil:
|
||||||
|
|
@ -834,6 +838,17 @@ proc len*(disp: PDispatcher): int =
|
||||||
|
|
||||||
# ---- Async macro
|
# ---- 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.} =
|
proc toYieldVar(n: PNimrodNode): seq[PNimrodNode] {.compiletime.} =
|
||||||
## Transforms a var/let section
|
## Transforms a var/let section
|
||||||
## E.g:
|
## E.g:
|
||||||
|
|
@ -843,57 +858,50 @@ proc toYieldVar(n: PNimrodNode): seq[PNimrodNode] {.compiletime.} =
|
||||||
expectLen(n[0], 3) # IdentDefs
|
expectLen(n[0], 3) # IdentDefs
|
||||||
let insideAwait = n[0][2][1]
|
let insideAwait = n[0][2][1]
|
||||||
let reqCall = $insideAwait[0].ident
|
let reqCall = $insideAwait[0].ident
|
||||||
case reqCall
|
|
||||||
|
expectLen(insideAwait, 2)
|
||||||
|
let sockName = $insideAwait[1].ident
|
||||||
|
case reqCall.normalize
|
||||||
of "accept":
|
of "accept":
|
||||||
expectLen(insideAwait, 2)
|
|
||||||
let sockName = $insideAwait[1].ident
|
|
||||||
let acceptReqVar = "acceptReq"
|
let acceptReqVar = "acceptReq"
|
||||||
# TODO: Random var names which do not conflict. or wait for gensym?
|
# TODO: Random var names which do not conflict. or wait for gensym?
|
||||||
var reqObj = parseExpr(
|
result.add createRequestNode(acceptReqVar,
|
||||||
"""var $# = PRequest(socket: $#, kind: reqAccept,
|
"socket: $#, kind: reqAccept, client: nil" % sockName)
|
||||||
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])
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nnkLetSection:
|
of nnkLetSection:
|
||||||
result.add parseExpr("let $# = $#.client" % [$nameIdent, acceptReqVar])
|
result.add parseExpr("let $# = $#.client" % [$nameIdent, acceptReqVar])
|
||||||
of nnkVarSection:
|
of nnkVarSection:
|
||||||
result.add parseExpr("var $# = $#.client" % [$nameIdent, acceptReqVar])
|
result.add parseExpr("var $# = $#.client" % [$nameIdent, acceptReqVar])
|
||||||
else: error "Bad node kind in toYieldVar"
|
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:
|
else:
|
||||||
error(reqCall & " is not a valid async call")
|
error(reqCall & " is not a valid async call")
|
||||||
|
|
||||||
const typeDef =
|
const typeDef =
|
||||||
"""
|
"""
|
||||||
type
|
type
|
||||||
PArgObject = ref object of TObject
|
P$#ArgObject = ref object of TObject
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc transformCallWithArg(call: PNimrodNode): PNimrodNode {.compiletime.} =
|
proc transformCallWithArg(call: PNimrodNode): PNimrodNode {.compiletime.} =
|
||||||
result = parseStmt(typeDef)
|
result = newNimNode(nnkStmtList)
|
||||||
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[0][0][2][0][2] = RecList
|
result.add parseExpr("var argsToPass: P$#ArgObject" % [$call[1][0].ident])
|
||||||
result.add parseExpr("var argsToPass: PArgObject")
|
|
||||||
result.add parseExpr("new argsToPass")
|
result.add parseExpr("new argsToPass")
|
||||||
|
|
||||||
for i in 1 .. call[1].len-1:
|
for i in 1 .. call[1].len-1:
|
||||||
case call[1][i].kind
|
case call[1][i].kind
|
||||||
of nnkIdent:
|
of nnkLiterals, nnkIdent:
|
||||||
result.add parseExpr("argsToPass.$1 = $1" % [$call[1][i].ident])
|
|
||||||
of nnkLiterals:
|
|
||||||
let dotExpr = newDotExpr(newIdentNode("argsToPass"),
|
let dotExpr = newDotExpr(newIdentNode("argsToPass"),
|
||||||
newIdentNode("dummy" & $i))
|
newIdentNode("dummy" & $i))
|
||||||
result.add newAssignment(dotExpr, call[1][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"
|
if $n[0].ident != "await": error "'await' expected"
|
||||||
result = @[]
|
result = @[]
|
||||||
let callIdent = $n[1][0].ident
|
let callIdent = $n[1][0].ident
|
||||||
case callIdent
|
case callIdent.normalize
|
||||||
of "send": error("TODO")
|
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:
|
else:
|
||||||
result.add(transformCallWithArg(n))
|
result.add(transformCallWithArg(n))
|
||||||
# reqRegister
|
# reqRegister
|
||||||
|
|
@ -945,29 +958,53 @@ proc transform(n: PNimrodNode): PNimrodNode {.compiletime.} =
|
||||||
else:
|
else:
|
||||||
result.add(son)
|
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
|
## Transforms formal params into a typedef with a dummy type
|
||||||
## ``ref object of TObject``. The PRequest.param is then casted to it,
|
## ``ref object of TObject``. This is inserted above the proc definition.
|
||||||
## and immutable vars are defined as specified the proc's params.
|
|
||||||
expectKind(formalParams, nnkFormalParams)
|
expectKind(formalParams, nnkFormalParams)
|
||||||
result = parseStmt(typeDef)
|
result = parseStmt(typeDef % procName)
|
||||||
|
|
||||||
var RecList = newNimNode(nnkRecList)
|
var RecList = newNimNode(nnkRecList)
|
||||||
|
|
||||||
for i in 1 .. formalParams.len-1:
|
for i in 1 .. formalParams.len-1:
|
||||||
expectKind(formalParams[i], nnkIdentDefs)
|
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[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:
|
for i in 1 .. formalParams.len-1:
|
||||||
expectKind(formalParams[i], nnkIdentDefs)
|
expectKind(formalParams[i], nnkIdentDefs)
|
||||||
result.add(parseExpr("let $1: $2 = passedInParams.$1" %
|
result.add(parseExpr("let $1: $2 = passedInParams.$3" %
|
||||||
[$formalParams[i][0].ident, $formalParams[i][1].ident]))
|
[$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.} =
|
macro async*(n: stmt): stmt {.immediate.} =
|
||||||
expectKind(n, nnkProcDef)
|
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("-------------")
|
#echo("-------------")
|
||||||
result = newNimNode(nnkIteratorDef)
|
result = newNimNode(nnkIteratorDef)
|
||||||
for i in 0 .. n.len-1:
|
for i in 0 .. n.len-1:
|
||||||
|
|
@ -989,21 +1026,35 @@ macro async*(n: stmt): stmt {.immediate.} =
|
||||||
|
|
||||||
result[6] = newNimNode(nnkStmtList)
|
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:
|
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
|
# Body
|
||||||
var body = transform(n[6])
|
var body = transform(n[6])
|
||||||
result[6].add(body)
|
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 treeRepr(result)
|
||||||
echo result.toStrLit().strVal
|
echo result.toStrLit().strVal
|
||||||
|
|
||||||
# ---- Async macro end
|
# ---- 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:
|
when isMainModule:
|
||||||
|
|
||||||
proc testConnect(s: PAsyncSocket, no: int) =
|
proc testConnect(s: PAsyncSocket, no: int) =
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ import sockets, asyncio, strutils
|
||||||
proc processRequest(client: PAsyncSocket, test: string) {.async.} =
|
proc processRequest(client: PAsyncSocket, test: string) {.async.} =
|
||||||
assert test == "ahha"
|
assert test == "ahha"
|
||||||
assert client != nil
|
assert client != nil
|
||||||
echo(test)
|
echo("Test = ", test)
|
||||||
|
let line = await(readLine(client))
|
||||||
|
echo("Read: ", line)
|
||||||
|
|
||||||
|
await send(client, "Goodbye.\c\L")
|
||||||
client.close()
|
client.close()
|
||||||
|
|
||||||
proc processServer() {.async.} =
|
proc processServer() {.async.} =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue