Commits

Kirill Simonov  committed 26fdb7f

Added YAML serialization for token and syntax nodes.

  • Participants
  • Parent commits 7ae8dd7

Comments (0)

Files changed (3)

File src/htsql/core/syn/syntax.py

 
 
 from ..util import (maybe, listof, oneof, Clonable, Hashable, Printable,
-        to_name, to_literal)
+        YAMLable, to_name, to_literal)
 import re
 import decimal
 
 
-class Syntax(Clonable, Hashable, Printable):
+class Syntax(Clonable, Hashable, Printable, YAMLable):
     """
     A syntax node.
     """
     def __unicode__(self):
         return u"%s:=%s" % (self.larm, self.rarm)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class SpecifySyntax(Syntax):
     """
             chunks.append(u")")
         return u"".join(chunks)
 
+    def __yaml__(self):
+        yield ('larms', self.larms)
+        if self.rarms is not None:
+            yield ('rarms', self.rarms)
+
 
 class ApplySyntax(Syntax):
     """
         return u"%s(%s)" % (self.identifier,
                             u",".join(unicode(arm) for arm in self.arms))
 
+    def __yaml__(self):
+        yield ('identifier', self.identifier)
+        yield ('arms', self.arms)
+
 
 class PipeSyntax(ApplySyntax):
     """
             chunks.append(u")")
         return u"".join(chunks)
 
+    def __yaml__(self):
+        yield ('is_flow', self.is_flow)
+        yield ('is_open', self.is_open)
+        yield ('identifier', self.identifier)
+        yield ('larm', self.larm)
+        if self.rarms or not self.is_open:
+            yield ('rarms', self.rarms)
+
 
 class OperatorSyntax(ApplySyntax):
     """
     def __unicode__(self):
         return u"%s%s%s" % (self.larm, self.symbol, self.rarm)
 
+    def __yaml__(self):
+        yield ('symbol', self.symbol)
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class PrefixSyntax(ApplySyntax):
     """
     def __unicode__(self):
         return u"%s%s" % (self.symbol, self.arm)
 
+    def __yaml__(self):
+        yield ('symbol', self.symbol)
+        yield ('arm', self.arm)
+
 
 class FilterSyntax(OperatorSyntax):
     """
     def __init__(self, larm, rarm):
         super(FilterSyntax, self).__init__(u'?', larm, rarm)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class ProjectSyntax(OperatorSyntax):
     """
     def __init__(self, larm, rarm):
         super(ProjectSyntax, self).__init__(u'^', larm, rarm)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class LinkSyntax(OperatorSyntax):
     """
     def __init__(self, larm, rarm):
         super(LinkSyntax, self).__init__(u'->', larm, rarm)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class AttachSyntax(OperatorSyntax):
     """
     def __init__(self, larm, rarm):
         super(AttachSyntax, self).__init__(u'@', larm, rarm)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class DetachSyntax(PrefixSyntax):
     """
     def __init__(self, arm):
         super(DetachSyntax, self).__init__(u'@', arm)
 
+    def __yaml__(self):
+        yield ('arm', self.arm)
+
 
 class CollectSyntax(PrefixSyntax):
     """
     def __init__(self, arm):
         super(CollectSyntax, self).__init__(u'/', arm)
 
+    def __yaml__(self):
+        yield ('arm', self.arm)
+
 
 class DirectSyntax(Syntax):
     """
     def __unicode__(self):
         return u"%s%s" % (self.arm, self.symbol)
 
+    def __yaml__(self):
+        yield ('symbol', self.symbol)
+        yield ('arm', self.arm)
+
 
 class ComposeSyntax(Syntax):
     """
         chunks.append(unicode(self.rarm))
         return u"".join(chunks)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class UnpackSyntax(Syntax):
     """
             chunks.append(u")")
         return u"".join(chunks)
 
+    def __yaml__(self):
+        if self.index is not None:
+            yield ('is_open', self.is_open)
+            yield ('index', self.index)
+
 
 class LiftSyntax(Syntax):
     """
     def __unicode__(self):
         return u"(%s)" % self.arm
 
+    def __yaml__(self):
+        yield ('arm', self.arm)
+
 
 class SelectSyntax(Syntax):
     """
     def __unicode__(self):
         return u"%s%s" % (self.larm, self.rarm)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class LocateSyntax(Syntax):
     """
     def __unicode__(self):
         return u"%s%s" % (self.larm, self.rarm)
 
+    def __yaml__(self):
+        yield ('larm', self.larm)
+        yield ('rarm', self.rarm)
+
 
 class RecordSyntax(Syntax):
     """
     def __unicode__(self):
         return u"{%s}" % u",".join(unicode(arm) for arm in self.arms)
 
+    def __yaml__(self):
+        yield ('arms', self.arms)
+
 
 class ListSyntax(Syntax):
     """
         chunks.append(u")")
         return u"".join(chunks)
 
+    def __yaml__(self):
+        yield ('arms', self.arms)
+
 
 class IdentitySyntax(Syntax):
     """
             chunks.append(u")")
         return u"".join(chunks)
 
+    def __yaml__(self):
+        yield ('is_hard', self.is_hard)
+        yield ('arms', self.arms)
+
 
 class ReferenceSyntax(Syntax):
     """
     def __unicode__(self):
         return u"$%s" % self.identifier
 
+    def __yaml__(self):
+        yield ('identifier', self.identifier)
+
 
 class IdentifierSyntax(Syntax):
     """
     def __unicode__(self):
         return self.text
 
+    def __yaml__(self):
+        yield ('text', self.text)
+        if self.name != self.text:
+            yield ('name', self.name)
+
 
 class LiteralSyntax(Syntax):
     """
     def __unicode__(self):
         return self.text
 
+    def __yaml__(self):
+        yield ('text', self.text)
+
 
 class StringSyntax(LiteralSyntax):
     """

File src/htsql/core/syn/token.py

 #
 
 
-from ..util import Clonable, Hashable, Printable
+from ..util import Clonable, Hashable, Printable, YAMLable
 import urllib
 
 
-class Token(Clonable, Hashable, Printable):
+class Token(Clonable, Hashable, Printable, YAMLable):
     """
     A lexical token.
 
     def __basis__(self):
         return (self.code, self.text)
 
+    def __nonzero__(self):
+        # `False` for EOF token; `True` otherwise.
+        return bool(self.code)
+
     def __unicode__(self):
         # '$', '`<code>`:<text>' or '%<code>:<text>'
         chunks = []
             chunks.append(text)
         return u"".join(chunks)
 
-    def __nonzero__(self):
-        # `False` for EOF token; `True` otherwise.
-        return bool(self.code)
+    def __yaml__(self):
+        yield ('code', self.code)
+        if self.code.isalpha() or self.code != self.text:
+            yield ('text', self.text)
 
 
 #

File src/htsql/core/util.py

 import datetime, time
 import collections
 import unicodedata
+import yaml
 
 
 #
 
     def __init__(self):
         # Must be overriden in subclasses.
-        raise NotImplementedError()
+        raise NotImplementedError("%s.__init__()" % self.__class__.__name__)
 
     def clone(self, **replacements):
         """
         return "<%s %s>" % (self.__class__.__name__, self)
 
 
+class YAMLable(object):
+    """
+    An object with YAML representation.
+
+    Subclasses of :class:`YAMLable` must override :meth:`__yaml__` to generate
+    a list of ``(field, value)`` pairs.
+    """
+
+    def to_yaml(self):
+        """
+        Returns YAML representation of the object.
+        """
+        return yaml.dump(self, Dumper=YAMLableDumper)
+
+    def __yaml__(self):
+        # Override in subclasses.
+        return []
+
+    def __str__(self):
+        return self.to_yaml()
+
+    def __unicode__(self):
+        return self.to_yaml().decode('utf-8')
+
+    def __repr__(self):
+        return "<%s>" % self.__class__.__name__
+
+
+class YAMLableDumper(yaml.Dumper):
+    # Serializer for `YAMLable` instances.
+
+    def represent_str(self, data):
+        # Represent both `str` and `unicode` objects as YAML strings.
+        # Use block style for multiline strings.
+        if isinstance(data, unicode):
+            data = data.encode('utf-8')
+        tag = None
+        style = None
+        if data.endswith('\n'):
+            style = '|'
+        try:
+            data = data.decode('utf-8')
+            tag = u'tag:yaml.org,2002:str'
+        except UnicodeDecodeError:
+            data = data.encode('base64')
+            tag = u'tag:yaml.org,2002:binary'
+            style = '|'
+        return self.represent_scalar(tag, data, style=style)
+
+    def represent_yamlable(self, data):
+        # Represent `YAMLable` objects.
+        tag = unicode('!'+data.__class__.__name__)
+        mapping = list(data.__yaml__())
+        # Use block style if any field value is a multiline string.
+        flow_style = None
+        if any(isinstance(item, (str, unicode)) and '\n' in item
+                for key, item in mapping):
+            flow_style = False
+        return self.represent_mapping(tag, mapping, flow_style=flow_style)
+
+    def generate_anchor(self, node):
+        # Use the class name for anchor names.
+        if not isinstance(self.last_anchor_id, dict):
+            self.last_anchor_id = { u'': 1 }
+        if node.tag.startswith(u'!'):
+            text = node.tag[1:]
+        else:
+            text = u''
+        self.last_anchor_id.setdefault(text, 1)
+        index = self.last_anchor_id[text]
+        self.last_anchor_id[text] += 1
+        if text:
+            text += u'-%s' % index
+        else:
+            text = unicode(index)
+        return text
+
+
+YAMLableDumper.add_representer(str, YAMLableDumper.represent_str)
+YAMLableDumper.add_representer(unicode, YAMLableDumper.represent_str)
+YAMLableDumper.add_multi_representer(YAMLable,
+        YAMLableDumper.represent_yamlable)
+
+
+def to_yaml(data):
+    """
+    Represents the value in YAML format.
+    """
+    return yaml.dump(data, Dumper=YAMLableDumper)
+
+
 #
 # Database connection parameters.
 #