
    <iC                     2   S r SSKrSSKrSSKJrJr  SSKJr  SSKJ	r	J
r
Jr  SSKJr  SSKJr  SSKJr  SS	KJr  SS
KJr  SSKJrJrJrJrJrJrJr  S r " S S\5      r " S S\5      r " S S\5      r " S S\5      r " S S\5      r " S S\5      r g)zD
Transformation utilities for STIX pattern observation expressions.
    N)iter_initer_lex_cmp)observation_expression_cmp)ChainTransformerSettleTransformerTransformer)SpecialValueCanonicalization)AbsorptionTransformer)DNFTransformer)FlattenTransformer)OrderDedupeTransformer)AndObservationExpressionFollowedByObservationExpressionObservationExpressionOrObservationExpressionParentheticalExpressionQualifiedObservationExpression_CompoundObservationExpressionc                    [        U [        5      (       a1  [        U R                   Vs/ s H  n[        U5      PM     sn5      nU$ [        U [        5      (       a1  [	        U R                   Vs/ s H  n[        U5      PM     sn5      nU$ [        U [
        5      (       a1  [        U R                   Vs/ s H  n[        U5      PM     sn5      nU$ [        U [        5      (       a+  [        [        U R                  5      U R                  5      nU$ [        U [        5      (       a  U nU$ [        S[        U 5      R                  -   5      es  snf s  snf s  snf )a  
Create a duplicate of the given AST.  The AST root must be an observation
expression of some kind (AND/OR/qualified, etc).

Note: the observation expression "leaves", i.e. simple square-bracket
observation expressions are currently not duplicated.  I don't think it's
necessary as of this writing.  But revisit this if/when necessary.

Args:
    ast: The AST to duplicate

Returns:
    The duplicate AST
zCan't duplicate )
isinstancer   operands	_dupe_astr   r   r   observation_expression	qualifierr   	TypeErrortype__name__)astchildresults      i/home/james-whalen/.local/lib/python3.13/site-packages/stix2/equivalence/pattern/transform/observation.pyr   r       sB    #/00)*-,,+
*6Ie,+
 4 M- 
C0	1	1(*-,,*
*6Ie,*
 * M# 
C8	9	90*-,,2
*6Ie,2
   M 
C7	8	8/c0013==
 M 
C.	/	/
 M *T#Y-?-??@@1+

*

2
s   E/E5Ec            
       B    \ rS rSrSr\S\S\S\S\	S0r
S rS	 rS
 rSrg) ObservationExpressionTransformerM   a;  
Transformer base class with special support for transforming observation
expressions.  The transform method implemented here performs a bottom-up
in-place transformation, with support for some observation
expression-specific callbacks.  It recurses down as far as the "leaf node"
observation expressions; it does not go inside of them, to the individual
components of a comparison expression.

Specifically, subclasses can implement methods:
    "transform_or" for OR nodes
    "transform_and" for AND nodes
    "transform_followedby" for FOLLOWEDBY nodes
    "transform_qualified" for qualified nodes (all qualifier types)
    "transform_observation" for "leaf" observation expression nodes
    "transform_default" for all types of nodes

"transform_default" is a fallback, if a type-specific callback is not
found.  The default implementation does nothing to the AST.  The
type-specific callbacks are preferred over the default, if both exist.

In all cases, the callbacks are called with an AST for a subtree rooted at
the appropriate node type, where the AST's children have already been
transformed.  They must return the same thing as the base transform()
method: a 2-tuple with the transformed AST and a boolean for change
detection.  See doc for the superclass' method.

This process currently silently drops parenthetical nodes.
observationandor
followedby	qualifiedc                    Sn[        U[        5      (       a  U R                  U5      u  p4U(       a  SnX24$ [        U[        5      (       ai  [	        UR
                  5       H1  u  pVU R                  U5      u  p4U(       d  M!  X1R
                  U'   SnM3     U R                  U5      u  p4U(       a  SnX24$ [        U[        5      (       aK  U R                  UR                  5      u  p4U(       a  X1l        SnU R                  U5      u  p4U(       a  SnX24$ [        U[        5      (       a"  U R                  UR                  5      u  p7SnX24$ [        SR                  [        U5      R                  [        U5      5      5      e)NFTz%Not an observation expression: {}: {})r   r   5_ObservationExpressionTransformer__dispatch_transformr   	enumerater   	transformr   r   r   
expressionr   formatr   r   str)selfr   changedr    this_changedioperand_s           r!   r-   *ObservationExpressionTransformer.transformt   so   c011 $(#<#<S#A FJ G ;<<'5
'+~~g'>$<&,LLO"G	 6 $(#<#<S#A F4 1 ;<< $(>>#2L2L#M F-3*#'#<#<S#A F  455s~~6IFG  7>>I&&C     c                     U R                   R                  [        U5      5      nU(       a  SU-   n[        XU R                  5      nOU R                  nU" U5      $ )z
Invoke a transformer callback method based on the given ast root node
type.

Args:
    ast: The AST

Returns:
    The callback's result

transform_)_DISPATCH_NAME_MAPgetr   getattrtransform_default)r1   r   dispatch_name	meth_namemeths        r!   __dispatch_transform5ObservationExpressionTransformer.__dispatch_transform   sR     //33DI>$}4I4D,B,BCD))DCyr8   c                 
    US4$ )NF r1   r   s     r!   r>   2ObservationExpressionTransformer.transform_default   s    Ezr8   rE   N)r   
__module____qualname____firstlineno____doc__r   r   r   r   r   r;   r-   r+   r>   __static_attributes__rE   r8   r!   r#   r#   M   s:    > 	} %'&-^*r8   r#   c                   0    \ rS rSrSrS rS rS rS rSr	g)	r      z
Flatten an observation expression AST.  E.g.:

    A and (B and C) => A and B and C
    A or (B or C) => A or B or C
    A followedby (B followedby C) => A followedby B followedby C
    (A) => A
c                 ^   Sn[        UR                  5      S:X  a  UR                  S   nSnX24$ / nUR                   Hb  n[        U[        5      (       a9  UR                  UR                  :X  a  UR                  UR                  5        SnMQ  UR                  U5        Md     XAl        UnX24$ )NF   r   T)lenr   r   r   operatorextendappend)r1   r   r2   r    flat_childrenr5   s         r!   __transformFlattenTransformer.__transform   s    s||! \\!_FG  M<<g'EFFLLG,<,<<!(()9)9:"G!((1 ( )LFr8   c                 $    U R                  U5      $ N_FlattenTransformer__transformrF   s     r!   transform_and FlattenTransformer.transform_and       $$r8   c                 $    U R                  U5      $ rY   rZ   rF   s     r!   transform_orFlattenTransformer.transform_or   r^   r8   c                 $    U R                  U5      $ rY   rZ   rF   s     r!   transform_followedby'FlattenTransformer.transform_followedby   r^   r8   rE   N)
r   rH   rI   rJ   rK   r[   r\   r`   rc   rL   rE   r8   r!   r   r      s    2%%%r8   r   c                   *    \ rS rSrSrS rS rS rSrg)r      zo
Order AND/OR expressions, and dedupe ORs.  E.g.:

    A or A => A
    B or A => A or B
    B and A => A and B
c                 p   [        UR                  [        R                  " [        5      S9nUR
                  S:X  aL  [        R                  " U[        R                  " [        5      S9 VVs/ s H  u  p4UR                  PM     nnnOUn[        UR                  U[        5      S:g  nXQl        X4$ s  snnf )N)keyORr   )
sortedr   	functools
cmp_to_keyr   rR   	itertoolsgroupbyobjr   )r1   r   sorted_childrenrh   r6   deduped_childrenr2   s          r!   rV   "OrderDedupeTransformer.__transform   s     LLi223MN

 <<4&/&7&7#)=)=2*' 'FC '     /LL*,F
 (|! s   -B2c                 $    U R                  U5      $ rY   "_OrderDedupeTransformer__transformrF   s     r!   r\   $OrderDedupeTransformer.transform_and  r^   r8   c                 $    U R                  U5      $ rY   rt   rF   s     r!   r`   #OrderDedupeTransformer.transform_or  r^   r8   rE   N)	r   rH   rI   rJ   rK   ru   r\   r`   rL   rE   r8   r!   r   r      s    2%%r8   r   c                   *    \ rS rSrSrS rS rS rSrg)r
   i  z
Applies boolean "absorption" rules for observation expressions, for AST
simplification:

    A or (A and B) = A
    A or (A followedby B) = A
    A or (B followedby A) = A

Other variants do not hold for observation expressions.
c                     [        U5      nSnU H.  n[        U5       H  u  pg[        XW5      S:X  d  M  X6	   M)     Sn  U$    U$ )a  
Determine whether the "containee" expressions are contained in the
"container" expressions, with AND semantics (order-independent but need
distinct bindings).  For example (with containee on left and container
on right):

    (A and A and B) or (A and B and C)

In the above, all of the lhs vars have a counterpart in the rhs, but
there are two A's on the left and only one on the right.  Therefore,
the right does not "contain" the left.  You would need two A's on the
right.

Args:
    exprs_containee: The expressions we want to check for containment
    exprs_container: The expressions acting as the "container"

Returns:
    True if the containee is contained in the container; False if not
Tr   F)listr,   r   )r1   exprs_containeeexprs_container	containerr    eer4   ers           r!   __is_contained_and(AbsorptionTransformer.__is_contained_and"  s[    0 )	!B"9--b5: " .  " r8   c                     [        U5      n[        U5      nSn [        US5      nU(       d   U$  [        US5      nU(       a  [        Xg5      S:X  a  OOOM'  U(       d  Sn U$ ML  )a  
Determine whether the "containee" expressions are contained in the
"container" expressions, with FOLLOWEDBY semantics (order-sensitive and
need distinct bindings).  For example (with containee on left and
container on right):

    (A followedby B) or (B followedby A)

In the above, all of the lhs vars have a counterpart in the rhs, but
the vars on the right are not in the same order.  Therefore, the right
does not "contain" the left.  The container vars don't have to be
contiguous though.  E.g. in:

    (A followedby B) or (D followedby A followedby C followedby B)

in the container (rhs), B follows A, so it "contains" the lhs even
though there is other stuff mixed in.

Args:
    exprs_containee: The expressions we want to check for containment
    exprs_container: The expressions acting as the "container"

Returns:
    True if the containee is contained in the container; False if not
TNr   F)iternextr   )r1   r|   r}   ee_iterer_iterr    r   r   s           r!   __is_contained_followedby/AbsorptionTransformer.__is_contained_followedbyK  s    6 ''gt$B  '4(1"9Q> ?   # r8   c                    Sn[        5       n[        UR                  5       GH2  u  pEXC;   a  M  [        U[        5      (       a  M$  [        UR                  5       H  u  pgXF:X  d  Xc;   a  M  [        U[
        [        45      (       d  M.  [        XWR                  [        5      (       a  UR                  U5        M`  [        U5      [        U5      L d  My  [        U[
        5      (       a'  U R                  UR                  UR                  5      nO&U R                  UR                  UR                  5      nU(       d  M  UR                  U5        M     GM5     U(       a*  Sn[        [        U5      5       H  nUR                  U	 M     X4$ )NFT)setr,   r   r   r   r   r   r   r   addr   (_AbsorptionTransformer__is_contained_and/_AbsorptionTransformer__is_contained_followedbyreversedrj   )	r1   r   r2   	to_deleter4   child1jchild2can_simplifys	            r!   r`   "AbsorptionTransformer.transform_or}  sD   E	"3<<0IA~ &"@AA&s||4	6Q^07  1K  "a( ff5%f.FGG+/+B+B &,L ,0+I+I &,L (<%MM!,; 5 1N GfY/0LLO 1 |r8   rE   N)	r   rH   rI   rJ   rK   r   r   r`   rL   rE   r8   r!   r
   r
     s    	'R0d0r8   r
   c                   *    \ rS rSrSrS rS rS rSrg)r   i  a  
Transform an observation expression to DNF.  This will distribute AND and
FOLLOWEDBY over OR:

    A and (B or C) => (A and B) or (A and C)
    A followedby (B or C) => (A followedby B) or (A followedby C)
    (A or B) followedby C => (A followedby C) or (B followedby C)
c                 R   [        S UR                   5       5      (       a  / nUR                   HG  n[        U[        5      (       a  UR	                  UR                  5        M5  UR	                  U45        MI     [        U5      n[        R                  " U6  VVs/ s H9  nU" [        R                  " U5       Vs/ s H  n[        U5      PM     sn5      PM;     nnnU Vs/ s H  o0R                  U5      S   PM     nn[        U5      nSn	X4$ UnSn	X4$ s  snf s  snnf s  snf )Nc              3   B   #    U  H  n[        U[        5      v   M     g 7frY   )r   r   ).0r   s     r!   	<genexpr>-DNFTransformer.__transform.<locals>.<genexpr>  s"      
% u566%s   r   TF)anyr   r   r   rT   r   rm   productchainr   r-   )
r1   r   	iterablesr   	root_typeprod_seqsub_astdistributed_childrenr    r2   s
             r!   rV   DNFTransformer.__transform  s@     

 
 
 Ie%<==$$U^^4$$eX.	 & S	I !* 1 19 =$ !>H 6?oo 777Ig& 7 
 !> ! $ 7K$6JUu%a(6J ! $ --ABFG  FG+$$s    D<DD#D$Dc                 $    U R                  U5      $ rY   _DNFTransformer__transformrF   s     r!   r\   DNFTransformer.transform_and  r^   r8   c                 $    U R                  U5      $ rY   r   rF   s     r!   rc   #DNFTransformer.transform_followedby  r^   r8   rE   N)	r   rH   rI   rJ   rK   r   r\   rc   rL   rE   r8   r!   r   r     s    )V%%r8   r   c                   $    \ rS rSrSrS rS rSrg))NormalizeComparisonExpressionsTransformeri  z'
Normalize all comparison expressions.
c                     [        5       n[        5       n[        5       n[        XU5      n[	        U5      n[        5       n[        5       n[        XeXu5      U l        g rY   )CFlattenTransformerCOrderDedupeTransformerCAbsorptionTransformerr   r   r	   CDNFTransformer:_NormalizeComparisonExpressionsTransformer__comp_normalize)r1   comp_flatten
comp_ordercomp_absorbsimplifysettle_simplifycomp_specialcomp_dnfs           r!   __init__2NormalizeComparisonExpressionsTransformer.__init__  sR    *,,.
,.#LkJ+H535"$ 08!
r8   c                 f    UR                   nU R                  R                  U5      u  p4X1l         X4$ rY   )r5   r   r-   )r1   r   	comp_exprnorm_comp_exprr2   s        r!   transform_observation?NormalizeComparisonExpressionsTransformer.transform_observation  s1    KK	"&"7"7"A"A)"L$|r8   )__comp_normalizeN)r   rH   rI   rJ   rK   r   r   rL   rE   r8   r!   r   r     s    
r8   r   )!rK   rk   rm   !stix2.equivalence.pattern.comparer   r   -stix2.equivalence.pattern.compare.observationr   #stix2.equivalence.pattern.transformr   r   r   .stix2.equivalence.pattern.transform.comparisonr	   r
   r   r   r   r   r   r   r   stix2.patternsr   r   r   r   r   r   r   r   r#   r   rE   r8   r!   <module>r      s      C 4&.6  *Zl{ l^*%9 *%Z(%$(%VY$Yx9%5 9%x$r8   