/**
 * Implosion of productions that result in constructor applications.
 *
 * An application of a context-free production is transformed
 * into an actual application of a constructor name to the list
 * of children. The Stratego primitive \verb|mkterm| takes a pair
 * of a string \verb|c| and a list of terms \verb|ts| and turns
 * it into the term \verb|c(ts)|. The constructor \verb|c| is
 * derived from the production. If it has a \verb|cons(c)|
 * attribute that attribute is taken. Otherwise the production
 * should be some derived production for regular expressions.
 */
module stratego/asfix/implode/constructor
strategies

  ReplCons(impl) =
       ReplNamedCons(impl)
    <+ ReplAstPattern(impl)
    <+ ReplConsNil(impl)
    <+ ReplConsNone(impl)
    <+ ReplConsIns(impl)
    <+ ReplConsSome(impl)
    <+ ReplConsConc(impl)

rules

  ReplNamedCons(impl) : 
    appl(p, ts){a*} -> c#(<map(impl)>ts){a*}
    where
      <get-cons> p => c

  get-cons : 
    prod(_, _, as) -> x
    where
      <oncetd(?cons(x))> as

rules

  /**
   * Handles the experimental ast annotations.
   */
  ReplAstPattern(impl) : 
    appl(p, ts){a*} -> term{c*}
    where
      <get-ast-pattern> p => pattern 
    ; <map(impl)>ts => is
    ; {| ReplacePlaceholder: 
        <map-with-index({ ?(i, t) ; rules( ReplacePlaceholder : i -> t ) })>is
      ; !pattern
      ; rec rec(
           alltd({ ph, t1, t2, a*, b*, c*:
             if is-placeholder then
               ?_{a*}
               ; get-placeholder => ph
               ; (  ReplacePlaceholder

                 <+ "conc"#([rec, rec])
                    ; if "conc"#([is-list, is-list]) then
                        \ "conc"#([xs, ys]) -> <conc> (xs, ys) \
                      end

                 <+ ?"yield"#([<ReplacePlaceholder>])
                    ; asfix-yield-appl

                 <+ log(| Error()
                        , <concat-strings>[ "Could not replace placeholder <",<write-to-string>ph,"> in pattern "
                                          , <write-to-string>pattern]) ; <exit> 1 )
               ; if not([] := a*) then
                   \ t1{b*} -> t1{c*} where c* := <conc> (<map(rec)> a*, b*) \
                 end
             else
               ?_{^[_ | _]}
               ; \ t1{a*} -> t2{b*} where b* := <map(rec)> a*; t2 := <rec> t1{^[]} \
             end
           })
         )
      ; ?term{b*}
      ; if [] := a* then
          !b*
        else
          if [] := b* then
            <conc> (a*, b*)
          else
            !a*
          end
        end
      ; ?c*
      |}

  /**
   * @todo Cache the parsed placeholders.
   */
  get-ast-pattern : 
    prod(_, _, as) -> <read-from-string>x
    where <oncetd(?ast(x))> as

/**
 * @todo Find a more appropiate place for this.
 */
signature
  constructors
    ast : Term -> Term

rules

  ReplConsNil(impl) : 
    appl(p, []) -> []
    where
      <is-nil> p

  ReplConsNone(impl) : 
    appl(p, []) -> None()
    where
      <is-none> p

  ReplConsIns(impl) : 
    appl(p, [t]) -> [<impl>t]
    where
      <is-ins> p

/**
 * In some case a present literal is relevant.
 */
rules

  ReplConsIns(impl) : 
    appl(prod([lit(x)], sym, _), []) -> t
    where
      t := <implode-constant-literal(|x)> sym

  ReplConsIns(impl) : 
    appl(prod([cilit(x)], sym, _), []) -> t
    where
      t := <implode-constant-literal(|x)> sym

  implode-constant-literal(|constant) =
    rec rec(
      ?cf(<rec>)
    + ?lex(<rec>)
    + \ iter(<rec>) -> [<id>] \
    + \ iter-star(<rec>) -> [<id>] \
    + \ iter-sep(<rec>, _) -> [<id>] \
    + \ iter-star-sep(<rec>, _) -> [<id>] \
    + \ opt(<rec>) -> Some(<id>) \
    + \ lit(_) -> constant \
    + \ cilit(_) -> constant \
    )

rules

  ReplConsSome(impl) : 
    appl(p, [t]) -> Some(<impl>t)
    where
      <is-some> p

  ReplConsConc(impl) : 
    appl(p, [t1,t2]) -> t
    where
      <is-conc> p
      ; <impl>t1 => ts1
      ; <impl>t2 => ts2
      ; (<[] + [id|id]> ts2; <conc>(ts1, ts2)
          <+ !"Conc"#([ts1, ts2])) => t

  /**
   * Note: the separator of this concatenation operator is
   * made into an element of the list. Does this make sense?
   */
  ReplConsConc(impl) : 
    appl(p, [t1,t2,t3]) -> <conc>(<impl>t1, [<impl>t2 | <impl>t3])
    where
      <is-conc> p

strategies

  is-nil  = ?prod([], cf(iter-star-sep(_, _)), _)
  is-nil  = ?prod([], cf(iter-star(_)), _)
  is-nil  = ?prod([], iter-star-sep(_, _), _)
  is-nil  = ?prod([], iter-star(_), _)

  is-ins  = ?prod([cf(x)], cf(iter-sep(x,_)), _)
  is-ins  = ?prod([cf(x)], cf(iter(x)), _)
  is-ins  = ?prod([x], iter-sep(x,_), _)
  is-ins  = ?prod([x], iter(x), _)

  is-conc = ?prod([_, _, _], cf(iter(_)), _)
  is-conc = ?prod([_,    _],    iter(_), _)
  is-conc = ?prod([_, _, _], cf(iter-star(_)), _)
  is-conc = ?prod([_, _, _],    iter-star(_), _)
  is-conc = ?prod([_, _, _, _, _], cf(iter-sep(_, _)), _)
  is-conc = ?prod([_,    _,    _], iter-sep(_, _), _)
  is-conc = ?prod([_, _, _, _, _], cf(iter-star-sep(_, _)), _)
  is-conc = ?prod([_,    _,    _], cf(iter-star-sep(_, _)), _)

  /**
   * @todo Not sure what this case is for.
   */
  is-conc = ?prod([_, _], cf(iter-star-sep(_, _)), _)

  is-none = ?prod([], cf(opt(_)), _)
  is-none = ?prod([], opt(_), _)

  is-some = ?prod([cf(x)], cf(opt(x)), _)
  is-some = ?prod([x], opt(x), _)

strategies

  Cns = CnsNil <+ CnsGeneric

rules

  CnsNil : 
    appl(p, ts) -> []
    where
      <ConstrNil> p => c 

  ConstrNil : prod([],cf(iter-star-sep(_,_)),_)     -> "Nil"
  ConstrNil : prod([],cf(iter-star(_)),_)           -> "Nil"
  ConstrNil : prod([],iter-star-sep(_,_),_)         -> "Nil"
  ConstrNil : prod([],iter-star(_),_)               -> "Nil"

  CnsGeneric : 
    appl(p, ts) -> c#(ts)
    where <Constr0 <+ Constr1> p => c 

  /**
   * @todo Optimize the oncetd
   */
  Constr0 : 
    prod(_, _, as) -> x
    where
      <oncetd(?cons(x))> as

  Constr1 : prod([cf(x)], cf(iter-sep(x,_)), _)   -> "Ins"
  Constr1 : prod([cf(x)], cf(iter(x)), _)         -> "Ins"
  Constr1 : prod([x], iter-sep(x,_), _)           -> "Ins"
  Constr1 : prod([x], iter(x), _)                 -> "Ins"

  Constr1 : prod([_,_,_,_,_],cf(iter-sep(_,_)),_) -> "Conc"
  Constr1 : prod([_,_,_],    cf(iter-sep(_,_)),_) -> "Conc" // after layout removal
  Constr1 : prod([_,_,_],    cf(iter(_)),_)       -> "Conc"
  Constr1 : prod([_,_],      cf(iter(_)),_)       -> "Conc" // after layout removal
  Constr1 : prod([_,_,_],    iter-sep(_,_),_)	  -> "Conc"
  Constr1 : prod([_,_],      iter(_),_)           -> "Conc"

  Constr1 : prod([_,_,_,_,_], cf(iter-star-sep(_,_)),_) -> "Conc"
  Constr1 : prod([_,_,_],     cf(iter-star-sep(_,_)),_) -> "Conc"

  Constr1 : prod([], cf(opt(_)), _)		  -> "None"
  Constr1 : prod([], opt(_), _)			  -> "None"

  Constr1 : prod([cf(x)], cf(opt(x)), _)          -> "Some"
  Constr1 : prod([x], opt(x), _)                  -> "Some"

  Constr23 : 
    prod(args, cf(iter-sep(x, y)), _) -> c
    where <(?[cf(x)]; !"Ins" <+ ?[_,_,_,_,_]; !"Conc")> args => c