/**
 * Module containing the strategies used to post-process a 
 * parsed PHP-source. 
 * Post-processing includes:
 *   - Rewriting HereDoc an DoubleQuoted-parts to lists
 *
 * @author Eric Bouwers
 */
module php/parse/postprocess

strategies
  /**
   * Rewrites all the structures of DQ- and HereDoc strings to lists.
   * This is needed because a follow-restriction cannot prevent ambiguity
   */
   post-process-structures = 
        bottomup(try( content-to-list
                   <+ fix-heredoc 
                    )
                )
      ; bottomup(try(check-heredoc)) // needs seperate phase, incorrect heredoc can exists intermediately

strategies

  /**
   * Fixes the ambigious HereDoc when multiple HereDocs occur in a program.
   */
  fix-heredoc =
     ?amb(lst) 
   ; lst' := <map(fix-and-length)> lst
   ; list-accum(tup-min)

  fix-and-length = 
      try(fix-conc)
    ; !(<length>,<id>) 

  fix-conc = 
     ?Conc(a,b)
   ; <conc>  (<try(fix-heredoc)> a,<try(fix-heredoc)> b)

  tup-min = 
    ?((l1,xs1),(l2,xs2))
  ; if <gt> (l1,l2)
    then !xs1 
    else !xs2 
    end 

strategies
  
  /**
   * Checks whether the HereDoc start-tag is equal to the end-tag.
   * We can just check if it ends-with the start-tag because otherwise
   * the HereDoc would not parse.
   *
   * @type HereDoc(_,_,_) -> exit
   */
  check-heredoc = 
     ?HereDoc(HereDocStart(starttag),_,endtag)
   ; <not(string-ends-with(|starttag))> endtag
   ; !(starttag,endtag)
   ; debug(!"There exist a HereDoc without matching start- and endtag.\n")
   ; <exit> 1

rules
  /**
   * Transforms the content of a DoubleQuoted string into a
   * list.
   * TODO: Integrate both HereDoc- and DQ-content
   *
   * @type DQContent() -> List()
   */
  content-to-list :
    DQContent(lit) -> list
      where list := <make-list> lit

  content-to-list :
    HereDocContent(lit) -> list
      where list := <make-list> lit

  content-to-list :
    DQContent(lit1,esc,lit2) -> list
      where lit1' := <make-list> lit1
          ; lit2' := <make-list> lit2
          ; esc'  := <maybe-wrap-in-list> esc
          ; list  := <conc> (lit1',esc',lit2')

  content-to-list :
    HereDocContent(lit1,esc,lit2) -> list
      where lit1' := <make-list> lit1
          ; lit2' := <make-list> lit2
          ; esc' := <maybe-wrap-in-list> esc
          ; list  := <conc> (lit1',esc',lit2')

  content-to-list :
   DQContent(esc1,lit,esc2) -> list
      where lit'  := <make-list> lit
          ; esc1' := <maybe-wrap-in-list> esc1
          ; esc2' := <maybe-wrap-in-list> esc2
          ; list  := <conc> (esc1',lit',esc2')

  content-to-list :
   HereDocContent(esc1,lit,esc2) -> list
      where lit'  := <make-list> lit
          ; esc1' := <maybe-wrap-in-list> esc1
          ; esc2' := <maybe-wrap-in-list> esc2
          ; list  := <conc> (esc1',lit',esc2')

  /**
   * Utility function to transform a (possible transformed)
   *
   */
  make-list:
   Some(s) -> [Literal(s)]

  make-list:
   None() -> []

  make-list:
   [x | xs ] -> [x | xs]

  make-list:
   []  -> []

strategies
  /**
   * Wraps a certain term in a list when it is not a list.
   * @type _ -> List(_)
   */
  maybe-wrap-in-list= ?esc
    ; if <?[x | xs]> esc
      then esc' := esc
      else esc' := [esc]
      end