o
    @aV                     @   s   d Z ddlZddlmZ ddlmZ ddlmZ ddl	m
Z
mZ ddlmZ ddlmZ dd	lmZmZmZ dd
lmZmZ G dd deZG dd deZdd Zdd ZdddZG dd deZG dd deZG dd deZ dS )zB Module Analysing code to extract positive subscripts from code.      N)defaultdict)contextmanager)reduce)AliasesCFG)	Intrinsic)ModuleAnalysis)IntervalIntervalTupleUNKNOWN_RANGE)MODULES
attributesc                   @      e Zd ZdS )UnsupportedExpressionN__name__
__module____qualname__ r   r   ?/usr/lib/python3/dist-packages/pythran/analyses/range_values.pyr          r   c                   @   r   )RangeValueTooCostlyNr   r   r   r   r   r      r   r   c                 C   s@   d | jj }ztt||||W S  ty   t Y S w )Nz__{}__)format	__class__r   lowergetattrtypeAttributeErrorr   )opZnode0Znode1keyr   r   r   combine   s   r    c                 C   s   t | tjr	t t | tjrDt | jtjr&t| jt	 gt
dd gS t | jtjr0| jS t | jtjr:| jS t | jtjrD| jS t | tjrpdd | jD }t | jtjratt |S t | jtjrptt |S t | tjrdd t| jg| jd d  | j| jD }t|dkr|d S tt |S t | tj	rt S t | tjrt	 S t | tjrt S t | tjrt S t | tjrt S t | tjrt S t | tjrt S t | tjrt S t | tjr| jdkrt
dd S | jd	krt
d
d S t )Nc                 S   s   g | ]
}t t  |qS r   )astUnaryOpNot).0vr   r   r   
<listcomp>6   s    znegate.<locals>.<listcomp>c                 S   s(   g | ]\}}}t |t|g|gqS r   )r"   Comparenegate)r%   xoyr   r   r   r'   ?   s       r   FalseTTrueF) 
isinstancer"   Namer   r#   r   Invertr(   operandEqZConstantr$   UAddUSubBoolOpvaluesOrAndzipleftcomparatorsopslenZNotEqGtLtEGtELtInNotInZ	Attributeattr)nodeZ
new_valuesZcmpsr   r   r   r)       sf   r)   c                    s`  |du rt  }t|tjrdS t|tjr/zt|j}t||| W dS  ty.   Y dS w t|tj	rt|j
tjrK|jD ]	}t||| q?dS t|j
tjrfdd|jD }t|j|D ]\}}t|||| qb|D ] t fdd|dd |d    < qpdS dS t|tjr,|j}t|jtjr||jj t|j|jD ]\}	}
t|
tjr||
j t|tjrǈ|j }n| }t|
tjr׈|
j }n|
 }|j|j}}|j|j}}d }}t|	tjrt||t||}}||krtt||t|| }}nt|	tjr8tt||d t||d }tt||d t||d }nt|	tjrVtt||t||}tt||t||}nt|	tjr|tt||d t||d }tt||d t||d }nt|	tjrtt||t||}tt||t||}nmt|	tj rt|
tj!tj"tj#fr|
j$rtfdd	|
j$D }tfd
d	|
j$D }t||}n8t|
tj%r||
j& D ]+}t'|dsd} n |(fdd|
j)D }|du r|}q|*|+|
}q|durt|tjr||j< |dur)t|
tjr)||
j< |
}qdS dS )a'  
    Bound the idenifier in `mapping' with the expression in `node'.
    `aliases' is the result of aliasing analysis and `modified' is
    updated with the set of identifiers possibly `bounded' as the result
    of the call.

    Returns `modified' or a fresh set of modified identifiers.

    Nc                    s   g | ]}   qS r   )copy)r%   _mappingr   r   r'          zbound_range.<locals>.<listcomp>c                    s   |  |  S N)unionr*   r,   )kr   r   <lambda>   s    zbound_range.<locals>.<lambda>r-   r   c                 3       | ]} | j V  qd S rM   )lowr%   eltrJ   r   r   	<genexpr>       zbound_range.<locals>.<genexpr>c                 3   rR   rM   )highrT   rJ   r   r   rV      rW   return_range_contentc                    s   g | ]} | qS r   r   )r%   argrJ   r   r   r'      rL   ),setr0   r"   r1   r#   r)   r3   bound_ranger   r7   r   r:   r8   r9   r;   r   r(   r<   addidr>   r=   rS   rX   r4   maxminr	   rC   rA   r@   rB   rD   ZListZTupleSeteltsCallfunchasattrrY   argsrN   return_range)rK   aliasesrG   ZmodifiedZnegatedvalueZmappingsZmapping_cpyr<   r   rightZleft_intervalZright_intervalZl_lZl_hZr_lZr_hZr_iZl_irS   rX   aliasZrrcr   )rP   rK   r   r\   a   s   





  



r\   c                       s   e Zd Ze Z fddZdd Zdd Zdd Zd	d
 Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd  Z  ZS )!RangeValuesBasec                    s8   t dd | _ddlm} tt| tt| | | _	dS )zAInitialize instance variable and gather globals name information.c                   S      t S rM   r   r   r   r   r   rQ          z*RangeValuesBase.__init__.<locals>.<lambda>r   )UseOMPN)
r   resultpythran.analysesrp   superrl   __init__r   r   parent)selfrp   r   r   r   rt      s   
zRangeValuesBase.__init__c                 C   s6   || j vr|| j |< n| j | || j |< | j | S )z
        Add a new low and high bound for a variable.

        As it is flow insensitive, it compares it with old values and update it
        if needed.
        )rq   rN   )rv   variablerange_r   r   r   r]      s   

zRangeValuesBase.addc                 C   sB   |  D ]\}}|| jv r| j| || j|< q|| j|< qd S rM   )itemsrq   rN   )rv   otherrP   r&   r   r   r   unionify   s
   
zRangeValuesBase.unionifyc                 C   sV   || _ | D ]!\}}| j |d }|d u r|| j |< q||ur(||| j |< qd S rM   )rq   rz   getwiden)rv   currr{   rP   r&   wr   r   r   r~      s   zRangeValuesBase.widenc                    s>   t t fdd|jD  } |tt|d t|d S )a   Merge right and left operands ranges.

        TODO : We could exclude some operand with this range information...

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = 2
        ...     c = 3
        ...     d = a or c''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['d']
        Interval(low=2, high=3)
        c                    s   g | ]	}  | qS r   )visitZboundsrT   rv   r   r   r'   
  s    z0RangeValuesBase.visit_BoolOp.<locals>.<listcomp>r   r-   )listr;   r8   r]   r	   r`   r_   rv   rG   resr   r   r   visit_BoolOp   s   "zRangeValuesBase.visit_BoolOpc                 C   s*   t |j| |j| |j}| ||S )a   Combine operands ranges for given operator.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = 2
        ...     c = 3
        ...     d = a - c''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['d']
        Interval(low=-1, high=-1)
        )r    r   r   r<   rj   r]   r   r   r   r   visit_BinOp  s   zRangeValuesBase.visit_BinOpc                 C   s   |  |j}t|jtjrtdd}n8t|jtjr0t|jt	r0t|j
t	r0t|j |j
 }nt|jtjr8nt|jtjrIt|j |j
 }nt}| ||S )ap   Update range with given unary operation.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = 2
        ...     c = -a
        ...     d = ~a
        ...     f = +a
        ...     e = not a''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['f']
        Interval(low=2, high=2)
        >>> res['c']
        Interval(low=-2, high=-2)
        >>> res['d']
        Interval(low=-3, high=-3)
        >>> res['e']
        Interval(low=0, high=1)
        r   r-   )r   r3   r0   r   r"   r$   r	   r2   rX   intrS   r5   r6   r   r]   r   r   r   r   visit_UnaryOp  s   

zRangeValuesBase.visit_UnaryOpc                 C   s6   |  |j |  |j}|  |j}| |||S )a   Use worst case for both possible values.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = 2 or 3
        ...     b = 4 or 5
        ...     c = a if a else b''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['c']
        Interval(low=2, high=5)
        )r   testbodyorelser]   rN   )rv   rG   Zbody_resZ
orelse_resr   r   r   visit_IfExpE  s   zRangeValuesBase.visit_IfExpc                 C   s   t dd |jD r| | | |tddS | |j}g }t|j|jD ]@\}}| |}t	
t	dt	 dd|gt	dt	 ddg}t	|}t	| tt	|dd	}|t|||d
 q't|ru| |tddS t dd |D r| |tddS | |tddS )a:   Boolean are possible index.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = 2 or 3
        ...     b = 4 or 5
        ...     c = a < b
        ...     d = b < 3
        ...     e = b == 4''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['c']
        Interval(low=1, high=1)
        >>> res['d']
        Interval(low=0, high=0)
        >>> res['e']
        Interval(low=0, high=1)
        c                 s   s*    | ]}t |tjtjtjtjfV  qd S rM   )r0   r"   rD   rE   ZIsZIsNot)r%   r   r   r   r   rV   n  s    "z0RangeValuesBase.visit_Compare.<locals>.<genexpr>r   r-   r*   Nr,   z<range_values>evalrO   c                 s   s,    | ]}|j |j  kod kn  V  qdS )r   N)rS   rX   )r%   rr   r   r   rV     s   * )anyr>   generic_visitr]   r	   r   r<   r;   r=   r"   r(   r1   ZLoadZ
ExpressionZfix_missing_locationscompileZgast_to_astappendr   all)rv   rG   r   r   r   Z
comparatorZfakeexprr   r   r   visit_CompareY  s,   



zRangeValuesBase.visit_Comparec                    s    j |j D ]j}|td d u r&|jd j}t| d } ||d qt|t	r?| fdd|jD } || qt|t
jrc| jvrY  } j|  |  | j|  q j|d  |  S  j| S )a   Function calls are not handled for now.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = builtins.range(10)''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=-inf, high=inf)
        builtinsr   r!   Nc                       g | ]}  |qS r   r   r%   nr   r   r   r'         z.RangeValuesBase.visit_Call.<locals>.<listcomp>)rh   rd   r   rf   ri   r   r]   rg   r0   r   r"   FunctionDefrq   
save_stateru   r   restore_statepopr   )rv   rG   rk   	attr_name	attributeZalias_rangestater   r   r   
visit_Call  s&   



zRangeValuesBase.visit_Callc                 C   s*   t |jttfr| |t|j|jS tS )z" Handle literals integers values. )r0   ri   boolr   r]   r	   r   rv   rG   r   r   r   visit_Constant  s   zRangeValuesBase.visit_Constantc                 C   s   |  || j|j S )z; Get range for parameters for examples or false branching. )r]   rq   r^   r   r   r   r   
visit_Name  s   zRangeValuesBase.visit_Namec                    s      |t fdd|jD S )Nc                 3   s    | ]}  |V  qd S rM   r   rT   r   r   r   rV     rW   z.RangeValuesBase.visit_Tuple.<locals>.<genexpr>)r]   r
   rb   r   r   r   r   visit_Tuple  s   zRangeValuesBase.visit_Tuplec                 C   s   |  || |jS rM   )r]   r   ri   r   r   r   r   visit_Index  s   zRangeValuesBase.visit_Indexc              	      s   t |jtjre j|jj D ]?}|td d u r/|jjd j}t| d } 	||
d  qt |trG 	||
 fdd|jjD  q |  S  j|jj sZ |S  |j  j| S  |j} |j} 	||| S )Nr   r   r!   c                    r   r   r   r   r   r   r   r'     r   z3RangeValuesBase.visit_Subscript.<locals>.<listcomp>)r0   ri   r"   rc   rh   rd   r   rf   r   r]   rY   r   r   r   slicerq   )rv   rG   rk   r   r   ri   r   r   r   r   visit_Subscript  s(   


zRangeValuesBase.visit_Subscriptc                 C   s~   || j v rdS | jrdS t| j |< | j tjd}| | | j |= | || j tj  |dur8|| j tj< dS | j tj= dS )a`   Set default range value for globals and attributes.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse("def foo(a, b): pass")
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=-inf, high=inf)
        N)rq   use_ompr   r}   rl   ResultHolderfunction_visitorr]   )rv   rG   Zprev_resultr   r   r   visit_FunctionDef  s   


z!RangeValuesBase.visit_FunctionDef)r   r   r   objectr   rt   r]   r|   r~   r   r   r   r   r   r   r   r   r   r   r   r   __classcell__r   r   rw   r   rl      s$    	&,!rl   c                       s   e Zd ZdZd fdd	Z fddZdd Zd	d
 Zdd Zdd Z	dd Z
dd Zdd Zdd ZdddZdd Zdd Zdd Z  ZS ) RangeValuesSimplea*  
    This analyse extract positive subscripts from code.

    It is flow sensitive and aliasing is not taken into account as integer
    doesn't create aliasing in Python.

    >>> import gast as ast
    >>> from pythran import passmanager, backend
    >>> node = ast.parse('''
    ... def foo(a):
    ...     for i in builtins.range(1, 10):
    ...         c = i // 2''')
    >>> pm = passmanager.PassManager("test")
    >>> res = pm.gather(RangeValuesSimple, node)
    >>> res['c'], res['i']
    (Interval(low=0, high=5), Interval(low=1, high=10))
    Nc                    sL   |d ur|| _ |j| _|j| _|j| _|j| _|j| _d S tt|   d S rM   )	ru   ctxZdepsrq   rh   Zpassmanagerrs   r   rt   )rv   ru   rw   r   r   rt     s   zRangeValuesSimple.__init__c                    s   t t| | | |tS )4 Other nodes are not known and range value neither. )rs   r   r   r]   r   r   rw   r   r   r     s   zRangeValuesSimple.generic_visitc                 C   s   | j fS rM   rh   r   r   r   r   r     s   zRangeValuesSimple.save_statec                 C   s   |\| _ d S rM   r   rv   r   r   r   r   r     s   zRangeValuesSimple.restore_statec                 C   s   |j D ]}| | qd S rM   )r   r   )rv   rG   stmtr   r   r   r     s   
z"RangeValuesSimple.function_visitorc                 C   s*   |j r| |j }| tj|S | |S rM   )ri   r   r]   RangeValuesr   r   rv   rG   rg   r   r   r   visit_Return  s   
zRangeValuesSimple.visit_Returnc                 C   s    |  | t| j| j|j dS )a  
        Constraint the range of variables

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse("def foo(a): assert a >= 1; b = a + 1")
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['a']
        Interval(low=1, high=inf)
        >>> res['b']
        Interval(low=2, high=inf)
        N)r   r\   rq   rh   r   r   r   r   r   visit_Assert#  s   
zRangeValuesSimple.visit_Assertc                 C   sB   |  |j}|jD ]}t|tjr| |j| q	|  | q	dS )a  
        Set range value for assigned variable.

        We do not handle container values.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse("def foo(): a = b = 2")
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['a']
        Interval(low=2, high=2)
        >>> res['b']
        Interval(low=2, high=2)
        N)r   ri   targetsr0   r"   r1   r]   r^   rv   rG   Zassigned_rangetargetr   r   r   visit_Assign4  s   
zRangeValuesSimple.visit_Assignc                 C   sN   |  | t|jtjr%|jj}t|j| j| | j|j	 }|| j|< dS dS )a`   Update range value for augassigned variables.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse("def foo(): a = 2; a -= 1")
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['a']
        Interval(low=1, high=1)
        N)
r   r0   r   r"   r1   r^   r    r   rq   ri   rv   rG   namer   r   r   r   visit_AugAssignL  s   

z!RangeValuesSimple.visit_AugAssignc              	      s   t |jtjsJ d |j t |jtjr; j|jj D ]}t |t	r: 
|jj| fdd|jjD  q |t|jt g|jg dS )a`   Handle iterate variable in for loops.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = b = c = 2
        ...     for i in builtins.range(1):
        ...         a -= 1
        ...         b += 1''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['a']
        Interval(low=-inf, high=2)
        >>> res['b']
        Interval(low=2, high=inf)
        >>> res['c']
        Interval(low=2, high=2)

        >>> node = ast.parse('''
        ... def foo():
        ...     for i in (1, 2, 4):
        ...         a = i''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['a']
        Interval(low=1, high=4)
        For apply on variables.c                    r   r   r   r   r   r   r   r'     r   z/RangeValuesSimple.visit_For.<locals>.<listcomp>N)r0   r   r"   r1   r   iterrc   rh   rd   r   r]   r^   rY   rf   
visit_loopr(   rD   )rv   rG   rk   r   r   r   	visit_For_  s   

zRangeValuesSimple.visit_Forc                 C   s   |dur| j }| j  | _ t| j | j| |jD ]}| | q| j  }|jD ]}| | q(| D ]\}}| j | || j |< q4|duret| j | j| |jD ]}| | qS| | | | |j	D ]}| | qhdS )a=   Handle incremented variables in loop body.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = b = c = 2
        ...     while a > 0:
        ...         a -= 1
        ...         b += 1''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['a']
        Interval(low=0, high=2)
        >>> res['b']
        Interval(low=2, high=inf)
        >>> res['c']
        Interval(low=2, high=2)
        N)
rq   rH   r\   rh   r   r   rz   r~   r|   r   )rv   rG   Zcond
init_ranger   	old_ranger   ry   r   r   r   r     s(   






zRangeValuesSimple.visit_loopc                 C   s   |  |j | ||jS rM   )r   r   r   r   r   r   r   visit_While  s   zRangeValuesSimple.visit_Whilec                 C   s   |  |j | j}| | _t| j| j|j |jD ]}|  | q| j}| | _|jD ]}|  | q-| j}|| _| | dS )aO   Handle iterate variable across branches

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> pm = passmanager.PassManager("test")

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if a > 1: b = 1
        ...     else: b = 3''')

        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['b']
        Interval(low=1, high=3)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if a > 1: b = a
        ...     else: b = 3''')
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['b']
        Interval(low=2, high=inf)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if 0 < a < 4: b = a
        ...     else: b = 3''')
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['b']
        Interval(low=1, high=3)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if (0 < a) and (a < 4): b = a
        ...     else: b = 3''')
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['b']
        Interval(low=1, high=3)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if (a == 1) or (a == 2): b = a
        ...     else: b = 3''')
        >>> res = pm.gather(RangeValuesSimple, node)
        >>> res['b']
        Interval(low=1, high=3)
        N)	r   r   rq   rH   r\   rh   r   r   r|   )rv   rG   r   r   Z
body_rangeZorelse_ranger   r   r   visit_If  s   0



zRangeValuesSimple.visit_Ifc                 C   s   | j }| | _ |jD ]}| | q| | | j  }|jD ]}| | j | _ }|jD ]}| | q.| | q || j | _ }|jD ]}| | qF| | |jD ]}| | qVd S rM   )rq   rH   r   r   r|   handlersr   	finalbody)rv   rG   r   r   handler
prev_stater   r   r   	visit_Try  s$   








zRangeValuesSimple.visit_TryrM   )r   r   r   __doc__rt   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rw   r   r     s     
)4Br   c                       s   e Zd ZdZ fddZ fddZd!ddZd	d
 Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd  Z  ZS )"r   a7  
    This analyse extract positive subscripts from code.

    It is flow sensitive and aliasing is not taken into account as integer
    doesn't create aliasing in Python.

    >>> import gast as ast
    >>> from pythran import passmanager, backend
    >>> node = ast.parse('''
    ... def foo(a):
    ...     for i in builtins.range(1, 10):
    ...         c = i // 2
    ...     return''')
    >>> pm = passmanager.PassManager("test")
    >>> res = pm.gather(RangeValues, node)
    >>> res['c'], res['i']
    (Interval(low=0, high=5), Interval(low=1, high=10))
    c                    s   t t|   d S rM   )rs   r   rt   r   rw   r   r   rt   0  s   zRangeValues.__init__c                    sB   t t| | t|tjr|| jv r| j|S dS | |t	S )r   N)
rs   r   r   r0   r"   r   cfg
successorsr]   r   r   rw   r   r   r   3  s   
zRangeValues.generic_visitNc                 C   sj   |g}|d u r
t  n| }|r3| }||v rq|| | |}|r/|dd |D  |sd S d S )Nc                 s   s    | ]
}|t jur|V  qd S rM   )r   ZNILr   r   r   r   rV   G  s    z(RangeValues.cfg_visit.<locals>.<genexpr>)r[   rH   r   r]   r   extend)rv   rG   skipr   Zvisited	successorZnextsr   r   r   	cfg_visit=  s   

zRangeValues.cfg_visitc                 C   s   | j | j| j| j| jfS rM   r   rh   r   no_backwardno_if_splitr   r   r   r   r   I  s   zRangeValues.save_statec                 C   s   |\| _ | _| _| _| _d S rM   r   r   r   r   r   r   M  s   zRangeValues.restore_statec                 C   s   | j }tdd | _ | D ]\}}t|tjr|| j |< qz%d| _d| _| t	| j
| | j  D ]\}}|||< q5|| _ W d S  tyY   || _ t| }|| Y d S w )Nc                   S   rm   rM   rn   r   r   r   r   rQ   S  ro   z.RangeValues.function_visitor.<locals>.<lambda>r   )rq   r   rz   r0   r"   r   r   r   r   nextr   r   r   r   r   )rv   rG   Zparent_resultrP   r&   Zrvsr   r   r   r   Q  s$   

zRangeValues.function_visitorc                 C   s,   |j r| |j }| tj| | j|S rM   )ri   r   r]   r   r   r   r   r   r   r   r   r   g  s   zRangeValues.visit_Returnc                 C   s*   |  |j t| j| j|j | j|S )a  
        Constraint the range of variables

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse("def foo(a): assert a >= 1; b = a + 1")
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=1, high=inf)
        >>> res['b']
        Interval(low=2, high=inf)
        )r   r   r\   rq   rh   r   r   r   r   r   r   r   m  s   zRangeValues.visit_Assertc                 C   sH   |  |j}|jD ]}t|tjr|| j|j< q	|  | q	| j	|S )a  
        Set range value for assigned variable.

        We do not handle container values.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse("def foo(): a = b = 2")
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=2, high=2)
        >>> res['b']
        Interval(low=2, high=2)
        )
r   ri   r   r0   r"   r1   rq   r^   r   r   r   r   r   r   r     s   
zRangeValues.visit_Assignc                 C   sR   |  | t|jtjr#|jj}t|j| j| | j|j	 }|| j|< | j
|S )aZ   Update range value for augassigned variables.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse("def foo(): a = 2; a -= 1")
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=1, high=1)
        )r   r0   r   r"   r1   r^   r    r   rq   ri   r   r   r   r   r   r   r     s   


zRangeValues.visit_AugAssignc                 C   sX   | j |D ]#}||jd ur)t|tjr$t| j| jt	t
 |j |g  S qd S Nr   )r   r   r   r0   r"   ZWhiler\   rq   rh   r#   r$   r   )rv   rG   r   r   r   r   visit_loop_successor  s   

z RangeValues.visit_loop_successorc                    s   t  jtjsJ d|  j | j }t| j| j	t
 jt g jg  fdd| j D }|  | j jd |d}| jrM|  S 	 | j}| | _| j jd |d | | j| | j jd |d | | 	 |  S )a   Handle iterate variable in for loops.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = b = c = 2
        ...     for i in builtins.range(1):
        ...         a -= 1
        ...         b += 1
        ...     return''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=-inf, high=2)
        >>> res['b']
        Interval(low=2, high=inf)
        >>> res['c']
        Interval(low=2, high=2)

        >>> node = ast.parse('''
        ... def foo():
        ...     for i in (1, 2, 4):
        ...         a = i
        ...     return''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=1, high=4)
        r   c                       h | ]}| j d  ur|qS r   r   r%   r*   rG   r   r   	<setcomp>      z(RangeValues.visit_For.<locals>.<setcomp>r   r   )r0   r   r"   r1   r   r   rq   rH   r\   rh   r(   rD   r   r   r]   r   r   r   r   r~   r|   )rv   rG   
init_stater   Znext_r   r   r   r   r     s*   





zRangeValues.visit_Forc                    s:  |   j}| j } fdd| j D }|  d|v r:t| j D ]}| jd ur9| j	||d q)t
| j| j j | j	 jd |d | jr^d|v rY| | |  S 	 | j}| | _| j	 jd |d | | j| | j	 jd |d d|v r| | n| | |   j 	 |  S )aU   Handle incremented variables in loop body.

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> node = ast.parse('''
        ... def foo():
        ...     a = b = c = 10
        ...     while a > 0:
        ...         a -= 1
        ...         b += 1
        ...     return''')
        >>> pm = passmanager.PassManager("test")
        >>> res = pm.gather(RangeValues, node)
        >>> res['a']
        Interval(low=-inf, high=0)
        >>> res['b']
        Interval(low=11, high=inf)
        >>> res['c']
        Interval(low=10, high=10)
        c                    r   r   r   r   r   r   r   r     r   z*RangeValues.visit_While.<locals>.<setcomp>r   r   )r   r   rq   rH   r   r   r]   r   r   r   r\   rh   r   r|   r   r~   )rv   rG   
test_ranger   r   r   r   r   r   r   r     s6   






zRangeValues.visit_Whilec                 C   sR  | j dkrt |  j d7  _ | |j}| j }d|v r/t| j| j|j | |j	d  |j	d h}|j
rid|v r`| j}| | _t| j| jtt |j | |j
d  | | ||j
d  n7d|v r| j|}t|D ](}||vr| | j| _}t| j| jtt |j | | | | qw|  j d8  _ dS )a   Handle iterate variable across branches

        >>> import gast as ast
        >>> from pythran import passmanager, backend
        >>> pm = passmanager.PassManager("test")

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if a > 1: b = 1
        ...     else: b = 3
        ...     pass''')

        >>> res = pm.gather(RangeValues, node)
        >>> res['b']
        Interval(low=1, high=3)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if a > 1: b = a
        ...     else: b = 3
        ...     pass''')
        >>> res = pm.gather(RangeValues, node)
        >>> res['b']
        Interval(low=2, high=inf)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if 0 < a < 4: b = a
        ...     else: b = 3
        ...     pass''')
        >>> res = pm.gather(RangeValues, node)
        >>> res['b']
        Interval(low=1, high=3)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if (0 < a) and (a < 4): b = a
        ...     else: b = 3
        ...     pass''')
        >>> res = pm.gather(RangeValues, node)
        >>> res['b']
        Interval(low=1, high=3)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if (a == 1) or (a == 2): b = a
        ...     else: b = 3
        ...     pass''')
        >>> res = pm.gather(RangeValues, node)
        >>> res['b']
        Interval(low=1, high=3)

        >>> node = ast.parse('''
        ... def foo(a):
        ...     b = 5
        ...     if a > 0: b = a
        ...     pass''')
        >>> res = pm.gather(RangeValues, node)
        >>> res['a'], res['b']
        (Interval(low=-inf, high=inf), Interval(low=1, high=inf))

        >>> node = ast.parse('''
        ... def foo(a):
        ...     if a > 3: b = 1
        ...     else: b = 2
        ...     if a > 1: b = 2
        ...     pass''')
        >>> res = pm.gather(RangeValues, node)
        >>> res['b']
        Interval(low=2, high=2)
           r-   r   N)r   r   r   r   rq   rH   r\   rh   r   r   r   r"   r#   r$   r|   r]   r   r   r   )rv   rG   r   r   Zvisited_successorsr   r   r   r   r   r   r   3  s>   
J






zRangeValues.visit_Ifc                 C   sp   | j }| | _ | |jd  | | | j  }|jD ]}| | j | _ }| |jd  | | qd S r   )rq   rH   r   r   r|   r   )rv   rG   r   r   r   r   r   r   r     s   



zRangeValues.visit_TryrM   )r   r   r   r   rt   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rw   r   r     s"    

=Bor   rM   )!r   Zgastr"   collectionsr   
contextlibr   	functoolsr   rr   r   r   Zpythran.intrinsicr   Zpythran.passmanagerr   Zpythran.intervalr	   r
   r   Zpythran.tablesr   r   NotImplementedErrorr   RuntimeErrorr   r    r)   r\   rl   r   r   r   r   r   r   <module>   s,    
Ap    /