/**
 * reflect/files main-file
 *
 * Strategies for including and requiring files as in
 * PHP. It adds the ATerms of the files to the environment.
 *
 * @author Eric Bouwers
 */
module php/reflect/inclusion/main
imports 
  php/reflect/inclusion/path
  php/reflect/inclusion/currentdir
  php/reflect/inclusion/workingdir
  php/reflect/inclusion/inclusionid
 
strategies
  //main strategy
  include-and-require-files =
     procedural-topdown(try(process-include-and-require-files(include-and-require-files)))

rules
  /**
   * Process and inclusion of files. This rule finds the statements
   * that should be processed.
   * Note that this is a simple strategy. It includes all files
   * that are directly accessiable to the environment. For a more
   * enhanced version see the analysis for constant propogation.
   */
  process-include-and-require-files(s):
    t@InternalFunction(Include(expr)) -> t'
      where  includeid := <process-include(s)> expr
           ; t' := <add-inclusion-id(|includeid)> t

  process-include-and-require-files(s):
    t@InternalFunction(Require(expr)) -> t'
      where  requireid := <process-include(s)> expr
           ; t' := <add-inclusion-id(|requireid)> t

  process-include-and-require-files(s):
    t@InternalFunction(IncludeOnce(expr)) -> t'
      where  includeid := <process-include(s)> expr
           ; t' := <add-inclusion-id(|includeid)> t

  process-include-and-require-files(s):
    t@InternalFunction(RequireOnce(expr)) -> t'
      where  requireid := <process-include(s)> expr
           ; t' := <add-inclusion-id(|requireid)> t

rules
  /**
   * Two special functions that we need to handle.
   *  - chdir:   changes working directory
   *  - ini-set: sets the include path if first argument is 'include_path'
   */
  process-include-and-require-files(s):
   t@FunctionCall(name,params) -> t
      where <php-handle-function-call> (name,<map(main-analyse-const-prop)> params)

rules
  /**
   * The real inclusion. The first two rules only work on direct single- or doublequoted
   * strings with nothing fancy. Other rules can be added that take care of other
   * scenario's.
   */
  process-include(s):
    ConstantEncapsedString(SingleQuoted([Literal(filename)])) -> includeid
      where includeid := <process-inclusion(s)> filename

  process-include(s):
    ConstantEncapsedString(DoubleQuoted([Literal(filename)])) -> includeid
      where includeid := <process-inclusion(s)> filename
           
strategies
  /**
   * Processes the inclusion of a filename. The strategy has
   * the same semantics as within PHP. The file is looked-up within
   * the current directory. When this fails and the filename is not relative
   * the file is looked up within the current directory.
   *
   * The strategy 's' is applied to the newly parsed AST after which the
   * new AST is added to the current environment.
   *
   * precondition: There must be an environment
   *
   * @param s: AST -> AST
   * @type  filename::String -> Inclusionid
   */
  process-inclusion(s) = ?arg;
      file := <find-php-file> arg
    ; where( file' := <get-current-php-file>   
        ; <set-current-php-file> file
        )
    ; <include-php-file(s)> file
    ; where(
       <set-current-php-file> file'
    )

  /**
   * Real inclusion of a filename and a strategy that must be applied to
   * the included and parsed file.
   *
   * @param s: AST -> AST
   * @type  filename::String -> Inclusionid
   */
  include-php-file(s) =
      ?file
    ; newcurdir  := <?FILE(curdirpath); !curdirpath; dirname > file
    ; log(|Debug(), "Start including new file: ", curdirpath)
    ; includeid  := <new-inclusion-id> curdirpath
    ; get-php-environment ; add-inclusion-filename(|curdirpath,includeid)
    ; set-php-current-directory(|newcurdir)
    ; ast        := <parse-php> file
    ; ast'       := <s> ast
    ; prev-php-current-directory
    ; get-php-environment ; add-inclusion-file(|ast',includeid)
    ; !includeid
    ; log(|Debug(), "Finish including new file: ", curdirpath)

  /**
   * Takes a filename and searches for this filename in the same way that 
   * PHP looks for the files to include.
   *
   * @type  filename::String -> absolutepath::String
   */
  find-php-file =
   ?filename
   ; (  file       := <find-file-in-working-directory(|filename)>
     <+ if   <is-php-relative> filename
        then fail
        else file       := <find-file-in-current-directory(|filename)>
        end
     )
   ; !file

/**
 * Section to store current file being processed
 */  
 /**
  * Returnes the name of the file currently being processed
  */
 get-current-php-file = 
     <get-config> "current-php-file"
  <+ get-input-path 
 
 /**
  * Sets the name of the file currently being processed
  */
 set-current-php-file = 
   <set-config> ("current-php-file", <id>)