/**
 * Constant propogation for variables in PHP. 
 */
module php/strategy/evaluation/variables

/***********************************************************************\
                 Start Variable Handling
\***********************************************************************/
strategies  
  /**
   * Variable-construct
   */
  variable-const-prop(main,set,get,get-valid) =
      ?Variable(var)
    ; where ( name  := <main ; php-get-raw-variable-name> var )
    ; try( add-php-simple-value(|<PHPSuperGlobalValue <+ php-variable-value> name))
    ; try( set(|<php-variable-value(get-valid)> name))

  /**
   * ConstantVariable-construct 
   */
  variable-const-prop(main,set,get,get-valid) =
      ?ConstantVariable(name) 
    ; try(add-php-simple-value(|<php-constant-get-value> name))
    ; try(set(|<php-constant-get-value(get-valid)> name))

  /**
   * @type IndirectReference(_) -> PHPSimpleValue
   */
  variable-const-prop(main,set,get,get-valid) =
      ?IndirectReference(expr)
    ; where( expr'    := <main> expr
           ; varname  := <get-simple-raw-string-value> expr'
           ; evalExpr := <main> Variable(Simple(varname))
           )
    ; try(add-php-simple-value(|<get-php-simple-value> evalExpr))
    ; try(set(|<get> evalExpr))

/***********************************************************************\
                 End Variable Handling
\***********************************************************************/

/***********************************************************************\
                 Start ArrayAccess Handling
\***********************************************************************/
strategies  
  /**
   * General array acces on an array.  
   * @type ArrayAccess(type:PHPArray,expr) -> value
   */
  variable-const-prop(main,set,get,get-valid) =
     variable-const-prop(main,set,get,get-valid
                        ,array-access-on-array // this one incorperates const-prop itself
                        ,id
                        ,id
                        )

  variable-const-prop(main,set,get,get-valid
                     ,array-value
                     ,string-value
                     ,other-value) =
    (  ArrayAccess(main; ?array,main) 
    <+ StringAccess(main; ?array,main) 
    )
    ;(( where( <has-php-array-value> array)
      ; array-value(main,get,set,get-valid)
      )
     <+
      ( where( <has-php-string-value> array)
      ; array-access-on-string
      ; string-value 
      )
     <+
      ( where( <has-php-boolean-value> array 
            <+ <has-php-integer-value> array
            <+ <has-php-float-value> array
            <+ <has-php-null-value> array
       )
      ; array-access-on-other
      ; other-value 
      ))
  
   /**
    * Special case for the GLOBALS array. This array holds all variables, so
    * we try to extract the value of that variable first. If this fails then we
    * try the integer value as a key.
    */
  array-access-on-array(main,get,set,valid-get) =
      ?ArrayAccess(array,Some(expr)) 
    ; where( aid   := <get-simple-array-value> array)
    ;(( where( <is-globals-array> aid
             ; strkey    := <get-simple-raw-string-value> expr
             ; <explode-string; ?[<is-alpha> | _ ]> strkey 
             ; evalExpr  := <main> Variable(Simple(strkey))
       )
      ; ( try(add-php-simple-value(|<get-php-simple-value> evalExpr))
        ; try(set(|<get> evalExpr))
        )
      )
      <+
      ( try(add-php-simple-value(|<generic-arrayaccess-value> (aid,expr))) 
      ; try(set(|<generic-arrayaccess-value(valid-get)> (aid,expr))) 
      ))
             
  
  array-access-on-string =
    (  ?StringAccess(array,expr) 
    <+ ?ArrayAccess(array,Some(expr))
    )
    ; where( str   := <get-simple-raw-string-value> array 
           ; num   := <get-simple-raw-integer-value> expr
           ; if <gt> (num, < string-length> str)
             then value := ""
             else  <explode-string>str
                 ; at-index( ?ch | num)
                 ; value := <implode-string> [ch]
             end
           )  
    ; add-php-simple-value(|PHPString(value))

  array-access-on-other =
    ?ArrayAccess(val,_) 
   ;(  <has-php-boolean-value> val
    <+ <has-php-integer-value> val
    <+ <has-php-float-value> val
    <+ <has-php-null-value> val
    )
   ; add-php-simple-value(|PHPNull())
  
  /**
   * Generic array-acces for botht the special case for GLOBALS and the
   * normal case.
   */
  generic-arrayaccess-value = 
    generic-arrayaccess-value(php-val-id-get-rule)

  generic-arrayaccess-value(valid-get) = 
      ?(aid,expr) 
    ; where (( strkey    := <get-simple-raw-string-value> expr
             ; value     := <get-array-entry(valid-get|strkey)> aid
             )
             <+
             ( intkey    := <get-simple-raw-integer-value> expr
             ; value     := <get-array-entry(valid-get|intkey)> aid       
             )
            )
    ; !value


  /** 
   * Checks wheter or not the ArrayId matches that of the GLOBALS-array
   * @type PHPArray() -> _
   */
  is-globals-array=
    ?PHPArray("superglobal_GLOBALS")
  
/***********************************************************************\
                 End ArrayAccess Handling
\***********************************************************************/

/***********************************************************************\
                 Begin Rules for Variables
\***********************************************************************/
  /**
   * Adds a rule that maps a variable to a value.
   *
   * @type (name,value) -> (name,value)
   */
  add-php-variable-rule =
    add-php-variable-rule(php-val-id-add-rule)

  add-php-variable-rule(valid-put) =
     ?(Variable(var),value)
   ; name             := <php-get-raw-variable-name> var
   ; value-identifier := <get-php-value-identifier> var
   ; rules(EvalPHPVar  : name -> value-identifier)     //extra pointer to make implementation
   ; valid-put(|value-identifier,value)               //of references easy

 /** 
  * Adds a mapping from a variable to a value-identifier.
  *
  * @type (name,value) -> (name,value)
  */
 add-php-variable-identifier-rule = 
     ?(Variable(var),value-identifier)
   ; name := <php-get-raw-variable-name> var
   ; rules(EvalPHPVar: name -> value-identifier)

  /**
   * Get's the real value of an variable. This means applying the strategy
   * EvalPHPVar twice. First getting the pointer to the value and then getting
   * the real value.
   *
   * @type String -> PHPSimpleValue
   */
  php-variable-value =
    php-variable-value(php-val-id-get-rule)

  php-variable-value(s) =  
    EvalPHPVar;s

  /**
   * Makes a new identifier that is unique for each variable.
   * Reuses an old identifier if it exists, otherwise it uses the
   * name of the variable, which should be unique in the program.
   *
   * @type name -> String
   */
  get-php-value-identifier =
    ( php-get-raw-variable-name
    ; EvalPHPVar
    )
    <+                 // no old for variable
    ( EvalPHPArray
    )
    <+                 // no old for array
    (php-get-raw-variable-name
    )
    <+                 // no name, try array
    {?(aid,key)
    ; if <is-int> key
      then key' := <int-to-string> key
      else key' := key
      end
    ; <?PHPArray(aid')> aid
    ; <conc-strings> (aid',key')
    }

  get-php-value-identifier-pair =
    get-php-value-identifier-pair(php-val-id-get-rule)

  get-php-value-identifier-pair(valid-get) =
      ?var 
    ; identifier := <get-php-value-identifier> var
    ; value      := <valid-get> identifier
    ; !(identifier,value)


  /**
   * Extracts the name of a variable. This strategy handles
   * all the cases that can contain a variable name.
   *
   * @type Simple(_) OR Braced() -> String
   */
  php-get-raw-variable-name =
    (  ?Braced(Simple(name))
    <+ ?Simple(name)
    <+ ( ?Braced(expr)
       ; name  := <get-simple-raw-string-value> expr
       )
    )
    ; <rm-annotations> name

  /**
   * Removes the rule that maps a variable to a value.
   *
   * @type name -> name
   */
  remove-php-variable-rule =
    remove-php-variable-rule(php-val-id-remove-rule)

  remove-php-variable-rule(valid-remove) =
     ?Variable(var)
   ; name               := <php-get-raw-variable-name> var
   ; value-identifier   := <get-php-value-identifier> var
   ; valid-remove(|value-identifier)   // also remove mapping to value.
   ; rules(EvalPHPVar   :- name)               // unknown RHS also invalidates all aliases

   /**
    * Adds a dynamic rule for a PHP constant.
    *
    * @type (String,PHPValue) -> _
    */
   add-php-constant-rule =
     add-php-constant-rule(php-constant-put-value)
      
   add-php-constant-rule(put-valid) =
     ?(name,value)
    ; value_identifier := <newname> "php_constant_"
    ; rules(EvalPHPConstant: name -> value_identifier)
    ; put-valid(|value_identifier,value) 

   php-constant-get-value  = 
     php-constant-get-value(EvalPHPValId)

   php-constant-get-value(get-valid) =
      EvalPHPConstant ; get-valid
   
   php-constant-put-value(|ident,val)  = 
     rules(EvalPHPValId: ident -> val)
   
   php-val-id-remove-rule(|key) = 
     rules(EvalPHPValId :- key)  
   
   php-val-id-add-rule(|key,val) = 
     rules(EvalPHPValId : key -> val)  

   php-val-id-get-rule = 
     EvalPHPValId 
      
rules
  /**
   * Rules that rewrite the value of a superglobal to an 
   * array-type.
   *
   * @type: String -> PHPArray(_)
   */
   PHPSuperGlobalValue:
      "_GET" -> PHPArray("superglobal_GET")

   PHPSuperGlobalValue:
      "_POST" -> PHPArray("superglobal_POST")

   PHPSuperGlobalValue:
      "_REQUEST" -> PHPArray("superglobal_REQUEST")

   PHPSuperGlobalValue:
      "_SERVER" -> PHPArray("superglobal_SERVER")

   PHPSuperGlobalValue:
      "_FILES" -> PHPArray("superglobal_FILES")

   PHPSuperGlobalValue:
      "_ENV" -> PHPArray("superglobal_ENV")

   PHPSuperGlobalValue:
      "_COOKIE" -> PHPArray("superglobal_COOKIE")

   PHPSuperGlobalValue:
      "GLOBALS" -> PHPArray("superglobal_GLOBALS")

   PHPSuperGlobalValue:
      "_SESSION" -> PHPArray("superglobal_SESSION")
      
/***********************************************************************\
                 End Rules for Variables
\***********************************************************************/