Created
April 3, 2014 20:08
-
-
Save Varriount/9961883 to your computer and use it in GitHub Desktop.
asFunc macro
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import macros | |
| import strutils | |
| macro asFunc(baseProc: stmt): stmt {.immediate.} = | |
| ## Analyses a procedure which takes a var parameter as a 'result' parameter, | |
| ## and generates an additional procedure which lacks the var parameter and | |
| ## calls the original procedure with a nil variable value. | |
| ## | |
| ## The target procedure *must* have a `var` type parameter named `result` | |
| ## in its parameter list. The parameter may be in any normally allowable | |
| ## position within the parameter list. | |
| ## | |
| ## Example: | |
| ## ``` | |
| ## proc appendTwice(s: string, result: var string) {.asFunc.} = | |
| ## if result.isNil: | |
| ## result = "" | |
| ## | |
| ## result.add(s) | |
| ## result.add(s) | |
| ## | |
| ## # The procedure can be called normally, with an already existing variable: | |
| ## var y = "Hello " | |
| ## var x = "World" | |
| ## appendTwice(x, y) | |
| ## echo(y) # Prints "Hello WorldWorld" | |
| ## | |
| ## # Or the procedure can be called without the var parameter: | |
| ## echo(appendTwice("Hello")) # Prints "Hello Hello" | |
| ## | |
| ## ``` | |
| result = newStmtList() | |
| result.add(baseProc) | |
| ## Search the base procedure's params for the 'result' argument. | |
| ## Also, construct a call to the procedure. | |
| var | |
| resultParam: PNimrodNode | |
| resultIndice: int | |
| resultCall = newCall(baseProc.name.baseName) | |
| for i in 1 .. <baseProc.params.len: | |
| template node: expr = baseProc.params[i] | |
| resultCall.add(node[0]) | |
| if resultParam.isNil() and "result".eqIdent(normalize($ node[0])): | |
| resultParam = node | |
| resultIndice = i | |
| if node[1].kind != nnkVarTy: | |
| macros.error("\n " & baseProc.lineInfo() & " Error: 'result' parameter not a 'var' type.") | |
| if isNil(resultParam): | |
| macros.error("\n " & baseProc.lineInfo() & " Error: 'result' parameter could not be found.") | |
| # Generate a new procedure, with the type of result argument moved to the | |
| # return value. | |
| var otherProc = baseProc.copy() | |
| otherProc.body= newStmtList() | |
| otherProc.params.del(resultIndice) | |
| otherProc.params[0] = resultParam[1][0] | |
| # If needed, add an initial assignment to the result variable which represents | |
| # the default value. | |
| if resultParam[2].kind != nnkEmpty: | |
| otherProc.body.add( | |
| newAssignment( | |
| newIdentNode("result"), | |
| resultParam[2].copy() | |
| ) | |
| ) | |
| # Generate the body of the new procedure, which is a call to the base | |
| # Procedure. | |
| otherProc.body.add(resultCall) | |
| result.add(otherProc) | |
| # echo(repr(result)) | |
| # echo(treeRepr(result)) | |
| proc appendTwice(s: string, result: var string) {.asFunc.} = | |
| if result.isNil: | |
| result = "" | |
| result.add(s) | |
| result.add(s) | |
| # The procedure can be called normally, with an already existing variable: | |
| var y = "Hello " | |
| var x = "World" | |
| appendTwice(x, y) | |
| echo(y) # Prints "Hello WorldWorld" | |
| # Or the procedure can be called without the var parameter: | |
| echo(appendTwice("Hello ")) # Prints "Hello Hello" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment