summary refs log blame commit diff stats
path: root/tests/run/tusingstatement.nim
blob: b9d4663775a41bb810eb3660f867489ad639f887 (plain) (tree)



























                                                                        

                                          
                


                                                                       

                 






                                   

                                  






































                                                                   
                    

















                                             
discard """
  file: "tusingstatement.nim"
  output: "Using test.Closing test."
"""

import 
  macros

# This macro mimics the using statement from C#
#
# XXX: 
#  It doen't match the C# version exactly yet.
#  In particular, it's not recursive, which prevents it from dealing 
#  with exceptions thrown from the variable initializers when multiple.
#  variables are used.
#
#  Also, since nimrod relies less on exceptions in general, a more
#  idiomatic definition could be:
#  var x = init()
#  if opened(x): 
#    try:
#      body
#    finally:
#      close(x)
#
#  `opened` here could be an overloaded proc which any type can define.
#  A common practice can be returing an Optional[Resource] obj for which
#  `opened` is defined to `optional.hasValue`
macro using(e: expr): stmt {.immediate.} =
  let e = callsite()
  if e.len != 3:
    error "Using statement: unexpected number of arguments. Got " &
      $e.len & ", expected: 1 or more variable assignments and a block"

  var args = e
  var body = e[2]
  
  var 
    variables : seq[PNimrodNode]
    closingCalls : seq[PNimrodNode]

  newSeq(variables, 0)
  newSeq(closingCalls, 0)
  
  for i in countup(1, args.len-2):
    if args[i].kind == nnkExprEqExpr:
      var varName = args[i][0]
      var varValue = args[i][1]
 
      var varAssignment = newNimNode(nnkIdentDefs)
      varAssignment.add(varName)
      varAssignment.add(newNimNode(nnkEmpty)) # empty means no type
      varAssignment.add(varValue)
      variables.add(varAssignment)

      closingCalls.add(newCall(!"close", varName))
    else:
      error "Using statement: Unexpected expression. Got " &
        $args[i].kind & " instead of assignment."
  
  var varSection = newNimNode(nnkVarSection)
  varSection.add(variables)

  var finallyBlock = newNimNode(nnkStmtList)
  finallyBlock.add(closingCalls)

  # XXX: Use a template here once getAst is working properly
  var targetAst = parseStmt"""block:
    var
      x = foo()
      y = bar()

    try:
      body()

    finally:
      close x
      close y
  """

  targetAst[0][1][0] = varSection
  targetAst[0][1][1][0] = body
  targetAst[0][1][1][1][0] = finallyBlock
  
  result = targetAst

type 
  TResource* = object
    field*: string

proc openResource(param: string): TResource =
  result.field = param

proc close(r: var TResource) =
  write(stdout, "Closing " & r.field & ".")

proc use(r: var TResource) =
  write(stdout, "Using " & r.field & ".")

using(r = openResource("test")):
  use r