Пример #1
0
 def from_equivs(cls, equivs):
     """Form from a sequence of possibly non-disjoint equivalence
     classes. Equivalence classes of size 1 are ignored.
     """
     part = cls()
     for eq in equivs:
         for x, y in pairs(eq):
             part.equate(x, y)
     return part
Пример #2
0
 def visit_Compare(self, node):
     # Result: bool
     # Cond: compare_helper(left, op, right) for each pair
     values = (node.left,) + node.comparators
     values = [self.visit(v) for v in values]
     for (left, right), op in zip(pairs(values), node.ops):
         self.compare_helper(node, op, left.type, right.type)
     return node._replace(left=values[0], comparators=values[1:],
                          type=booltype)
Пример #3
0
 def visit_Compare(self, node):
     # Result: bool
     # Cond: compare_helper(left, op, right) for each pair
     values = (node.left, ) + node.comparators
     values = [self.visit(v) for v in values]
     for (left, right), op in zip(pairs(values), node.ops):
         self.compare_helper(node, op, left.type, right.type)
     return node._replace(left=values[0],
                          comparators=values[1:],
                          type=booltype)
Пример #4
0
    def visit_Assign(self, node):
        node = self.generic_visit(node)

        # Translate multiple assignments as a single statement
        # (e.g. "a = b = c") into sequential single assignments.
        if len(node.targets) > 1:
            stmts = []
            values = list(node.targets) + [node.value]
            for lhs, rhs in reversed(list(pairs(values))):
                rhs_load = P.ContextSetter.run(rhs, P.Load)
                stmts.append(P.Assign([lhs], rhs_load))
            node = stmts

        return node
Пример #5
0
    def visit_Assign(self, node):
        node = self.generic_visit(node)

        # Translate multiple assignments as a single statement
        # (e.g. "a = b = c") into sequential single assignments.
        if len(node.targets) > 1:
            stmts = []
            values = list(node.targets) + [node.value]
            for lhs, rhs in reversed(list(pairs(values))):
                rhs_load = P.ContextSetter.run(rhs, P.Load)
                stmts.append(P.Assign([lhs], rhs_load))
            node = stmts

        return node
Пример #6
0
def make_comp_maint_code(spec, resrel, deltarel, op, elem, prefix, *,
                         maint_impl, rc, selfjoin):
    """Construct comprehension maintenance code. Return the code and
    a list of maintenance comprehensions used.
    
        spec:
          CompSpec of the comprehension to be computed incrementally.
        
        resrel:
          Name of the relation holding the saved result.
        
        deltarel:
          Name of the updated relation that triggered maintenance.
        
        op:
          Update operation ('add' or 'remove').
        
        elem:
          AST of the element added or removed to deltarel.
        
        prefix:
          The prefix to use for making fresh local variables.
        
        maint_impl:
          Value to use for the 'impl' option of emitted
          maintenance comprehensions ('batch' or 'auxonly').
        
        rc:
          Whether or not the incrementally computed comprehension
          uses reference counts ('yes', 'no', 'safe').
        
        selfjoin:
          Strategy for computing self-joins. Possible values:
            'sub':
              use subtractive clauses
              (Code must be placed after addition / before removal.)
            'aug':
              use augmented clauses
              (Code must be placed before addition / after removal.)
            'das':
              use a differential assignment set
            'assume_disjoint':
              naive, only valid if joins are disjoint
            'assume_disjoint_verify':
              naive, use das to assert disjoint at runtime
    """
    assert op in ['add', 'remove']
    assert maint_impl in ['batch', 'auxonly']
    assert rc in ['yes', 'no', 'safe']
    assert selfjoin in ['sub', 'aug', 'das', 'assume_disjoint',
                        'assume_disjoint_verify']
    
    assert deltarel in spec.join.rels
    
    if len(spec.params) > 0:
        raise ValueError('Cannot incrementalize comprehension with '
                         'parameters')
    
    # Get the maintenance comprehensions.
    disjoint_strat = (selfjoin if selfjoin in ['sub', 'aug']
                      else 'das')
    maint_joins = spec.join.get_maint_joins(elem, deltarel, op, prefix,
                                            disjoint_strat=disjoint_strat)
    maint_comps = [j.to_comp({'impl': maint_impl})
                   for j in maint_joins]
    # Get the maintenance joins' enumvars.
    assert all(j1.enumvars == j2.enumvars
               for j1, j2 in pairs(maint_joins))
    maint_projvars = maint_joins[0].enumvars
    
    # Decide whether the body is a normal update or
    # a reference-counted one.
    use_rc = {'yes': True,
              'no': False,
              'safe': not spec.is_duplicate_safe}[rc]
    if use_rc:
        op = 'rc' + op
    resvars = L.VarsFinder.run(spec.resexp, ignore_functions=True)
    resexp = L.prefix_names(spec.resexp, resvars, prefix)
    body = L.pc('''
        RES.OP(RESEXP)
        ''', subst={'RES': resrel,
                    '@OP': op,
                    'RESEXP': resexp})
    
    # Create code according to the choice of self-join strategy.
    if selfjoin in ['sub', 'aug', 'assume_disjoint']:
        code = for_rels_union_disjoint_code(
                        maint_projvars, maint_comps, body)
    else:
        dasprefix = prefix + 'DAS'
        ver_dis = selfjoin == 'assume_disjoint_verify'
        code = for_rels_union_code(
                        maint_projvars, maint_comps, body,
                        dasprefix, verify_disjoint=ver_dis)
    
    return code, maint_comps
Пример #7
0
 def make_equalities(self, boundvars):
     """Opposite of elim_equalities(). Produce a semantically
     equivalent join in which no enumeration variable appears more
     than once among the left-hand sides of all enumerators (whether
     in the same clause or different clauses). Multiple occurrences
     of the same variable get renamed to fresh variables, with new
     condition clauses added to equate the fresh variables to the
     first variable.
     
     Variables that appear in boundvars are considered to have
     occurred once already outside the join.
     
     Enumerators in which all enumeration variables have been
     replaced in this manner get turned into condition clauses.
     
     Some occurrences inside enumerators are not replaced,
     depending on the clause's pat_mask field.
     """
     # Map from each enum var to a counter, used to make fresh
     # identifiers for its occurrences. 
     repl_counts = defaultdict(lambda: 0)
     # Map from each enum var to a list of the names that will
     # be used to replace its occurrences, in order. Occurrences
     # that should not be replaced map to themselves in the list.
     # If the var is in boundvars, there is one extra occurrence
     # at the front of the list. Occurrences inside condition
     # clauses are not accounted for in the list.
     repl_map = defaultdict(lambda: [])
     
     def add_repl_occ(v):
         """Process an occurrence that is subject to renaming."""
         # Ignore wildcards.
         if v == '_':
             return
         # First occurrence is not renamed, later occurrences are.
         repl_counts[v] += 1
         occ_name = (v if repl_counts[v] == 1
                     else v + '_' + str(repl_counts[v]))
         repl_map[v].append(occ_name)
     
     def add_skip_occ(v):
         """Process an occurrence that is left unchanged."""
         # Wildcards should not occur.
         # (Not sure about this, could make it just skip, like above.)
         assert v != '_'
         repl_map[v].append(v)
     
     for v in boundvars:
         add_repl_occ(v)
     
     # Process occurrences in the clauses. In addition,
     # all-bound enumerators get turned into condition clauses.
     new_clauses = []
     for cl in self.clauses:
         # Skip conditions.
         if cl.kind is Clause.KIND_COND:
             new_clauses.append(cl)
             continue
         
         # All-bound enum becomes a condition.
         if set(cl.enumlhs).issubset(repl_counts):
             cl = self.factory.enum_to_membercond(cl)
             new_clauses.append(cl)
             continue
         
         # Normal case.
         for v, p in zip(cl.enumlhs, cl.pat_mask):
             if p:
                 add_repl_occ(v)
             else:
                 add_skip_occ(v)
         new_clauses.append(cl)
     
     # Create new condition clauses to equate the new variables.
     new_conds = []
     for v in self.enumvars:
         repl_list = repl_map[v]
         for v1, v2 in pairs(repl_list):
             # No equality needed for identity replacements.
             if v == v2:
                 continue
             condcl = self.factory.from_AST(L.cmpeq(L.ln(v1), L.ln(v2)))
             new_conds.append(condcl)
     
     # Define a substitution that calls a function to consume
     # the next identifier from the appropriate list.
     # For boundvars, start replacing at the second slot onward.
     
     for v in repl_map:
         if v in boundvars:
             repl_map[v].pop(0)
     
     def var_renamer(v):
         return repl_map[v].pop(0)
     
     # Use rewrite_lhs(), not rewrite_subst().
     # We don't want to rewrite demparams, for instance.
     subst = {v: var_renamer for v in self.enumvars}
     new_clauses = [self.factory.rewrite_lhs(cl, subst)
                       if cl.kind is Clause.KIND_ENUM else cl
                    for cl in new_clauses]
     
     # Insert each new condition clause immediately after both
     # equated variables have been seen.
     # For each clause in new_join, pull in the applicable cond
     # clauses and delete them from the new_conds list.
     new_clauses_with_conds = []
     seenvars = set(boundvars)
     for cl in new_clauses:
         new_clauses_with_conds.append(cl)
         seenvars.update(cl.enumvars)
         
         for condcl in list(new_conds):
             lhs, rhs = condcl.eqvars
             if lhs in seenvars and rhs in seenvars:
                 new_conds.remove(condcl)
                 new_clauses_with_conds.append(condcl)
     assert len(new_conds) == 0
     
     return self._replace(clauses=new_clauses_with_conds)
Пример #8
0
    def make_equalities(self, boundvars):
        """Opposite of elim_equalities(). Produce a semantically
        equivalent join in which no enumeration variable appears more
        than once among the left-hand sides of all enumerators (whether
        in the same clause or different clauses). Multiple occurrences
        of the same variable get renamed to fresh variables, with new
        condition clauses added to equate the fresh variables to the
        first variable.
        
        Variables that appear in boundvars are considered to have
        occurred once already outside the join.
        
        Enumerators in which all enumeration variables have been
        replaced in this manner get turned into condition clauses.
        
        Some occurrences inside enumerators are not replaced,
        depending on the clause's pat_mask field.
        """
        # Map from each enum var to a counter, used to make fresh
        # identifiers for its occurrences.
        repl_counts = defaultdict(lambda: 0)
        # Map from each enum var to a list of the names that will
        # be used to replace its occurrences, in order. Occurrences
        # that should not be replaced map to themselves in the list.
        # If the var is in boundvars, there is one extra occurrence
        # at the front of the list. Occurrences inside condition
        # clauses are not accounted for in the list.
        repl_map = defaultdict(lambda: [])

        def add_repl_occ(v):
            """Process an occurrence that is subject to renaming."""
            # Ignore wildcards.
            if v == '_':
                return
            # First occurrence is not renamed, later occurrences are.
            repl_counts[v] += 1
            occ_name = (v if repl_counts[v] == 1 else v + '_' +
                        str(repl_counts[v]))
            repl_map[v].append(occ_name)

        def add_skip_occ(v):
            """Process an occurrence that is left unchanged."""
            # Wildcards should not occur.
            # (Not sure about this, could make it just skip, like above.)
            assert v != '_'
            repl_map[v].append(v)

        for v in boundvars:
            add_repl_occ(v)

        # Process occurrences in the clauses. In addition,
        # all-bound enumerators get turned into condition clauses.
        new_clauses = []
        for cl in self.clauses:
            # Skip conditions.
            if cl.kind is Clause.KIND_COND:
                new_clauses.append(cl)
                continue

            # All-bound enum becomes a condition.
            if set(cl.enumlhs).issubset(repl_counts):
                cl = self.factory.enum_to_membercond(cl)
                new_clauses.append(cl)
                continue

            # Normal case.
            for v, p in zip(cl.enumlhs, cl.pat_mask):
                if p:
                    add_repl_occ(v)
                else:
                    add_skip_occ(v)
            new_clauses.append(cl)

        # Create new condition clauses to equate the new variables.
        new_conds = []
        for v in self.enumvars:
            repl_list = repl_map[v]
            for v1, v2 in pairs(repl_list):
                # No equality needed for identity replacements.
                if v == v2:
                    continue
                condcl = self.factory.from_AST(L.cmpeq(L.ln(v1), L.ln(v2)))
                new_conds.append(condcl)

        # Define a substitution that calls a function to consume
        # the next identifier from the appropriate list.
        # For boundvars, start replacing at the second slot onward.

        for v in repl_map:
            if v in boundvars:
                repl_map[v].pop(0)

        def var_renamer(v):
            return repl_map[v].pop(0)

        # Use rewrite_lhs(), not rewrite_subst().
        # We don't want to rewrite demparams, for instance.
        subst = {v: var_renamer for v in self.enumvars}
        new_clauses = [
            self.factory.rewrite_lhs(cl, subst)
            if cl.kind is Clause.KIND_ENUM else cl for cl in new_clauses
        ]

        # Insert each new condition clause immediately after both
        # equated variables have been seen.
        # For each clause in new_join, pull in the applicable cond
        # clauses and delete them from the new_conds list.
        new_clauses_with_conds = []
        seenvars = set(boundvars)
        for cl in new_clauses:
            new_clauses_with_conds.append(cl)
            seenvars.update(cl.enumvars)

            for condcl in list(new_conds):
                lhs, rhs = condcl.eqvars
                if lhs in seenvars and rhs in seenvars:
                    new_conds.remove(condcl)
                    new_clauses_with_conds.append(condcl)
        assert len(new_conds) == 0

        return self._replace(clauses=new_clauses_with_conds)