/**
 * Evaluation of statements in PHP.
 *
 * @author Eric Bouwers
 */
module php/strategy/evaluation/statements

strategies

  /**
   * Echo, map evaluation over all expressions in list
   *
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
    Echo(map(main))
  
  /**
   * The Declare-construct
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
    Declare(map(main),main) 

// IF
strategies
  /**
   * If-statement without else or else-if.
   * Condition is always evaluated, block is only checked.
   *
   * @type If(cond,stats) -> If(cond,stats)
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
     If(main, id)
   ; maybe-inclusion-wrap(
             rule-union(id
                       ,If(id, main)
                       )
            )
     
  /**
   * Evaluation of alternative If
   * @type AltIf(_,_) -> AltIf(_,_)
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
     ?AltIf(cond,ifbranch)
   ; If(cond',Block(ifbranch')) := <main> If(cond,Block(ifbranch))
   ; !AltIf(cond',ifbranch')

  /**
   * If-statement with else or else-if.
   * Condition is always evaluated, block is only checked.
   *
   * @type If(cond,stats,List(ElseIf)) -> If(cond,stats,List(ElseIf))
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      If(main, id, id)   //always tries first if
   ; ?If(cond, ifbranch, ifelses)
   ; maybe-inclusion-wrap(
          (ifbranch',ifelses') := <eval-php-cases(php-evaluate-elseif(main|),main,rule-union|ifbranch)> ifelses
     )
   ; !If(cond, ifbranch', ifelses')

  /**
   * Evaluation of alternative If
   * @type AltIf(_,_,_) -> AltIf(_,_,_)
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
     ?AltIf(cond,ifbranch,elseifs)
   ; If(cond',Block(ifbranch'),Block(elseifs')) := 
             <main> If(cond,Block(ifbranch),elseifs)
   ; !AltIf(cond',ifbranch',elseifs')


  /**
   * If-statement with else and no else-ifs.
   * Condition is always evaluated, block is only checked.
   *
   * @type If(cond,stats,List(ElseIf)) -> If(cond,stats,List(ElseIf))
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
     ?If(_, _, [], _)                        // basic if-else
   ; If(main, id, id, id)   //always tries first if
   ; maybe-inclusion-wrap(
             rule-union(If(id, main,id,id) 
                        ,If(id, id, id,main)
                        )
             )
  
  /**
   * If-statement with else and else-ifs.
   * Condition is always evaluated, block is only checked.
   *
   * @type If(cond,stats,List(ElseIf)) -> If(cond,stats,List(ElseIf))
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
     not(?If(_, _, [], _))                   // no empty else-ifs
   ; If(main, id, id, id)   //always tries first if
   ; ?If(cond, ifbranch, ifelses,ifelse)
   ; maybe-inclusion-wrap(
             rule-union(ifelse' := <main> ifelse
                       ,(ifbranch',ifelses') := 
                             <eval-php-cases(php-evaluate-elseif(main|),main,rule-union|ifbranch)> ifelses
                       )
             )
   ; !If(cond, ifbranch', ifelses',ifelse')

  /**
   * Evaluation of alternative If
   * @type AltIf(_,_,_,_) -> AltIf(_,_,_,_)
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
     ?AltIf(cond,ifbranch,elseifs,elsebranch)
   ; If(cond',ifbranch',elseifs',elsebranch') := 
          <main> If(cond,Block(ifbranch),elseifs,Block(elsebranch))
   ; !AltIf(cond',ifbranch',elseifs',elsebranch')


  /**
   * Evaluates a single ElseIf statement
   * @type ElseIf() -> ElseIf()
   */
  php-evaluate-elseif(main|) = 
     ElseIf(main,main)
  <+ AltElseIf(main,map(main))

strategies
  /**
   * Evaluates a list of statements. It does this by a
   * foldr in which each strategy splits and intersects a list of dynamic rules.
   * This makes the ruleset complete for all different branches.
   *
   * @s     Strategy Term -> Term The strategy to apply to the elements
   * @sunit Strategy Term -> Term The strategy to apply to the unit element
   * @param unit Statement The unit-statement for the intersection with the last
   *                       statement
   * @type List(Term) -> (Unit,List(Term))
   */
  eval-php-cases(s,sunit,rule-union|unit) =
   (  ( ?[x] 
      ; rule-union(x' := <s> x 
                  , unit' := <sunit> unit 
                  ) 
      ; !(unit',[x']) 
      )
      <+
      ( ?[y|ys]
      ; rule-union( y'          := <s> y
                  , (unit',ys') :=  <eval-php-cases(s,sunit,rule-union|unit)> ys
               )
      ; !(unit',<conc>([y'],ys'))
      )
   ) 

//SWITCH
strategies
  /**
   * Evaluates a Switch statement.
   * Almost the same semantics as an if-statement
   * @type Switch() -> Switch
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
     Switch(main,id)
   ; ?Switch(expr,cases) 
   ;((where(<leq> (<length> cases,1))
     ; maybe-inclusion-wrap(
          rule-union(id
                    ,Switch(id, map(main))
                    )
       )
     )
    <+
     ( <?[x|xs]> cases
     ; maybe-inclusion-wrap(
         (x',xs') := <eval-php-cases(php-evaluate-case(main|),php-evaluate-case(main|),rule-union|x)> xs
       )
     ; !Switch(expr,<conc>([x'],xs'))
     )
    <+ 
     ( !"Handling of cases in the Switch has failed." 
     ; debug(!"Error code 001: "))
     )

  /**
   * Evaluates an alternative Switch statement.
   * Almost the same semantics as an if-statement
   * @type AltSwitch() -> AltSwitch
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?AltSwitch(cond, cases)
    ; Switch(cond',cases') := <main> Switch(cond, cases)
    ; !AltSwitch(cond', cases')

  /**
   * Evaluates a case statement
   */
  php-evaluate-case(main|) =
       Case(main,id,map(main))
    <+ DefaultCase(id,map(main))

// WHILE
strategies
  /**
   * Evaluates a while. Condition is evaluated atleast once
   * File within a while might be included, but nothing is sure.
   * 
   * @type While() -> While()
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?While(_, _)
    ; While(main,id)  
    ; maybe-inclusion-wrap(
        rule-intersect(While(id,main))
        )
      

  /**
   * Evaluates an alternative while.
   * Uses the normal intersect and iterate syntax
   * @type AltWhile() -> AltWhile()
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?AltWhile(cond, stats)
    ; While(cond',Block(stats')) := <main> While(cond, Block(stats))
    ; !AltWhile(cond', stats')

// DOWHILE
strategies
  /**
   * Evaluates a dowhile.
   * DoWhile always takes 1 iteration for sure. After this it 
   * acts like a normal while.
   *
   * @type DoWhile() -> DoWhile()
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?DoWhile(_, _)
    ; DoWhile(main, main)
    ; maybe-inclusion-wrap(
       rule-intersect(
          DoWhile(main, main)
        )
      )
    
// FOR
strategies
  /**
   * Evaluates a for.
   * Is almost like the normal while. Initialization and test are performed
   * atleast once while the rest is performed on every iteration.
   *
   * @type For() -> For()
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?For(_, _, _, _)
    ; For(map(main), id, id, id) 
    ; For(id,map(main), id, id) 
    ; maybe-inclusion-wrap(
         rule-intersect(
            For(id,
              map(main),
              map(main),
              main)
       )
     )
      
  /**
   * Evaluates an alternative for.
   * Uses the normal intersect and iterate syntax
   * @type AltFor() -> AltFor()
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?AltFor(cond1, cond2, cond3, stats)
    ; For(cond1', cond2', cond3', Block(stats')) := 
         <main> For(cond1, cond2, cond3, Block(stats))
    ; !AltFor(cond1', cond2', cond3', stats')

// FOREACH
strategies
  /**
   * Evaluates a foreach.
   * Foreach is just a while with some extra initializations.
   *
   * @type ForEach() -> ForEach()
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?ForEach(_, _, _)
    ; maybe-inclusion-wrap(
        rule-intersect(
          ForEach(main,
                  id,                      // pattern needs no evaluation
                  main)
       )
      )
     
  /**
   * Evaluates an alternative for.
   * Uses the normal intersect and iterate syntax
   * @type AltForEach() -> AltForEach()
   */
  statement-const-prop(main,rule-union,rule-intersect|) =
      ?AltForEach(expr, pattern, stats)
    ; ForEach(expr', pattern', Block(stats')) := <main> ForEach(expr, pattern, Block(stats))
    ; !AltForEach(expr', pattern', stats')

// Control flow statements
strategies
  statement-const-prop(main,rule-union,rule-intersect|) = 
    Break(id)