rkruppe avatar rkruppe committed b59cc92

Split the object -> primitives encoding and the primitives -> yaml conversion

Comments (0)

Files changed (3)

tutagx/meta/__init__.py

-from . import model, process, to_yaml, from_yaml, query, validate
+from . import encode, model, process, to_yaml, from_yaml, query, validate
 

tutagx/meta/encode.py

+from tutagx.util import codegen as cg
+from tutagx.meta import model, process
+from tutagx.meta.model import ModelMeta
+from tutagx.meta.common import wrap_model, typeident
+
+
+@process.oneshot
+def make_encoder():
+    RESERVED = ('item', 'synth_id', 'oid', 'result', 'CLASSES', 'kind')
+    sig = cg.Signature('$obj', 'seen')
+    symtab = cg.SymbolTable(RESERVED, sig)
+    emit = cg.CodeEmitter()
+    classes = {}
+
+    def conversion(expr, t):
+        if isinstance(t, (model.Integer, model.Float, model.String)):
+            return expr
+        if isinstance(t, model.List):
+            list_conv = yield from list_conv(t, expr)
+            return conv
+        if isinstance(t, model.Maybe):
+            t_conv = yield from conversion(expr, t.t)
+            if t_conv == expr:
+                return expr
+            return '(None if {0} is None else {1})'.format(expr, t_conv)
+        if isinstance(t, model.Ref):
+            return expr + '.object_id'
+        func = yield t
+        return sig.call(func, expr)
+
+    def _list_conv(node, list_expr):
+        item_expr = yield from conversion('item', node.items)
+        return '[{} for item in {}]'.format(item_expr, list_expr)
+
+    def _names_and_conversions(node):
+        struct = ModelMeta.struct_for(node)
+        member_names = tuple(name for name, _ in struct.members)
+        convs = []
+        for name, t in struct.members:
+            conv = yield from conversion('obj.' + name, t)
+            convs.append(conv)
+        return member_names, tuple(convs)
+
+    def visit_toplevel(cls):
+        node = wrap_model(cls)
+        if isinstance(node, model.Value):
+            yield from visit_value(node)
+            return
+        member_names, convs = yield from _names_and_conversions(node)
+        emit.line('synth_id = id(obj)')
+        emit.line('if synth_id in seen: return seen[synth_id]')
+        emit.line('oid = obj.object_id')
+        emit.line('seen[synth_id] = oid')
+        emit.line('result = {')
+        emit.indent()
+        for member_name, conv in zip(member_names, convs):
+            emit.linef('{!r}: {},', member_name, conv)
+        emit.dedent()
+        emit.line('}')
+        emit.line('result["$id"] = oid')
+        emit.line('return result')
+
+    def visit_ref(node):
+        emit.line('return obj.object_id')
+
+    def visit_value(node):
+        member_names, convs = yield from _names_and_conversions(node)
+        emit.line('synth_id = id(obj)')
+        with emit.block('if synth_id in seen'):
+            emit.line(
+                'raise RuntimeError("{{}} is a value type ',
+                'and must not be shared".format(obj))'
+            )
+        emit.line('seen[synth_id] = None')
+        emit.line('return {')
+        emit.indent()
+        for member_name, conv in zip(member_names, convs):
+            emit.linef('{!r}: {},', member_name, conv)
+        emit.dedent()
+        emit.line('}')
+
+    def visit_list(node):
+        with emit.block('if id(obj) in seen'):
+            emit.line('raise RuntimeError("Lists must not be shared")')
+        emit.line('seen[id(obj)] = None')
+        conv = yield from _list_conv(node, 'obj')
+        emit.line('return ', conv)
+
+    def visit_union(node):
+        for tag, t in node.alternatives:
+            cls = ModelMeta.model_for(t)
+            classes[cls.__qualname__] = cls
+            key = repr(cls.__qualname__)
+            with emit.block('if isinstance(obj, CLASSES[', key, '])'):
+                conv = yield from conversion('obj', t)
+                emit.line('return ', conv)
+
+    visit_float = visit_integer = visit_string = NotImplemented
+    visit_dict = visit_maybe = visit_struct = NotImplemented
+
+    code_ns = {'CLASSES': classes}
+    return cg.generate_code(
+        locals(), typeident, sig, emit, symtab,
+        entry_point=visit_toplevel, code_ns=code_ns
+    )
+

tutagx/meta/to_yaml.py

 from collections import namedtuple
 import yaml
 
-from tutagx.util import codegen as cg
-from tutagx.meta import model, process
-from tutagx.meta.model import ModelMeta
-from tutagx.meta.common import wrap_model, typeident
+from tutagx.meta import model, encode
 
-_WriterNamespace = namedtuple('_WriterNamespace', 'seen')
+WriterContext = namedtuple('WriterContext', 'seen')
 
 
-@process.oneshot
-def _encoder():
-    RESERVED = ('item', 'synth_id', 'oid', 'result', 'CLASSES', 'kind')
-    sig = cg.Signature('$obj', 'seen')
-    symtab = cg.SymbolTable(RESERVED, sig)
-    emit = cg.CodeEmitter()
-    classes = {}
-
-    def conversion(expr, t):
-        if isinstance(t, (model.Integer, model.Float, model.String)):
-            return expr
-        if isinstance(t, model.List):
-            list_conv = yield from list_conv(t, expr)
-            return conv
-        if isinstance(t, model.Maybe):
-            t_conv = yield from conversion(expr, t.t)
-            if t_conv == expr:
-                return expr
-            return '(None if {0} is None else {1})'.format(expr, t_conv)
-        if isinstance(t, model.Ref):
-            return expr + '.object_id'
-        func = yield t
-        return sig.call(func, expr)
-
-    def _list_conv(node, list_expr):
-        item_expr = yield from conversion('item', node.items)
-        return '[{} for item in {}]'.format(item_expr, list_expr)
-
-    def _names_and_conversions(node):
-        struct = ModelMeta.struct_for(node)
-        member_names = tuple(name for name, _ in struct.members)
-        convs = []
-        for name, t in struct.members:
-            conv = yield from conversion('obj.' + name, t)
-            convs.append(conv)
-        return member_names, tuple(convs)
-
-    def visit_toplevel(cls):
-        node = wrap_model(cls)
-        if isinstance(node, model.Value):
-            yield from visit_value(node)
-            return
-        member_names, convs = yield from _names_and_conversions(node)
-        emit.line('synth_id = id(obj)')
-        emit.line('if synth_id in seen: return seen[synth_id]')
-        emit.line('oid = obj.object_id')
-        emit.line('seen[synth_id] = oid')
-        emit.line('result = {')
-        emit.indent()
-        for member_name, conv in zip(member_names, convs):
-            emit.linef('{!r}: {},', member_name, conv)
-        emit.dedent()
-        emit.line('}')
-        emit.line('result["$id"] = oid')
-        emit.line('return result')
-
-    def visit_ref(node):
-        emit.line('return obj.object_id')
-
-    def visit_value(node):
-        member_names, convs = yield from _names_and_conversions(node)
-        emit.line('synth_id = id(obj)')
-        with emit.block('if synth_id in seen'):
-            emit.line(
-                'raise RuntimeError("{{}} is a value type ',
-                'and must not be shared".format(obj))'
-            )
-        emit.line('seen[synth_id] = None')
-        emit.line('return {')
-        emit.indent()
-        for member_name, conv in zip(member_names, convs):
-            emit.linef('{!r}: {},', member_name, conv)
-        emit.dedent()
-        emit.line('}')
-
-    def visit_list(node):
-        with emit.block('if id(obj) in seen'):
-            emit.line('raise RuntimeError("Lists must not be shared")')
-        emit.line('seen[id(obj)] = None')
-        conv = yield from _list_conv(node, 'obj')
-        emit.line('return ', conv)
-
-    def visit_union(node):
-        for tag, t in node.alternatives:
-            cls = ModelMeta.model_for(t)
-            classes[cls.__qualname__] = cls
-            key = repr(cls.__qualname__)
-            with emit.block('if isinstance(obj, CLASSES[', key, '])'):
-                conv = yield from conversion('obj', t)
-                emit.line('return ', conv)
-
-    visit_float = visit_integer = visit_string = NotImplemented
-    visit_dict = visit_maybe = visit_struct = NotImplemented
-
-    code_ns = {'CLASSES': classes}
-    return cg.generate_code(
-        locals(), typeident, sig, emit, symtab,
-        entry_point=visit_toplevel, code_ns=code_ns
-    )
-
 # By default, the "scalar style" is all-or-nothing.
 # Either *every single scalar* is forced into a particular style (which
 # leads to an unholy mess with all the numbers, short strings, ``$id``s, etc)
 
 class YAMLWriter:
     def __init__(self, model):
-        self._encoder = _encoder(model)
+        self._encode = encode.make_encoder(model)
 
     def encode(self, obj, namespace=None):
         return self.encode_many([obj], namespace)[0]
     def encode_many(self, objs, namespace=None):
         if namespace is None:
             namespace = YAMLWriter.new_namespace()
-        return [self._encoder(obj, namespace.seen) for obj in objs]
+        return [self._encode(obj, namespace.seen) for obj in objs]
 
     def dump(self, obj, f, namespace=None):
         self.dump_many([obj], f, namespace)
         Namespaces are only relevant if you plan to use one namespace across
         multiple documents, or wish to.
         """
-        return _WriterNamespace({})
+        return WriterContext({})
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.