3f0c8b58d9c8245a768d4cc8427569f31119ad0a — Jasper den Ouden 8 months ago d14b1c6
Unioning with "no opposing side" cases keeping the element.
A examples/no_ans.tsc => examples/no_ans.tsc +7 -0
@@ 0,0 1,7 @@

1 x. ;
2 x. ;
9 x. 3 + y. ;

{ x } { no_ans } (x x. 6 >).if ah.

A test/tp/no_ans.py => test/tp/no_ans.py +22 -0
@@ 0,0 1,22 @@

from type_stack_calc.sets.intersection import do_op_2
from type_stack_calc.tp.intersection import the_no_ans, TpIntersection as ti
from type_stack_calc.sets.range import SetRange as r

print(ti(the_no_ans, the_no_ans))
print(ti(the_no_ans, 4))
print(ti(1, 4))

print(ti(r(0, 5), 3))
print(ti(5, r(0, 5), 3))

x = ti(2, the_no_ans, the_no_ans)
y = ti(3)


print('...', ti(r(2,3)).set_union(ti(the_no_ans)))
print('...', ti(the_no_ans).smoosh(ti(r(2,3))))

M type_stack_calc/sets/intersection.py => type_stack_calc/sets/intersection.py +40 -20
@@ 42,7 42,7 @@ def merge_1(into, add, meth_name):
            yield from gen
    yield add  # And finally the potentially merged remains.

from type_stack_calc.util.various import list_flatten
from type_stack_calc.util.various import list_flatten, filter_none

def merge(gen, meth_name):
    """Merges the sets and simplifies."""

@@ 60,39 60,52 @@ def _do_op_1(self, opkey):
    """Operation for one argument.. NOTE: if no function, it's dropped.."""
    for set in self:
        fun = getattr(type(set), opkey, None)
        got = fun and fun(set)
        if type(got) == list:
            yield from got
        elif got is not None:
            assert not isinstance(got, (int, float)), (opkey, got)
            yield got
        if fun:
            yield fun(set)

def _do_op_2(x, opkey, y):
    """Do the operation with the given single other argument. Raw generator version."""
    # NOTE: used to be code here to keep untouched stuff. Unsure of utility to removed.(8d1b363)
    for ex in x:
        assert not isinstance(ex, (int, float)), (opkey, ex)
        fun = getattr(type(ex), opkey, None)
        if fun is not None:  # Has a function, for this operation try each,
            yield from filter_none(fun(ex, ey) for ey in y)

def _do_op_2_track(x, opkey, y, did_i, did_j):
    """Sometimes no-operation between things means they should stay,
this function helps with that."""
    for i, ex in enumerate(x):
        assert not isinstance(ex, (int, float)), (opkey, ex)
        fun = getattr(type(ex), opkey, None)
        if fun is not None:  # Has a function, for this operation try each.
        if fun is not None:
            for j, ey in enumerate(y):
                got = fun(ex, ey)
                if got is not None:
                    assert not isinstance(got, (int, float)), (opkey, got)
                    if type(got) == list:  # Possibly deal with list.
                        yield from got
                        yield got
                if type(got) is list:
                    yield from got
                elif got is not None:
                    yield got

def do_op_1(self, opkey):
    return filter_none(list_flatten(_do_op_1(self, opkey)))

def do_op_2(x, opkey, y): 
    return filter_none(list_flatten(_do_op_2(x, opkey, y)))

def _m(n, key):
    """Generates function to calculate the new type based on all applicable methods, for one or two arguments. If two arguments it will try iparam and rev_iparam."""
    opkey, rev_opkey  = f"iparam_{key}", f"rev_iparam_{key}"
    if n == 1:
        def ret(self):
            return type(self)(*_do_op_1(self, opkey))
            return type(self)(*do_op_1(self, opkey))
    elif n == 2:
        def ret(y, x):
            return type(x)(*_do_op_2(x, opkey, y),
                           *_do_op_2(y, rev_opkey, x))
            return type(x)(*do_op_2(x, opkey, y),
                           *do_op_2(y, rev_opkey, x))
        assert False, f"Intersection-combination type calculation only for upto 2 arguments. Got {n}"
    return ret

@@ 129,10 142,17 @@ class SetIntersection(tuple):
        ret = (*self, *y) if isinstance(y, SetIntersection) else (*self, y)
        return type(self)(*ret)

    def _set_union(self, y):
        did_i, did_j = set(), set()
        yield from _do_op_2_track(self, 'set_union', y, did_i, did_j)
        yield from _do_op_2_track(y, 'set_union', self, did_j, did_i)
        if len(did_i) < len(self):  # Non-touched things stay.
            yield from (ex for i, ex in enumerate(self) if i not in did_i)
        if len(did_j) < len(y):
            yield from (ey for j, ey in enumerate(y) if j not in did_j)

    def set_union(self, y):
        # TODO if neither touches each other, something maybe lost?
        return type(self)(*_do_op_2(self, 'set_union', y),
                          *_do_op_2(y, 'set_union', self))
        return type(self)(*self._set_union(y))

    def get_param_final(self, name):
        """Gets out a component.

M type_stack_calc/tp/intersection.py => type_stack_calc/tp/intersection.py +0 -4
@@ 23,10 23,6 @@ class TpNoAns(tuple, BaseType):
    name, c_name, is_const = 'NoAns', 'NoAns', True
    tkey = ('no_ans',)

    def set_union(self, _other):
        """When unioning, it stays."""
        return self

    def set_intersect(self, other):
        """One no-answer-is-possible indication is sufficient."""
        if isinstance(other, TpNoAns): return self

M type_stack_calc/util/various.py => type_stack_calc/util/various.py +4 -0
@@ 12,6 12,10 @@ def first_or_none(gen, alt=None):
    assert next(gen, None) is None
    return ret

def filter_none(lst):
    """Filter all the `None` values."""
    return (el for el in lst if el is not None)

def list_flatten(gen):
    """If an item in `gen` is a list yield it flatly, and continue so recursively."""
    for el in gen: