o
    @a                     @   s   d Z ddlmZmZmZmZmZ ddlmZ ddl	m
Z
 dZG dd deZG dd	 d	eZG d
d deZG dd deZG dd deZG dd deZdS )z9 Module to looks for a specified pattern in a given AST.     )ASTiter_fieldsNodeVisitorDictSet)permutations)isnan
   c                   @      e Zd ZdZdS )DamnTooLongPatternz@ Exception for long dict/set comparison to reduce compile time. N__name__
__module____qualname____doc__ r   r   >/usr/lib/python3/dist-packages/pythran/analyses/ast_matcher.pyr   
       r   c                       s"   e Zd ZdZd fdd	Z  ZS )Placeholderz= Class to save information from ast while check for pattern. Nc                    s   || _ || _tt|   dS )z1 Placehorder are identified using an identifier. N)idtypesuperr   __init__)selfZ
identifierr   	__class__r   r   r      s   zPlaceholder.__init__Nr   r   r   r   r   __classcell__r   r   r   r   r      s    r   c                   @   r
   )AST_anyz< Class to specify we don't care about a field value in ast. Nr   r   r   r   r   r      r   r   c                       s    e Zd ZdZ fddZ  ZS )AST_orz
    Class to specify multiple possibles value for a given field in ast.

    Attributes
    ----------
    args: [ast field value]
        List of possible value for a field of an ast.
    c                    s   || _ tt|   dS )z) Initialiser to keep track of arguments. N)argsr   r    r   )r   r!   r   r   r   r   *   s   zAST_or.__init__r   r   r   r   r   r       s    	r    c                   @   sh   e Zd ZdZdd Zdd Zdd Zedd	 Zd
d Z	dd Z
dd Zdd Zedd Zdd ZdS )Checka  
    Checker for ast <-> pattern.

    NodeVisitor is needed for specific behavior checker.

    Attributes
    ----------
    node : AST
        node we want to compare with pattern
    placeholders : [AST]
        list of placeholder value for later comparison or replacement.
    c                 C   s   || _ || _dS )z Initialize attributes. N)nodeplaceholders)r   r#   r$   r   r   r   r   ?   s   
zCheck.__init__c                    s0   t |t |kr
dS t fddt||D S )z" Check if list of node are equal. Fc                 3   s&    | ]\}}t | j|V  qd S r   )r"   r$   visit).0Znode_eltZpattern_eltr   r   r   	<genexpr>H       z#Check.check_list.<locals>.<genexpr>)lenallzip)r   Z	node_listZpattern_listr   r'   r   
check_listD   s
   zCheck.check_listc                 C   sZ   |j | jv rt| j| j| j|j  sdS |jdur$t| j|js$dS | j| j|j < dS )z
        Save matching node or compare it with the existing one.

        FIXME : What if the new placeholder is a better choice?
        FNT)r   r$   r"   r#   r%   r   
isinstancer   patternr   r   r   visit_PlaceholderK   s   
zCheck.visit_Placeholderc                 C   s   dS )z Every node match with it. Tr   )_r   r   r   visit_AST_any\   s   zCheck.visit_AST_anyc                    s   t  fdd|jD S )z; Match if any of the or content match with the other node. c                 3   s    | ]
}   j|V  qd S r   )field_matchr#   )r&   Zvalue_orr'   r   r   r(   c   s    z%Check.visit_AST_or.<locals>.<genexpr>)anyr!   r/   r   r'   r   visit_AST_ora   s   zCheck.visit_AST_orc                    sB   t  jtsdS t|jtkrtdt fddt|jD S )z Set have unordered values. FzPattern for Set is too longc                 3   s     | ]}   jj|V  qd S r   )r-   r#   elts)r&   Zpattern_eltsr'   r   r   r(   l   s    z"Check.visit_Set.<locals>.<genexpr>)	r.   r#   r   r*   r7   MAX_UNORDERED_LENGTHr   r5   r   r/   r   r'   r   	visit_Setf   s   zCheck.visit_Setc                    s   t | jtsdS t jtkrtdttt| jjD ]-}t	|D ]\}}| 
| jj|  j| s6 nq# fdd|D }| | jj|  S qdS )z' Dict can match with unordered values. FzPattern for Dict is too longc                    s   g | ]} j | qS r   )values)r&   ir0   r   r   
<listcomp>{   s    z$Check.visit_Dict.<locals>.<listcomp>)r.   r#   r   r*   keysr8   r   r   range	enumerater4   r-   r:   )r   r0   Zpermutationr;   valueZpattern_valuesr   r<   r   
visit_Dicto   s   zCheck.visit_Dictc                 C   s>   t |tr| ||S t |trt|| j|S t||S )z
        Check if two fields match.

        Field match if:
            - If it is a list, all values have to match.
            - If if is a node, recursively check it.
            - Otherwise, check values are equal.
        )r.   listr-   r   r"   r$   r%   	strict_eq)r   Z
node_fieldZpattern_fieldr   r   r   r4      s
   
	
zCheck.field_matchc                 C   s4   | |krdS z	t | ot |W S  ty   Y dS w )NTF)r   	TypeError)Zf0f1r   r   r   rD      s   zCheck.strict_eqc                    s2   t  tjs
dS t fddtjD S )z
        Check if the pattern match with the checked node.

        a node match if:
            - type match
            - all field match
        Fc                 3   s&    | ]\}} |t |V  qd S r   )r4   getattr)r&   ZfieldrA   r0   r   r   r   r(      r)   z&Check.generic_visit.<locals>.<genexpr>)r.   r   r#   r+   r   r/   r   rH   r   generic_visit   s
   zCheck.generic_visitN)r   r   r   r   r   r-   r1   staticmethodr3   r6   r9   rB   r4   rD   rI   r   r   r   r   r"   0   s    
	
r"   c                       s0   e Zd ZdZ fddZdd Zdd Z  ZS )
ASTMatchera  
    Visitor to gather node matching with a given pattern.

    Examples
    --------
    >>> import gast as ast
    >>> code = "[(i, j) for i in range(a) for j in range(b)]"
    >>> pattern = ast.Call(func=ast.Name('range', ctx=ast.Load(),
    ...                                  annotation=None,
    ...                                  type_comment=None),
    ...                    args=AST_any(), keywords=[])
    >>> len(ASTMatcher(pattern).search(ast.parse(code)))
    2
    >>> code = "[(i, j) for i in range(a) for j in range(b)]"
    >>> pattern = ast.Call(func=ast.Name(id=AST_or('range', 'range'),
    ...                                  ctx=ast.Load(),
    ...                                  annotation=None,
    ...                                  type_comment=None),
    ...                    args=AST_any(), keywords=[])
    >>> len(ASTMatcher(pattern).search(ast.parse(code)))
    2
    >>> code = "{1:2, 3:4}"
    >>> pattern = ast.Dict(keys=[ast.Constant(3, None), ast.Constant(1, None)],
    ...                    values=[ast.Constant(4, None),
    ...                            ast.Constant(2, None)])
    >>> len(ASTMatcher(pattern).search(ast.parse(code)))
    1
    >>> code = "{1, 2, 3}"
    >>> pattern = ast.Set(elts=[ast.Constant(3, None),
    ...                         ast.Constant(2, None),
    ...                         ast.Constant(1, None)])
    >>> len(ASTMatcher(pattern).search(ast.parse(code)))
    1
    c                    s    || _ t | _tt|   dS )z? Basic initialiser saving pattern and initialising result set. N)r0   setresultr   rK   r   r/   r   r   r   r      s   zASTMatcher.__init__c                 C   s.   t |t | jr| j| | | dS )z
        Visitor looking for matching between current node and pattern.

        If it match, save it but whatever happen, keep going.
        N)r"   dictr%   r0   rM   addrI   r   r#   r   r   r   r%      s   zASTMatcher.visitc                 C   s   |  | | jS )z9 Facility to get values of the matcher for a given node. )r%   rM   rP   r   r   r   search   s   
zASTMatcher.search)r   r   r   r   r   r%   rQ   r   r   r   r   r   rK      s
    #
rK   N)r   Zgastr   r   r   r   r   	itertoolsr   Zmathr   r8   	Exceptionr   r   r   r    r"   rK   r   r   r   r   <module>   s    v