Kirill Simonov avatar Kirill Simonov committed 020f003

Refactoring signatures.

Added class `Bag` encapsulating function arguments; moved argument
manipulation methods from `Signature` to `Bag`. Renamed `Parameter`
to `Slot`; other renames.

Comments (0)

Files changed (10)

src/htsql/tr/assemble.py

         self.signature = code.signature
         self.domain = code.domain
         self.arguments = code.arguments
-        self.signature.extract(self, self.arguments)
 
     def __call__(self):
-        arguments = self.signature.apply(self.state.evaluate, self.arguments)
+        arguments = self.arguments.map(self.state.evaluate)
         if self.is_null_regular:
-            is_nullable = any(argument.is_nullable
-                              for argument in self.signature.iterate(arguments))
+            is_nullable = any(cell.is_nullable for cell in arguments.cells())
         else:
             is_nullable = self.is_nullable
         return FunctionPhrase(self.signature,

src/htsql/tr/binding.py

 from ..domain import Domain, VoidDomain, BooleanDomain, TupleDomain
 from .syntax import Syntax
 from .coerce import coerce
-from .signature import Signature
+from .signature import Signature, Bag
 
 
 class Binding(Clonable, Printable):
 
     def __init__(self, signature, domain, syntax, **arguments):
         assert isinstance(signature, Signature)
-        signature.verify(Binding, arguments)
+        arguments = Bag(**arguments)
+        assert arguments.admits(Binding, signature)
         super(FunctionBinding, self).__init__(domain, syntax)
         self.signature = signature
         self.arguments = arguments
-        signature.extract(self, arguments)
+        arguments.impress(self)
 
 
 class WrapperBinding(Binding):

src/htsql/tr/code.py

 from .syntax import IdentifierSyntax
 from .binding import Binding, QueryBinding, SegmentBinding
 from .coerce import coerce
-from .signature import Signature
+from .signature import Signature, Bag
 
 
 class Expression(Comparable, Clonable, Printable):
 
     def __init__(self, signature, domain, binding, **arguments):
         assert isinstance(signature, Signature)
-        signature.verify(Code, arguments)
+        arguments = Bag(**arguments)
+        assert arguments.admits(Code, signature)
         units = []
-        for code in signature.iterate(arguments):
-            units.extend(code.units)
-        equality_vector = ((signature.__class__, domain)
-                           + signature.freeze(arguments))
+        for cell in arguments.cells():
+            units.extend(cell.units)
+        equality_vector = (signature, domain, arguments.freeze())
         super(FunctionCode, self).__init__(
                     domain=domain,
                     units=units,
         self.arguments = arguments
         # For convenience, we permit access to function arguments using
         # object attributes.
-        signature.extract(self, arguments)
+        arguments.impress(self)
 
 
 class Unit(Code):

src/htsql/tr/encode.py

         self.signature = binding.signature
         self.domain = binding.domain
         self.arguments = binding.arguments
-        self.signature.extract(self, self.arguments)
 
 
 class EncodeBySignature(EncodeBySignatureBase):
 
     def __call__(self):
-        arguments = self.signature.apply(self.state.encode, self.arguments)
+        arguments = self.arguments.map(self.state.encode)
         return FunctionCode(self.signature,
                             self.domain,
                             self.binding,

src/htsql/tr/fn/bind.py

     def match(self):
         operands = self.syntax.arguments[:]
         arguments = {}
-        parameters = []
+        slots = []
         if self.signature is not None:
-            parameters = self.signature.parameters
-        for index, parameter in enumerate(parameters):
-            name = parameter.name
+            slots = self.signature.slots
+        for index, slot in enumerate(slots):
+            name = slot.name
             value = None
             if not operands:
-                if parameter.is_mandatory:
+                if slot.is_mandatory:
                     raise BindError("missing argument %s" % name,
                                     self.syntax.mark)
-                if parameter.is_list:
+                if not slot.is_singular:
                     value = []
-            elif not parameter.is_list:
+            elif slot.is_singular:
                 value = operands.pop(0)
             else:
-                if index == len(self.signature.parameters)-1:
+                if index == len(slots)-1:
                     value = operands[:]
                     operands[:] = []
                 else:
     def bind(self):
         arguments = self.match()
         bound_arguments = {}
-        parameters = []
+        slots = []
         if self.signature is not None:
-            parameters = self.signature.parameters
-        for parameter in parameters:
-            name = parameter.name
+            slots = self.signature.slots
+        for slot in slots:
+            name = slot.name
             value = arguments[name]
             bound_value = None
-            if not parameter.is_list:
+            if slot.is_singular:
                 if value is not None:
                     bound_values = self.state.bind_all(value)
                     if len(bound_values) > 1:
                         raise BindError("unexpected list argument",
                                         value.mark)
-                    if parameter.is_mandatory and not bound_values:
+                    if slot.is_mandatory and not bound_values:
                         raise BindError("unexpected empty argument",
                                         value.mark)
                     if bound_values:
                 elif len(value) == 1:
                     [value] = value
                     bound_value = self.state.bind_all(value)
-                    if parameter.is_mandatory and not bound_value:
+                    if slot.is_mandatory and not bound_value:
                         raise BindError("missing argument %s" % name,
                                         value.mark)
                 else:
 
     def correlate(self, **arguments):
         assert self.signature is not None
-        assert len(self.signature.parameters) == len(self.domains)
+        assert len(self.signature.slots) == len(self.domains)
         assert self.codomain is not None
         cast_arguments = {}
-        for domain, parameter in zip(self.domains, self.signature.parameters):
+        for domain, slot in zip(self.domains, self.signature.slots):
             domain = coerce(domain)
-            name = parameter.name
+            name = slot.name
             value = arguments[name]
-            if not parameter.is_list:
+            if slot.is_singular:
                 if value is not None:
                     value = CastBinding(value, domain, value.syntax)
             else:
 
     def correlate(self, **arguments):
         domains = []
-        for parameter in self.signature.parameters:
-            if parameter.is_list or not parameter.is_mandatory:
+        for slot in self.signature.slots:
+            if not (slot.is_singular and slot.is_mandatory):
                 break
-            name = parameter.name
+            name = slot.name
             value = arguments[name]
             domains.append(value.domain)
         correlate = self.correlation(*domains)
         if not correlate():
             raise BindError("incompatible arguments", self.syntax.mark)
         correlated_arguments = arguments.copy()
-        for domain, parameter in zip(correlate.domains,
-                                     self.signature.parameters):
-            name = parameter.name
+        for domain, slot in zip(correlate.domains, self.signature.slots):
+            name = slot.name
             value = correlated_arguments[name]
             value = CastBinding(value, coerce(domain), value.syntax)
             correlated_arguments[name] = value
     def correlate(self, **arguments):
         assert self.signature is not None
         domains = []
-        for parameter in self.signature.parameters:
-            name = parameter.name
+        for slot in self.signature.slots:
+            name = slot.name
             value = arguments[name]
-            if not parameter.is_list:
+            if slot.is_singular:
                 if value is not None:
                     domains.append(value.domain)
             else:
         if domain is None:
             raise BindError("incompatible arguments", self.syntax.mark)
         cast_arguments = {}
-        for parameter in self.signature.parameters:
-            name = parameter.name
+        for slot in self.signature.slots:
+            name = slot.name
             value = arguments[name]
-            if not parameter.is_list:
+            if slot.is_singular:
                 if value is not None:
                     value = CastBinding(value, domain, value.syntax)
             else:

src/htsql/tr/fn/signature.py

 """
 
 
-from ..signature import Signature, Parameter
+from ..signature import (Signature, Slot, NullarySig, UnarySig, BinarySig,
+                         NArySig, ConnectiveSig, PolarSig, CompareSig)
 
 
 class ThisSig(Signature):
 
 class DirectSig(Signature):
 
-    parameters = [
-            Parameter('table'),
+    slots = [
+            Slot('table'),
     ]
 
 
 class FiberSig(Signature):
 
-    parameters = [
-            Parameter('table'),
-            Parameter('image'),
-            Parameter('counterimage', is_mandatory=False),
+    slots = [
+            Slot('table'),
+            Slot('image'),
+            Slot('counterimage', is_mandatory=False),
     ]
 
 
 class AsSig(Signature):
 
-    parameters = [
-            Parameter('base'),
-            Parameter('title'),
+    slots = [
+            Slot('base'),
+            Slot('title'),
     ]
 
 
 class SortDirectionSig(Signature):
 
-    parameters = [
-            Parameter('base'),
+    slots = [
+            Slot('base'),
     ]
 
     def __init__(self, direction):
         assert direction in [+1, -1]
+        super(SortDirectionSig, self).__init__(equality_vector=(direction,))
         self.direction = direction
 
 
 class LimitSig(Signature):
 
-    parameters = [
-            Parameter('limit'),
-            Parameter('offset', is_mandatory=False),
+    slots = [
+            Slot('limit'),
+            Slot('offset', is_mandatory=False),
     ]
 
 
 class SortSig(Signature):
 
-    parameters = [
-            Parameter('order', is_list=True),
+    slots = [
+            Slot('order', is_singular=False),
     ]
 
 
 
 class CastSig(Signature):
 
-    parameters = [
-            Parameter('base'),
+    slots = [
+            Slot('base'),
     ]
 
 
 class DateSig(Signature):
 
-    parameters = [
-            Parameter('year'),
-            Parameter('month'),
-            Parameter('day'),
+    slots = [
+            Slot('year'),
+            Slot('month'),
+            Slot('day'),
     ]
 
 
-class UnarySig(Signature):
+class EqualSig(BinarySig, PolarSig):
+    pass
 
-    parameters = [
-            Parameter('op'),
-    ]
 
+class AmongSig(NArySig, PolarSig):
+    pass
 
-class BinarySig(Signature):
 
-    parameters = [
-            Parameter('lop'),
-            Parameter('rop'),
-    ]
-
-
-class NArySig(Signature):
-
-    parameters = [
-            Parameter('lop'),
-            Parameter('rops', is_list=True),
-    ]
-
-
-class EqualSig(BinarySig):
-
-    def __init__(self, polarity):
-        assert polarity in [+1, -1]
-        self.polarity = polarity
-
-
-class AmongSig(NArySig):
-
-    def __init__(self, polarity):
-        assert polarity in [+1, -1]
-        self.polarity = polarity
-
-
-class TotallyEqualSig(BinarySig):
-
-    def __init__(self, polarity):
-        assert polarity in [+1, -1]
-        self.polarity = polarity
+class TotallyEqualSig(BinarySig, PolarSig):
+    pass
 
 
 class AndSig(BinarySig):
     pass
 
 
-class CompareSig(BinarySig):
-
-    def __init__(self, relation):
-        assert relation in ['<', '<=', '>', '>=']
-        self.relation = relation
-
-
 class AddSig(BinarySig):
     pass
 
 
 class RoundToSig(Signature):
 
-    parameters = [
-            Parameter('op'),
-            Parameter('precision'),
+    slots = [
+            Slot('op'),
+            Slot('precision'),
     ]
 
 
 
 class IfSig(Signature):
 
-    parameters = [
-            Parameter('predicates', is_list=True),
-            Parameter('consequents', is_list=True),
-            Parameter('alternative', is_mandatory=False),
+    slots = [
+            Slot('predicates', is_singular=False),
+            Slot('consequents', is_singular=False),
+            Slot('alternative', is_mandatory=False),
     ]
 
 
 class SwitchSig(Signature):
 
-    parameters = [
-            Parameter('variable'),
-            Parameter('variants', is_list=True),
-            Parameter('consequents', is_list=True),
-            Parameter('alternative', is_mandatory=False),
+    slots = [
+            Slot('variable'),
+            Slot('variants', is_singular=False),
+            Slot('consequents', is_singular=False),
+            Slot('alternative', is_mandatory=False),
     ]
 
 
     pass
 
 
-class ContainsSig(BinarySig):
-
-    def __init__(self, polarity):
-        assert polarity in [+1, -1]
-        self.polarity = polarity
+class ContainsSig(BinarySig, PolarSig):
+    pass
 
 
 class StringContainsSig(ContainsSig):
     pass
 
 
-class QuantifySig(Signature):
+class QuantifySig(PolarSig):
 
-    parameters = [
-            Parameter('base'),
-            Parameter('op'),
+    slots = [
+            Slot('base'),
+            Slot('op'),
     ]
 
-    def __init__(self, polarity):
-        assert polarity in [+1, -1]
-        self.polarity = polarity
-
 
 class ExistsSig(QuantifySig):
 
     def __init__(self):
-        super(ExistsSig, self).__init__(+1)
+        super(ExistsSig, self).__init__(polarity=+1)
 
 
 class EverySig(QuantifySig):
 
     def __init__(self):
-        super(EverySig, self).__init__(-1)
+        super(EverySig, self).__init__(polarity=-1)
 
 
 class WrapExistsSig(UnarySig):
 
 class AggregateSig(Signature):
 
-    parameters = [
-            Parameter('base'),
-            Parameter('op'),
+    slots = [
+            Slot('base'),
+            Slot('op'),
     ]
 
 

src/htsql/tr/frame.py

 from .coerce import coerce
 from .code import Expression
 from .term import Term, QueryTerm
-from .signature import Signature
+from .signature import Signature, Bag
 
 
 class Clause(Comparable, Clonable, Printable):
 
     def __init__(self, signature, domain, is_nullable, expression, **arguments):
         assert isinstance(signature, Signature)
-        signature.verify(Phrase, arguments)
-        equality_vector = ((signature.__class__, domain)
-                           + signature.freeze(arguments))
+        arguments = Bag(**arguments)
+        assert arguments.admits(Phrase, signature)
+        equality_vector = (signature, domain, arguments.freeze())
         super(FunctionPhrase, self).__init__(domain, is_nullable, expression,
                                              equality_vector)
         self.signature = signature
         self.arguments = arguments
         # For convenience, we permit access to function arguments using
         # object attributes.
-        signature.extract(self, arguments)
+        arguments.impress(self)
 
 
 class ExportPhrase(Phrase):

src/htsql/tr/reduce.py

         self.domain = phrase.domain
         self.arguments = phrase.arguments
         self.is_nullable = phrase.is_nullable
-        self.signature.extract(self, self.arguments)
 
     def __call__(self):
-        arguments = self.signature.apply(self.state.reduce, self.arguments)
+        arguments = self.arguments.map(self.state.reduce)
         return FunctionPhrase(self.signature,
                               self.domain,
                               self.is_nullable,

src/htsql/tr/serialize.py

         self.signature = phrase.signature
         self.domain = phrase.domain
         self.arguments = phrase.arguments
-        self.signature.extract(self, self.arguments)
 
     def __call__(self):
         raise NotImplementedError()

src/htsql/tr/signature.py

 """
 
 
-from ..util import maybe, listof
+from ..util import maybe, listof, Comparable
 
 
-class Parameter(object):
+class Slot(object):
 
-    def __init__(self, name, is_mandatory=True, is_list=False):
+    def __init__(self, name, is_mandatory=True, is_singular=True):
         assert isinstance(name, str) and len(name) > 0
         assert isinstance(is_mandatory, bool)
-        assert isinstance(is_list, bool)
+        assert isinstance(is_singular, bool)
         self.name = name
         self.is_mandatory = is_mandatory
-        self.is_list = is_list
+        self.is_singular = is_singular
 
 
-class Signature(object):
+class Signature(Comparable):
 
-    parameters = []
+    slots = []
 
-    @classmethod
-    def verify(cls, kind, arguments):
+    def __init__(self, equality_vector=()):
+        super(Signature, self).__init__(equality_vector=equality_vector)
+
+    def __iter__(self):
+        return iter(self.slots)
+
+
+class Bag(dict):
+
+    def __init__(self, **keywords):
+        self.update(keywords)
+
+    def admits(self, kind, signature):
         assert isinstance(kind, type)
-        assert isinstance(arguments, dict)
-        assert set(arguments) == set(parameter.name
-                                     for parameter in cls.parameters)
-        for parameter in cls.parameters:
-            value = arguments[parameter.name]
-            if not parameter.is_list:
-                assert isinstance(value, maybe(kind))
-                if parameter.is_mandatory:
-                    assert value is not None
+        assert isinstance(signature, Signature)
+        if set(self.keys()) != set(slot.name for slot in signature):
+            return False
+        for slot in signature:
+            value = self[slot.name]
+            if slot.is_singular:
+                if not isinstance(value, maybe(kind)):
+                    return False
+                if slot.is_mandatory:
+                    if value is None:
+                        return False
             else:
-                assert isinstance(value, listof(kind))
-                if parameter.is_mandatory:
-                    assert len(value) > 0
+                if not isinstance(value, listof(kind)):
+                    return False
+                if slot.is_mandatory:
+                    if not value:
+                        return False
+        return True
 
-    @classmethod
-    def extract(cls, instance, arguments):
-        assert isinstance(arguments, dict)
-        for parameter in cls.parameters:
-            assert not hasattr(instance, parameter.name)
-            value = arguments[parameter.name]
-            setattr(instance, parameter.name, value)
+    def cells(self):
+        cells = []
+        for key in sorted(self.keys()):
+            value = self[key]
+            if value is not None:
+                if isinstance(value, list):
+                    cells.extend(value)
+                else:
+                    cells.append(value)
+        return cells
 
-    @classmethod
-    def apply(cls, method, arguments):
-        assert isinstance(arguments, dict)
-        result = {}
-        for parameter in cls.parameters:
-            value = arguments[parameter.name]
-            if not parameter.is_list:
-                if value is not None:
+    def impress(self, owner):
+        for key in sorted(self.keys()):
+            assert not hasattr(owner, key)
+            setattr(owner, key, self[key])
+
+    def map(self, method):
+        keywords = {}
+        for key in sorted(self.keys()):
+            value = self[key]
+            if value is not None:
+                if isinstance(value, list):
+                    value = [method(item) for item in value]
+                else:
                     value = method(value)
-            else:
-                value = [method(item) for item in value]
-            result[parameter.name] = value
-        return result
+            keywords[key] = value
+        return self.__class__(**keywords)
 
-    @classmethod
-    def iterate(cls, arguments):
-        for parameter in cls.parameters:
-            value = arguments[parameter.name]
-            if not parameter.is_list:
-                if value is not None:
-                    yield value
-            else:
-                for item in value:
-                    yield item
+    def freeze(self):
+        values = []
+        for key in sorted(self.keys()):
+            value = self[key]
+            if isinstance(value, list):
+                value = tuple(value)
+            values.append(value)
+        return tuple(values)
 
-    @classmethod
-    def freeze(cls, arguments):
-        result = []
-        for parameter in cls.parameters:
-            value = arguments[parameter.name]
-            if parameter.is_list:
-                value = tuple(value)
-            result.append(value)
-        return tuple(result)
 
+class NullarySig(Signature):
 
+    slots = []
+
+
+class UnarySig(Signature):
+
+    slots = [
+            Slot('op'),
+    ]
+
+
+class BinarySig(Signature):
+
+    slots = [
+            Slot('lop'),
+            Slot('rop'),
+    ]
+
+
+class NArySig(Signature):
+
+    slots = [
+            Slot('lop'),
+            Slot('rops', is_singular=False),
+    ]
+
+
+class ConnectiveSig(Signature):
+
+    slots = [
+            Slot('ops', is_singular=False),
+    ]
+
+
+class PolarSig(Signature):
+
+    def __init__(self, polarity):
+        assert polarity in [+1, -1]
+        super(PolarSig, self).__init__(equality_vector=(polarity,))
+        self.polarity = polarity
+
+
+class IsEqualSig(BinarySig, PolarSig):
+    pass
+
+
+class IsTotallyEqualSig(BinarySig, PolarSig):
+    pass
+
+
+class IsInSig(NArySig, PolarSig):
+    pass
+
+
+class IsNullSig(UnarySig, PolarSig):
+    pass
+
+
+class IfNullSig(NArySig):
+    pass
+
+
+class NullIfSig(NArySig):
+    pass
+
+
+class CompareSig(BinarySig):
+
+    def __init__(self, relation):
+        assert relation in ['<', '<=', '>', '>=']
+        super(CompareSig, self).__init__(equality_vector=(relation,))
+        self.relation = relation
+
+
+class AndSig(ConnectiveSig):
+    pass
+
+
+class OrSig(ConnectiveSig):
+    pass
+
+
+class NotSig(UnarySig):
+    pass
+
+
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.