(***********************************************************************)
(*                                                                     *)
(*                         Applied Type System                         *)
(*                                                                     *)
(***********************************************************************)

(*
** ATS/Postiats - Unleashing the Potential of Types!
** Copyright (C) 2011-2013 Hongwei Xi, ATS Trustful Software, Inc.
** All rights reserved
**
** ATS is free software;  you can  redistribute it and/or modify it under
** the terms of  the GNU GENERAL PUBLIC LICENSE (GPL) as published by the
** Free Software Foundation; either version 3, or (at  your  option)  any
** later version.
** 
** ATS is distributed in the hope that it will be useful, but WITHOUT ANY
** WARRANTY; without  even  the  implied  warranty  of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License
** for more details.
** 
** You  should  have  received  a  copy of the GNU General Public License
** along  with  ATS;  see the  file COPYING.  If not, please write to the
** Free Software Foundation,  51 Franklin Street, Fifth Floor, Boston, MA
** 02110-1301, USA.
*)

(* ****** ****** *)
//
// Author: Hongwei Xi
// Authoremail: gmhwxi AT gmail DOT com
// Start Time: November, 2011
//
(* ****** ****** *)
//
staload
ATSPRE =
"./pats_atspre.dats"
//
(* ****** ****** *)

staload
"./pats_basics.sats"

(* ****** ****** *)

staload
UN =
"prelude/SATS/unsafe.sats"

(* ****** ****** *)
//
staload "./pats_errmsg.sats"
staload
_(*anon*) = "./pats_errmsg.dats"
//
implement
prerr_FILENAME<>
(
  // argless
) =
  prerr "pats_trans3_appsym"
//
(* ****** ****** *)

staload "./pats_staexp2.sats"
staload "./pats_staexp2_util.sats"
staload "./pats_dynexp2.sats"
staload "./pats_dynexp3.sats"

(* ****** ****** *)

staload "./pats_trans3.sats"
staload "./pats_trans3_env.sats"

(* ****** ****** *)

#define l2l list_of_list_vt

(* ****** ****** *)

extern
fun aritest_d2exparglst_s2exp
  (d2as: d2exparglst, s2e: s2exp): bool
(*
** HX: for handling dynamic overloading
*)
local
//
fun loop
(
  d2as: d2exparglst
, s2e: s2exp, npf: int, d2es: d2explst
) : bool = let
  val s2e = s2exp_hnfize (s2e)
in
  case+ s2e.s2exp_node of
  | S2Efun
    (
      _(*fc*), _(*lin*), _(*eff*), npf1, s2es_arg, s2e_res
    ) =>
      if (npf = npf1) then let
        val sgn =
          list_length_compare (d2es, s2es_arg) in
        // end of [val]
        if sgn = 0
          then aritest_d2exparglst_s2exp (d2as, s2e_res) else false
        // end of [if]
      end else false // end of [if]
    // end of [S2Efun]
  | S2Eexi (_(*s2vs*), _(*s2ps*), s2e) => loop (d2as, s2e, npf, d2es)
  | S2Euni (_(*s2vs*), _(*s2ps*), s2e) => loop (d2as, s2e, npf, d2es)
  | S2Emetfun (_(*opt*), _(*met*), s2e) => loop (d2as, s2e, npf, d2es)
  | _ => false // end of [_]
end // end of [loop]
//
in // in of [local]

implement
aritest_d2exparglst_s2exp
  (d2as, s2e) = let
in
  case+ d2as of
  | list_cons
      (d2a, d2as) => (
    case+ d2a of
    | D2EXPARGdyn (
        npf, _(*loc*), d2es
      ) => loop (d2as, s2e, npf, d2es)
    | D2EXPARGsta _ =>
        aritest_d2exparglst_s2exp (d2as, s2e)
    ) // end of [list_cons]
  | list_nil ((*void*)) => true
end // end of [aritest_d2exparglst_s2exp]

end // end of [local]

(* ****** ****** *)

fun
d2exp_trup_item
(
  loc0: loc_t, d2i: d2itm, t2mas: t2mpmarglst
) : d3exp = let
(*
  val () = (
    print "d2exp_trup_item: d2i = ";
    fprint_d2itm (stdout_ref, d2i); print_newline ()
  ) // end [val]
*)
in
//
case+ d2i of
| D2ITMcst d2c => let
    val s2qs =
      d2cst_get_decarg (d2c)
    // end of [val]
    val s2e = d2cst_get_type (d2c)
    val s2e = s2exp_unis (s2qs, s2e)
  in
    d3exp_item (loc0, s2e, d2i, t2mas)
  end
| D2ITMvar d2v => let
    val s2qs = d2var_get_decarg (d2v)
    val-Some (s2e) = d2var_get_type (d2v)
    val s2e = s2exp_unis (s2qs, s2e)
  in
    d3exp_item (loc0, s2e, d2i, t2mas)
  end
| _ => let
    val () = prerr_error3_loc (loc0)
    val () = filprerr_ifdebug "d2exp_trup_item"
    val () = prerr ": a dynamic constant or variable is expected."
    val () = prerr_newline ()
    val () =  the_trans3errlst_add (T3E_d2exp_trup_item (loc0, d2i))
  in
    d3exp_errexp (loc0)
  end // end of [_]
//
end // end of [d2exp_trup_item]

(* ****** ****** *)

fun d3exp_trup_item
  (d3e0: d3exp): d3exp = let
//
val loc0 = d3e0.d3exp_loc
val-D3Eitem (d2i, t2mas) = d3e0.d3exp_node
val isnil = list_is_nil (t2mas)
//
in
//
case+ d2i of
| D2ITMcst d2c => (
    if isnil then
      d2exp_trup_cst (loc0, d2c)
    else
      d2exp_trup_tmpcst (loc0, d2c, t2mas)
    // end of [if]
  ) // end of [D2ITMcst]
| D2ITMvar d2v => (
    if isnil then
      d2exp_trup_var (loc0, d2v)
    else
      d2exp_trup_tmpvar (loc0, d2v, t2mas)
    // end of [if]
  ) // end of [D2ITMvar]
| _ => let
    val () = assertloc (false) in d3exp_errexp (loc0)
  end // end of [_]
//
end // end of [d3exp_trup_item]

(* ****** ****** *)
//
datatype
d3pitm = D3PITM of (int, d3exp)
//
typedef d3pitmlst = List(d3pitm)
vtypedef d3pitmlst_vt = List_vt(d3pitm)
//
(* ****** ****** *)

fun
d3pitm_make
(
  pval: int, d3e: d3exp
) : d3pitm = D3PITM(pval, d3e)
// end of [d3pitm_make]

fun
d3pitm_get_dexp
(
  d3pi: d3pitm
) : d3exp = let
  val D3PITM(pval, d3e) = d3pi in d3e
end // end of [d3pitm_get_dexp]

fun
d3pitm_get_type
(
  d3pi: d3pitm
) : s2exp = let
  val D3PITM(pval, d3e) = d3pi in d3e.d3exp_type
end // end of [d3pitm_get_type]

fun
d3pitm_get_pval
(
  d3pi: d3pitm
) : int = let
  val D3PITM(pval, d3e) = d3pi in pval
end // end of [d3pitm_get_pval]

fun
fprint_d3pitm
(
  out: FILEref, x: d3pitm
) : void = let
  val D3PITM (pval, d3e) = x
  val-D3Eitem
    (d2i, t2mas) = d3e.d3exp_node
  val () = fprint_d2itm (out, d2i)
  val () = fprint_string (out, " of ")
  val () = fprint_int (out, pval)
in
  // nothing
end // end of [fprint_d3pitm]

(* ****** ****** *)
//
datatype
d3exparg = 
| D3EXPARGsta of
    (loc_t(*arg*), s2exparglst)
  // end of [D3EXPARGsta]
| D3EXPARGdyn of // HX: notice the argument list [d3es]
    (int(*npf*), loc_t(*arg*), d3explst) // are not opened
  // end of [D3EXPARGdyn]
//
typedef d3exparglst = List(d3exparg)
vtypedef d3exparglst_vt = List_vt(d3exparg)
//
(* ****** ****** *)

extern
fun
d3exp_trup_applst
(
  d2e0: d2exp, d3e_fun: d3exp, d3as: d3exparglst
) : d3exp // end of [d3exp_trup_applst]
implement
d3exp_trup_applst
  (d2e0, d3e_fun, d3as) = let
(*
  val () = (
    print "d3exp_trup_applst: ..."; print_newline ()
  ) // end of [val]
*)
  val loc0 = d2e0.d2exp_loc
in
//
case+ d3as of
//
| list_cons (d3a, d3as) => (
  case+ d3a of
  | D3EXPARGsta
      (locarg, s2as) => let
      val loc_fun = d3e_fun.d3exp_loc
      val s2e_fun = d3e_fun.d3exp_type
      var err: int = 0
      val (s2e_fun, s2ps) =
        s2exp_uni_instantiate_sexparglst (s2e_fun, s2as, err)
      // end of [val]
      val () = trans3_env_add_proplst_vt (loc_fun, s2ps)
      val d3e_fun = d3exp_app_sta (loc0, s2e_fun, d3e_fun)
    in
      d3exp_trup_applst (d2e0, d3e_fun, d3as)
    end // end of [D3EXPARGsta]
  | D3EXPARGdyn
      (npf, locarg, d3es_arg) => let
      val loc_fun = d3e_fun.d3exp_loc
      val s2e_fun = d3e_fun.d3exp_type
      val () = d3explst_open_and_add (d3es_arg)
      var err: int = 0
      val (s2e_fun, s2ps) =
        s2exp_unimet_instantiate_all (s2e_fun, locarg, err)
      // HX: [err] is not used
      val () = trans3_env_add_proplst_vt (loc_fun, s2ps)
      val d3e_fun = d3exp_app_unista (loc0, s2e_fun, d3e_fun)
      val-S2Efun (
        fc, _(*lin*), s2fe_fun, _(*npf*), s2es_fun_arg, s2e_fun_res
      ) = s2e_fun.s2exp_node // end of[val]
//
      val loc_app = $LOC.location_combine (loc_fun, locarg)
      val s2es_fun_arg = 
        s2fun_opninv_and_add (locarg, s2es_fun_arg, s2e_fun_res)
      val d3es_arg = d3explst_trdn_arg (d3es_arg, s2es_fun_arg)
//
      val (
        iswth, s2e_res, wths2es
      ) = un_s2exp_wthtype (loc_app, s2e_fun_res)
//
      val d3e_fun = d3exp_fun_restore (fc, d3e_fun)
      val d3es_arg =
      (
        if iswth
          then d3explst_arg_restore (d3es_arg, s2es_fun_arg, wths2es)
          else d3es_arg
        // end of [if]
      ) : d3explst // end of [val]
//
      val err = the_effenv_check_s2eff (loc_app, s2fe_fun)
      val (
      ) = if (err > 0) then (
        the_trans3errlst_add (T3E_d3exp_trup_applst_eff (loc_app, s2fe_fun))
      ) // end of [if] // end of [val]
//
      val d3e_fun = d3exp_app_dyn (loc0, s2e_res, d3e_fun, npf, d3es_arg)
    in
      d3exp_trup_applst (d2e0, d3e_fun, d3as)
    end // end of [D3EXPARGdyn]
  ) (* end of [list_cons] *)
//
| list_nil ((*void*)) => d3e_fun // end of [list_nil]
//
end // end of [d3exp_trup_applst]

(* ****** ****** *)

local

(* ****** ****** *)

typedef XY = (d3pitm, s2kexp)

(* ****** ****** *)

fun
auxsel_arity
(
  locsym: loc_t
, d2pis: d2pitmlst
, d2piss: List_vt(d2pitmlst)
, t2mas: t2mpmarglst
, d2args: d2exparglst
) : d3pitmlst_vt = let
in
//
case+ d2pis of
| list_nil
    ((*void*)) =>
  (
  case+ d2piss of
  | ~list_vt_nil() => list_vt_nil ()
  | ~list_vt_cons(d2pis, d2piss) =>
      auxsel_arity (locsym, d2pis, d2piss, t2mas, d2args)
    // end of [list_cons]
  ) (* end of [list_nil] *)
| list_cons
    (d2pi, d2pis) => let
    val D2PITM (pval, d2i) = d2pi
  in
    case+ d2i of
    | D2ITMsymdef
        (_(*sym*), d2pis_new) => let
        val d2piss = list_vt_cons (d2pis, d2piss)
      in
        auxsel_arity (locsym, d2pis_new, d2piss, t2mas, d2args)
      end // end of [D2ITMsymdef]
    | _ => let
        val d3e = d2exp_trup_item(locsym, d2i, t2mas)
        val test = aritest_d2exparglst_s2exp(d2args, d3e.d3exp_type)
        val d3pis = auxsel_arity(locsym, d2pis, d2piss, t2mas, d2args)
      in
        if test then let
          val d3pi = d3pitm_make(pval, d3e) in list_vt_cons(d3pi, d3pis)
        end else d3pis // end of [if]
      end // end of [_]
  end // end of [list_cons]
//
end // end of [auxsel_arity]

fun
auxsel_skexplst
(
  xys:List_vt(XY)
, s2kes: s2kexplst
) : List_vt(XY) = let
(*
val () =
(
  print "auxsel_skexplst: s2kes = ";
  fprint_s2kexplst (stdout_ref, s2kes); print_newline ()
) // end of [val]
*)
in
//
case+ xys of
| ~list_vt_nil() =>
    list_vt_nil((*void*))
  // list_vt_nil
| ~list_vt_cons(xy, xys) =>
  (
  case+ xy.1 of
  | S2KEfun
    (
      s2kes_arg, s2ke_res
    ) => let
(*
//
      val
      out = stdout_ref
//
      val () =
      (
        print "auxsel_skexplst: s2kes_arg = ";
        fprint_s2kexplst(out, s2kes_arg); print_newline()
      ) (* end of [val] *)
*)
      val ismat =
        s2kexplst_ismat(s2kes, s2kes_arg)
      // end of [val]
(*
      val () = println! ("auxsel_skexplst: ismat = ", ismat)
*)
      val x = (xy.0, s2ke_res)
      val xs = auxsel_skexplst(xys, s2kes)
    in
      if ismat then list_vt_cons (x, xs) else xs
    end // end of [list_vt_cons]
  | _ (*non-S2KEfun*) => auxsel_skexplst(xys, s2kes)
  ) (* end of [list_vt_cons] *)
//
end // end of [auxsel_skexplst]

fun
auxsel_arglst
(
  xys: List_vt(XY)
, d2as: d2exparglst, d3as: d3exparglst_vt
) :
( d3pitmlst
, d3exparglst
, d2exparglst ) = let
//
fun
auxmap
(xys: List_vt(XY)): d3pitmlst =
(
case+ xys of
| ~list_vt_nil
    ((*void*)) => list_nil()
| ~list_vt_cons
    (xy, xys) => list_cons(xy.0, auxmap(xys))
) (* end of [auxmap] *)
//
in
//
case+ d2as of
| list_nil() => let
    val d3as =
      list_vt_reverse(d3as)
    // end of [val]
  in
    (auxmap(xys), (l2l)d3as, d2as)
  end // end of [list_nil]
| list_cons(_, _) => (
  case+ xys of
  | list_vt_nil
      ((*void*)) => let
      val () = free@{XY}(xys)
      val d3as =
        list_vt_reverse (d3as)
      // end of [val]
    in
      (list_nil(), (l2l)d3as, d2as)
    end // end of [list_vt_nil]
  | list_vt_cons
      (xy, !p_xys1) => (
    case+ !p_xys1 of
    | ~list_vt_nil () => let
        val () = free@{XY}{0}(xys)
        val d3as = list_vt_reverse(d3as)
      in
        (list_sing(xy.0), (l2l)d3as, d2as)
      end
    | _ => let
        val () = fold@(xys)
        val+list_cons (d2a, d2as) = d2as
      in
        case+ d2a of
        | D2EXPARGsta
            (locarg, s2as) => let
            val d3a = D3EXPARGsta(locarg, s2as)
          in
            auxsel_arglst(xys, d2as, list_vt_cons(d3a, d3as))
          end (* D2EXPARGsta *)
        | D2EXPARGdyn
            (npf, locarg, d2es) => let
            val d3es = d2explst_trup (d2es)
            val s2kes = list_map_fun<d3exp><s2kexp>
              (d3es, lam d3e =<1> s2kexp_make_s2exp(d3e.d3exp_type))
            val xs = auxsel_skexplst(xys, $UN.castvwtp1{s2kexplst}(s2kes))
            val () = list_vt_free (s2kes)
            val d3a = D3EXPARGdyn (npf, locarg, d3es)
          in
            auxsel_arglst (xs, d2as, list_vt_cons (d3a, d3as))
          end (* D2EXPARGdyn *)
      end // end of [_]
    ) // end of [list_vt_cons]
  ) // end of [list_cons]
//
end // end of [auxsel_arglst]

#define ITMPVALMIN ~1000000

fun
auxselmax
(
  d3pis: d3pitmlst
) : d3pitmlst = let
//
fun
loop
(
xs: d3pitmlst, mpval: int
) : int =
(
case+ xs of
| list_nil() => mpval
| list_cons(x, xs) => let
    val pval = d3pitm_get_pval(x)
  in
    loop(xs, max_int_int(mpval, pval))
  end
) (* end of [loop] *)
//
val
mpval = loop(d3pis, ITMPVALMIN)
val
d3pis =
list_filter_cloptr<d3pitm>
( d3pis
, lam (x) =<1> d3pitm_get_pval(x) = mpval
) (* end of [val] *)
//
in
  list_of_list_vt(d3pis)
end // end of [auxselmax]

fun
auxeq_d2itm
(
  d2i1: d2itm
, d2i2: d2itm
) : bool =
(
case+ d2i1 of
| D2ITMcst(d2c1) =>
  (
  case+ d2i2 of
  | D2ITMcst(d2c2) => (d2c1 = d2c2) | _ => false
  )
| D2ITMvar(d2v1) =>
  (
  case+ d2i2 of
  | D2ITMvar(d2v2) => (d2v1 = d2v2) | _ => false
  )
| _(* rest-of-d2itm *) => false
) (* end of [auxeq_d2itm] *)


fun
auxeq_d3pitm
(
  d3pi1: d3pitm
, d3pi2: d3pitm
) : bool = let
//
val d3e1 = d3pitm_get_dexp(d3pi1)
val d3e2 = d3pitm_get_dexp(d3pi2)
//
in
//
case+
d3e1.d3exp_node
of (* case+ *)
| D3Eitem(d2i1, _) =>
  (
  case+
  d3e2.d3exp_node
  of (* case+ *)
  | D3Eitem(d2i2, _) => auxeq_d2itm(d2i1, d2i2) | _ => false
  )
| _ (*non-D3Eitem*) => false
//
end // end of [auxeq_d3pitm]

fun
auxeq_d3pitm_remdup
(
  d3pis: d3pitmlst
) : d3pitmlst =
(
case+ d3pis of
| list_nil() => list_nil()
| list_cons(d3pi0, d3pis) => let
    val 
    d3pis =
    list_filter_cloptr<d3pitm>
    ( d3pis
    , lam(x) =<1> ~auxeq_d3pitm(d3pi0, x)
    ) (* end of [val] *)
  in
    list_cons(d3pi0, list_of_list_vt(d3pis))
  end
) (* end of [auxeq_d3pitm_remdup] *)

in (* in of [local] *)

implement
d2exp_trup_applst_sym
  (d2e0, d2s, d2as) = let
//
val t2mas = list_nil(*void*)
//
in
  d2exp_trup_applst_tmpsym(d2e0, d2s, t2mas, d2as)
end // end of [d2exp_trup_applst_sym]

implement
d2exp_trup_applst_tmpsym
  (d2e0, d2s, t2mas, d2as) = let
(*
val () =
(
  print "d2exp_trup_applst_sym: d2s = ";
  fprint_d2sym (stdout_ref, d2s); print_newline ();
  print "d2exp_trup_applst_sym: d2as = ";
  fprint_d2exparglst (stdout_ref, d2as); print_newline ();
) (* end of [val] *)
*)
val
loc0 = d2e0.d2exp_loc
val
locsym = d2s.d2sym_loc
//
val
d2pis = d2s.d2sym_pitmlst
val
d3pis =
auxsel_arity
  (locsym, d2pis, list_vt_nil, t2mas, d2as)
//
val xys = let
  fun
  fopr
  (
    d3pi: d3pitm
  ) : XY = let
    val s2e =
      d3pitm_get_type(d3pi)
    // end of [val]
  in
    (d3pi, s2kexp_make_s2exp(s2e))
  end // end of [fopr]
in
  list_map_fun<d3pitm>
    ($UN.castvwtp1{d3pitmlst}(d3pis), fopr)
  // end of [list_map_fun]
end // end of [val]
val ((*freed*)) = list_vt_free(d3pis)
//
val xyz = auxsel_arglst(xys, d2as, list_vt_nil)
//
val d3pis = xyz.0
val d3pis = auxselmax(d3pis)
//
// HX-2017-02-12:
// For removing duplicates of
// overloaded dyncsts and dynvars
val d3pis = auxeq_d3pitm_remdup(d3pis)
//
in
//
case+ d3pis of
//
| list_nil
  (
    (*void*)
  ) => let
    val () =
      prerr_error3_loc (loc0)
    // end of [val]
    val () = prerr ": the symbol [";
    val () = fprint_d2sym(stderr_ref, d2s)
    val () = prerr "] cannot be resolved as no match is found."
    val () = prerr_newline((*void*))
    val () =
    the_trans3errlst_add
      (T3E_d2exp_trup_applst_sym_nil(d2e0, d2s))
    // end of [val]
  in
    d3exp_errexp(loc0)
  end // end of [list_nil]
//
| list_cons
  (
    d3pi, list_nil()
  ) => let
    val d3e_fun = d3pitm_get_dexp(d3pi)
    val d3e_fun = d3exp_trup_item(d3e_fun)
    val d3e_fun = d3exp_trup_applst(d2e0, d3e_fun, xyz.1)
  in
    d23exp_trup_applst(d2e0, d3e_fun, xyz.2)
  end // end of [list_sing]
//
| list_cons
  (
    d3pi1, list_cons(d3pi2, _)
  ) => let
    val () = prerr_error3_loc(loc0)
    val () = prerr ": the symbol [";
    val () = fprint_d2sym(stderr_ref, d2s)
    val () = prerr "] cannot be resolved due to too many matches:\n"
    val () = fprint_d3pitm(stderr_ref, d3pi1)
    val () = prerr_newline()
    val () = fprint_d3pitm(stderr_ref, d3pi2)
    val () = prerr_newline()
    val () =
      the_trans3errlst_add (T3E_d2exp_trup_applst_sym_cons2(d2e0, d2s))
    // end of [val]
  in
    d3exp_errexp (loc0)
  end // end of [list_cons2]
//
end // end of [d2exp_trup_applst_tmpsym]
//
end // end of [local]

(* ****** ****** *)

local

fun
auxins
(
  npf: int
, d2e_rt: d2exp, d2es: d2explst
) : d2explst =
(
if npf > 0
  then let
    val-list_cons (d2e, d2es) = d2es
  in
    list_cons (d2e, auxins (npf-1, d2e_rt, d2es))
  end // end of [then]
  else list_cons (d2e_rt, d2es)
// end of [if]
)

fun
auxins2
(
  d2e0: d2exp
, d2e_rt: d2exp, d2as: d2exparglst
) : d2exparglst =
(
case+ d2as of
//
| list_nil() => let
    val d2a =
      D2EXPARGdyn(~1(*npf*), d2e0.d2exp_loc, list_sing(d2e_rt))
    // end of [val]
  in
    list_sing (d2a)
  end // end of [list_nil]
//
| list_cons(d2a, d2as) =>
  (
    case+ d2a of
    | D2EXPARGsta _ =>
        list_cons(d2a, auxins2 (d2e0, d2e_rt, d2as))
    | D2EXPARGdyn(npf, loc_arg, d2es) => let
        val d2a =
          D2EXPARGdyn(npf, loc_arg, auxins (npf, d2e_rt, d2es))
        // end of [val]
      in
        list_cons(d2a, d2as)
      end // end of [D2EXPARGdyn]
  ) (* end of [list_cons] *)  
//
) (* end of [auxins2] *)

in (* in-of-local *)

implement
d2exp_trup_applst_seloverld
  (d2e0, d2e_fun, d2s, d2as_arg) = let
//
val
d2e_rt =
d2exp_get_seloverld_root(d2e_fun)
val
d2as_arg = auxins2(d2e0, d2e_rt, d2as_arg)
//
in
  d2exp_trup_applst_sym(d2e0, d2s, d2as_arg)
end // end of [d2exp_trup_applst_seloverld]

end // end of [local]

(* ****** ****** *)

(* end of [pats_trans3_funsel.dats] *)
