def isinstanceo_goal(S): nonlocal u, u_type u_rf, u_type_rf = reify((u, u_type), S) if not isground(u_rf, S) or not isground(u_type_rf, S): if not isinstance(S, ConstrainedState): S = ConstrainedState(S) cs = S.constraints.setdefault(IsinstanceStore, IsinstanceStore()) try: cs.add(u_rf, u_type_rf) except TypeError: # If the instance object can't be hashed, we can simply use a # logic variable to uniquely identify it. u_lv = var() S[u_lv] = u_rf cs.add(u_lv, u_type_rf) if cs.post_unify_check(S.data, u_rf, u_type_rf): yield S # elif isground(u_type, S): # yield from lany(eq(u_type, u_t) for u_t in type(u).mro())(S) elif (isinstance(u_type_rf, type) # or ( # isinstance(u_type, Iterable) # and all(isinstance(t, type) for t in u_type) # ) ) and isinstance(u_rf, u_type_rf): yield S
def typeo_goal(S): nonlocal u, u_type u_rf, u_type_rf = reify((u, u_type), S) if not isground(u_rf, S) or not isground(u_type_rf, S): if not isinstance(S, ConstrainedState): S = ConstrainedState(S) cs = S.constraints.setdefault(TypeStore, TypeStore()) try: cs.add(u_rf, u_type_rf) except TypeError: # If the instance object can't be hashed, we can simply use a # logic variable to uniquely identify it. u_lv = var() S[u_lv] = u_rf cs.add(u_lv, u_type_rf) if cs.post_unify_check(S.data, u_rf, u_type_rf): yield S elif isinstance(u_type_rf, type) and type(u_rf) == u_type_rf: yield S
def post_unify_check(self, lvar_map, lvar=None, value=None, old_state=None): for lv_key, constraints in list(self.lvar_constraints.items()): lv = reify(lv_key, lvar_map) is_lv_ground = self.constraint_isground(lv, lvar_map) or isground( lv, lvar_map) if not is_lv_ground: # This constraint isn't ready to be checked continue # if is_lv_ground and not self.cterm_type_check(lv): # self.lvar_constraints[lv_key] # return False constraint_grps = groupby(lambda x: isground(x, lvar_map), reify(iter(constraints), lvar_map)) constraints_unground = constraint_grps.get(False, ()) constraints_ground = constraint_grps.get(True, ()) if len(constraints_ground) > 0 and not all( self.cparam_type_check(c) for c in constraints_ground): # Some constraint parameters aren't the correct type, so fail. # del self.lvar_constraints[lv_key] return False assert constraints_unground or constraints_ground if is_lv_ground and len(constraints_unground) == 0: if self.require_all_constraints and any( not self.constraint_check(lv, t) for t in constraints_ground): return False elif not self.require_all_constraints and not any( self.constraint_check(lv, t) for t in constraints_ground): return False # The instance and constraint parameters are all ground and the # constraint is satisfied, so, since nothing should change from # here on, we can remove the constraint. del self.lvar_constraints[lv_key] # Some types are unground, so we continue checking until they are return True
def test_unground_lvars(): a_lv, b_lv = var(), var() for ctor in (tuple, list, iter, set, frozenset): if ctor not in (set, frozenset): sub_ctor = list else: sub_ctor = tuple assert unground_lvars(ctor((1, 2)), {}) == set() assert unground_lvars( ctor((1, sub_ctor((a_lv, sub_ctor((b_lv, 2)), 3)))), {}) == {a_lv, b_lv} assert unground_lvars( ctor((1, sub_ctor((a_lv, sub_ctor((b_lv, 2)), 3)))), {a_lv: 4}) == {b_lv} assert (unground_lvars( ctor((1, sub_ctor((a_lv, sub_ctor((b_lv, 2)), 3)))), { a_lv: 4, b_lv: 5 }) == set()) assert isground(ctor((1, 2)), {}) assert isground(ctor((1, a_lv)), {a_lv: 2}) assert isground(ctor((a_lv, sub_ctor((b_lv, 2)), 3)), { a_lv: b_lv, b_lv: 1 }) assert not isground(ctor((1, a_lv)), {a_lv: b_lv}) assert not isground(ctor((1, var())), {}) assert not isground( ctor((1, sub_ctor((a_lv, sub_ctor((b_lv, 2)), 3)))), {}) assert not isground(ctor((a_lv, sub_ctor((b_lv, 2)), 3)), { a_lv: b_lv, b_lv: var("c") }) # Make sure that no composite elements are constructed within the # groundedness checks. class CounterList(list): constructions = 0 def __new__(cls, *args, **kwargs): cls.constructions += 1 return super().__new__(cls, *args, **kwargs) test_l = CounterList([1, 2, CounterList([a_lv, CounterList([4])])]) assert CounterList.constructions == 3 assert not isground(test_l, {}) assert CounterList.constructions == 3 assert unground_lvars(test_l, {}) == {a_lv}
def ground_order_key(S, x): if isvar(x): return 2 elif isground(x, S): return -1 elif issubclass(type(x), ConsPair): return 1 else: return 0
def typeo_goal(S: ConstrainedState): nonlocal v, t v_rf, t_rf = reify((v, t), S) isground_all = [ isground(reified_value, S) for reified_value in [v_rf, t_rf] ] if not all(isground_all): if not isground(t_rf, S): g = eq(type(v_rf), t_rf) yield from g(S) # TODO: What if v_rf is not yet grounded? else: # g = applyo(isinstance, v_rf, t_rf) yield S else: if type(v_rf) == t_rf: yield S
def binopo_goal(S: ConstrainedState): nonlocal x, y, v, op uuid = str(uuid4())[:4] t = var("type_" + uuid) x_rf, y_rf, v_rf, op_rf = reify((x, y, v, op), S) isground_all = [ isground(reified_value, S) for reified_value in [x_rf, y_rf, v_rf, op_rf] ] if not all(isground_all): # We can only fix one LV at a time if len([uv for uv in isground_all if uv is False]) == 1: # TODO: Revop the other vars if not isground(v_rf, S): try: g = lall( isinstanceo(x_rf, t), isinstanceo(y_rf, t), isinstanceo(v_rf, t), eq(op_rf(x_rf, y_rf), v_rf), ) except Exception as e: logger.debug( "Got exception during binop with args {}: {}". format([x_rf, y_rf, v_rf, op_rf], e)) return yield from g(S) else: # TODO: Why isnt this covered by the `typeo` goal? try: g = lall( isinstanceo(x_rf, t), isinstanceo(y_rf, t), isinstanceo(v_rf, t), eq(op_rf(x_rf, y_rf), v_rf), ) except Exception as e: logger.debug( "Got exception during ungrounded binop with args {}: {}". format([x_rf, y_rf, v_rf, op_rf], e)) return yield from g(S)
def permuteo_goal(S): nonlocal a, b, default_ConsNull, inner_eq a_rf, b_rf = reify((a, b), S) # If the lengths differ, then fail a_len, b_len = length_hint(a_rf, -1), length_hint(b_rf, -1) if a_len > 0 and b_len > 0 and a_len != b_len: return if isinstance(a_rf, Sequence): a_type = type(a_rf) a_perms = permutations(a_rf) if no_ident: next(a_perms) if isinstance(b_rf, Sequence): b_type = type(b_rf) # Fail on mismatched types or straight equality (when # `no_ident` is enabled) if a_type != b_type or (no_ident and a_rf == b_rf): return try: # `a` and `b` are sequences, so let's see if we can pull out # all the (hash-)equivalent elements. # XXX: Use of this requires that the equivalence relation # implied by `inner_eq` be a *superset* of `eq`. cntr_a, cntr_b = Counter(a_rf), Counter(b_rf) rdcd_a, rdcd_b = cntr_a - cntr_b, cntr_b - cntr_a if len(rdcd_a) == len(rdcd_b) == 0: yield S return elif len(rdcd_a) < len(cntr_a): a_rf, b_rf = tuple(rdcd_a.elements()), b_type( rdcd_b.elements()) a_perms = permutations(a_rf) except TypeError: # TODO: We could probably get more coverage for this case # by using `HashableForm`. pass # If they're both ground and we're using basic unification, # then simply check that one is a permutation of the other and # be done. No need to create and evaluate a bunch of goals in # order to do something that can be done right here. # Naturally, this assumes that the `isground` checks aren't # nearly as costly as all that other stuff. If the gains # depend on the sizes of `a` and `b`, then we could do # `length_hint` checks first. if inner_eq == eq and isground(a_rf, S) and isground(b_rf, S): if tuple(b_rf) in a_perms: yield S return else: # This has to be a definitive check, since we can only # use the `a_perms` generator once; plus, we don't want # to iterate over it more than once! return yield from lany(inner_eq(b_rf, a_type(i)) for i in a_perms)(S) elif isinstance(b_rf, Sequence): b_type = type(b_rf) b_perms = permutations(b_rf) if no_ident: next(b_perms) yield from lany(inner_eq(a_rf, b_type(i)) for i in b_perms)(S) else: # None of the arguments are proper sequences, so state that one # should be and apply `permuteo` to that. a_itero_g = itero(a_rf, nullo_refs=(b_rf, ), default_ConsNull=default_ConsNull) for S_new in a_itero_g(S): a_new = reify(a_rf, S_new) a_type = type(a_new) a_perms = permutations(a_new) if no_ident: next(a_perms) yield from lany(inner_eq(b_rf, a_type(i)) for i in a_perms)(S_new)