def can_coerce(self, constraint_node, consequence_node):
        # see if the consequence expression can be forced
        # into the constraint form.

        # this really needs to be generalized

        self.logger.debug('Trying to coerce %s' % consequence_node.to_s())
        self.logger.debug(constraint_node.to_s() % '... to %s')

        # ugly special case on union
        if constraint_node.op == 'FUNCTION' and \
                constraint_node.lhs == 'union' and \
                consequence_node.op == 'FUNCTION' and \
                consequence_node.lhs == 'union':
            self.logger.debug('doing union constraint')

            if constraint_node.rhs[0].op == 'IDENTIFIER' and \
                    consequence_node.rhs[0].op == 'IDENTIFIER' and \
                    constraint_node.rhs[0].lhs == consequence_node.rhs[0].lhs:

                can_constrain, ns = self.can_coerce(constraint_node.rhs[1],
                                                    consequence_node.rhs[1])

                return can_constrain, ns
            else:
                self.logger.debug('different arrays (%s, %s)' %
                                  (consequence_node.rhs[0],
                                   constraint_node.rhs[0]))
            return False, {}

        if constraint_node.op not in ['IDENTIFIER', 'STRING',
                                      'NUMBER', 'BOOL']:
            self.logger.debug('Cannot coerce vars not ID, STRING, NUM, BOOL:'
                              ' %s' % constraint_node.op)
            return False, {}

        if consequence_node.op not in ['IDENTIFIER', 'STRING',
                                       'NUMBER', 'BOOL']:
            self.logger.debug('Cannot coerce vars not ID, STRING, NUM, BOOL:'
                              ' %s' % consequence_node.op)
            return False, {}

        # if consequence_node.op != constraint_node.op:
        #     self.logger.debug('Cannot coerce dissimilar op types: %s != %s' %
        #                       (consequence_node.op, constraint_node.op))
        #     return False, {}

        str_consequence = str(consequence_node.lhs)
        str_constraint = str(constraint_node.lhs)

        if str_consequence == str_constraint and \
                consequence_node.op == constraint_node.op:
            self.logger.debug('Equal literals!  Success!')
            return True, {}

        match = re.match("(.*)\{(.*?)}(.*)", str_consequence)
        if match is None:
            self.logger.debug('dissimilar literals')
            return False, {}

        if not str_constraint.startswith(match.group(1)) or \
                not str_constraint.endswith(match.group(3)):
            self.logger.debug('cant coerce even with var binding')
            return False, {}

        key = match.group(2)
        value = str_constraint[len(match.group(1)):]
        if len(match.group(3)):
            value = value[:-len(match.group(3))]

        return True, {key: value}
dataset/ETHPy150Open rcbops/opencenter/opencenter/webapp/solver.py/Solver.can_coerce/swapped operands of `%`