Skip to content

Instantly share code, notes, and snippets.

@Varriount
Created April 3, 2014 20:08
Show Gist options
  • Save Varriount/9961883 to your computer and use it in GitHub Desktop.
Save Varriount/9961883 to your computer and use it in GitHub Desktop.
asFunc macro
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