summary refs log tree commit diff stats
path: root/tests/vm/tcomponent.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tests/vm/tcomponent.nim')
-rw-r--r--tests/vm/tcomponent.nim132
1 files changed, 132 insertions, 0 deletions
diff --git a/tests/vm/tcomponent.nim b/tests/vm/tcomponent.nim
new file mode 100644
index 000000000..efeba2a6d
--- /dev/null
+++ b/tests/vm/tcomponent.nim
@@ -0,0 +1,132 @@
+discard """
+  output: '''`:)` @ 0,0
+FOO: blah'''
+"""
+
+#
+# magic.nim
+#
+
+# bug #3729
+
+import macros, sequtils, tables
+import strutils
+import future, meta
+
+type
+  Component = object
+    fields: FieldSeq
+    field_index: seq[string]
+    procs: ProcSeq
+    procs_index: seq[string]
+
+  Registry = object
+    field_index: seq[string]
+    procs_index: seq[string]
+    components: Table[string, Component]
+    builtin: Component
+
+proc newRegistry(): Registry =
+  result.field_index = @[]
+  result.procs_index = @[]
+  result.components = initTable[string, Component]()
+
+var registry {.compileTime.} = newRegistry()
+
+proc validateComponent(r: var Registry, name: string, c: Component) =
+  if r.components.hasKey(name):
+    let msg = "`component` macro cannot consume duplicated identifier: " & name
+    raise newException(ValueError, msg)
+
+  for field_name in c.field_index:
+    if r.field_index.contains(field_name):
+      let msg = "`component` macro cannot delcare duplicated field: " & field_name
+      raise newException(ValueError, msg)
+    r.field_index.add(field_name)
+
+  for proc_name in c.procs_index:
+    if r.procs_index.contains(proc_name):
+      let msg = "`component` macro cannot delcare duplicated proc: " & proc_name
+      raise newException(ValueError, msg)
+    r.procs_index.add(proc_name)
+
+proc addComponent(r: var Registry, name: string, c: Component) =
+  r.validateComponent(name, c)
+  r.components.add(name, c)
+
+proc parse_component(body: NimNode): Component =
+  result.field_index = @[]
+  result.procs_index = @[]
+  for node in body:
+    case node.kind:
+      of nnkVarSection:
+        result.fields = newFieldSeq(node)
+        for field in result.fields:
+          result.field_index.add(field.identifier.name)
+      of nnkMethodDef, nnkProcDef:
+        let new_proc = meta.newProc(node)
+        result.procs = result.procs & @[new_proc]
+        for procdef in result.procs:
+          result.procs_index.add(procdef.identifier.name)
+      else: discard
+
+macro component*(name: expr, body: stmt): stmt {.immediate.} =
+  let component = parse_component(body)
+  registry.addComponent($name, component)
+  parseStmt("discard")
+
+macro component_builtins(body: stmt): stmt {.immediate.} =
+  let builtin = parse_component(body)
+  registry.field_index = builtin.field_index
+  registry.procs_index = builtin.procs_index
+  registry.builtin = builtin
+
+proc bind_methods*(component: var Component, identifier: Ident): seq[NimNode] =
+  result = @[]
+  for procdef in component.procs.mitems:
+    let this_field = newField(newIdent("this"), identifier)
+    procdef.params.insert(this_field, 0)
+    result.add(procdef.render())
+
+macro bind_components*(type_name, component_names: expr): stmt {.immediate.} =
+  result = newStmtList()
+  let identifier = newIdent(type_name)
+  let components = newBracket(component_names)
+  var entity_type = newTypeDef(identifier, true, "object", "RootObj")
+  entity_type.fields = registry.builtin.fields
+  for component_name, component in registry.components:
+    if components.contains(newIdent(component_name)):
+      entity_type.fields = entity_type.fields & component.fields
+  # TODO why doesn't the following snippet work instead of the one above?
+  # for name in components:
+  #   echo "Registering $1 to $2" % [name.name, identifier.name]
+  #   let component = registry.components[name.name]
+  #   entity_type.fields = entity_type.fields & component.fields
+  let type_section: TypeDefSeq = @[entity_type]
+  result.add type_section.render
+  var builtin = registry.builtin
+  let builtin_methods = bind_methods(builtin, identifier)
+  for builtin_proc in builtin_methods:
+    result.add(builtin_proc)
+  echo "SIGSEV here"
+  for component in registry.components.mvalues():
+    for method_proc in bind_methods(component, identifier):
+      result.add(method_proc)
+
+component_builtins:
+  proc foo(msg: string) =
+    echo "FOO: $1" % msg
+
+component position:
+  var x*, y*: int
+
+component name:
+  var name*: string
+  proc render*(x, y: int) = echo "`$1` @ $2,$3" % [this.name, $x, $y]
+
+bind_components(Entity, [position, name])
+
+var e = new(Entity)
+e.name = ":)"
+e.render(e.x, e.y)
+e.foo("blah")