Kirill Simonov avatar Kirill Simonov committed 2442e9b

Refactored the encoder.

Renamed module `htsql.tr.encoder` to `htsql.tr.encode`; renamed some
classes and attributes too.
Introduced encoding adapters: `Encode`, `Relate`, `Direct`.
Introduced `Convert` adapter to handle untyped and tuple->boolean casts.

Comments (0)

Files changed (11)

src/htsql/domain.py

 
     This domain is assigned to table expressions.
     """
+    # FIXME: add a reference to the underlying `TableEntity`.  This may
+    # require importing `TableEntity` from `htsql.entity`, which creates
+    # a circular module dependency.  To break it, we will have to split
+    # `htsql.domain` into two modules: `htsql.type`, containing `Domain`
+    # and all its subclasses representing real database types, and
+    # `htsql.domain`, which imports all types from `htsql.type` and
+    # adds special domains like `VoidDomain`, `TupleDomain` and
+    # `UntypedDomain`.
 
 
 class BooleanDomain(Domain):

src/htsql/fmt/entitle.py

 
 class EntitleWrapper(Entitle):
 
-    adapts_many(SieveBinding, SortBinding, WrapperBinding)
+    adapts_many(SieveBinding, SortBinding, WrapperBinding, CastBinding)
 
     def __call__(self):
         if self.with_strong:
         return super(EntitleWrapper, self).__call__()
 
 
-class EntitleCast(Entitle):
-
-    adapts(CastBinding)
-
-    def __call__(self):
-        if self.with_strong:
-            title = entitle(self.binding.op, with_weak=False)
-            if title is not None:
-                return title
-        return super(EntitleCast, self).__call__()
-
-
 class EntitleTitle(Entitle):
 
     adapts(TitleBinding)

src/htsql/request.py

 from .error import EngineError
 from .tr.parse import parse
 from .tr.bind import bind
-from .tr.encoder import Encoder
+from .tr.encode import encode
 from .tr.assembler import Assembler
 from .tr.outliner import Outliner
 from .tr.compiler import Compiler
     def translate(self):
         syntax = parse(self.uri)
         binding = bind(syntax)
-        encoder = Encoder()
-        code = encoder.encode(binding)
+        code = encode(binding)
         assembler = Assembler()
         term = assembler.assemble(code)
         outliner = Outliner()

src/htsql/tr/binding.py

         return str(self.syntax)
 
 
+class QueryBinding(Binding):
+    """
+    Represents the whole HTSQL query.
+
+    `root` (:class:`RootBinding`)
+        The root binding associated with the query.
+
+    `segment` (:class:`SegmentBinding` or ``None``)
+        The query segment.
+    """
+
+    def __init__(self, root, segment, syntax):
+        assert isinstance(root, RootBinding)
+        assert isinstance(segment, maybe(SegmentBinding))
+        super(QueryBinding, self).__init__(VoidDomain(), syntax)
+        self.root = root
+        self.segment = segment
+
+
+class SegmentBinding(Binding):
+    """
+    Represents a segment of an HTSQL query.
+
+    `base` (:class:`Binding`)
+        The base of the segment.
+
+    `elements` (a list of :class:`Binding`)
+        The segment elements.
+    """
+
+    def __init__(self, base, elements, syntax):
+        assert isinstance(base, Binding)
+        assert isinstance(elements, listof(Binding))
+        super(SegmentBinding, self).__init__(VoidDomain(), syntax)
+        self.base = base
+        self.elements = elements
+
+
 class ChainBinding(Binding):
     """
     Represents a link binding node.
         self.link = link
 
 
-class QueryBinding(Binding):
-    """
-    Represents the whole HTSQL query.
-
-    `root` (:class:`RootBinding`)
-        The root binding associated with the query.
-
-    `segment` (:class:`SegmentBinding` or ``None``)
-        The query segment.
-    """
-
-    def __init__(self, root, segment, syntax):
-        assert isinstance(root, RootBinding)
-        assert isinstance(segment, maybe(SegmentBinding))
-        super(QueryBinding, self).__init__(VoidDomain(), syntax)
-        self.root = root
-        self.segment = segment
-
-
-class SegmentBinding(Binding):
-    """
-    Represents a segment of an HTSQL query.
-
-    `base` (:class:`Binding`)
-        The base of the segment.
-
-    `elements` (a list of :class:`Binding`)
-        The segment elements.
-    """
-
-    def __init__(self, base, elements, syntax):
-        assert isinstance(base, Binding)
-        assert isinstance(elements, listof(Binding))
-        super(SegmentBinding, self).__init__(VoidDomain(), syntax)
-        self.base = base
-        self.elements = elements
-
-
 class LiteralBinding(Binding):
     """
     Represents a literal value.
     """
     Represents a type conversion operator.
 
-    `op` (:class:`Binding`)
-        The operand to convert.
+    `base` (:class:`Binding`)
+        The expression to convert.
 
     `domain` (:class:`htsql.domain.Domain`)
         The target domain.
     """
 
-    def __init__(self, op, domain, syntax):
+    def __init__(self, base, domain, syntax):
         super(CastBinding, self).__init__(domain, syntax)
-        self.op = op
+        self.base = base
 
 
 class FunctionBinding(Binding):
         A mapping from argument names to values.
     """
 
+    # FIXME: should logical operators (`EqualityBinding`, etc) inherit
+    # from `FunctionBinding`?
+
     def __init__(self, domain, syntax, **arguments):
         super(FunctionBinding, self).__init__(domain, syntax)
         self.arguments = arguments
         self.base = base
 
 
-class OrderBinding(WrapperBinding):
+class DirectionBinding(WrapperBinding):
     """
-    Represents an order decorator (postfix ``+`` and ``-`` operators).
+    Represents a direction decorator (postfix ``+`` and ``-`` operators).
 
     `base` (:class:`Binding`)
         The decorated binding.
 
-    `dir` (``+1`` or ``-1``).
+    `direction` (``+1`` or ``-1``).
         Indicates the direction; ``+1`` for ascending, ``-1`` for descending.
     """
 
-    def __init__(self, base, dir, syntax):
-        assert dir in [-1, +1]
-        super(OrderBinding, self).__init__(base, syntax)
-        self.dir = dir
+    def __init__(self, base, direction, syntax):
+        assert direction in [-1, +1]
+        super(DirectionBinding, self).__init__(base, syntax)
+        self.direction = direction
 
 
 class TitleBinding(WrapperBinding):

src/htsql/tr/code.py

     """
     Represents a type conversion operator.
 
-    `op` (:class:`Code`)
-        The operand to convert.
+    `base` (:class:`Code`)
+        The expression to convert.
 
     `domain` (:class:`htsql.domain.Domain`)
         The target domain.
     """
 
-    def __init__(self, op, domain, binding):
+    def __init__(self, base, domain, binding):
         super(CastCode, self).__init__(
                     domain=domain,
-                    units=op.units,
+                    units=base.units,
                     binding=binding,
-                    equality_vector=(op, domain))
-        self.op = op
+                    equality_vector=(base, domain))
+        self.base = base
 
 
 class FunctionCode(Code):

src/htsql/tr/compiler.py

     adapts(CastCode, Compiler)
 
     def evaluate(self, references):
-        phrase = self.compiler.evaluate(self.expression.op, references)
+        phrase = self.compiler.evaluate(self.expression.base, references)
+        if isinstance(phrase.domain, self.expression.domain.__class__):
+            return phrase
         return CastPhrase(phrase, self.expression.domain, phrase.is_nullable,
                           self.expression.mark)
 

src/htsql/tr/encode.py

+#
+# Copyright (c) 2006-2010, Prometheus Research, LLC
+# Authors: Clark C. Evans <cce@clarkevans.com>,
+#          Kirill Simonov <xi@resolvent.net>
+#
+
+
+"""
+:mod:`htsql.tr.encoder`
+=======================
+
+This module implements the encoding adapter.
+"""
+
+
+from ..adapter import Adapter, adapts
+from ..domain import Domain, UntypedDomain, TupleDomain, BooleanDomain
+from .error import EncodeError
+from .binding import (Binding, RootBinding, QueryBinding, SegmentBinding,
+                      TableBinding, FreeTableBinding, JoinedTableBinding,
+                      ColumnBinding, LiteralBinding, SieveBinding,
+                      SortBinding, EqualityBinding, TotalEqualityBinding,
+                      ConjunctionBinding, DisjunctionBinding,
+                      NegationBinding, CastBinding, WrapperBinding,
+                      DirectionBinding)
+from .code import (ScalarSpace, CrossProductSpace, JoinProductSpace,
+                   FilteredSpace, OrderedSpace,
+                   QueryExpression, SegmentExpression, LiteralCode,
+                   EqualityCode, TotalEqualityCode,
+                   ConjunctionCode, DisjunctionCode, NegationCode,
+                   CastCode, ColumnUnit)
+
+
+class EncodingState(object):
+
+    with_cache = True
+
+    def __init__(self):
+        self.binding_to_code = {}
+        self.binding_to_space = {}
+
+    def encode(self, binding):
+        if self.with_cache:
+            if binding not in self.binding_to_code:
+                code = encode(binding, self)
+                self.binding_to_code[binding] = code
+            return self.binding_to_code[binding]
+        return encode(binding, self)
+
+    def relate(self, binding):
+        if self.with_cache:
+            if binding not in self.binding_to_space:
+                space = relate(binding, self)
+                self.binding_to_space[binding] = space
+            return self.binding_to_space[binding]
+        return relate(binding, self)
+
+    def direct(self, binding):
+        return direct(binding, self)
+
+
+class EncodeBase(Adapter):
+
+    adapts(Binding)
+
+    def __init__(self, binding, state):
+        assert isinstance(binding, Binding)
+        assert isinstance(state, EncodingState)
+        self.binding = binding
+        self.state = state
+
+
+class Encode(EncodeBase):
+
+    def __call__(self):
+        raise EncodeError("expected a valid code expression",
+                          self.binding.mark)
+
+
+class Relate(EncodeBase):
+
+    def __call__(self):
+        raise EncodeError("expected a valid space expression",
+                          self.binding.mark)
+
+
+class Direct(EncodeBase):
+
+    def __call__(self):
+        return None
+
+
+class EncodeQuery(Encode):
+
+    adapts(QueryBinding)
+
+    def __call__(self):
+        segment = None
+        if self.binding.segment is not None:
+            segment = self.state.encode(self.binding.segment)
+        return QueryExpression(segment, self.binding)
+
+
+class EncodeSegment(Encode):
+
+    adapts(SegmentBinding)
+
+    def __call__(self):
+        space = self.state.relate(self.binding.base)
+        order = []
+        elements = []
+        for binding in self.binding.elements:
+            element = self.state.encode(binding)
+            direction = self.state.direct(binding)
+            if direction is not None:
+                order.append((element, direction))
+            elements.append(element)
+        space = OrderedSpace(space, order, None, None, self.binding)
+        return SegmentExpression(space, elements, self.binding)
+
+
+class RelateRoot(Relate):
+
+    adapts(RootBinding)
+
+    def __call__(self):
+        return ScalarSpace(None, self.binding)
+
+
+class RelateFreeTable(Relate):
+
+    adapts(FreeTableBinding)
+
+    def __call__(self):
+        base = self.state.relate(self.binding.base)
+        return CrossProductSpace(base, self.binding.table, self.binding)
+
+
+class RelateJoinedTable(Relate):
+
+    adapts(JoinedTableBinding)
+
+    def __call__(self):
+        space = self.state.relate(self.binding.base)
+        for join in self.binding.joins:
+            space = JoinProductSpace(space, join, self.binding)
+        return space
+
+
+class RelateSieve(Relate):
+
+    adapts(SieveBinding)
+
+    def __call__(self):
+        space = self.state.relate(self.binding.base)
+        filter = self.state.encode(self.binding.filter)
+        return FilteredSpace(space, filter, self.binding)
+
+
+class DirectSieve(Direct):
+
+    adapts(SieveBinding)
+
+    def __call__(self):
+        return self.state.direct(self.binding.base)
+
+
+class RelateSort(Relate):
+
+    adapts(SortBinding)
+
+    def __call__(self):
+        space = self.state.relate(self.binding.base)
+        order = []
+        for binding in self.binding.order:
+            code = self.state.encode(binding)
+            direction = self.state.direct(binding)
+            if direction is None:
+                direction = +1
+            order.append((code, direction))
+        limit = self.binding.limit
+        offset = self.binding.offset
+        return OrderedSpace(space, order, limit, offset, self.binding)
+
+
+class EncodeColumn(Encode):
+
+    adapts(ColumnBinding)
+
+    def __call__(self):
+        space = self.state.relate(self.binding.base)
+        return ColumnUnit(self.binding.column, space, self.binding)
+
+
+class RelateColumn(Relate):
+
+    adapts(ColumnBinding)
+
+    def __call__(self):
+        if self.binding.link is not None:
+            return self.state.relate(self.binding.link)
+        return super(RelateColumn, self).__call__()
+
+
+class EncodeLiteral(Encode):
+
+    adapts(LiteralBinding)
+
+    def __call__(self):
+        return LiteralCode(self.binding.value, self.binding.domain,
+                           self.binding)
+
+
+class EncodeEquality(Encode):
+
+    adapts(EqualityBinding)
+
+    def __call__(self):
+        lop = self.state.encode(self.binding.lop)
+        rop = self.state.encode(self.binding.rop)
+        return EqualityCode(lop, rop, self.binding)
+
+
+class EncodeTotalEquality(Encode):
+
+    adapts(TotalEqualityBinding)
+
+    def __call__(self):
+        lop = self.state.encode(self.binding.lop)
+        rop = self.state.encode(self.binding.rop)
+        return TotalEqualityCode(lop, rop, self.binding)
+
+
+class EncodeConjunction(Encode):
+
+    adapts(ConjunctionBinding)
+
+    def __call__(self):
+        ops = [self.state.encode(op) for op in self.binding.ops]
+        return ConjunctionCode(ops, self.binding)
+
+
+class EncodeDisjunction(Encode):
+
+    adapts(DisjunctionBinding)
+
+    def __call__(self):
+        ops = [self.state.encode(op) for op in self.binding.ops]
+        return DisjunctionCode(ops, self.binding)
+
+
+class EncodeNegation(Encode):
+
+    adapts(NegationBinding)
+
+    def __call__(self):
+        op = self.state.encode(self.binding.op)
+        return NegationCode(op, self.binding)
+
+
+class Convert(Adapter):
+
+    adapts(Domain, Domain)
+
+    @classmethod
+    def dispatch(interface, binding, *args, **kwds):
+        assert isinstance(binding, CastBinding)
+        return (type(binding.base.domain), type(binding.domain))
+
+    def __init__(self, binding, state):
+        assert isinstance(binding, CastBinding)
+        assert isinstance(state, EncodingState)
+        self.binding = binding
+        self.base = binding.base
+        self.domain = binding.domain
+        self.state = state
+
+    def __call__(self):
+        base = self.state.encode(self.base)
+        if base.domain == self.domain:
+            return base
+        return CastCode(base, self.domain, self.binding)
+
+
+class ConvertUntyped(Convert):
+
+    adapts(UntypedDomain, Domain)
+
+    def __call__(self):
+        base = self.state.encode(self.base)
+        assert isinstance(base, LiteralCode)
+        assert isinstance(base.domain, UntypedDomain)
+        try:
+            value = self.domain.parse(base.value)
+        except ValueError, exc:
+            raise EncodeError(str(exc), self.binding.mark)
+        return LiteralCode(value, self.domain, self.binding)
+
+
+class ConvertTupleToBoolean(Convert):
+
+    adapts(TupleDomain, BooleanDomain)
+
+    def __call__(self):
+        space = self.state.relate(self.binding.base)
+        if space.table is None:
+            raise EncodeError("expected a space with a prominent table",
+                              self.binding.mark)
+        for column in space.table.columns:
+            if not column.is_nullable:
+                break
+        else:
+            raise EncodeError("expected a table with at least one"
+                              " non-nullable column", self.binding.mark)
+        unit = ColumnUnit(column, space, self.binding)
+        literal = LiteralCode(None, column.domain, self.binding)
+        return NegationCode(TotalEqualityCode(unit, literal, self.binding),
+                            self.binding)
+
+
+class EncodeCast(Encode):
+
+    adapts(CastBinding)
+
+    def __call__(self):
+        convert = Convert(self.binding, self.state)
+        return convert()
+
+
+class DirectCast(Direct):
+
+    adapts(CastBinding)
+
+    def __call__(self):
+        return self.state.direct(self.binding.base)
+
+
+class EncodeWrapper(Encode):
+
+    adapts(WrapperBinding)
+
+    def __call__(self):
+        return self.state.encode(self.binding.base)
+
+
+class RelateWrapper(Relate):
+
+    adapts(WrapperBinding)
+
+    def __call__(self):
+        return self.state.relate(self.binding.base)
+
+
+class DirectWrapper(Direct):
+
+    adapts(WrapperBinding)
+
+    def __call__(self):
+        return self.state.direct(self.binding.base)
+
+
+class DirectDirection(Direct):
+
+    adapts(DirectionBinding)
+
+    def __call__(self):
+        direction = self.binding.direction
+        base_direction = self.state.direct(self.binding.base)
+        if base_direction is not None:
+            direction *= base_direction
+        return direction
+
+
+def encode(binding, state=None):
+    if state is None:
+        state = EncodingState()
+    encode = Encode(binding, state)
+    return encode()
+
+
+def relate(binding, state=None):
+    if state is None:
+        state = EncodingState()
+    relate = Relate(binding, state)
+    return relate()
+
+
+def direct(binding, state=None):
+    if state is None:
+        state = EncodingState()
+    direct = Direct(binding, state)
+    return direct()
+
+

src/htsql/tr/encoder.py

-#
-# Copyright (c) 2006-2010, Prometheus Research, LLC
-# Authors: Clark C. Evans <cce@clarkevans.com>,
-#          Kirill Simonov <xi@resolvent.net>
-#
-
-
-"""
-:mod:`htsql.tr.encoder`
-=======================
-
-This module implements the encoding adapter.
-"""
-
-
-from ..adapter import Adapter, adapts
-from ..domain import UntypedDomain, TupleDomain
-from .binding import (Binding, RootBinding, QueryBinding, SegmentBinding,
-                      TableBinding, FreeTableBinding, JoinedTableBinding,
-                      ColumnBinding, LiteralBinding, SieveBinding,
-                      SortBinding, EqualityBinding, TotalEqualityBinding,
-                      ConjunctionBinding, DisjunctionBinding,
-                      NegationBinding, CastBinding, WrapperBinding,
-                      OrderBinding)
-from .code import (ScalarSpace, CrossProductSpace, JoinProductSpace,
-                   FilteredSpace, OrderedSpace,
-                   QueryExpression, SegmentExpression, LiteralCode,
-                   EqualityCode, TotalEqualityCode,
-                   ConjunctionCode, DisjunctionCode, NegationCode,
-                   CastCode, ColumnUnit)
-from ..error import InvalidArgumentError
-
-
-class Encoder(object):
-
-    def encode(self, binding):
-        encode = Encode(binding, self)
-        return encode.encode()
-
-    def relate(self, binding):
-        encode = Encode(binding, self)
-        return encode.relate()
-
-    def order(self, binding):
-        encode = Encode(binding, self)
-        return encode.order()
-
-
-class Encode(Adapter):
-
-    adapts(Binding, Encoder)
-
-    def __init__(self, binding, encoder):
-        self.binding = binding
-        self.encoder = encoder
-
-    def encode(self):
-        raise InvalidArgumentError("unable to encode a node",
-                                   self.binding.mark)
-
-    def relate(self):
-        raise InvalidArgumentError("unable to relate a node",
-                                   self.binding.mark)
-
-    def order(self):
-        return None
-
-
-class EncodeQuery(Encode):
-
-    adapts(QueryBinding, Encoder)
-
-    def encode(self):
-        segment = None
-        if self.binding.segment is not None:
-            segment = self.encoder.encode(self.binding.segment)
-        return QueryExpression(segment, self.binding)
-
-
-class EncodeSegment(Encode):
-
-    adapts(SegmentBinding, Encoder)
-
-    def encode(self):
-        space = self.encoder.relate(self.binding.base)
-        elements = []
-        order = []
-        for binding in self.binding.elements:
-            element = self.encoder.encode(binding)
-            direction = self.encoder.order(binding)
-            if direction is not None:
-                order.append((element, direction))
-            elements.append(element)
-        space = OrderedSpace(space, order, None, None, self.binding)
-        return SegmentExpression(space, elements, self.binding)
-
-
-class EncodeRoot(Encode):
-
-    adapts(RootBinding, Encoder)
-
-    def relate(self):
-        return ScalarSpace(None, self.binding)
-
-
-class EncodeFreeTable(Encode):
-
-    adapts(FreeTableBinding, Encoder)
-
-    def relate(self):
-        parent = self.encoder.relate(self.binding.base)
-        return CrossProductSpace(parent, self.binding.table, self.binding)
-
-
-class EncodeJoinedTable(Encode):
-
-    adapts(JoinedTableBinding, Encoder)
-
-    def relate(self):
-        space = self.encoder.relate(self.binding.base)
-        for join in self.binding.joins:
-            space = JoinProductSpace(space, join, self.binding)
-        return space
-
-
-class EncodeColumn(Encode):
-
-    adapts(ColumnBinding, Encoder)
-
-    def relate(self):
-        if self.binding.link is not None:
-            return self.encoder.relate(self.binding.link)
-        return super(EncodeColumn, self).relate()
-
-    def encode(self):
-        space = self.encoder.relate(self.binding.base)
-        return ColumnUnit(self.binding.column, space, self.binding)
-
-
-class EncodeSieve(Encode):
-
-    adapts(SieveBinding, Encoder)
-
-    def relate(self):
-        space = self.encoder.relate(self.binding.base)
-        filter = self.encoder.encode(self.binding.filter)
-        return FilteredSpace(space, filter, self.binding)
-
-    def order(self):
-        return self.encoder.order(self.binding.base)
-
-
-class EncodeSort(Encode):
-
-    adapts(SortBinding, Encoder)
-
-    def relate(self):
-        space = self.encoder.relate(self.binding.base)
-        order = []
-        for binding in self.binding.order:
-            code = self.encoder.encode(binding)
-            direction = self.encoder.order(binding)
-            if direction is None:
-                direction = +1
-            order.append((code, direction))
-        limit = self.binding.limit
-        offset = self.binding.offset
-        return OrderedSpace(space, order, limit, offset, self.binding)
-
-
-class EncodeLiteral(Encode):
-
-    adapts(LiteralBinding, Encoder)
-
-    def encode(self):
-        return LiteralCode(self.binding.value, self.binding.domain,
-                           self.binding)
-
-
-class EncodeEquality(Encode):
-
-    adapts(EqualityBinding, Encoder)
-
-    def encode(self):
-        left = self.encoder.encode(self.binding.lop)
-        right = self.encoder.encode(self.binding.rop)
-        return EqualityCode(left, right, self.binding)
-
-
-class EncodeTotalEquality(Encode):
-
-    adapts(TotalEqualityBinding, Encoder)
-
-    def encode(self):
-        left = self.encoder.encode(self.binding.lop)
-        right = self.encoder.encode(self.binding.rop)
-        return TotalEqualityCode(left, right, self.binding)
-
-
-class EncodeConjunction(Encode):
-
-    adapts(ConjunctionBinding, Encoder)
-
-    def encode(self):
-        terms = [self.encoder.encode(op) for op in self.binding.ops]
-        return ConjunctionCode(terms, self.binding)
-
-
-class EncodeDisjunction(Encode):
-
-    adapts(DisjunctionBinding, Encoder)
-
-    def encode(self):
-        terms = [self.encoder.encode(op) for op in self.binding.ops]
-        return DisjunctionCode(terms, self.binding)
-
-
-class EncodeNegation(Encode):
-
-    adapts(NegationBinding, Encoder)
-
-    def encode(self):
-        term = self.encoder.encode(self.binding.op)
-        return NegationCode(term, self.binding)
-
-
-class EncodeCast(Encode):
-
-    adapts(CastBinding, Encoder)
-
-    def encode(self):
-        if isinstance(self.binding.op.domain, TupleDomain):
-            space = self.encoder.relate(self.binding.op)
-            primary_key = space.table.primary_key
-            assert primary_key is not None
-            column_name = primary_key.origin_column_names[0]
-            column = space.table.columns[column_name]
-            code = ColumnUnit(column, space, self.binding)
-            literal = LiteralCode(None, column.domain, self.binding)
-            return NegationCode(TotalEqualityCode(code, literal, self.binding),
-                                self.binding)
-        code = self.encoder.encode(self.binding.op)
-        if isinstance(code.domain, self.binding.domain.__class__):
-            return code
-        if isinstance(code.domain, UntypedDomain):
-            try:
-                value = self.binding.domain.parse(code.value)
-            except ValueError, exc:
-                raise InvalidArgumentError(str(exc), code.mark)
-            return LiteralCode(value, self.binding.domain, self.binding)
-        return CastCode(code, self.binding.domain, self.binding)
-
-    def order(self):
-        return self.encoder.order(self.binding.op)
-
-
-class EncodeWrapper(Encode):
-
-    adapts(WrapperBinding, Encoder)
-
-    def encode(self):
-        return self.encoder.encode(self.binding.base)
-
-    def relate(self):
-        return self.encoder.relate(self.binding.base)
-
-    def order(self):
-        return self.encoder.order(self.binding.base)
-
-
-class EncodeOrder(Encode):
-
-    adapts(OrderBinding, Encoder)
-
-    def order(self):
-        dir = self.binding.dir
-        base_dir = self.encoder.order(self.binding.base)
-        if base_dir is not None:
-            dir *= base_dir
-        return dir
-
-

src/htsql/tr/error.py

     kind = "bind error"
 
 
+class EncodeError(TranslateError):
+    """
+    Represents an encoder error.
+
+    This error is raised when the encoder is unable to encode or relate
+    a binding node.
+    """
+
+    kind = "encode error"
+
+

src/htsql/tr/fn/function.py

 from ..binding import (LiteralBinding, SortBinding, FunctionBinding,
                        EqualityBinding, TotalEqualityBinding,
                        ConjunctionBinding, DisjunctionBinding, NegationBinding,
-                       CastBinding, TitleBinding, OrderBinding)
-from ..encoder import Encoder, Encode
+                       CastBinding, TitleBinding, DirectionBinding)
+from ..encode import Encode
 from ..code import (FunctionCode, NegationCode, AggregateUnit,
                     CorrelatedUnit, LiteralCode, FilteredSpace)
 from ..compiler import Compiler, Evaluate
     ]
 
     def correlate(self, base):
-        yield OrderBinding(base, +1, self.syntax)
+        yield DirectionBinding(base, +1, self.syntax)
 
 
 class DescOrderFunction(ProperFunction):
     ]
 
     def correlate(self, base):
-        yield OrderBinding(base, -1, self.syntax)
+        yield DirectionBinding(base, -1, self.syntax)
 
 
 class LimitMethod(ProperMethod):
     @classmethod
     def factory(cls, function, binding_class, expression_class):
         name = 'Encode' + function.__name__
-        signature = (binding_class, Encoder)
+        signature = (binding_class,)
         encode_class = type(name, (cls,),
                             {'function': function,
                              'signatures': [signature],
                              'expression_class': expression_class})
         return encode_class
 
-    def encode(self):
+    def __call__(self):
         arguments = {}
         for parameter in self.function.parameters:
             value = self.binding.arguments[parameter.name]
             if not parameter.is_mandatory and value is None:
                 argument = None
             elif parameter.is_list:
-                argument = [self.encoder.encode(item) for item in value]
+                argument = [self.state.encode(item) for item in value]
             else:
-                argument = self.encoder.encode(value)
+                argument = self.state.encode(value)
             arguments[parameter.name] = argument
         for name in sorted(self.binding.arguments):
             if name not in arguments:
     @classmethod
     def factory(cls, function, binding_class, expression_class, wrapper_class):
         name = 'Encode' + function.__name__
-        signature = (binding_class, Encoder)
+        signature = (binding_class,)
         encode_class = type(name, (cls,),
                             {'function': function,
                              'signatures': [signature],
                              'wrapper_class': wrapper_class})
         return encode_class
 
-    def encode(self):
-        expression = self.encoder.encode(self.binding.expression)
+    def __call__(self):
+        expression = self.state.encode(self.binding.expression)
         expression = self.expression_class(self.binding.domain,
                                            self.binding,
                                            expression=expression)
-        space = self.encoder.relate(self.binding.base)
+        space = self.state.relate(self.binding.base)
         plural_units = [unit for unit in expression.units
                              if not space.spans(unit.space)]
         if not plural_units:
     is_exists = False
     is_every = False
 
-    def encode(self):
-        expression = self.encoder.encode(self.binding.expression)
+    def __call__(self):
+        expression = self.state.encode(self.binding.expression)
         if self.is_every:
             expression = NegationCode(expression, self.binding)
-        space = self.encoder.relate(self.binding.base)
+        space = self.state.relate(self.binding.base)
         plural_units = [unit for unit in expression.units
                              if not space.spans(unit.space)]
         if not plural_units:
 
 class EncodeExists(EncodeExistsEvery):
 
-    adapts(ExistsBinding, Encoder)
+    adapts(ExistsBinding)
     is_exists = True
 
 
 
 class EncodeEvery(EncodeExistsEvery):
 
-    adapts(EveryBinding, Encoder)
+    adapts(EveryBinding)
     is_every = True
 
 

test/output/pgsql.yaml

           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            invalid argument: invalid Boolean literal: expected 'true' or 'false'; got '':
+            encode error: invalid Boolean literal: expected 'true' or 'false'; got '':
                 /{boolean('')}
-                          ^^
+                  ^^^^^^^^^^^
         - uri: /{boolean('X')}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            invalid argument: invalid Boolean literal: expected 'true' or 'false'; got 'X':
+            encode error: invalid Boolean literal: expected 'true' or 'false'; got 'X':
                 /{boolean('X')}
-                          ^^^
+                  ^^^^^^^^^^^^
         - uri: /{boolean(integer(null())),boolean(0),boolean(1), boolean(0.0),boolean(1.0),boolean(0e0),boolean(1e0)}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            invalid argument: invalid integer literal: expected an integer in a decimal format; got 'X':
+            encode error: invalid integer literal: expected an integer in a decimal format; got 'X':
                 /{integer('X')}
-                          ^^^
+                  ^^^^^^^^^^^^
         - uri: /{integer(string('X'))}
           status: 409 Conflict
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            invalid argument: invalid decimal literal: Invalid literal for Decimal: 'X':
+            encode error: invalid decimal literal: Invalid literal for Decimal: 'X':
                 /{decimal('X')}
-                          ^^^
+                  ^^^^^^^^^^^^
         - uri: /{decimal(string('X'))}
           status: 409 Conflict
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            invalid argument: invalid float literal: invalid literal for float(): X:
+            encode error: invalid float literal: invalid literal for float(): X:
                 /{float('X')}
-                        ^^^
+                  ^^^^^^^^^^
         - uri: /{float(string('X'))}
           status: 409 Conflict
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            invalid argument: invalid date literal: expected a valid date in a 'YYYY-MM-DD' format; got 'X':
+            encode error: invalid date literal: expected a valid date in a 'YYYY-MM-DD' format; got 'X':
                 /{date('X')}
-                       ^^^
+                  ^^^^^^^^^
         - uri: /{date(string('X'))}
           status: 409 Conflict
           headers:
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.