File: tusingstatement.nim

package info (click to toggle)
nim 0.19.4-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 462,356 kB
  • sloc: sh: 11,089; ansic: 4,699; makefile: 706; python: 309; sql: 297; asm: 141; xml: 13
file content (87 lines) | stat: -rw-r--r-- 1,995 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
discard """
  file: "tusingstatement.nim"
  output: "Using test.Closing test."
"""

import
  macros

# This macro mimics the using statement from C#
#
# It's kept only as a test for the macro system
# Nim's destructors offer a mechanism for automatic
# disposal of resources.
#
macro autoClose(args: varargs[untyped]): untyped =
  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[NimNode]
    closingCalls : seq[NimNode]

  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 & ".")

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