mclass_macro.nim 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import macros
  2. macro class*(head, body: untyped): untyped =
  3. # The macro is immediate, since all its parameters are untyped.
  4. # This means, it doesn't resolve identifiers passed to it.
  5. var typeName, baseName: NimNode
  6. # flag if object should be exported
  7. var exported: bool
  8. if head.kind == nnkInfix and head[0].kind == nnkIdent and $head[0] == "of":
  9. # `head` is expression `typeName of baseClass`
  10. # echo head.treeRepr
  11. # --------------------
  12. # Infix
  13. # Ident !"of"
  14. # Ident !"Animal"
  15. # Ident !"RootObj"
  16. typeName = head[1]
  17. baseName = head[2]
  18. elif head.kind == nnkInfix and head[0].kind == nnkIdent and
  19. $head[0] == "*" and head[2].kind == nnkPrefix and
  20. head[2][0].kind == nnkIdent and $head[2][0] == "of":
  21. # `head` is expression `typeName* of baseClass`
  22. # echo head.treeRepr
  23. # --------------------
  24. # Infix
  25. # Ident !"*"
  26. # Ident !"Animal"
  27. # Prefix
  28. # Ident !"of"
  29. # Ident !"RootObj"
  30. typeName = head[1]
  31. baseName = head[2][1]
  32. exported = true
  33. else:
  34. quit "Invalid node: " & head.lispRepr
  35. # The following prints out the AST structure:
  36. #
  37. # import macros
  38. # dumptree:
  39. # type X = ref object of Y
  40. # z: int
  41. # --------------------
  42. # StmtList
  43. # TypeSection
  44. # TypeDef
  45. # Ident !"X"
  46. # Empty
  47. # RefTy
  48. # ObjectTy
  49. # Empty
  50. # OfInherit
  51. # Ident !"Y"
  52. # RecList
  53. # IdentDefs
  54. # Ident !"z"
  55. # Ident !"int"
  56. # Empty
  57. # create a type section in the result
  58. result = newNimNode(nnkStmtList)
  59. result.add(
  60. if exported:
  61. # mark `typeName` with an asterisk
  62. quote do:
  63. type `typeName`* = ref object of `baseName`
  64. else:
  65. quote do:
  66. type `typeName` = ref object of `baseName`
  67. )
  68. # echo treeRepr(body)
  69. # --------------------
  70. # StmtList
  71. # VarSection
  72. # IdentDefs
  73. # Ident !"name"
  74. # Ident !"string"
  75. # Empty
  76. # IdentDefs
  77. # Ident !"age"
  78. # Ident !"int"
  79. # Empty
  80. # MethodDef
  81. # Ident !"vocalize"
  82. # Empty
  83. # Empty
  84. # FormalParams
  85. # Ident !"string"
  86. # Empty
  87. # Empty
  88. # StmtList
  89. # StrLit ...
  90. # MethodDef
  91. # Ident !"age_human_yrs"
  92. # Empty
  93. # Empty
  94. # FormalParams
  95. # Ident !"int"
  96. # Empty
  97. # Empty
  98. # StmtList
  99. # DotExpr
  100. # Ident !"this"
  101. # Ident !"age"
  102. # var declarations will be turned into object fields
  103. var recList = newNimNode(nnkRecList)
  104. # expected name of constructor
  105. let ctorName = newIdentNode("new" & $typeName)
  106. # Iterate over the statements, adding `this: T`
  107. # to the parameters of functions, unless the
  108. # function is a constructor
  109. for node in body.children:
  110. case node.kind:
  111. of nnkMethodDef, nnkProcDef:
  112. # check if it is the ctor proc
  113. if node.name.kind != nnkAccQuoted and node.name.basename == ctorName:
  114. # specify the return type of the ctor proc
  115. node.params[0] = typeName
  116. else:
  117. # inject `self: T` into the arguments
  118. node.params.insert(1, newIdentDefs(ident("self"), typeName))
  119. result.add(node)
  120. of nnkVarSection:
  121. # variables get turned into fields of the type.
  122. for n in node.children:
  123. recList.add(n)
  124. else:
  125. result.add(node)
  126. # Inspect the tree structure:
  127. #
  128. # echo result.treeRepr
  129. # --------------------
  130. # StmtList
  131. # TypeSection
  132. # TypeDef
  133. # Ident !"Animal"
  134. # Empty
  135. # RefTy
  136. # ObjectTy
  137. # Empty
  138. # OfInherit
  139. # Ident !"RootObj"
  140. # Empty <= We want to replace this
  141. # MethodDef
  142. # ...
  143. result[0][0][2][0][2] = recList
  144. # Lets inspect the human-readable version of the output
  145. #echo repr(result)