Commits

Kirill Simonov committed 6c65964

Added a new translation phase: routing.

Comments (0)

Files changed (17)

src/htsql/core/tr/compile.py

 
     def __call__(self):
         # Initialize the all state spaces with a root scalar space.
-        self.state.set_root(RootSpace(None, self.expression.binding))
+        self.state.set_root(RootSpace(None, self.expression.flow))
         # Compile the segment term.
         segment = None
         if self.expression.segment is not None:
             filters = []
             for code in self.space.kernels:
                 filter = FormulaCode(IsNullSig(-1), coerce(BooleanDomain()),
-                                     code.binding, op=code)
+                                     code.flow, op=code)
                 filters.append(filter)
             if len(filters) == 1:
                 [filter] = filters
             else:
                 filter = FormulaCode(AndSig(), coerce(BooleanDomain()),
-                                     self.space.binding, ops=filters)
+                                     self.space.flow, ops=filters)
             # The final seed term.
             seed_term = FilterTerm(self.state.tag(), seed_term, filter,
                                    seed_term.space, seed_term.baseline,
         # Clear out companions to avoid infinite recursion.
         quotient = self.backbone.clone(companions=[])
         # The plural space for the aggregates.
-        complement = ComplementSpace(quotient, self.space.binding)
+        complement = ComplementSpace(quotient, self.space.flow)
         # We can only inject aggregates if the seed term has the regular shape.
         if self.space.companions and is_regular:
             # We are going to disguise the seed term as a complement.
             # The routing table for the complement term.
             routes = {}
             for code in seed_term.routes:
-                unit = CoveringUnit(code, complement, code.binding)
+                unit = CoveringUnit(code, complement, code.flow)
                 routes[unit] = seed_term.tag
             for code in self.space.kernels:
-                unit = CoveringUnit(code, complement, code.binding)
+                unit = CoveringUnit(code, complement, code.flow)
                 routes[unit] = seed_term.tag
             for unit in spread(self.space.seed.inflate()):
                 routes[unit.clone(space=complement)] = seed_term.routes[unit]
             # and the list of units.
             for joint in seed_joints:
                 basis.append(joint.rop)
-                unit = KernelUnit(joint.rop, self.backbone, joint.rop.binding)
+                unit = KernelUnit(joint.rop, self.backbone, joint.rop.flow)
                 units.append(unit)
                 joints.append(joint.clone(rop=unit))
 
         # The units attaching the seed ground to the parent space.
         for lop, rop in tie(self.space.ground):
             basis.append(rop)
-            unit = KernelUnit(rop, self.backbone, rop.binding)
+            unit = KernelUnit(rop, self.backbone, rop.flow)
             units.append(unit)
         # The kernel expressions.
         for code in self.space.kernels:
             basis.append(code)
-            unit = KernelUnit(code, self.backbone, code.binding)
+            unit = KernelUnit(code, self.backbone, code.flow)
             units.append(unit)
         # Injected complement aggregates (regular case only).
         for code in aggregates:
             unit = AggregateUnit(code, complement, self.backbone,
-                                 code.binding)
+                                 code.flow)
             units.append(unit)
 
         # FIXME: incomplete; not reachable because we raise an error
         # is added to the projection basis.
         if all(not code.units for code in self.space.kernels):
             basis_code = LiteralCode(True, coerce(BooleanDomain()),
-                                     self.space.binding)
+                                     self.space.flow)
             basis_unit = ScalarUnit(basis_code, self.space.seed,
-                                    basis_code.binding)
+                                    basis_code.flow)
             basis.append(basis_unit)
             routes = seed_term.routes.copy()
             routes[basis_unit] = seed_term.tag
             filters = []
             for code in self.space.kernels:
                 filter = FormulaCode(IsNullSig(-1), coerce(BooleanDomain()),
-                                     code.binding, op=code)
+                                     code.flow, op=code)
                 filters.append(filter)
             if len(filters) == 1:
                 [filter] = filters
             else:
                 filter = FormulaCode(AndSig(), coerce(BooleanDomain()),
-                                     self.space.binding, ops=filters)
+                                     self.space.flow, ops=filters)
             seed_term = FilterTerm(self.state.tag(), seed_term, filter,
                                    seed_term.space, seed_term.baseline,
                                    seed_term.routes.copy())
                 seed_joints = self.glue_terms(trunk_term, seed_term)
                 for joint in seed_joints:
                     unit = CoveringUnit(joint.rop, self.backbone,
-                                        joint.rop.binding)
+                                        joint.rop.flow)
                     joints.append(joint.clone(rop=unit))
                     # Make sure the joint is exported by the complement term.
                     covering_units.append(unit)
 
         # Wrap everything produced by the seed term.
         for code in seed_term.routes:
-            unit = CoveringUnit(code, self.backbone, code.binding)
+            unit = CoveringUnit(code, self.backbone, code.flow)
             covering_units.append(unit)
         # Ensure we export serial ties.
         for lop, rop in tie(self.space.ground):
-            unit = CoveringUnit(rop, self.backbone, rop.binding)
+            unit = CoveringUnit(rop, self.backbone, rop.flow)
             covering_units.append(unit)
         # Export the kernel and any requested companion units.
         for code in self.space.kernels + self.space.companions:
-            unit = CoveringUnit(code, self.backbone, code.binding)
+            unit = CoveringUnit(code, self.backbone, code.flow)
             covering_units.append(unit)
 
         # Generate the routing table and the complement term.
                 seed_joints = self.glue_terms(trunk_term, shoot_term)
             for joint in seed_joints:
                 unit = CoveringUnit(joint.rop, self.backbone,
-                                    joint.rop.binding)
+                                    joint.rop.flow)
                 joints.append(joint.clone(rop=unit))
             # Append regular joints.
             joints += tie(self.space)
 
         # Wrap everything produced by the seed term.
         for code in seed_term.routes:
-            unit = CoveringUnit(code, self.backbone, code.binding)
+            unit = CoveringUnit(code, self.backbone, code.flow)
             units.append(unit)
         # Ensure we can satisfy the joints.
         for joint in joints:
             units.append(joint.rop)
         # Export any requested companion units and other generated codes.
         for code in codes:
-            unit = CoveringUnit(code, self.backbone, code.binding)
+            unit = CoveringUnit(code, self.backbone, code.flow)
             units.append(unit)
 
         # Generate the routing table and the covering term.
         ops = []
         for code, direction in order:
             op = FormulaCode(SortDirectionSig(direction=direction),
-                             code.domain, code.binding, base=code)
+                             code.domain, code.flow, base=code)
             ops.append(op)
         row_number_code = FormulaCode(RowNumberSig(), coerce(IntegerDomain()),
-                                      self.space.binding,
+                                      self.space.flow,
                                       partition=partition, order=ops)
         row_number_unit = ScalarUnit(row_number_code, term.space.base,
-                                     term.space.binding)
+                                     term.space.flow)
         tag = self.state.tag()
         routes = term.routes.copy()
         routes[row_number_unit] = tag
         if self.space.limit is not None:
             right_bound = left_bound+self.space.limit
         left_bound_code = LiteralCode(left_bound, coerce(IntegerDomain()),
-                                      term.space.binding)
+                                      term.space.flow)
         right_bound_code = LiteralCode(right_bound, coerce(IntegerDomain()),
-                                       term.space.binding)
+                                       term.space.flow)
         left_filter = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                  term.space.binding,
+                                  term.space.flow,
                                   lop=row_number_unit, rop=left_bound_code)
         right_filter = FormulaCode(CompareSig('<'), coerce(BooleanDomain()),
-                                   term.space.binding,
+                                   term.space.flow,
                                    lop=row_number_unit, rop=right_bound_code)
         filter = FormulaCode(AndSig(), coerce(BooleanDomain()),
-                             term.space.binding,
+                             term.space.flow,
                              ops=[left_filter, right_filter])
         return FilterTerm(self.state.tag(), term, filter,
                           term.space, term.baseline, term.routes.copy())
         # and the units suggested be injected together with it.
         units = [self.unit]
         for code in self.unit.companions:
-            companion_unit = ScalarUnit(code, self.space, code.binding)
+            companion_unit = ScalarUnit(code, self.space, code.flow)
             # This test rarely fails since injecting any of the companions
             # injects the whole group.
             if companion_unit not in self.term.routes:
         units = [self.unit]
         for code in self.unit.companions:
             companion_unit = AggregateUnit(code, self.plural_space,
-                                           self.space, code.binding)
+                                           self.space, code.flow)
             # This test rarely fails since injecting any of the companions
             # injects the whole group.
             if companion_unit not in self.term.routes:
         # FIXME: should the kernel of the quotient be `basis`?
         projected_space = QuotientSpace(self.space.inflate(),
                                       self.plural_space, [],
-                                      self.expression.binding)
+                                      self.expression.flow)
         # The routing table of the projected term and join conditions
         # connecting the projected term to the unit term.
         tag = self.state.tag()
         joints = []
         routes = {}
         for joint in unit_joints:
-            rop = KernelUnit(joint.rop, projected_space, joint.rop.binding)
+            rop = KernelUnit(joint.rop, projected_space, joint.rop.flow)
             routes[rop] = tag
             joints.append(joint.clone(rop=rop))
 
             correlations.append(lop)
             lop = CorrelationCode(lop)
             filter = FormulaCode(IsEqualSig(+1), coerce(BooleanDomain()),
-                                 self.space.binding, lop=lop, rop=rop)
+                                 self.space.flow, lop=lop, rop=rop)
             filters.append(filter)
         if len(filters) == 0:
             filter = None
             [filter] = filters
         else:
             filter = FormulaCode(AndSig(), coerce(BooleanDomain()),
-                                 self.space.binding, ops=filters)
+                                 self.space.flow, ops=filters)
         if filter is not None:
             plural_term = FilterTerm(self.state.tag(), plural_term, filter,
                                      plural_term.space, plural_term.baseline,

src/htsql/core/tr/encode.py

         OpaqueDomain)
 from ..error import Error, translate_guard
 from .coerce import coerce
-from .binding import (Binding, QueryBinding, SegmentBinding,
-        WeakSegmentBinding, WrappingBinding, DecorateBinding, SelectionBinding,
-        HomeBinding, RootBinding, TableBinding, ChainBinding, ColumnBinding,
-        QuotientBinding, KernelBinding, ComplementBinding, IdentityBinding,
-        LocateBinding, CoverBinding, ForkBinding, AttachBinding, ClipBinding,
-        SieveBinding, SortBinding, CastBinding, RescopingBinding,
-        LiteralBinding, FormulaBinding)
+from .binding import WeakSegmentBinding
+from .flow import (Flow, QueryFlow, SegmentFlow, SelectionFlow, HomeFlow,
+        RootFlow, TableFlow, ChainFlow, ColumnFlow, QuotientFlow, KernelFlow,
+        ComplementFlow, IdentityFlow, LocateFlow, CoverFlow, ForkFlow,
+        AttachFlow, ClipFlow, SieveFlow, SortFlow, CastFlow, RescopingFlow,
+        LiteralFlow, FormulaFlow)
 from .lookup import direct
 from .space import (RootSpace, ScalarSpace, DirectTableSpace, FiberTableSpace,
         QuotientSpace, ComplementSpace, MonikerSpace, LocatorSpace,
 
     Currently encoding is a stateless process, but we will likely add
     extra state in the future.  The state is also used to store the
-    cache of binding to space and binding to code translations.
+    cache of flow to space and flow to code translations.
     """
 
     # Indicates whether results of `encode` or `relate` are cached.
     # Caching means that two calls of `encode` (or `relate`) on the
-    # same `Binding` instance produce the same object.
+    # same `Flow` instance produce the same object.
     #
     # By default, caching is turned on; however the translator must
     # never rely on that.  That is, the result generated by the
 
     def __init__(self):
         # A mapping of cached results of `encode()`.
-        self.binding_to_code = {}
+        self.flow_to_code = {}
         # A mapping of cached results of `relate()`.
-        self.binding_to_space = {}
+        self.flow_to_space = {}
 
     def flush(self):
         """
         Clears the encoding state.
         """
-        self.binding_to_code.clear()
-        self.binding_to_state.clear()
+        self.flow_to_code.clear()
+        self.flow_to_state.clear()
 
-    def encode(self, binding):
+    def encode(self, flow):
         """
-        Encodes the given binding node to a code expression node.
+        Encodes the given flow node to a code expression node.
 
         Returns a :class:`htsql.core.tr.space.Code` node (in some cases,
         a :class:`htsql.core.tr.space.Expression` node).
 
-        `binding` (:class:`htsql.core.tr.binding.Binding`)
-            The binding node to encode.
+        `flow` (:class:`htsql.core.tr.flow.Flow`)
+            The flow node to encode.
         """
-        # When caching is enabled, we check if `binding` was
+        # When caching is enabled, we check if `flow` was
         # already encoded.  If not, we encode it and save the
         # result.
-        with translate_guard(binding):
+        with translate_guard(flow):
             if self.with_cache:
-                if binding not in self.binding_to_code:
+                if flow not in self.flow_to_code:
                     #FIXME: reduce recursion depth
-                    #code = encode(binding, self)
-                    code = Encode.__prepare__(binding, self)()
-                    self.binding_to_code[binding] = code
-                return self.binding_to_code[binding]
+                    #code = encode(flow, self)
+                    code = Encode.__prepare__(flow, self)()
+                    self.flow_to_code[flow] = code
+                return self.flow_to_code[flow]
             # Caching is disabled; return a new instance every time.
-            return encode(binding, self)
+            return encode(flow, self)
 
-    def relate(self, binding):
+    def relate(self, flow):
         """
-        Encodes the given binding node to a space expression node.
+        Encodes the given flow node to a space expression node.
 
         Returns a :class:`htsql.core.tr.space.Space` node.
 
-        `binding` (:class:`htsql.core.tr.binding.Binding`)
-            The binding node to encode.
+        `flow` (:class:`htsql.core.tr.flow.Flow`)
+            The flow node to encode.
         """
-        # When caching is enabled, we check if `binding` was
+        # When caching is enabled, we check if `flow` was
         # already encoded.  If not, we encode it and save the
         # result.
-        with translate_guard(binding):
+        with translate_guard(flow):
             if self.with_cache:
-                if binding not in self.binding_to_space:
+                if flow not in self.flow_to_space:
                     #FIXME: reduce recursion depth
-                    #space = relate(binding, self)
-                    space = Relate.__prepare__(binding, self)()
-                    self.binding_to_space[binding] = space
-                return self.binding_to_space[binding]
+                    #space = relate(flow, self)
+                    space = Relate.__prepare__(flow, self)()
+                    self.flow_to_space[flow] = space
+                return self.flow_to_space[flow]
             # Caching is disabled; return a new instance every time.
-            return relate(binding, self)
+            return relate(flow, self)
 
 
 class EncodeBase(Adapter):
     """
-    Applies an encoding adapter to a binding node.
+    Applies an encoding adapter to a flow node.
 
     This is a base class for the two encoding adapters: :class:`Encode`
     and :class:`Relate`; it encapsulates methods and attributes shared
     between these adapters.
 
-    The encoding process translates binding nodes to data spaces or
+    The encoding process translates flow nodes to data spaces or
     expressions over data spaces.
 
-    `binding` (:class:`htsql.core.tr.binding.Binding`)
-        The binding node to encode.
+    `flow` (:class:`htsql.core.tr.flow.Flow`)
+        The flow node to encode.
 
     `state` (:class:`EncodingState`)
         The current state of the encoding process.
     """
 
-    adapt(Binding)
+    adapt(Flow)
 
-    def __init__(self, binding, state):
-        assert isinstance(binding, Binding)
+    def __init__(self, flow, state):
+        assert isinstance(flow, Flow)
         assert isinstance(state, EncodingState)
-        self.binding = binding
+        self.flow = flow
         self.state = state
 
 
 class Encode(EncodeBase):
     """
-    Translates a binding node to a code expression node.
+    Translates a flow node to a code expression node.
 
     This is an interface adapter; see subclasses for implementations.
 
     The :class:`Encode` adapter has the following signature::
 
-        Encode: (Binding, EncodingState) -> Expression
+        Encode: (Flow, EncodingState) -> Expression
 
-    The adapter is polymorphic on the `Binding` argument.
+    The adapter is polymorphic on the `Flow` argument.
 
-    This adapter provides non-trivial implementation for binding
+    This adapter provides non-trivial implementation for flow
     nodes representing HTSQL functions and operators.
     """
 
 
 class Relate(EncodeBase):
     """
-    Translates a binding node to a data space node.
+    Translates a flow node to a data space node.
 
     This is an interface adapter; see subclasses for implementations.
 
     The :class:`Relate` adapter has the following signature::
 
-        Relate: (Binding, EncodingState) -> Space
+        Relate: (Flow, EncodingState) -> Space
 
-    The adapter is polymorphic on the `Binding` argument.
+    The adapter is polymorphic on the `Flow` argument.
 
     The adapter provides non-trivial implementations for scoping
-    and chaining bindings.
+    and chaining flows.
     """
 
     def __call__(self):
 
 class EncodeQuery(Encode):
 
-    adapt(QueryBinding)
+    adapt(QueryFlow)
 
     def __call__(self):
         # Encode the segment node if it is provided.
         segment = None
-        if self.binding.segment is not None:
-            segment = self.state.encode(self.binding.segment)
+        if self.flow.segment is not None:
+            segment = self.state.encode(self.flow.segment)
         # Construct the expression node.
-        return QueryExpr(segment, self.binding)
+        return QueryExpr(segment, self.flow)
 
 
 class EncodeSegment(Encode):
 
-    adapt(SegmentBinding)
+    adapt(SegmentFlow)
 
     def __call__(self):
-        root = self.state.relate(self.binding.base)
-        code = self.state.encode(self.binding.seed)
+        root = self.state.relate(self.flow.base)
+        code = self.state.encode(self.flow.seed)
         # List of all unit expressions.
         units = code.units
         # No units means a root scalar space.
         if not units:
-            space = RootSpace(None, self.binding)
+            space = RootSpace(None, self.flow)
         # Otherwise, find a dominating unit space.
         else:
             # List of dominating spaces.
                 isinstance(code.domain, UntypedDomain)):
             if code.value is None:
                 filter = LiteralCode(False, coerce(BooleanDomain()),
-                                     code.binding)
-                space = FilteredSpace(space, filter, space.binding)
+                                     code.flow)
+                space = FilteredSpace(space, filter, space.flow)
         elif coerce(code.domain) is not None:
             filter = FormulaCode(IsNullSig(-1), coerce(BooleanDomain()),
-                                 code.binding, op=code)
-            space = FilteredSpace(space, filter, space.binding)
-        if isinstance(self.binding, WeakSegmentBinding):
+                                 code.flow, op=code)
+            space = FilteredSpace(space, filter, space.flow)
+        if isinstance(self.flow.binding, WeakSegmentBinding):
             if not root.spans(space):
                 raise Error("Expected a singular expression")
-        return SegmentCode(root, space, code, self.binding)
+        return SegmentCode(root, space, code, self.flow)
 
 
 class RelateRoot(Relate):
 
-    adapt(RootBinding)
+    adapt(RootFlow)
 
     def __call__(self):
-        # The root binding gives rise to a root space.
-        return RootSpace(None, self.binding)
+        # The root flow gives rise to a root space.
+        return RootSpace(None, self.flow)
 
 
 class RelateHome(Relate):
 
-    adapt(HomeBinding)
+    adapt(HomeFlow)
 
     def __call__(self):
         # Generate the parent space.
-        base = self.state.relate(self.binding.base)
-        # A home binding gives rise to a scalar space.
-        return ScalarSpace(base, self.binding)
+        base = self.state.relate(self.flow.base)
+        # A home flow gives rise to a scalar space.
+        return ScalarSpace(base, self.flow)
 
 
 class RelateTable(Relate):
 
-    adapt(TableBinding)
+    adapt(TableFlow)
 
     def __call__(self):
         # Generate the parent space.
-        base = self.state.relate(self.binding.base)
+        base = self.state.relate(self.flow.base)
         # Produce a link from a scalar to a table class.
-        return DirectTableSpace(base, self.binding.table, self.binding)
+        return DirectTableSpace(base, self.flow.table, self.flow)
 
 
 class RelateChain(Relate):
 
-    adapt(ChainBinding)
+    adapt(ChainFlow)
 
     def __call__(self):
         # Generate the parent space.
-        space = self.state.relate(self.binding.base)
+        space = self.state.relate(self.flow.base)
         # Produce a link between table classes.
-        for join in self.binding.joins:
-            space = FiberTableSpace(space, join, self.binding)
+        for join in self.flow.joins:
+            space = FiberTableSpace(space, join, self.flow)
         return space
 
 
 class RelateSieve(Relate):
 
-    adapt(SieveBinding)
+    adapt(SieveFlow)
 
     def __call__(self):
         # Generate the parent space.
-        space = self.state.relate(self.binding.base)
+        space = self.state.relate(self.flow.base)
         # Encode the predicate expression.
-        filter = self.state.encode(self.binding.filter)
+        filter = self.state.encode(self.flow.filter)
         # Produce a filtering space operation.
-        return FilteredSpace(space, filter, self.binding)
+        return FilteredSpace(space, filter, self.flow)
 
 
 class RelateSort(Relate):
 
-    adapt(SortBinding)
+    adapt(SortFlow)
 
     def __call__(self):
         # Generate the parent space.
-        space = self.state.relate(self.binding.base)
+        space = self.state.relate(self.flow.base)
         # List of pairs `(code, direction)` containing the expressions
         # to sort by and respective direction indicators.
         order = []
-        # Iterate over ordering binding nodes.
-        for binding in self.binding.order:
-            # Encode the binding node.
-            code = self.state.encode(binding)
-            # Extract the direction modifier; assume `+` if none.
-            direction = direct(binding)
-            if direction is None:
-                direction = +1
+        # Iterate over ordering flow nodes.
+        for flow, direction in self.flow.order:
+            # Encode the flow node.
+            code = self.state.encode(flow)
             order.append((code, direction))
         # The slice indicators.
-        limit = self.binding.limit
-        offset = self.binding.offset
+        limit = self.flow.limit
+        offset = self.flow.offset
         # Produce an ordering space operation.
-        return OrderedSpace(space, order, limit, offset, self.binding)
+        return OrderedSpace(space, order, limit, offset, self.flow)
 
 
 class RelateQuotient(Relate):
 
-    adapt(QuotientBinding)
+    adapt(QuotientFlow)
 
     def __call__(self):
         # Generate the parent space.
-        base = self.state.relate(self.binding.base)
+        base = self.state.relate(self.flow.base)
         # Generate the seed space of the quotient.
-        seed = self.state.relate(self.binding.seed)
+        seed = self.state.relate(self.flow.seed)
         # Verify that the seed is a plural descendant of the parent space.
         with translate_guard(seed):
             if base.spans(seed):
             if not seed.spans(base):
                 raise Error("Expected a descendant expression")
         # Encode the kernel expressions.
-        kernels = [self.state.encode(binding)
-                   for binding in self.binding.kernels]
+        kernels = [self.state.encode(flow)
+                   for flow in self.flow.kernels]
         # Note: we need to check that the kernel is not scalar, but we can't
         # do it here because some units may be removed by the unmasking
         # process; so the check is delegated to unmasking.
         # Produce a quotient space.
-        return QuotientSpace(base, seed, kernels, self.binding)
+        return QuotientSpace(base, seed, kernels, self.flow)
 
 
 class RelateComplement(Relate):
 
-    adapt(ComplementBinding)
+    adapt(ComplementFlow)
 
     def __call__(self):
         # Generate the parent space.
-        base = self.state.relate(self.binding.base)
+        base = self.state.relate(self.flow.base)
         # Produce a complement space.
-        return ComplementSpace(base, self.binding)
+        return ComplementSpace(base, self.flow)
 
 
 class RelateMoniker(Relate):
 
-    adapt(CoverBinding)
+    adapt(CoverFlow)
 
     def __call__(self):
         # Generate the parent space.
-        base = self.state.relate(self.binding.base)
+        base = self.state.relate(self.flow.base)
         # Generate the seed space.
-        seed = self.state.relate(self.binding.seed)
+        seed = self.state.relate(self.flow.seed)
         # Produce a masking space operation.
-        return MonikerSpace(base, seed, self.binding)
+        return MonikerSpace(base, seed, self.flow)
 
 
 class RelateFork(Relate):
 
-    adapt(ForkBinding)
+    adapt(ForkFlow)
 
     def __call__(self):
         # Generate the parent space.
-        base = self.state.relate(self.binding.base)
+        base = self.state.relate(self.flow.base)
         # The seed coincides with the parent space -- but could be changed
         # after the rewrite step.
         seed = base
         # Generate the fork kernel.
-        kernels = [self.state.encode(binding)
-                   for binding in self.binding.kernels]
+        kernels = [self.state.encode(flow)
+                   for flow in self.flow.kernels]
         # Verify that the kernel is singular against the parent space.
         # FIXME: handled by the compiler.
         #for code in kernels:
         #    if not all(seed.spans(unit.space) for unit in code.units):
         #        raise Error("a singular expression is expected",
         #                    code.mark)
-        return ForkedSpace(base, seed, kernels, self.binding)
+        return ForkedSpace(base, seed, kernels, self.flow)
 
 
 class RelateAttach(Relate):
 
-    adapt(AttachBinding)
+    adapt(AttachFlow)
 
     def __call__(self):
         # Generate the parent and the seed spaces.
-        base = self.state.relate(self.binding.base)
-        seed = self.state.relate(self.binding.seed)
+        base = self.state.relate(self.flow.base)
+        seed = self.state.relate(self.flow.seed)
         # Encode linking expressions.
-        images = [(self.state.encode(lbinding), self.state.encode(rbinding))
-                  for lbinding, rbinding in self.binding.images]
+        images = [(self.state.encode(lflow), self.state.encode(rflow))
+                  for lflow, rflow in self.flow.images]
         # Verify that linking pairs are singular against the parent and
         # the seed spaces.
         # FIXME: don't check as the constraint may be violated after
         #        raise Error("a singular expression is expected",
         #                    rcode.mark)
         filter = None
-        if self.binding.condition is not None:
-            filter = self.state.encode(self.binding.condition)
-        return AttachSpace(base, seed, images, filter, self.binding)
+        if self.flow.condition is not None:
+            filter = self.state.encode(self.flow.condition)
+        return AttachSpace(base, seed, images, filter, self.flow)
 
 
 class RelateClip(Relate):
 
-    adapt(ClipBinding)
+    adapt(ClipFlow)
 
     def __call__(self):
-        base = self.state.relate(self.binding.base)
-        seed = self.state.relate(self.binding.seed)
+        base = self.state.relate(self.flow.base)
+        seed = self.state.relate(self.flow.seed)
         if not (seed.spans(base) and not base.spans(seed)):
-            with translate_guard(self.binding.seed):
+            with translate_guard(self.flow.seed):
                 raise Error("Expected a plural expression")
-        return ClippedSpace(base, seed, self.binding.limit,
-                           self.binding.offset, self.binding)
+        return ClippedSpace(base, seed, self.flow.limit,
+                           self.flow.offset, self.flow)
 
 
 class RelateLocator(Relate):
 
-    adapt(LocateBinding)
+    adapt(LocateFlow)
 
     def __call__(self):
-        base = self.state.relate(self.binding.base)
-        seed = self.state.relate(self.binding.seed)
+        base = self.state.relate(self.flow.base)
+        seed = self.state.relate(self.flow.seed)
         images = [(self.state.encode(lop), self.state.encode(rop))
-                  for lop, rop in self.binding.images]
+                  for lop, rop in self.flow.images]
         filter = None
-        if self.binding.condition is not None:
-            filter = self.state.encode(self.binding.condition)
-        return LocatorSpace(base, seed, images, filter, self.binding)
+        if self.flow.condition is not None:
+            filter = self.state.encode(self.flow.condition)
+        return LocatorSpace(base, seed, images, filter, self.flow)
 
 
 class EncodeColumn(Encode):
 
-    adapt(ColumnBinding)
+    adapt(ColumnFlow)
 
     def __call__(self):
         # Find the space of the column.
-        space = self.state.relate(self.binding.base)
+        space = self.state.relate(self.flow.base)
         # Generate a column unit node on the space.
-        return ColumnUnit(self.binding.column, space, self.binding)
+        return ColumnUnit(self.flow.column, space, self.flow)
 
 
 class RelateColumn(Relate):
 
-    adapt(ColumnBinding)
+    adapt(ColumnFlow)
 
     def __call__(self):
-        # If the column binding has an associated table binding node,
+        # If the column flow has an associated table flow node,
         # delegate the adapter to it.
-        if self.binding.link is not None:
-            return self.state.relate(self.binding.link)
+        if self.flow.link is not None:
+            return self.state.relate(self.flow.link)
         # Otherwise, let the parent produce an error message.
         return super(RelateColumn, self).__call__()
 
 
 class EncodeKernel(Encode):
 
-    adapt(KernelBinding)
+    adapt(KernelFlow)
 
     def __call__(self):
         # Get the quotient space of the kernel.
-        space = self.state.relate(self.binding.base)
+        space = self.state.relate(self.flow.base)
         # Extract the respective kernel expression from the space.
-        code = space.family.kernels[self.binding.index]
+        code = space.family.kernels[self.flow.index]
         # Generate a unit expression.
-        return KernelUnit(code, space, self.binding)
+        return KernelUnit(code, space, self.flow)
 
 
 class EncodeLiteral(Encode):
 
-    adapt(LiteralBinding)
+    adapt(LiteralFlow)
 
     def __call__(self):
-        # Switch the class from `Binding` to `Code` keeping all attributes.
-        return LiteralCode(self.binding.value, self.binding.domain,
-                           self.binding)
+        # Switch the class from `Flow` to `Code` keeping all attributes.
+        return LiteralCode(self.flow.value, self.flow.domain,
+                           self.flow)
 
 
 class EncodeCast(Encode):
 
-    adapt(CastBinding)
+    adapt(CastFlow)
 
     def __call__(self):
         # Delegate it to the `Convert` adapter.
-        return Convert.__prepare__(self.binding, self.state)()
+        return Convert.__prepare__(self.flow, self.state)()
 
 
 class Convert(Adapter):
     """
-    Encodes a cast binding to a code node.
+    Encodes a cast flow to a code node.
 
     This is an auxiliary adapter used to encode
-    :class:`htsql.core.tr.binding.CastBinding` nodes.  The adapter is polymorphic
+    :class:`htsql.core.tr.flow.CastFlow` nodes.  The adapter is polymorphic
     by the origin and the target domains.
 
     The purpose of the adapter is multifold.  The :class:`Convert` adapter:
     - when possible, expresses the cast in terms of other operations; otherwise,
       generates a new :class:`htsql.core.tr.space.CastCode` node.
 
-    `binding` (:class:`htsql.core.tr.binding.CastBinding`)
-        The binding node to encode.
+    `flow` (:class:`htsql.core.tr.flow.CastFlow`)
+        The flow node to encode.
 
         Note that the adapter is dispatched on the pair
-        `(binding.base.domain, binding.domain)`.
+        `(flow.base.domain, flow.domain)`.
 
     `state` (:class:`EncodingState`)
         The current state of the encoding process.
 
     Aliases:
 
-    `base` (:class:`htsql.core.tr.binding.Binding`)
-        An alias for `binding.base`; the operand of the cast expression.
+    `base` (:class:`htsql.core.tr.flow.Flow`)
+        An alias for `flow.base`; the operand of the cast expression.
 
     `domain` (:class:`htsql.core.domain.Domain`)
-        An alias for `binding.domain`; the target domain.
+        An alias for `flow.domain`; the target domain.
     """
 
     adapt(Domain, Domain)
 
     @classmethod
-    def __dispatch__(interface, binding, *args, **kwds):
+    def __dispatch__(interface, flow, *args, **kwds):
         # We override the standard extract of the dispatch key, which
         # returns the type of the first argument(s).  For `Convert`,
         # the dispatch key is the pair of the origin and the target domains.
-        assert isinstance(binding, CastBinding)
-        return (type(binding.base.domain), type(binding.domain))
+        assert isinstance(flow, CastFlow)
+        return (type(flow.base.domain), type(flow.domain))
 
-    def __init__(self, binding, state):
-        assert isinstance(binding, CastBinding)
+    def __init__(self, flow, state):
+        assert isinstance(flow, CastFlow)
         assert isinstance(state, EncodingState)
-        self.binding = binding
-        self.base = binding.base
-        self.domain = binding.domain
+        self.flow = flow
+        self.base = flow.base
+        self.domain = flow.domain
         self.state = state
 
     def __call__(self):
     adapt(UntypedDomain, Domain)
 
     def __call__(self):
-        # The base binding is of untyped domain, however it does not have
-        # to be an instance of `LiteralBinding` since the actual literal node
+        # The base flow is of untyped domain, however it does not have
+        # to be an instance of `LiteralFlow` since the actual literal node
         # could be wrapped by decorators.  However after we encode the node,
         # the decorators are gone and the result must be a `LiteralCode`
         # The domain should remain the same too.
         # FIXME: the literal could possibly be wrapped into `ScalarUnit`
-        # if the literal binding was rescoped.
+        # if the literal flow was rescoped.
         base = self.state.encode(self.base)
         assert isinstance(base, (LiteralCode, ScalarUnit))
         assert isinstance(base.domain, UntypedDomain)
             raise Error(str(exc))
         # Generate a new literal node with the converted value and
         # the target domain.
-        code = LiteralCode(value, self.domain, self.binding)
+        code = LiteralCode(value, self.domain, self.flow)
         # If necessary, wrap the literal back into scalar units.
         while wrappers:
             wrapper = wrappers.pop()
 
     def __call__(self):
         # Encode and return the operand of the cast; drop the cast node itself.
-        return self.state.encode(self.binding.base)
+        return self.state.encode(self.flow.base)
 
 
 class ConvertEntityToBoolean(Convert):
                (RecordDomain, BooleanDomain))
 
     def __call__(self):
-        # When the binding domain is tuple, we assume that the binding
+        # When the flow domain is tuple, we assume that the flow
         # represents some space.  In this case, Boolean cast produces
         # an expression which is `FALSE` when the space is empty and
         # `TRUE` otherwise.  The actual expression is:
         # Translate the operand to a space node.
         space = self.state.relate(self.base)
         # A `TRUE` literal.
-        true_literal = LiteralCode(True, coerce(BooleanDomain()), self.binding)
+        true_literal = LiteralCode(True, coerce(BooleanDomain()), self.flow)
         # A `TRUE` constant as a function on the space.
-        unit = ScalarUnit(true_literal, space, self.binding)
+        unit = ScalarUnit(true_literal, space, self.flow)
         # Return `!is_null(unit)`.
         return FormulaCode(IsNullSig(-1), coerce(BooleanDomain()),
-                           self.binding, op=unit)
+                           self.flow, op=unit)
 
 
 class ConvertTextToBoolean(Convert):
         # Encode the operand of the cast.
         code = self.state.encode(self.base)
         # An empty string.
-        empty_literal = LiteralCode(u'', self.base.domain, self.binding)
+        empty_literal = LiteralCode(u'', self.base.domain, self.flow)
         # Construct: `null_if(base,'')`.
-        code = FormulaCode(NullIfSig(), self.base.domain, self.binding,
+        code = FormulaCode(NullIfSig(), self.base.domain, self.flow,
                            lop=code, rop=empty_literal)
         # Construct: `!is_null(null_if(base,''))`.
-        code = FormulaCode(IsNullSig(-1), self.domain, self.binding,
+        code = FormulaCode(IsNullSig(-1), self.domain, self.flow,
                            op=code)
         # Return `!is_null(null_if(base,''))`.
         return code
         # converted to `TRUE`.
 
         # Construct and return `!is_null(base)`.
-        return FormulaCode(IsNullSig(-1), self.domain, self.binding,
+        return FormulaCode(IsNullSig(-1), self.domain, self.flow,
                            op=self.state.encode(self.base))
 
 
         # We generate a cast code node leaving it to the serializer
         # to specialize on the origin data type.
         return CastCode(self.state.encode(self.base), self.domain,
-                        self.binding)
+                        self.flow)
 
 
 class ConvertToInteger(Convert):
         # engine even though we could handle it here because the
         # conversion may be engine-specific.
         return CastCode(self.state.encode(self.base), self.domain,
-                        self.binding)
+                        self.flow)
 
 
 class ConvertToDecimal(Convert):
                     value = decimal.Decimal(code.value)
                     return code.clone(value=value, domain=self.domain)
         # For the regular case, generate an appropriate cast node.
-        return CastCode(code, self.domain, self.binding)
+        return CastCode(code, self.domain, self.flow)
 
 
 class ConvertToFloat(Convert):
                     value = float(code.value)
                     return code.clone(value=value, domain=self.domain)
         # For the regular case, generate an appropriate cast node.
-        return CastCode(code, self.domain, self.binding)
+        return CastCode(code, self.domain, self.flow)
 
 
 class ConvertToDate(Convert):
         # engine even though we could handle it here because the
         # conversion may be engine-specific.
         return CastCode(self.state.encode(self.base), self.domain,
-                        self.binding)
+                        self.flow)
 
 
 class ConvertToTime(Convert):
     def __call__(self):
         # Leave conversion to the database engine.
         return CastCode(self.state.encode(self.base), self.domain,
-                        self.binding)
+                        self.flow)
 
 
 class ConvertToDateTime(Convert):
     def __call__(self):
         # Leave conversion to the database engine.
         return CastCode(self.state.encode(self.base), self.domain,
-                        self.binding)
+                        self.flow)
 
 
 class EncodeRescoping(Encode):
 
-    adapt(RescopingBinding)
+    adapt(RescopingFlow)
 
     def __call__(self):
         # Wrap the base expression into a scalar unit.
-        code = self.state.encode(self.binding.base)
-        space = self.state.relate(self.binding.scope)
-        return ScalarUnit(code, space, self.binding)
+        code = self.state.encode(self.flow.base)
+        space = self.state.relate(self.flow.scope)
+        return ScalarUnit(code, space, self.flow)
 
 
 class EncodeFormula(Encode):
 
-    adapt(FormulaBinding)
+    adapt(FormulaFlow)
 
     def __call__(self):
         # Delegate the translation to the `EncodeBySignature` adapter.
-        return EncodeBySignature.__prepare__(self.binding, self.state)()
+        return EncodeBySignature.__prepare__(self.flow, self.state)()
 
 
 class RelateFormula(Relate):
 
-    adapt(FormulaBinding)
+    adapt(FormulaFlow)
 
     def __call__(self):
         # Delegate the translation to the `RelateBySignature` adapter.
-        return RelateBySignature.__prepare__(self.binding, self.state)()
+        return RelateBySignature.__prepare__(self.flow, self.state)()
 
 
 class EncodeBySignatureBase(Adapter):
     :class:`EncodeBySignature` and :class:`RelateBySignature`;
     it encapsulates methods and attributes shared between these adapters.
 
-    The adapter accepts a binding formula node and is polymorphic
+    The adapter accepts a flow formula node and is polymorphic
     on the formula signature.
 
-    `binding` (:class:`htsql.core.tr.binding.FormulaBinding`)
+    `flow` (:class:`htsql.core.tr.flow.FormulaFlow`)
         The formula node to encode.
 
     `state` (:class:`EncodingState`)
     adapt(Signature)
 
     @classmethod
-    def __dispatch__(interface, binding, *args, **kwds):
+    def __dispatch__(interface, flow, *args, **kwds):
         # We need to override `dispatch` since the adapter is polymorphic
         # not on the type of the node itself, but on the type of the
         # node signature.
-        assert isinstance(binding, FormulaBinding)
-        return (type(binding.signature),)
+        assert isinstance(flow, FormulaFlow)
+        return (type(flow.signature),)
 
-    def __init__(self, binding, state):
-        assert isinstance(binding, FormulaBinding)
+    def __init__(self, flow, state):
+        assert isinstance(flow, FormulaFlow)
         assert isinstance(state, EncodingState)
-        self.binding = binding
+        self.flow = flow
         self.state = state
         # Extract commonly used attributes of the node.
-        self.signature = binding.signature
-        self.domain = binding.domain
-        self.arguments = binding.arguments
+        self.signature = flow.signature
+        self.domain = flow.domain
+        self.arguments = flow.arguments
 
 
 class EncodeBySignature(EncodeBySignatureBase):
     """
-    Translates a formula binding to a code node.
+    Translates a formula flow to a code node.
 
     This is an auxiliary adapter used to encode
-    class:`htsql.core.tr.binding.FormulaBinding` nodes.  The adapter is
+    class:`htsql.core.tr.flow.FormulaFlow` nodes.  The adapter is
     polymorphic on the formula signature.
 
     Unless overridden, the adapter encodes the arguments of the formula
         # Produce a formula code with the same signature.
         return FormulaCode(self.signature,
                            self.domain,
-                           self.binding,
+                           self.flow,
                            **arguments)
 
 
 class RelateBySignature(EncodeBySignatureBase):
     """
-    Translates a formula binding to a space node.
+    Translates a formula flow to a space node.
 
     This is an auxiliary adapter used to relate
-    class:`htsql.core.tr.binding.FormulaBinding` nodes.  The adapter is
+    class:`htsql.core.tr.flow.FormulaFlow` nodes.  The adapter is
     polymorphic on the formula signature.
 
     Unless overridden, the adapter generates an error.
 
 class EncodeSelection(Encode):
 
-    adapt(SelectionBinding)
+    adapt(SelectionFlow)
 
     def __call__(self):
-        space = self.state.relate(self.binding)
+        space = self.state.relate(self.flow)
         fields = [self.state.encode(element)
-                  for element in self.binding.elements]
-        code = RecordCode(fields, self.binding.domain, self.binding)
-        unit = ScalarUnit(code, space, self.binding)
-        indicator = LiteralCode(True, coerce(BooleanDomain()), self.binding)
-        indicator = ScalarUnit(indicator, space, self.binding)
-        return AnnihilatorCode(unit, indicator, self.binding)
+                  for element in self.flow.elements]
+        code = RecordCode(fields, self.flow.domain, self.flow)
+        unit = ScalarUnit(code, space, self.flow)
+        indicator = LiteralCode(True, coerce(BooleanDomain()), self.flow)
+        indicator = ScalarUnit(indicator, space, self.flow)
+        return AnnihilatorCode(unit, indicator, self.flow)
 
 
 class RelateSelection(Relate):
 
-    adapt(SelectionBinding)
+    adapt(SelectionFlow)
 
     def __call__(self):
-        return self.state.relate(self.binding.base)
+        return self.state.relate(self.flow.base)
 
 
 class EncodeIdentity(Encode):
 
-    adapt(IdentityBinding)
+    adapt(IdentityFlow)
 
     def __call__(self):
-        space = self.state.relate(self.binding.base)
+        space = self.state.relate(self.flow.base)
         fields = [self.state.encode(element)
-                  for element in self.binding.elements]
-        code = IdentityCode(fields, self.binding)
-        unit = ScalarUnit(code, space, self.binding)
-        indicator = LiteralCode(True, coerce(BooleanDomain()), self.binding)
-        indicator = ScalarUnit(indicator, space, self.binding)
-        return AnnihilatorCode(unit, indicator, self.binding)
+                  for element in self.flow.elements]
+        code = IdentityCode(fields, self.flow)
+        unit = ScalarUnit(code, space, self.flow)
+        indicator = LiteralCode(True, coerce(BooleanDomain()), self.flow)
+        indicator = ScalarUnit(indicator, space, self.flow)
+        return AnnihilatorCode(unit, indicator, self.flow)
 
 
-class EncodeWrapping(Encode):
-
-    adapt_many(WrappingBinding,
-               DecorateBinding)
-
-    def __call__(self):
-        # Delegate the adapter to the wrapped binding.
-        return self.state.encode(self.binding.base)
-
-
-class RelateWrapping(Relate):
+def encode(flow, state=None):
     """
-    Translates a wrapper binding to a space node.
-    """
-
-    adapt_many(WrappingBinding,
-               DecorateBinding)
-
-    def __call__(self):
-        # Delegate the adapter to the wrapped binding.
-        return self.state.relate(self.binding.base)
-
-
-def encode(binding, state=None):
-    """
-    Encodes the given binding to an expression node.
+    Encodes the given flow to an expression node.
 
     Returns a :class:`htsql.core.tr.space.Expression` instance (in most cases,
     a :class:`htsql.core.tr.space.Code` instance).
 
-    `binding` (:class:`htsql.core.tr.binding.Binding`)
-        The binding node to encode.
+    `flow` (:class:`htsql.core.tr.flow.Flow`)
+        The flow node to encode.
 
     `state` (:class:`EncodingState` or ``None``)
         The encoding state to use.  If not set, a new encoding state
     if state is None:
         state = EncodingState()
     # Realize and apply the `Encode` adapter.
-    return Encode.__invoke__(binding, state)
+    return Encode.__invoke__(flow, state)
 
 
-def relate(binding, state=None):
+def relate(flow, state=None):
     """
-    Encodes the given binding to a data space node.
+    Encodes the given flow to a data space node.
 
     Returns a :class:`htsql.core.tr.space.Space` instance.
 
-    `binding` (:class:`htsql.core.tr.binding.Binding`)
-        The binding node to encode.
+    `flow` (:class:`htsql.core.tr.flow.Flow`)
+        The flow node to encode.
 
     `state` (:class:`EncodingState` or ``None``)
         The encoding state to use.  If not set, a new encoding state
     if state is None:
         state = EncodingState()
     # Realize and apply the `Relate` adapter.
-    return Relate.__invoke__(binding, state)
+    return Relate.__invoke__(flow, state)
 
 

src/htsql/core/tr/flow.py

+#
+# Copyright (c) 2006-2013, Prometheus Research, LLC
+#
+
+
+from ..util import maybe, listof, tupleof, Clonable, Printable, Hashable
+from ..entity import TableEntity, ColumnEntity, Join
+from ..domain import (Domain, VoidDomain, BooleanDomain, ListDomain,
+        RecordDomain, EntityDomain, IdentityDomain, Profile)
+from ..error import point
+from .binding import Binding, VoidBinding
+from .signature import Signature, Bag, Formula
+
+
+class Flow(Clonable):
+
+    def __init__(self, base, domain, binding):
+        assert base is not None or isinstance(self, (RootFlow, VoidFlow))
+        assert isinstance(domain, Domain)
+        assert isinstance(binding, Binding)
+
+        self.base = base
+        self.domain = domain
+        self.binding = binding
+        point(self, binding)
+
+
+class VoidFlow(Flow):
+
+    def __init__(self):
+        super(VoidFlow, self).__init__(None, VoidDomain(), VoidBinding())
+
+
+class ScopeFlow(Flow):
+    pass
+
+
+class HomeFlow(ScopeFlow):
+
+    def __init__(self, base, binding):
+        super(HomeFlow, self).__init__(base, EntityDomain(), binding)
+
+
+class RootFlow(HomeFlow):
+
+    def __init__(self, binding):
+        super(RootFlow, self).__init__(None, binding)
+
+
+class TableFlow(ScopeFlow):
+
+    def __init__(self, base, table, binding):
+        assert isinstance(table, TableEntity)
+        super(TableFlow, self).__init__(base, EntityDomain(), binding)
+        self.table = table
+
+
+class ChainFlow(TableFlow):
+
+    def __init__(self, base, joins, binding):
+        assert isinstance(joins, listof(Join)) and len(joins) > 0
+        super(ChainFlow, self).__init__(base, joins[-1].target, binding)
+        self.joins = joins
+
+
+class ColumnFlow(ScopeFlow):
+
+    def __init__(self, base, column, link, binding):
+        assert isinstance(column, ColumnEntity)
+        assert isinstance(link, maybe(Flow))
+        super(ColumnFlow, self).__init__(base, column.domain, binding)
+        self.column = column
+        self.link = link
+
+
+class QuotientFlow(ScopeFlow):
+
+    def __init__(self, base, seed, kernels, binding):
+        super(QuotientFlow, self).__init__(base, EntityDomain(), binding)
+        self.seed = seed
+        self.kernels = kernels
+
+
+class CoverFlow(ScopeFlow):
+
+    def __init__(self, base, seed, binding):
+        super(CoverFlow, self).__init__(base, seed.domain, binding)
+        self.seed = seed
+
+
+class KernelFlow(CoverFlow):
+
+    def __init__(self, base, quotient, index, binding):
+        assert isinstance(quotient, QuotientFlow)
+        assert isinstance(index, int)
+        assert 0 <= index < len(quotient.kernels)
+        seed = quotient.kernels[index]
+        super(KernelFlow, self).__init__(base, seed, binding)
+        self.quotient = quotient
+        self.index = index
+
+
+class ComplementFlow(CoverFlow):
+
+    def __init__(self, base, quotient, binding):
+        assert isinstance(quotient, QuotientFlow)
+        super(ComplementFlow, self).__init__(base, quotient.seed, binding)
+        self.quotient = quotient
+
+
+class ForkFlow(CoverFlow):
+
+    def __init__(self, base, kernels, binding):
+        assert isinstance(kernels, listof(Flow))
+        super(ForkFlow, self).__init__(base, base, binding)
+        self.kernels = kernels
+
+
+class AttachFlow(CoverFlow):
+
+    def __init__(self, base, seed, images, condition, binding):
+        assert isinstance(images, listof(tupleof(Flow, Flow)))
+        assert isinstance(condition, maybe(Flow))
+        if condition is not None:
+            assert isinstance(condition.domain, BooleanDomain)
+        super(AttachFlow, self).__init__(base, seed, binding)
+        self.images = images
+        self.condition = condition
+
+
+class LocateFlow(AttachFlow):
+    pass
+
+
+class ClipFlow(CoverFlow):
+
+    def __init__(self, base, seed, order, limit, offset, binding):
+        assert isinstance(seed, Flow)
+        assert isinstance(order, listof(tupleof(Flow, int)))
+        assert isinstance(limit, maybe(int))
+        assert isinstance(offset, maybe(int))
+        super(ClipFlow, self).__init__(base, seed, binding)
+        self.order = order
+        self.limit = limit
+        self.offset = offset
+
+
+class QueryFlow(Flow):
+
+    def __init__(self, base, segment, profile, binding):
+        assert isinstance(base, RootFlow)
+        assert isinstance(segment, maybe(SegmentFlow))
+        assert isinstance(profile, Profile)
+        super(QueryFlow, self).__init__(base, VoidDomain(), binding)
+        self.segment = segment
+        self.profile = profile
+
+
+class SegmentFlow(Flow):
+
+    def __init__(self, base, seed, domain, binding):
+        assert isinstance(base, Flow)
+        assert isinstance(seed, Flow)
+        super(SegmentFlow, self).__init__(base, domain, binding)
+        self.seed = seed
+
+
+class SieveFlow(ScopeFlow):
+
+    def __init__(self, base, filter, binding):
+        assert isinstance(filter, Flow)
+        assert isinstance(filter.domain, BooleanDomain)
+        super(SieveFlow, self).__init__(base, base.domain, binding)
+        self.filter = filter
+
+
+class SortFlow(ScopeFlow):
+
+    def __init__(self, base, order, limit, offset, binding):
+        assert isinstance(order, listof(tupleof(Flow, int)))
+        assert isinstance(limit, maybe(int))
+        assert isinstance(offset, maybe(int))
+        super(SortFlow, self).__init__(base, base.domain, binding)
+        self.order = order
+        self.limit = limit
+        self.offset = offset
+
+
+class RescopingFlow(ScopeFlow):
+
+    def __init__(self, base, scope, binding):
+        assert isinstance(scope, Flow)
+        super(RescopingFlow, self).__init__(base, base.domain, binding)
+        self.scope = scope
+
+
+class SelectionFlow(ScopeFlow):
+
+    def __init__(self, base, elements, domain, binding):
+        assert isinstance(elements, listof(Flow))
+        super(SelectionFlow, self).__init__(base, domain, binding)
+        self.elements = elements
+
+
+class IdentityFlow(Flow):
+
+    def __init__(self, base, elements, binding):
+        assert isinstance(elements, listof(Flow))
+        domain = IdentityDomain([element.domain for element in elements])
+        super(IdentityFlow, self).__init__(base, domain, binding)
+        self.elements = elements
+        self.width = domain.width
+
+
+class LiteralFlow(Flow):
+
+    def __init__(self, base, value, domain, binding):
+        super(LiteralFlow, self).__init__(base, domain, binding)
+        self.value = value
+
+
+class CastFlow(Flow):
+
+    def __init__(self, base, domain, binding):
+        super(CastFlow, self).__init__(base, domain, binding)
+
+
+class ImplicitCastFlow(CastFlow):
+    pass
+
+
+class FormulaFlow(Formula, Flow):
+
+    def __init__(self, base, signature, domain, binding, **arguments):
+        assert isinstance(signature, Signature)
+        arguments = Bag(**arguments)
+        assert arguments.admits(Flow, signature)
+        super(FormulaFlow, self).__init__(signature, arguments,
+                                          base, domain, binding)
+
+

src/htsql/core/tr/fn/encode.py

 from ..encode import EncodeBySignature, EncodingState
 from ...error import Error, translate_guard
 from ..coerce import coerce
-from ..binding import RootBinding, LiteralBinding, CastBinding
+from ..flow import RootFlow, LiteralFlow, CastFlow
 from ..space import (LiteralCode, ScalarUnit, CorrelatedUnit,
                     AggregateUnit, FilteredSpace, FormulaCode)
 from ..signature import Signature, NotSig, NullIfSig, IfNullSig, CompareSig
 
     def __call__(self):
         code = super(EncodeLength, self).__call__()
-        zero = LiteralCode(0, code.domain, code.binding)
-        return FormulaCode(IfNullSig(), code.domain, code.binding,
+        zero = LiteralCode(0, code.domain, code.flow)
+        return FormulaCode(IfNullSig(), code.domain, code.flow,
                            lop=code, rop=zero)
 
 
     adapt(ContainsSig)
 
     def __call__(self):
-        lop = self.state.encode(self.binding.lop)
-        rop = self.state.encode(self.binding.rop)
+        lop = self.state.encode(self.flow.lop)
+        rop = self.state.encode(self.flow.rop)
         if isinstance(rop, LiteralCode):
             if rop.value is not None:
                 value = (u"%" + rop.value.replace(u"\\", u"\\\\")
                                          .replace(u"_", u"\\_") + u"%")
                 rop = rop.clone(value=value)
         else:
-            backslash_literal = LiteralCode(u"\\", rop.domain, self.binding)
-            xbackslash_literal = LiteralCode(u"\\\\", rop.domain, self.binding)
-            percent_literal = LiteralCode(u"%", rop.domain, self.binding)
-            xpercent_literal = LiteralCode(u"\\%", rop.domain, self.binding)
-            underscore_literal = LiteralCode(u"_", rop.domain, self.binding)
-            xunderscore_literal = LiteralCode(u"\\_", rop.domain, self.binding)
-            rop = FormulaCode(ReplaceSig(), rop.domain, self.binding,
+            backslash_literal = LiteralCode(u"\\", rop.domain, self.flow)
+            xbackslash_literal = LiteralCode(u"\\\\", rop.domain, self.flow)
+            percent_literal = LiteralCode(u"%", rop.domain, self.flow)
+            xpercent_literal = LiteralCode(u"\\%", rop.domain, self.flow)
+            underscore_literal = LiteralCode(u"_", rop.domain, self.flow)
+            xunderscore_literal = LiteralCode(u"\\_", rop.domain, self.flow)
+            rop = FormulaCode(ReplaceSig(), rop.domain, self.flow,
                               op=rop, old=backslash_literal,
                               new=xbackslash_literal)
-            rop = FormulaCode(ReplaceSig(), rop.domain, self.binding,
+            rop = FormulaCode(ReplaceSig(), rop.domain, self.flow,
                               op=rop, old=percent_literal,
                               new=xpercent_literal)
-            rop = FormulaCode(ReplaceSig(), rop.domain, self.binding,
+            rop = FormulaCode(ReplaceSig(), rop.domain, self.flow,
                               op=rop, old=underscore_literal,
                               new=xunderscore_literal)
-            rop = FormulaCode(ConcatenateSig(), rop.domain, self.binding,
+            rop = FormulaCode(ConcatenateSig(), rop.domain, self.flow,
                               lop=percent_literal, rop=rop)
-            rop = FormulaCode(ConcatenateSig(), rop.domain, self.binding,
+            rop = FormulaCode(ConcatenateSig(), rop.domain, self.flow,
                               lop=rop, rop=percent_literal)
         return FormulaCode(self.signature.clone_to(LikeSig),
-                           self.domain, self.binding, lop=lop, rop=rop)
+                           self.domain, self.flow, lop=lop, rop=rop)
 
 
 class EncodeHead(EncodeFunction):
     adapt(HeadSig)
 
     def __call__(self):
-        op = self.state.encode(self.binding.op)
+        op = self.state.encode(self.flow.op)
         op_length = FormulaCode(LengthSig(), coerce(IntegerDomain()),
-                                self.binding, op=op)
-        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.binding)
-        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.binding)
-        if self.binding.length is None:
+                                self.flow, op=op)
+        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.flow)
+        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.flow)
+        if self.flow.length is None:
             length = one_literal
         else:
-            length = self.state.encode(self.binding.length)
+            length = self.state.encode(self.flow.length)
         if isinstance(length, LiteralCode):
             if length.value is None:
                 length = one_literal
             if length.value >= 0:
-                return FormulaCode(SubstringSig(), self.binding.domain,
-                                   self.binding, op=op, start=one_literal,
+                return FormulaCode(SubstringSig(), self.flow.domain,
+                                   self.flow, op=op, start=one_literal,
                                    length=length)
-        length = FormulaCode(IfNullSig(), length.domain, self.binding,
+        length = FormulaCode(IfNullSig(), length.domain, self.flow,
                              lop=length, rop=one_literal)
-        negative_length = FormulaCode(AddSig(), length.domain, self.binding,
+        negative_length = FormulaCode(AddSig(), length.domain, self.flow,
                                       lop=op_length, rop=length)
         if_positive = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                  self.binding, lop=length, rop=zero_literal)
+                                  self.flow, lop=length, rop=zero_literal)
         if_negative = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                  self.binding, lop=negative_length,
+                                  self.flow, lop=negative_length,
                                   rop=zero_literal)
-        length = FormulaCode(IfSig(), length.domain, self.binding,
+        length = FormulaCode(IfSig(), length.domain, self.flow,
                              predicates=[if_positive, if_negative],
                              consequents=[length, negative_length],
                              alternative=zero_literal)
-        return FormulaCode(SubstringSig(), self.binding.domain, self.binding,
+        return FormulaCode(SubstringSig(), self.flow.domain, self.flow,
                            op=op, start=one_literal, length=length)
 
 
     adapt(TailSig)
 
     def __call__(self):
-        op = self.state.encode(self.binding.op)
+        op = self.state.encode(self.flow.op)
         op_length = FormulaCode(LengthSig(), coerce(IntegerDomain()),
-                                self.binding, op=op)
-        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.binding)
-        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.binding)
-        if self.binding.length is None:
+                                self.flow, op=op)
+        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.flow)
+        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.flow)
+        if self.flow.length is None:
             length = one_literal
         else:
-            length = self.state.encode(self.binding.length)
+            length = self.state.encode(self.flow.length)
         if isinstance(length, LiteralCode):
             if length.value is None:
                 length = one_literal
             if length.value < 0:
                 start = length.clone(value=1-length.value)
-                return FormulaCode(SubstringSig(), self.binding.domain,
-                                   self.binding, op=op,
+                return FormulaCode(SubstringSig(), self.flow.domain,
+                                   self.flow, op=op,
                                    start=start, length=None)
-        length = FormulaCode(IfNullSig(), length.domain, self.binding,
+        length = FormulaCode(IfNullSig(), length.domain, self.flow,
                              lop=length, rop=one_literal)
-        start = FormulaCode(SubtractSig(), length.domain, self.binding,
+        start = FormulaCode(SubtractSig(), length.domain, self.flow,
                             lop=one_literal, rop=length)
-        positive_start = FormulaCode(AddSig(), length.domain, self.binding,
+        positive_start = FormulaCode(AddSig(), length.domain, self.flow,
                                      lop=op_length, rop=start)
         if_negative = FormulaCode(CompareSig('<'), coerce(BooleanDomain()),
-                                  self.binding, lop=length, rop=zero_literal)
+                                  self.flow, lop=length, rop=zero_literal)
         if_positive = FormulaCode(CompareSig('<='), coerce(BooleanDomain()),
-                                  self.binding, lop=length, rop=op_length)
-        start = FormulaCode(IfSig(), length.domain, self.binding,
+                                  self.flow, lop=length, rop=op_length)
+        start = FormulaCode(IfSig(), length.domain, self.flow,
                             predicates=[if_negative, if_positive],
                             consequents=[start, positive_start],
                             alternative=one_literal)
-        return FormulaCode(SubstringSig(), self.binding.domain, self.binding,
+        return FormulaCode(SubstringSig(), self.flow.domain, self.flow,
                            op=op, start=start, length=None)
 
 
     adapt(SliceSig)
 
     def __call__(self):
-        op = self.state.encode(self.binding.op)
+        op = self.state.encode(self.flow.op)
         op_length = FormulaCode(LengthSig(), coerce(IntegerDomain()),
-                                self.binding, op=op)
-        null_literal = LiteralCode(None, coerce(IntegerDomain()), self.binding)
-        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.binding)
-        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.binding)
-        if self.binding.left is None:
+                                self.flow, op=op)
+        null_literal = LiteralCode(None, coerce(IntegerDomain()), self.flow)
+        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.flow)
+        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.flow)
+        if self.flow.left is None:
             left = zero_literal
         else:
-            left = self.state.encode(self.binding.left)
-        if self.binding.right is None:
+            left = self.state.encode(self.flow.left)
+        if self.flow.right is None:
             right = null_literal
         else:
-            right = self.state.encode(self.binding.right)
+            right = self.state.encode(self.flow.right)
         if isinstance(left, LiteralCode) and left.value is None:
             start = one_literal
         elif isinstance(left, LiteralCode) and left.value >= 0:
             start = left.clone(value=left.value+1)
         else:
-            left = FormulaCode(IfNullSig(), left.domain, self.binding,
+            left = FormulaCode(IfNullSig(), left.domain, self.flow,
                                lop=left, rop=zero_literal)
             start = left
-            negative_start = FormulaCode(AddSig(), left.domain, self.binding,
+            negative_start = FormulaCode(AddSig(), left.domain, self.flow,
                                          lop=left, rop=op_length)
             if_positive = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                      self.binding, lop=start,
+                                      self.flow, lop=start,
                                       rop=zero_literal)
             if_negative = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                      self.binding, lop=negative_start,
+                                      self.flow, lop=negative_start,
                                       rop=zero_literal)
-            start = FormulaCode(IfSig(), left.domain, self.binding,
+            start = FormulaCode(IfSig(), left.domain, self.flow,
                                 predicates=[if_positive, if_negative],
                                 consequents=[start, negative_start],
                                 alternative=zero_literal)
-            start = FormulaCode(AddSig(), left.domain, self.binding,
+            start = FormulaCode(AddSig(), left.domain, self.flow,
                                 lop=start, rop=one_literal)
         if isinstance(right, LiteralCode) and right.value is None:
-            return FormulaCode(SubstringSig(), self.binding.domain,
-                               self.binding, op=op,
+            return FormulaCode(SubstringSig(), self.flow.domain,
+                               self.flow, op=op,
                                start=start, length=None)
         elif isinstance(right, LiteralCode) and right.value >= 0:
             if isinstance(start, LiteralCode):
                 if value < 0:
                     value = 0
                 length = right.clone(value=value)
-                return FormulaCode(SubstringSig(), self.binding.domain,
-                                   self.binding, op=op,
+                return FormulaCode(SubstringSig(), self.flow.domain,
+                                   self.flow, op=op,
                                    start=start, length=length)
             end = right.clone(value=right.value+1)
         else:
             if not isinstance(right, LiteralCode):
-                right = FormulaCode(IfNullSig(), right.domain, self.binding,
+                right = FormulaCode(IfNullSig(), right.domain, self.flow,
                                     lop=right, rop=op_length)
             end = right
-            negative_end = FormulaCode(AddSig(), right.domain, self.binding,
+            negative_end = FormulaCode(AddSig(), right.domain, self.flow,
                                        lop=right, rop=op_length)
             if_positive = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                      self.binding, lop=end,
+                                      self.flow, lop=end,
                                       rop=zero_literal)
             if_negative = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                      self.binding, lop=negative_end,
+                                      self.flow, lop=negative_end,
                                       rop=zero_literal)
-            end = FormulaCode(IfSig(), right.domain, self.binding,
+            end = FormulaCode(IfSig(), right.domain, self.flow,
                                 predicates=[if_positive, if_negative],
                                 consequents=[end, negative_end],
                                 alternative=zero_literal)
-            end = FormulaCode(AddSig(), right.domain, self.binding,
+            end = FormulaCode(AddSig(), right.domain, self.flow,
                                 lop=end, rop=one_literal)
         length = FormulaCode(SubtractSig(), coerce(IntegerDomain()),
-                             self.binding, lop=end, rop=start)
+                             self.flow, lop=end, rop=start)
         condition = FormulaCode(CompareSig('<'), coerce(BooleanDomain()),
-                                self.binding, lop=start, rop=end)
-        length = FormulaCode(IfSig(), length.domain, self.binding,
+                                self.flow, lop=start, rop=end)
+        length = FormulaCode(IfSig(), length.domain, self.flow,
                              predicates=[condition],
                              consequents=[length],
                              alternative=zero_literal)
-        return FormulaCode(SubstringSig(), self.binding.domain, self.binding,
+        return FormulaCode(SubstringSig(), self.flow.domain, self.flow,
                            op=op, start=start, length=length)
 
 
     adapt(AtSig)
 
     def __call__(self):
-        op = self.state.encode(self.binding.op)
+        op = self.state.encode(self.flow.op)
         op_length = FormulaCode(LengthSig(), coerce(IntegerDomain()),
-                                self.binding, op=op)
-        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.binding)
-        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.binding)
-        index = self.state.encode(self.binding.index)
-        if self.binding.length is not None:
-            length = self.state.encode(self.binding.length)
+                                self.flow, op=op)
+        zero_literal = LiteralCode(0, coerce(IntegerDomain()), self.flow)
+        one_literal = LiteralCode(1, coerce(IntegerDomain()), self.flow)
+        index = self.state.encode(self.flow.index)
+        if self.flow.length is not None:
+            length = self.state.encode(self.flow.length)
         else:
             length = one_literal
         if isinstance(index, LiteralCode) and index.value is None:
-            return FormulaCode(SubstringSig(), self.binding.domain,
-                               self.binding, op=op, start=index,
+            return FormulaCode(SubstringSig(), self.flow.domain,
+                               self.flow, op=op, start=index,
                                length=zero_literal)
         if isinstance(length, LiteralCode) and length.value is None:
             length = one_literal
                 length_value = 0
             start = index.clone(value=index_value+1)
             length = length.clone(value=length_value)
-            return FormulaCode(SubstringSig(), self.binding.domain,
-                               self.binding, op=op, start=start, length=length)
-        length = FormulaCode(IfNullSig(), length.domain, self.binding,
+            return FormulaCode(SubstringSig(), self.flow.domain,
+                               self.flow, op=op, start=start, length=length)
+        length = FormulaCode(IfNullSig(), length.domain, self.flow,
                              lop=length, rop=one_literal)
-        negative_index = FormulaCode(AddSig(), index.domain, self.binding,
+        negative_index = FormulaCode(AddSig(), index.domain, self.flow,
                                      lop=index, rop=op_length)
         condition = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                self.binding, lop=index, rop=zero_literal)
-        index = FormulaCode(IfSig(), index.domain, self.binding,
+                                self.flow, lop=index, rop=zero_literal)
+        index = FormulaCode(IfSig(), index.domain, self.flow,
                             predicates=[condition], consequents=[index],
                             alternative=negative_index)
         condition = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                self.binding, lop=length, rop=zero_literal)
-        negative_index = FormulaCode(AddSig(), index.domain, self.binding,
+                                self.flow, lop=length, rop=zero_literal)
+        negative_index = FormulaCode(AddSig(), index.domain, self.flow,
                                      lop=index, rop=length)
         negative_length = FormulaCode(ReversePolaritySig(), length.domain,
-                                      self.binding, op=length)
-        index = FormulaCode(IfSig(), index.domain, self.binding,
+                                      self.flow, op=length)
+        index = FormulaCode(IfSig(), index.domain, self.flow,
                             predicates=[condition], consequents=[index],
                             alternative=negative_index)
-        length = FormulaCode(IfSig(), length.domain, self.binding,
+        length = FormulaCode(IfSig(), length.domain, self.flow,
                              predicates=[condition], consequents=[length],
                              alternative=negative_length)
         condition = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                self.binding, lop=index, rop=zero_literal)
-        negative_length = FormulaCode(AddSig(), length.domain, self.binding,
+                                self.flow, lop=index, rop=zero_literal)
+        negative_length = FormulaCode(AddSig(), length.domain, self.flow,
                                       lop=length, rop=index)
-        index = FormulaCode(IfSig(), index.domain, self.binding,
+        index = FormulaCode(IfSig(), index.domain, self.flow,
                             predicates=[condition], consequents=[index],
                             alternative=zero_literal)
-        length = FormulaCode(IfSig(), length.domain, self.binding,
+        length = FormulaCode(IfSig(), length.domain, self.flow,
                              predicates=[condition], consequents=[length],
                              alternative=negative_length)
         condition = FormulaCode(CompareSig('>='), coerce(BooleanDomain()),
-                                self.binding, lop=length, rop=zero_literal)
-        length = FormulaCode(IfSig(), length.domain, self.binding,
+                                self.flow, lop=length, rop=zero_literal)
+        length = FormulaCode(IfSig(), length.domain, self.flow,
                              predicates=[condition], consequents=[length],
                              alternative=zero_literal)
-        start = FormulaCode(AddSig(), index.domain, self.binding,
+        start = FormulaCode(AddSig(), index.domain, self.flow,
                             lop=index, rop=one_literal)
-        return FormulaCode(SubstringSig(), self.binding.domain,
-                           self.binding, op=op, start=start, length=length)
+        return FormulaCode(SubstringSig(), self.flow.domain,
+                           self.flow, op=op, start=start, length=length)
 
 
 class EncodeReplace(EncodeFunction):
     adapt(ReplaceSig)
 
     def __call__(self):
-        op = self.state.encode(self.binding.op)
-        old = self.state.encode(self.binding.old)
-        new = self.state.encode(self.binding.new)
-        empty = LiteralCode(u'', old.domain, self.binding)
-        old = FormulaCode(IfNullSig(), old.domain, self.binding,
+        op = self.state.encode(self.flow.op)
+        old = self.state.encode(self.flow.old)
+        new = self.state.encode(self.flow.new)
+        empty = LiteralCode(u'', old.domain, self.flow)
+        old = FormulaCode(IfNullSig(), old.domain, self.flow,
                           lop=old, rop=empty)
-        new = FormulaCode(IfNullSig(), old.domain, self.binding,
+        new = FormulaCode(IfNullSig(), old.domain, self.flow,
                           lop=new, rop=empty)
-        return FormulaCode(self.signature, self.domain, self.binding,
+        return FormulaCode(self.signature, self.domain, self.flow,
                            op=op, old=old, new=new)
 
 
         return plural_space
 
     def __call__(self):
-        op = self.state.encode(self.binding.op)
-        space = self.state.relate(self.binding.base)
+        op = self.state.encode(self.flow.op)
+        space = self.state.relate(self.flow.base)
         plural_space = None
-        if self.binding.plural_base is not None:
-            plural_space = self.state.relate(self.binding.plural_base)
+        if self.flow.plural_base is not None:
+            plural_space = self.state.relate(self.flow.plural_base)
         plural_space = self.aggregate(op, space, plural_space)
-        aggregate = AggregateUnit(op, plural_space, space, self.binding)
+        aggregate = AggregateUnit(op, plural_space, space, self.flow)
         wrapper = WrapAggregate.__invoke__(aggregate, self.state)
-        wrapper = ScalarUnit(wrapper, space, self.binding)
+        wrapper = ScalarUnit(wrapper, space, self.flow)
         return wrapper
 
 
     adapt(CountSig)
 
     def __call__(self):
-        op = self.state.encode(self.binding.op)
-        false_literal = LiteralCode(False, op.domain, op.binding)
-        op = FormulaCode(NullIfSig(), op.domain, op.binding,
+        op = self.state.encode(self.flow.op)
+        false_literal = LiteralCode(False, op.domain, op.flow)
+        op = FormulaCode(NullIfSig(), op.domain, op.flow,
                          lop=op, rop=false_literal)
-        return FormulaCode(CountSig(), self.binding.domain, self.binding,
+        return FormulaCode(CountSig(), self.flow.domain, self.flow,
                            op=op)
 
 
     adapt_many(CountSig, SumSig)
 
     def __call__(self):
-        root = RootBinding(self.unit.syntax)
-        zero_literal = LiteralBinding(root, u'0', UntypedDomain(),
-                                      self.unit.syntax)
-        zero_literal = CastBinding(zero_literal, self.unit.domain,
-                                   self.unit.syntax)
+        root = RootFlow(self.unit.binding)
+        zero_literal = LiteralFlow(root, u'0', UntypedDomain(),
+                                      self.unit.binding)