tcomponent.nim 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. discard """
  2. output: '''`:)` @ 0,0
  3. FOO: blah'''
  4. """
  5. #
  6. # magic.nim
  7. #
  8. # bug #3729
  9. import macros, sequtils, tables
  10. import strutils
  11. import sugar, meta
  12. type
  13. Component = object
  14. fields: FieldSeq
  15. field_index: seq[string]
  16. procs: ProcSeq
  17. procs_index: seq[string]
  18. Registry = object
  19. field_index: seq[string]
  20. procs_index: seq[string]
  21. components: Table[string, Component]
  22. builtin: Component
  23. proc newRegistry(): Registry =
  24. result.field_index = @[]
  25. result.procs_index = @[]
  26. result.components = initTable[string, Component]()
  27. var registry {.compileTime.} = newRegistry()
  28. proc validateComponent(r: var Registry, name: string, c: Component) =
  29. if r.components.hasKey(name):
  30. let msg = "`component` macro cannot consume duplicated identifier: " & name
  31. raise newException(ValueError, msg)
  32. for field_name in c.field_index:
  33. if r.field_index.contains(field_name):
  34. let msg = "`component` macro cannot delcare duplicated field: " & field_name
  35. raise newException(ValueError, msg)
  36. r.field_index.add(field_name)
  37. for proc_name in c.procs_index:
  38. if r.procs_index.contains(proc_name):
  39. let msg = "`component` macro cannot delcare duplicated proc: " & proc_name
  40. raise newException(ValueError, msg)
  41. r.procs_index.add(proc_name)
  42. proc addComponent(r: var Registry, name: string, c: Component) =
  43. r.validateComponent(name, c)
  44. r.components.add(name, c)
  45. proc parse_component(body: NimNode): Component =
  46. result.field_index = @[]
  47. result.procs_index = @[]
  48. for node in body:
  49. case node.kind:
  50. of nnkVarSection:
  51. result.fields = newFieldSeq(node)
  52. for field in result.fields:
  53. result.field_index.add(field.identifier.name)
  54. of nnkMethodDef, nnkProcDef:
  55. let new_proc = meta.newProc(node)
  56. result.procs = result.procs & @[new_proc]
  57. for procdef in result.procs:
  58. result.procs_index.add(procdef.identifier.name)
  59. else: discard
  60. macro component*(name, body: untyped): typed =
  61. let component = parse_component(body)
  62. registry.addComponent($name, component)
  63. parseStmt("discard")
  64. macro component_builtins(body: untyped): typed =
  65. let builtin = parse_component(body)
  66. registry.field_index = builtin.field_index
  67. registry.procs_index = builtin.procs_index
  68. registry.builtin = builtin
  69. proc bind_methods*(component: var Component, identifier: Ident): seq[NimNode] =
  70. result = @[]
  71. for procdef in component.procs.mitems:
  72. let this_field = newField(newIdent("this"), identifier)
  73. procdef.params.insert(this_field, 0)
  74. result.add(procdef.render())
  75. macro bind_components*(type_name, component_names: untyped): typed =
  76. result = newStmtList()
  77. let identifier = newIdent(type_name)
  78. let components = newBracket(component_names)
  79. var entity_type = newTypeDef(identifier, true, "object", "RootObj")
  80. entity_type.fields = registry.builtin.fields
  81. for component_name, component in registry.components:
  82. if components.contains(newIdent(component_name)):
  83. entity_type.fields = entity_type.fields & component.fields
  84. # TODO why doesn't the following snippet work instead of the one above?
  85. # for name in components:
  86. # echo "Registering $1 to $2" % [name.name, identifier.name]
  87. # let component = registry.components[name.name]
  88. # entity_type.fields = entity_type.fields & component.fields
  89. let type_section: TypeDefSeq = @[entity_type]
  90. result.add type_section.render
  91. var builtin = registry.builtin
  92. let builtin_methods = bind_methods(builtin, identifier)
  93. for builtin_proc in builtin_methods:
  94. result.add(builtin_proc)
  95. echo "SIGSEV here"
  96. for component in registry.components.mvalues():
  97. for method_proc in bind_methods(component, identifier):
  98. result.add(method_proc)
  99. component_builtins:
  100. proc foo(msg: string) =
  101. echo "FOO: $1" % msg
  102. component position:
  103. var x*, y*: int
  104. component name:
  105. var name*: string
  106. proc render*(x, y: int) = echo "`$1` @ $2,$3" % [this.name, $x, $y]
  107. bind_components(Entity, [position, name])
  108. var e = new(Entity)
  109. e.name = ":)"
  110. e.render(e.x, e.y)
  111. e.foo("blah")