d14b1c6c7eae0812e0eb3f1c757bb2532ece8420 — Jasper den Ouden 2 years ago 507b2d7
`param_`s to work towards being able to handle types to make wideners. "No answer" types instead of "undefined"
M type_stack_calc/intro/logic.py => type_stack_calc/intro/logic.py +3 -9
@@ 1,17 1,11 @@
"""Introduces some logic functions."""

from type_stack_calc.ib.ib_fun import InbuildFunction
from type_stack_calc.tp.intersection import TpIntersection as ti, the_no_ans

from type_stack_calc.tp.bool import TpBool

def _type_smoosh_pair(a, b):
    return a.smoosh(b)

intro = {
# TODO not entirely true, it may just definitely be the first one..
    'or'  : InbuildFunction(_type_smoosh_pair, tp='plain'),
    'and' : InbuildFunction(_type_smoosh_pair, tp='plain'),
    # TODO not

    'true' : TpBool(True), 'false' : TpBool(False),
    'no_ans' : ti(the_no_ans),
    'true' : ti(TpBool(True)), 'false' : ti(TpBool(False)),

M type_stack_calc/sets/bool.py => type_stack_calc/sets/bool.py +21 -4
@@ 1,6 1,9 @@

from type_stack_calc.sets.empty import the_empty

def xor(x, y):
    return x or y and not (x and y)

class SetBool(tuple):
    def __new__(cls, b):
        assert b is None or isinstance(b, bool)

@@ 14,10 17,6 @@ class SetBool(tuple):
    def case(self): return self[0]

    def true(self):    return self[0] is True
    def false(self):   return self[0] is False
    def unknown(self): return self[0] is None

    def set_union(self, y):
        if isinstance(y, SetBool):
            return self if y[0] == self[0] else SetBool(None)

@@ 28,3 27,21 @@ class SetBool(tuple):
            if y[0] == None:                       return self
        return the_empty

    def iparam_or(self, y):
        if isinstance(y, SetBool):
            opts = ({ (True, True): True, (False,False):False })
            return type(self)(opts.get((self[0], y[0])))

    def iparam_and(self, y):
        if isinstance(y, SetBool):
            return type(self)(False if (False in (self[0], y[0])) else
                              (True if (self[0], y[0]) == (True, True) else

    def iparam_not(self):
        return type(self)(None if self[0] is None else not self[0])

    def iparam_xor(self, y):
        if isinstance(y, SetBool):
            return type(self)(None if None in (self[0], y[0]) else
                                xor(self[0], y[0]))

M type_stack_calc/sets/intersection.py => type_stack_calc/sets/intersection.py +8 -5
@@ 28,8 28,7 @@ def _merge_1(a, b, meth_name):

def merge_1(into, add, meth_name):
    for el in into:
        if add == el:
        # if add == el: continue  # Simple, but likely doesn't happen much.
        gen = _merge_1(el, add, meth_name)
        got = next(gen, None)
        if got is None:

@@ 74,12 73,12 @@ def _do_op_2(x, opkey, y):
    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:
        if fun is not None:  # Has a function, for this operation try each.
            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:
                    if type(got) == list:  # Possibly deal with list.
                        yield from got
                        yield got

@@ 100,12 99,16 @@ def _m(n, key):

from inspect import signature

from type_stack_calc.sets.empty import Empty

class SetIntersection(tuple):
    """Note that intersections are also sort-of-used to make a combination of different elements."""

    def __new__(cls, *inp):
        # NOTE: used to be adding implicit types in here too, but not sure if needed. (8d1b363)
        ret = sorted(merge(inp, 'set_intersect'), key=lambda e:e.tkey)
        ret = (e for e in sorted(merge(inp, 'set_intersect'),
                                 key=lambda e:e.tkey)
               if not isinstance(e, Empty))
        return super(SetIntersection, cls).__new__(cls, ret)

    repr_prep = 'intersect'

M type_stack_calc/tp/bool.py => type_stack_calc/tp/bool.py +1 -6
@@ 6,14 6,9 @@ class TpBool(BaseType, SetBool):
    name = 'bool'

    def is_const(self): return isinstance(self, bool)
    def is_const(self): return isinstance(self[0], bool)

    def c_name(self):
        return ("true" if self[0] is True
                else ("false" if self[0] is False else "bool"))

class TpNone(BaseType):
    name, c_name, is_const = 'None', 'None', True

the_none = TpNone()

M type_stack_calc/tp/float.py => type_stack_calc/tp/float.py +3 -1
@@ 3,4 3,6 @@ from type_stack_calc.tp.tp_help import _TpHelp
from type_stack_calc.sets.float import SetFloat

class TpFloat(SetFloat, _TpHelp):
    name = 'float'
    name = 'Float'

    def param_bits(self): return self[0]

M type_stack_calc/tp/intersection.py => type_stack_calc/tp/intersection.py +32 -3
@@ 12,16 12,35 @@ from type_stack_calc.tp.base import BaseType

from type_stack_calc.tp.range import TpRange
from type_stack_calc.sets.intersection import SetIntersection
from type_stack_calc.sets.empty import Empty

class _BaseTpIntersection(SetIntersection, BaseType):
    """Stuff shared between `TpIntersection` and `TypeTpInter..`"""

class TpNoAns(tuple, BaseType):
    """Type that indicates no-answer, or that no-answer is possible."""
    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

    def __repr__(self): return '<NoAns>'

the_no_ans = TpNoAns()

class TpIntersection(_BaseTpIntersection):
    """Type of an intersection."""

    def __new__(cls, *inp):
        ret = ((TpRange(x, x) if (x==int or x==float) else x) for x in inp)
        ret = ((TpRange(x, x) if isinstance(x, (int,float)) else x)
                for x in inp)
        return SetIntersection.__new__(cls, *ret)

    repr_prep = "tp:intersect"

@@ 53,6 72,17 @@ class TpIntersection(_BaseTpIntersection):

    def param_tp(self): return TypeTpIntersection(*self)

    def certain_no_ans(self):
        return len(self) == 1 and type(self[0]) is TpNoAns

    def param_first_ans(self, y):
        if self.certain_no_ans():
            return y
        elif y.certain_no_ans():
            return self
            return self.smoosh(y)

    def get_comp(self, name):
        """Inside it uses components instead of params, and don't want them double-wrapped.
NOTE/TODO: it's a little off, the `param` stuff should really be about values, not components."""

@@ 107,9 137,8 @@ class TypeTpIntersection(_BaseTpIntersection):

from type_stack_calc.tp.stepping import TpStepping
from type_stack_calc.tp.float import TpFloat
from type_stack_calc.tp.bool import the_none

class TypeExtractor(_BaseTpIntersection):
    """Get individual types from the intersection."""
    def get_param_final(self, name):
        return next((tp for tp in self if tp.name == 'name'), the_none)
        return next((tp for tp in self if tp.name == 'name'), the_no_ans)

M type_stack_calc/tp/range.py => type_stack_calc/tp/range.py +4 -1
@@ 22,7 22,7 @@ from type_stack_calc.tp.stepping import TpStepping

class TpRange(SetRange, _TpHelp):
    """Type calculation version of the range set."""
    name = 'range'
    name = 'Range'
    boolify = _TpHelp.boolify
    Stepping = TpStepping

@@ 53,3 53,6 @@ class TpRange(SetRange, _TpHelp):
        """As type, make an integer too.
Equivalent to stepping with (origin, step) == (0,1)."""
        return intersection.set_intersect(TpStepping(0, 1))

    def param_fr(self): return self[0]
    def param_to(self): return self[1]

M type_stack_calc/tp/stepping.py => type_stack_calc/tp/stepping.py +6 -2
@@ 5,5 5,9 @@ from type_stack_calc.tp.tp_help import _TpHelp
from type_stack_calc.sets.stepping import SetStepping

class TpStepping(SetStepping, _TpHelp):
   boolify = _TpHelp.boolify
   name = 'stepping'
    boolify = _TpHelp.boolify
    name = 'Stepping'

    def param_orig(self): return self[0]
    def param_step(self): return self[1]

M type_stack_calc/tp_calc.py => type_stack_calc/tp_calc.py +4 -2
@@ 80,7 80,8 @@ class StackCalc:
            if not getattr(val, 'with_vars', None):
                inp = [(a.val if isinstance(a, BaseComponent) else a)
                       for a in inp]
                assert not any(isinstance(a, (type(None), BaseComponent)) for a in inp)
                assert not any(isinstance(a, (type(None), BaseComponent))
                               for a in inp), inp
            # Actually calculate the type, return new stack and result.
            stack, *tc_code = tp_fun(self, vs, inp)
            assert isinstance(stack, list), (val)

@@ 101,7 102,8 @@ class StackCalc:

        return stack[:-1] + [var], var  # No calculation needed.

    special_fun = {'+', '-', '*', '/', '%', '>', '<'}
    special_fun = {'+', '-', '*', '/', '%', '>', '<',
                   'and', 'or', 'not', 'xor'}

    def tp_calc(self, code, vs, stack):
        """Calculates types and also collects the calls again, with their types, some origins."""