/**
 * Simple common strategies for PHP
 *
 * @author Eric Bouwers
 */
module php/strategy/simple

strategies
 /**
  * Topdown without going into 
  * class- or function-declerations.
  *
  * @param s The strategy to be applied to each term
  */
 procedural-topdown(s) =
  generic-procedural-topdown(s,procedural-topdown(s))
 
 /**
  * Generic version of prodecural topdown
  *
  * @param s1 The strategy to be applied to each term
  * @param s2 The strategy to be called when going into children
  */
 generic-procedural-topdown(s1,s2) =
    ?t; where(<not(isClassDecl)> t)
      ; where(<not(isFunctionDecl)> t)
      ; where(<not(isOldFunctionDecl)> t)
      ; where(<not(isInterfaceDecl)> t)
      ; s1 
      ; all(s2)

 generic-procedural-topdown(s1,s2) =
    ?t; (  <isClassDecl>    t
	    <+ <isFunctionDecl> t
	    <+ <isOldFunctionDecl> t
	    <+ <isInterfaceDecl> t
        ) 
    ;!t

strategies
  /**
   * Counting occurences of everything Const that is in the given strategy,
   * without going into function- or class- or interface-definitions
   */
  procedural-occurrences(s) = 
    ?t; <not(isRequireOrInclude)> t
      ; procedural-occurrences-worker(s)
 
  procedural-occurrences(s) =
    ?t{a*}; <isRequireOrInclude> t{a*}
      ;((env := <get-php-environment>
        ; inclusionId := <get-inclusion-id> t{a*}
        ; ast := <get-inclusion-file(|inclusionId)> env
        ; num := <procedural-occurrences(s)> ast
        )
        <+ 
          num := 0
       )
     ; <add> (num,<procedural-occurrences-worker(s)> t)     

  procedural-occurrences-worker(s) = 
    ?t ; where(<not(isClassDecl)> t)
       ; where(<not(isFunctionDecl)> t) 
       ; where(<not(isOldFunctionDecl)> t)
       ; where(<not(isInterfaceDecl)> t)
       ; <add> ( <if s then !1 else !0 end>
               , <crush(!0, add, procedural-occurrences(s))>
               )
  
  procedural-occurrences-worker(s) = 
    ?t; (  <isClassDecl>    t
	    <+ <isFunctionDecl> t
	    <+ <isOldFunctionDecl> t
	    <+ <isInterfaceDecl> t
        )
    ;!0
 

strategies
  /**
   * Topdown with going into inclusion. This will try to extract
   * the AST from the enivironment and apply the strategy to it.
   * Stores the new AST in the environment.
   * Note that is tries to extract the AST. If there are no inclusions made this
   * strategy will behave like topdown.
   *
   * Note: This strategy does _NOT_ take the '_once' into account.
   */
  topdown-with-inclusion(s) =
    ?t; <not(isRequireOrInclude)> t
      ; s
      ; all(topdown-with-inclusion(s))

  topdown-with-inclusion(s) =
    ?t{a*}; <isRequireOrInclude> t{a*}
      ;where(try( env := <get-php-environment>
                ; inclusionId := <get-inclusion-id> t{a*}
                ; ast := <get-inclusion-file(|inclusionId)> env
                ; log(|Debug(), "Start topdown with inclusion into id: ", inclusionId)
                ; ast':= <topdown-with-inclusion(s)> ast
                ; <add-inclusion-file(|ast',inclusionId)> env
                ; set-php-environment(|<id>)
                ; log(|Debug(), "Finish topdown with inclusion into id: ", inclusionId)
                )
             )
      ; s
      ; all(topdown-with-inclusion(s))


strategies
  /**
   * Topdown with going into inclusion. This will try to extract
   * the AST from the enivironment and apply the strategy to it.
   * Stores the new AST in the environment. 
   * This strategy will not go into class- of function-declerations.
   *
   * Note: This strategy does _NOT_  take the '_once' into account.
   */
  procedural-topdown-with-inclusion(s) =
    ?t; <not(isRequireOrInclude)> t
      ; procedural-topdown-with-inclusion-worker(s) 
  
  procedural-topdown-with-inclusion(s) =
    ?t{a*}; <isRequireOrInclude> t{a*}
      ;where(try( env := <get-php-environment>
                ; inclusionId := <get-inclusion-id> t{a*}
                ; ast := <get-inclusion-file(|inclusionId)> env
                ; log(|Debug(), "Start procedural topdown with inclusion into id: ", inclusionId)
                ; ast':= <procedural-topdown-with-inclusion(s)> ast
                ; <add-inclusion-file(|ast',inclusionId)> env
                ; set-php-environment(|<id>)
                ; log(|Debug(), "Finish procedural topdown with inclusion into id: ", inclusionId)
                )
             )
      ; procedural-topdown-with-inclusion-worker(s) 
  
  procedural-topdown-with-inclusion-worker(s) =
   generic-procedural-topdown(s,procedural-topdown-with-inclusion(s))

strategies
  /**
   * Collects all the terms that succeed a given strategy. Is compareable
   * with the normal collect, but this strategy goes into included files when
   * they are found.
   *
   * @param s The strategy to try on the terms
   * @type a -> List(b) 
   */
   php-collect-inclusion(s) = 
    ?t; <not(isRequireOrInclude)> t
      ;(    ![<s> | <crush(![],union,php-collect-inclusion(s))>]
         <+ crush(![],union,php-collect-inclusion(s))
       )

   php-collect-inclusion(s) = 
    ?t{a*}; <isRequireOrInclude> t{a*}
    ; if ( env := <get-php-environment>
         ; inclusionId := <get-inclusion-id> t{a*}
         ; ast := <get-inclusion-file(|inclusionId)> env
         )
      then extra := <php-collect-inclusion(s)> ast  //only if a file is actually included
      else extra := []
      end        
    ; !t{a*} //Needs to be build to preserve current term
    ; (    ( ![<s> | <crush(![],union,php-collect-inclusion(s))>] ; ?normal)
      <+  ( crush(![],union,php-collect-inclusion(s)) ; ?normal)
     )
    ; <union> (extra,normal)

strategies
 /**
  * Different match functions for different constructors
  */
 isClassDecl =
    ?Class(_,_,_,_)
 <+ ?Class(_,_,_,_,_)

 isFunctionDecl = 
    ?FunctionDecl(_,_,_,_)
 <+ ?FunctionDecl(_,_,_)
 
 isOldFunctionDecl = 
    ?OldFunctionDecl(_,_,_)

 isInterfaceDecl = 
  ?InterfaceDecl(_,_,_)

 isRequireOrInclude=
     ?InternalFunction(Require(_))
  <+ ?InternalFunction(RequireOnce(_))
  <+ ?InternalFunction(Include(_))
  <+ ?InternalFunction(IncludeOnce(_))