Commits

rkruppe committed 920a93e

Refactoring to enable support for GameEnity and similar shenanigan

  • Participants
  • Parent commits 0c058bd
  • Branches experiments

Comments (0)

Files changed (1)

tutagx/meta/from_yaml.py

+import abc
 from collections import namedtuple
 import itertools
 import yaml
     # which refer to the placeholder, cycles would be inevitable.)
 
 
+class ObjectCreationStrategy(metaclass=abc.ABCMeta):
+    def __init__(self, emit):
+        self._emit = emit
+
+    @abc.abstractmethod
+    def construct(self, target, cls):
+        pass
+
+    @abc.abstractmethod
+    def add_attributes(self, target, params):
+        pass
+
+    @abc.abstractmethod
+    def finish(self, obj):
+        pass
+
+    @property
+    def extra_args(self):
+        return ()
+
+
+class PlainObjectCreation(ObjectCreationStrategy):
+    def construct(self, target, cls):
+        self._emit.linef(
+            '{} = classes[{!r}, {!r}](already_complete=False)',
+            target, cls.__module__, cls.__name__
+        )
+
+    def add_attributes(self, target, params):
+        for name, expr in params:
+            self._emit.linef('{}.{} = {}', target, name, expr)
+
+    def finish(self, obj):
+        self._emit.line(obj, '._on_loaded()')
+
+
 @process.oneshot
 def _decoder():
     RESERVED = ('classes', 'result', 'oid', 'json_val', 'k', '__Placeholder')
     sig = cg.Signature('$obj', 'seen', 'unfinished')
     symtab = cg.SymbolTable(RESERVED, sig)
     emit = cg.CodeEmitter()
+    ocs = PlainObjectCreation(emit)
 
     classes = {}
 
         val_expr = yield from conversion(str(dict_expr) + '[k]', node.values)
         return '{{{}: {} for k in {}}}'.format(key_expr, val_expr, dict_expr)
 
-    def _construct_object(target, cls):
-        emit.linef('{} = classes[{!r}, {!r}](already_complete=False)',
-            target, cls.__module__, cls.__name__
-        )
-
-    def _add_attributes(target, params):
-        for name, expr in params:
-            emit.linef('{}.{} = {}', target, name, expr)
-
-    def _finish_constructed(obj):
-        emit.line(obj, '._on_loaded()')
-
     def visit_toplevel(node):
         if isinstance(node, Value):
             yield from visit_value(node)
         emit.line('oid = obj["$id"]')
         emit.line('if oid in unfinished: result = seen[oid]')
         with emit.block('else'):
-            _construct_object('result', cls)
+            ocs.construct('result', cls)
             emit.line('seen[oid] = result')
         emit.line('unfinished.discard(oid)')
         emit.line('result.object_id = oid')
-        _add_attributes('result', zip(member_names, loaders))
-        _finish_constructed('result')
+        ocs.add_attributes('result', zip(member_names, loaders))
+        ocs.finish('result')
         emit.line('return result')
 
     def visit_ref(node):
             'assert isinstance(oid, str), repr(oid) + " is not an ID"'
         )
         emit.line('if oid in seen: return seen[oid]')
-        _construct_object('obj', cls)
+        ocs.construct('obj', cls)
         emit.line('seen[oid] = obj')
         emit.line('unfinished.add(oid)')
         emit.line('return obj')
             conv = yield from conversion('obj[{!r}]'.format(name), t)
             loaders.append(conv)
 
-        _construct_object('result', cls)
-        _add_attributes('result', zip(member_names, loaders))
-        _finish_constructed('result')
+        ocs.construct('result', cls)
+        ocs.add_attributes('result', zip(member_names, loaders))
+        ocs.finish('result')
         emit.line('return result')
 
-    # NOT a visit, as we can only handle structs behind Ref/Value.
-    # We needs a way to get to the class behind the model, to create objects.
-    # But of the model types, only Ref and Value store the real class.
-    def do_struct(node, cls, value):
-        member_names = tuple(name for name, _ in node.members)
-        loaders = []
-        for name, t in node.members:
-            conv = yield from conversion('obj[{!r}]'.format(name), t)
-            loaders.append(conv)
-        if value:
-            visit = do_value_struct
-        else:
-            visit = do_ref_struct
-        visit(cls, member_names, loaders)
-
     visit_integer = visit_string = visit_float = NotImplemented
     visit_list = visit_dict = NotImplemented
     visit_struct = visit_maybe = NotImplemented