(* Time-stamp: <modified the 21/07/2017 (at 15:53) by Erwan Jahier> *)


open Lxm
open Lv6errors
open AstV6
open AstCore
open Lic
open IdSolver

(** DEBUG FLAG POUR CE MODULE : *)
let dbg = (Lv6Verbose.get_flag "lazyc")

let finish_me msg = print_string ("\n\tXXX licTab:"^msg^" ->  finish me!\n")

let profile_info = Lv6Verbose.profile_info

(******************************************************************************)
(** Returns the ident on which the recursion was detected, plus an execution
    stack description. 
*)
exception Recursion_error of (Lv6Id.long as 'id) * (string list as 'stack)

exception BadCheckRef_error

let recursion_error (lxm : Lxm.t) (stack : string list) =
  let rec string_of_stack = function
    | [] -> "nostack" 
    | s::[] -> s
    | s::l  -> s^"\n   > "^(string_of_stack l)
  in
    raise ( Compile_error 
              (lxm, "Recursion loop detected: \n***   " ^(string_of_stack stack)
   ))


(******************************************************************************)
(* Structure principale *)
type t = {
  src_tab : AstTab.t;
  (* table des defs *)
  types  : (Lic.item_key, Lic.type_    Lic.check_flag) Hashtbl.t;
  consts : (Lic.item_key, Lic.const    Lic.check_flag) Hashtbl.t;
  nodes  : (Lic.node_key, Lic.node_exp Lic.check_flag) Hashtbl.t;
  (* table des prov *)
  prov_types  : (Lic.item_key, Lic.type_    Lic.check_flag) Hashtbl.t;
  prov_consts : (Lic.item_key, Lic.const    Lic.check_flag) Hashtbl.t;
  prov_nodes  : (Lic.node_key, Lic.node_exp Lic.check_flag) Hashtbl.t
}

(******************************************************************************)
(* exported *)

let (create : AstTab.t -> t) =
  fun tbl -> 
    let nodes_tbl =   Hashtbl.create 0 in
    let prov_nodes_tbl =   Hashtbl.create 0 in
      (* Iterated operators need to be in this table. Ideally, the lazy
         compiler should be able to pull such strings though...
      *)
    List.iter
      (fun op -> 
        let op_str = AstPredef.op2string op in
        let op_eff = LicEvalType.make_simple_node_exp_eff None true op 
                                                          (Lxm.dummy op_str) 
        in
        let op_key = AstPredef.op_to_long op, [] in
        Hashtbl.add nodes_tbl op_key (Lic.Checked op_eff);
        Hashtbl.add prov_nodes_tbl op_key (Lic.Checked op_eff)
      )
      AstPredef.iterable_op;
    {
      src_tab = tbl;
      types = Hashtbl.create 0;
      consts =  Hashtbl.create 0;
      nodes  = nodes_tbl;
      prov_types = Hashtbl.create 0;
      prov_consts =  Hashtbl.create 0;
      prov_nodes  = prov_nodes_tbl;
    } 

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

(** Type checking + constant checking/evaluation

   This is performed (lazily) by 10 mutually recursive functions:

   checking types 
   --------------
   (1) [type_check env type_name lxm]: type check the type id [type_name]
   (2) [type_check_do]: untabulated version of [type_check] (do the real work).

   (3) [type_check_interface]: ditto, but for the interface part
   (4) [type_check_interface_do]: untabulated version (do the real work)

   (5) [solve_type_idref] solves constant reference (w.r.t. short/long ident)

   checking constants 
   ------------------
   (6) [const_check env const_name lxm]: eval/check the constant [const_name]
   (7) [const_check_do] : untabulated version (do the real work)

   (8) [const_check_interface]: ditto, but for the interface part
   (9) [const_check_interface_do]: untabulated version (do the real work)

   (10) [solve_const_idref] solves constant reference (w.r.t. short/long ident)

   checking nodes 
   --------------

   (11) [node_check env node_name lxm]: check the node [node_name]
    checking a node means checking its interface and checking it equations/asserts.
    checking an equation means checking that the type and clock of the
    left part is the same as the ones of the rigth part.


   (12) [node_check_do] : untabulated version (do the real work)

   (13) [node_check_interface]: ditto, but for the interface part
   (14) [node_check_interface_do]: untabulated version (do the real work)

   (15) [solve_node_idref] solves constant reference (w.r.t. short/long ident)

    XXX checking clocks 
    -------------------
    Ditto, but todo!


    nb: for x in {type, const, node, clock}, there are several functions 
    that returns [x_eff]:
    - [x_check]
        o tabulates its result
        o takes an x_key and returns an [x_eff]
        o lookups its (syntaxic) definition (x_info) via the symbolTab.t
        o calls [Ast2lic.of_X] to translate its sub-terms

    - [Ast2lic.of_X]
        o takes a [x_exp] (i.e., an expression) and returns an [x_eff]
        o compute the effective static args (for nodes)
        o calls [solve_x_idref] (via [id_solver]) to translate its sub-terms


    - [solve_x_idref]
        o takes an idref (plus a �Lic.static_arg list� for x=node!)
        o perform name resolution
        o calls [x_check] (loop!)


    nb2: the top-level call is [node_check], on a node that necessarily contains
    no static parameters.  
  

*)

(* Before starting, let's define a few utilitary functions. *)

(** Intermediary results are put into a table. This tabulation handling
    is common to type and constant checking, and is performed by the
    2 following functions.

    Since [x] is meant to stand for [type], [const], or [node], those 2
    functions will lead to the definition of 6 functions:
    [type_check], [const_check], [node_check],
    [type_check_interface], [const_check_interface], [node_check_interface].

*)
let x_check 
   (* tab find_x x_check_do lookup_x_eff pack_of_x_key name_of_x_key this x_key lxm = *)
    (tab            : ('x_key, 'x_eff  Lic.check_flag) Hashtbl.t)
    (find_x         : AstTabSymbol.t -> Lv6Id.t -> Lxm.t -> 
                      ('x_info  Lxm.srcflagged) AstTabSymbol.elt)
    (x_check_do     : t -> 'x_key -> Lxm.t -> AstTabSymbol.t -> bool -> 
                      Lv6Id.pack_name -> 'x_info srcflagged -> 'x_eff)
    (x_builtin      : t -> 'x_key -> Lxm.t -> 'x_eff)
    (lookup_x_eff   : ('x_key, 'x_eff Lic.check_flag) Hashtbl.t -> 'x_key -> 
                      Lxm.t -> 'x_eff)
    (pack_of_x_key  : 'x_key -> string )
    (name_of_x_key  : 'x_key -> string)
    (this           : t)
    (x_key          : 'x_key)
    (lxm            : Lxm.t)
    : 'x_eff =
  Lv6Verbose.exe ~flag:dbg 
                 (fun () -> Printf.printf "#DBG: licTab.x_check '%s'\n" 
                                          (Lxm.details lxm));
  try lookup_x_eff tab x_key lxm 
  with Not_found -> (
    let res = try x_builtin this x_key lxm 
      with Not_found -> 
        Hashtbl.add tab x_key Lic.Checking;
        let (x_pack,xn) = (pack_of_x_key x_key, name_of_x_key x_key) in
        let x_pack_symbols = AstTab.pack_body_env this.src_tab x_pack in
        let x_def = match find_x x_pack_symbols xn lxm with
          | AstTabSymbol.Local x_def -> x_def
          | AstTabSymbol.Imported (lid,_) -> 
            print_string ("*** " ^ (Lv6Id.string_of_long false lid) ^ "???\n" ^ 
                             (Lxm.details lxm));
            assert false (* should not occur *)
        in
        x_check_do this x_key lxm x_pack_symbols false x_pack x_def
    in
    Hashtbl.replace tab x_key (Lic.Checked res);
    res
  )

let x_check_interface 
      tab find_x x_check x_check_interface_do x_builtin lookup_x_eff 
      pack_of_x_key name_of_x_key this x_key lxm =
  try lookup_x_eff tab x_key lxm
  with Not_found ->
    let res = (
      try x_builtin this x_key lxm
      with Not_found ->
           Hashtbl.add tab x_key Lic.Checking;
           let (xp,xn) = (pack_of_x_key x_key, name_of_x_key x_key) in
           let xp_prov_symbols_opt = AstTab.pack_prov_env ~lxm:lxm this.src_tab xp in
           match xp_prov_symbols_opt with
           | None -> 
              (* if [xp] have no provided symbol table, the whole package is exported. *)
              x_check this x_key lxm 
           | Some xp_prov_symbols ->
              let x_def = match find_x xp_prov_symbols xn lxm with
                | AstTabSymbol.Local x -> x
                | AstTabSymbol.Imported _ -> assert false (* should not occur *)
              in
              x_check_interface_do this x_key lxm xp_prov_symbols xp x_def
    ) in
    Hashtbl.replace tab x_key (Lic.Checked res);
    res

(* Returns the tabulated [type] or [const], if it has already been computed;
   otherwise, raise [Not_found] otherwise. *)
let lookup_x_eff x_label id_of_x_key x_tab x_key lxm  =
    match Hashtbl.find x_tab x_key with
      | Lic.Checked res -> res
      | Lic.Checking -> 
          raise (Recursion_error (id_of_x_key x_key, [x_label^(Lxm.details lxm)]))
      | Lic.Incorrect -> raise (BadCheckRef_error)

let (lookup_type_eff: (Lic.item_key, Lic.type_ Lic.check_flag) Hashtbl.t -> 
      Lv6Id.long -> Lxm.t -> Lic.type_) = 
  lookup_x_eff "type ref "  (fun k -> k)


let (type_builtin : t -> Lv6Id.long -> Lxm.t -> Lic.type_) = 
fun _ _ _ -> raise Not_found

let (lookup_const_eff:(Lic.item_key, Lic.const Lic.check_flag) Hashtbl.t -> 
      Lv6Id.long -> Lxm.t -> Lic.const) = 
  lookup_x_eff "const ref " (fun k -> k)

let (const_builtin : t -> Lv6Id.long -> Lxm.t -> Lic.const) =
fun _ _ _ -> raise Not_found

(*
LES NOEUDS (MACROS) BUILD-IN
sont track�s ici pour court-circuiter le node_check normal
(qui n�cessite un node_info)
*)
let lookup_node_exp_eff
   (tbl: (Lic.node_key, Lic.node_exp Lic.check_flag) Hashtbl.t)
   (key: Lic.node_key)
   (lxm: Lxm.t)
: Lic.node_exp = 
   try 
      let node_exp = lookup_x_eff "node ref "  (fun k -> fst k) tbl key lxm in
      Lv6Verbose.exe ~flag:dbg (fun () -> Printf.printf
        "#DBG: licTab.lookup_node_exp_eff: FOUND node key '%s'\n"
        (Lic.string_of_node_key key)
      ); 
      node_exp
   with Not_found -> (
      Lv6Verbose.exe ~flag:dbg (
         fun () -> 
            Printf.fprintf
              stderr "#DBG: licTab.lookup_node_exp_eff: node key '%s' NOT FOUND\n"
               (Lic.string_of_node_key key);
               flush stderr
         );
      raise Not_found
   )

let node_builtin (this: t) (key: Lic.node_key) (lxm: Lxm.t) : Lic.node_exp = 
   (* 12/07 *)
   (* ICI => courtcircuite les macros built-in *)
   let nk2nd = fun nk ->
      try
         match Hashtbl.find this.nodes nk with
         | Lic.Checked res -> res
         | _ -> assert false
      with Not_found -> assert false
   in
   let node_exp = LicMetaOp.do_node nk2nd key lxm in
      Lv6Verbose.exe ~flag:dbg (fun () -> Printf.printf 
      "#DBG: licTab.lookup_node_exp_eff: BUILT-IN node key '%s'\n"
         (Lic.string_of_node_key key)
      );
   Hashtbl.replace this.nodes key (Lic.Checked node_exp);
   Hashtbl.replace this.prov_nodes key (Lic.Checked node_exp);
   node_exp

(*   lookup_x_eff "node ref "  (fun k -> fst k) *)


(** This function performs the identifier (idref) resolution,
    i.e., when an ident is not explicitely prefixed by a module
    name, we decide here to which module it belongs. 

    The [provide_flag] indicates whether that function was called
    from a � provide � part or not.
*)
let solve_x_idref
    x_check_interface x_check find_x x_label to_x_key this symbols
    provide_flag currpack idr sargs lxm =
  let s = Lv6Id.name_of_idref idr in
    match Lv6Id.pack_of_idref idr with
      | Some p -> 
          if p = currpack 
          then x_check this (to_x_key currpack s) lxm
          else x_check_interface this (to_x_key p s) lxm
      | None ->
          (* no pack name: it must be in the symbols table *)
          try
            match (find_x symbols s lxm) with
              | AstTabSymbol.Local x_info ->
                  let x_key = to_x_key currpack s in
                  if provide_flag
                  then x_check_interface this x_key lxm
                  else x_check this x_key lxm

              | AstTabSymbol.Imported(fid,params) ->
                  let (pi,si) = (Lv6Id.pack_of_long fid, Lv6Id.of_long fid) in
                    (* todo *)
                    (* rien a faire : si on est arrive ici
                       c'est que les params statique ont pu etre evalues *)
                    (* assert(params=[]); *)
                    x_check_interface this (to_x_key pi si) lxm

          with Not_found ->
            (raise (Compile_error(lxm,"unbounded " ^ x_label ^ " ident")))

(******************************************************************************)
(* topologically sort vars wrt their clock dependecency *)
let find_var_info lxm vars id =
  try Hashtbl.find vars.vartable id
  with Not_found -> 
    raise (Compile_error (lxm,"\n*** Unknown ident: " ^ (Lv6Id.to_string id)))

(* returns the clock of a var *)
let find_direct_dep lxm v vars =
  match (find_var_info lxm vars v).it.var_clock with
  | Base -> None
  | NamedClock({it=(_,vclk)}) -> Some vclk

(* to track clock dependancy loops *)
type var_state = Todo | Doing | Done of Lv6Id.t list

let add_dep d deps =
  match deps with
    Done l -> d::l
  | _ -> assert false
                
let dep_star lxm vl vars =
  let tbl = Hashtbl.create (List.length vl) in
  List.iter (fun v -> Hashtbl.add tbl v Todo) vl ;
  let rec find_deps v = 
    Lv6Verbose.exe ~flag:dbg (fun () -> Printf.printf "  check clock dep : %s\n" v);
    if not (Hashtbl.mem tbl v) then Hashtbl.add tbl v Todo;
    match Hashtbl.find tbl v with
    | Done cl -> cl
    | Todo -> (
      Hashtbl.replace tbl v (Doing);
      match find_direct_dep lxm v vars with
      | None -> Hashtbl.replace tbl v (Done []);[] 
      | Some v2 ->
         let v2_deps = find_deps v2 in
         let v_deps =  v2::v2_deps in
         Hashtbl.replace tbl v (Done v_deps);
         v_deps
    )
    | Doing ->
       let lxm =
         try let vi = Hashtbl.find vars.vartable v in vi.src
         with Not_found -> lxm (* sno *)
       in
       raise(Compile_error (
        lxm, ("A loop in the clock dependancies exists for variable "^ v)))
  in
  List.iter  
    (fun v ->   
     match find_deps v with 
     | [] -> Hashtbl.remove tbl v (* cleaning *) 
     | _::_ -> () 
    ) 
    vl;
  let tbl2 = Hashtbl.create(List.length vl) in
  Hashtbl.iter (fun v dep ->
    match dep with Done cl -> Hashtbl.replace tbl2 v cl  | _ -> ()) tbl;
  tbl2
  
module TopoSortVars = 
  TopoSort.Make(
      struct 
        type elt = Lv6Id.t
        type store = (Lv6Id.t, Lv6Id.t list) Hashtbl.t
        let find_dep tbl x =
          try Hashtbl.find tbl x with Not_found -> []
        let have_dep tbl x = try Hashtbl.find tbl x <> [] with Not_found -> false
        let remove_dep tbl x = Hashtbl.remove tbl x; tbl
      end
    )

(* Looks like the one in Lic *)
let (sort_vars : Lxm.t -> AstCore.node_vars-> Lv6Id.t list -> Lv6Id.t list) =
  fun lxm vars l ->  (* we sort vars according to their clock deps *)
  profile_info "LicTab.sort_vars\n";
  let tbl = dep_star lxm l vars in
  TopoSortVars.f tbl l

(******************************************************************************)
(* And now we can start the big mutually recursive definition... *)

(** Tabulated version of [type_check_do]. *)
let rec type_check 
    (this: t)
    (key: Lv6Id.long)
    (lxm: Lxm.t)
    : Lic.type_ =
  Lv6Verbose.exe ~flag:dbg (fun () -> 
    Printf.printf "#DBG: licTab.type_check '%s'\n" (Lv6Id.string_of_long false key));
  x_check this.types AstTabSymbol.find_type type_check_do type_builtin lookup_type_eff 
    Lv6Id.pack_of_long Lv6Id.of_long this
    key lxm

(** Tabulated version of [const_check_do]. *)
and const_check
    (this: t)
    (key: Lv6Id.long)
    (lxm: Lxm.t)
    : Lic.const =
  Lv6Verbose.exe ~flag:dbg (fun() -> Printf.printf 
    "#DBG: licTab.const_check '%s'\n" (Lv6Id.string_of_long false key));
  x_check this.consts AstTabSymbol.find_const const_check_do const_builtin
          lookup_const_eff 
    Lv6Id.pack_of_long Lv6Id.of_long this
    key lxm

(** Tabulated version of [type_check_interface_do]. *)
and type_check_interface
    (this: t)
    (key: Lv6Id.long)
    (lxm: Lxm.t)
    : Lic.type_ =
  Lv6Verbose.exe ~flag:dbg (fun() -> Printf.printf
    "#DBG: licTab.type_check_interface '%s'\n" (Lv6Id.string_of_long false key));
  x_check_interface 
    this.prov_types AstTabSymbol.find_type type_check type_check_interface_do 
    type_builtin lookup_type_eff Lv6Id.pack_of_long Lv6Id.of_long this
    key lxm

(** Tabulated version of [const_check_interface_do]. *)
and const_check_interface
    (this: t)
    (key: Lv6Id.long)
    (lxm: Lxm.t)
    : Lic.const =
  Lv6Verbose.exe ~flag:dbg (fun () -> Printf.printf
    "#DBG: licTab.const_check_interface '%s'\n" (Lv6Id.string_of_long false key));
  x_check_interface 
    this.prov_consts AstTabSymbol.find_const const_check const_check_interface_do
    const_builtin lookup_const_eff Lv6Id.pack_of_long Lv6Id.of_long this
    key lxm

(** solving type and constant references *)
and (solve_type_idref : t -> AstTabSymbol.t -> bool -> Lv6Id.pack_name -> 
     Lv6Id.idref -> Lxm.t -> Lic.type_) =
  fun this symbols provide_flag currpack idr lxm -> 
    solve_x_idref
      type_check_interface type_check AstTabSymbol.find_type "type"
      (fun p id -> Lv6Id.make_long p id)
      this symbols provide_flag currpack idr [] lxm
      
and (solve_const_idref : t -> AstTabSymbol.t -> bool -> Lv6Id.pack_name -> 
     Lv6Id.idref -> Lxm.t -> Lic.const) =
  fun this symbols provide_flag currpack idr lxm ->
    solve_x_idref
      const_check_interface const_check AstTabSymbol.find_const "const"
      (fun p id -> Lv6Id.make_long p id)
      this symbols provide_flag currpack idr [] lxm


(* now the real work! *)
and (type_check_interface_do: t -> Lv6Id.long -> Lxm.t -> AstTabSymbol.t ->
     Lv6Id.pack_name -> AstCore.type_info srcflagged -> 
     Lic.type_) =
  fun this type_name lxm prov_symbols pack_name type_def ->
    (* We type check the interface and the body. 
       For non-abstract types, we also check that both effective types are
       the same.  *)
    let body_type_eff = type_check this type_name lxm in
    let prov_type_eff =
      type_check_do this type_name lxm prov_symbols true pack_name type_def
    in
    if Lic.type_are_compatible prov_type_eff body_type_eff then
      prov_type_eff
    else
      raise(Compile_error (
        type_def.src,
        ("provided type \n\t" ^ 
            (Lic.string_of_type prov_type_eff) ^
            "\n is not compatible with its implementation \n\t" ^ 
            (Lic.string_of_type body_type_eff))))


and (const_check_interface_do: t -> Lv6Id.long -> Lxm.t -> AstTabSymbol.t -> 
     Lv6Id.pack_name -> AstCore.const_info srcflagged -> 
     Lic.const) =
  fun this cn lxm prov_symbols p const_def -> 
    let prov_const_eff = const_check_do this cn lxm prov_symbols true p const_def in
    let body_const_eff = const_check this cn lxm in
    match prov_const_eff, body_const_eff with
      | Lic.Extern_const_eff (_), _ -> assert false
      | Lic.Abstract_const_eff (id, teff, v, is_exported),
        Lic.Abstract_const_eff (body_id, body_teff, body_v, body_is_exported)
        ->
        assert false
      (* indeed, how can a body constant be extern and have a value? *)

      | Lic.Abstract_const_eff (id, teff, v, is_exported),
          Lic.Extern_const_eff (body_id, body_teff) 
          -> 
        if (id <> cn) then assert false
        else if not (Lic.type_are_compatible teff body_teff) then 
          raise(Compile_error (
            const_def.src,
            ("provided constant type \n***\t" ^ 
                (Lic.string_of_type teff)  ^ 
                "   is not compatible with its implementation \n***\t" ^ 
                (Lic.string_of_type body_teff) ^ "")))
        else if 
            is_exported 
        then
          raise(Compile_error (const_def.src, " constant values mismatch"))
        else
          Lic.Extern_const_eff (body_id, body_teff)

      | Lic.Abstract_const_eff (id, teff, v, is_exported), _ -> 
        let body_teff = Lic.type_of_const body_const_eff in
        if (id <> cn) then assert false
        else if not (Lic.type_are_compatible teff body_teff) then 
          raise(Compile_error (
            const_def.src,
            ("provided constant type \n***\t" ^ 
                (Lic.string_of_type teff)  ^ 
                "   is not compatible with its implementation \n***\t" ^ 
                (Lic.string_of_type body_teff) ^ "")))
        else 
          if is_exported && body_const_eff <> v then
            raise(Compile_error (const_def.src, " constant values mismatch"))
          else
            Lic.Abstract_const_eff (id, teff, body_const_eff, is_exported)
              
      | Lic.Enum_const_eff (_, _), _
      | Lic.Bool_const_eff _, _
      | Lic.Int_const_eff _, _
      | Lic.Real_const_eff _, _
      | Lic.Struct_const_eff (_,_), _
      | Lic.Array_const_eff (_,_), _
        ->
        if prov_const_eff = body_const_eff then
          body_const_eff
        else
          raise(Compile_error (
            const_def.src, 
            "\n*** provided constant does not match with its definition."))
      | Lic.Tuple_const_eff _, _ ->
        print_internal_error "licTab.const_check_interface_do" 
                             "should not have been called for a tuple";
        assert false


and (type_check_do: t -> Lv6Id.long -> Lxm.t -> AstTabSymbol.t -> bool -> 
     Lv6Id.pack_name -> AstCore.type_info srcflagged -> 
     Lic.type_) =
  fun this type_name lxm symbols provide_flag pack_name type_def -> 
    try (
      (* Solveur d'idref pour les appels � eval_type/eval_const *)
      let id_solver = {
        id2var   = (fun id lxm -> raise (Unknown_var(lxm,id)) (* should not occur *)); 
        id2const = solve_const_idref this symbols provide_flag pack_name;
        id2type  = solve_type_idref this symbols provide_flag pack_name;
        id2node  = solve_node_idref this symbols provide_flag pack_name;
        global_symbols  = symbols;
        all_srcs = this.src_tab;
      }
      in
      let type_eff = 
        match type_def.it with
          | ArrayType _ -> finish_me " array handling "; assert false
          | ExternalType s -> (
            let lid = Lv6Id.make_long pack_name s in
            let idref = Lv6Id.idref_of_long lid in
            try 
              Abstract_type_eff (lid, id_solver.id2type idref lxm)
            with e ->
              External_type_eff (lid)
          )
          | AliasedType (s, texp) -> Ast2lic.of_type id_solver texp
          | EnumType (s, clst) -> (
            let n = Lv6Id.make_long pack_name s in
            let add_pack_name x = Lv6Id.make_long pack_name x.it in
            Enum_type_eff (n, List.map add_pack_name clst)
          )
          | StructType sti -> (
            let make_field (fname : Lv6Id.t) =
              let field_def = Hashtbl.find sti.st_ftable fname in
              let teff = Ast2lic.of_type id_solver field_def.it.fd_type in
              match field_def.it.fd_value with
                | None -> (fname, (teff, None))
                | Some vexp -> (
                  let veff = EvalConst.f id_solver vexp in
                  match veff with
                    | [v] -> (
                      let tv = Lic.type_of_const v in
                      if (tv = teff) then (fname, (teff, Some v)) else 
                        raise 
                          (Compile_error(field_def.src, Printf.sprintf
                            " this field is declared as '%s' but evaluated as '%s'"
                            (Lic.string_of_type teff)
                            (Lic.string_of_type tv)))
                    )
                    | [] -> assert false (* should not occur *)
                    | _::_ -> 
                      raise (Compile_error(field_def.src,
                                           "bad field value: tuple not allowed"))
                )
            in
            let n = Lv6Id.make_long pack_name sti.st_name in
            let eff_fields = List.map make_field sti.st_flist in
            Struct_type_eff (n, eff_fields)
          )
      in        
      type_eff
    )
    with
        (* capte et complete/stoppe les recursions *)
        Recursion_error (root, stack) ->
          if (root = type_name) then recursion_error type_def.src stack else
            raise ( Recursion_error (root, ("type ref "^(Lxm.details lxm))::stack))
              
              
and (const_check_do : t -> Lv6Id.long -> Lxm.t -> AstTabSymbol.t -> bool -> 
     Lv6Id.pack_name -> AstCore.const_info srcflagged -> 
     Lic.const) =
  fun this cn lxm symbols provide_flag currpack const_def ->
    (* [cn] and [lxm] are used for recursion errors. 
       [symbols] is the current symbol table.
    *)
    try (
      (* Solveur d'idref pour les  les appels � eval_type/eval_const *)
      let id_solver = {
        id2var   = (fun idref lxm -> assert false (* should not occur *)); 
        id2const = solve_const_idref this symbols provide_flag currpack;
        id2type  = solve_type_idref  this symbols provide_flag currpack;
        id2node  = solve_node_idref  this symbols provide_flag currpack;
        global_symbols  = symbols;
        all_srcs  = this.src_tab;
      }
      in
      let const_eff =
        match const_def.it with
          | ExternalConst (id, texp, val_opt) ->
            let lid = Lv6Id.make_long currpack id in
            let teff = Ast2lic.of_type id_solver texp in
            if provide_flag then 
              match val_opt with
                | None  -> 
                  (* we put a fake value here as we don't know yet the 
                     concrete value. this will be filled in 
                     const_check_interface_do. I could have put an option
                     type, but that would make quite a lot of noise in the
                     remaining...
                  *) 
                  Abstract_const_eff(lid, teff, Int_const_eff ("-666"), false)
                | Some c -> 
                  let ceff = match EvalConst.f id_solver c with
                    | [ceff] -> ceff
                    | _  -> assert false
                  in
                  Abstract_const_eff(lid, teff, ceff, true)

            else
              (match val_opt with
                | None  -> Extern_const_eff(lid, teff)
                | Some c -> assert false
              (* indeed, how can a body constant be extern and have a value? *)
              )
          | EnumConst (id, texp) ->
            Enum_const_eff ((Lv6Id.make_long currpack id), 
                            Ast2lic.of_type id_solver texp)

          | DefinedConst (id, texp_opt, vexp ) -> (
            match (EvalConst.f id_solver vexp) with
              | [ceff] -> (
                match texp_opt with
                  | None -> ceff
                  | Some texp -> (
                    let tdecl = Ast2lic.of_type id_solver texp in
                    let teff =  Lic.type_of_const ceff in
                    if (tdecl = teff ) then ceff else 
                      raise 
                        (Compile_error (const_def.src, Printf.sprintf
                          " this constant is declared as '%s' but evaluated as '%s'"
                          (Lic.string_of_type tdecl)
                          (Lic.string_of_type teff)
                         )))
              )
              | [] -> assert false (* should not occur *)
              | _::_ -> raise (Compile_error(const_def.src, 
                                             "bad constant value: tuple not allowed"))
          )
      in
      const_eff
    ) with Recursion_error (root, stack) -> (
      (* capte et complete/stoppe les recursions *)
      if (root = cn) then recursion_error const_def.src stack else 
        (* on complete la stack *)
        raise (Recursion_error (root, ("const ref "^(Lxm.details lxm))::stack))
    )


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



and (node_check_interface_do: t -> Lic.node_key -> Lxm.t ->
     AstTabSymbol.t -> Lv6Id.pack_name -> AstCore.node_info srcflagged ->
     Lic.node_exp) =
  fun this nk lxm symbols pn node_def ->
    (* DEUX checks :
       - le "complet" donne 'body_node_exp_eff' qui sera stock� comme le vrai r�sultat 
       - le "provide" donne 'prov_node_exp_eff', non stock�, sert � v�rifier la
       coh�rence avec l'�ventuelle d�claration 'provide' 
    *)
    let body_node_exp_eff = node_check this nk lxm in
    let prov_node_exp_eff = node_check_do this nk lxm symbols true pn node_def in
    (** [type_eff_are_compatible t1 t2] checks that t1 is compatible with t2, i.e., 
        if t1 = t2 or t1 is abstract and and t2.
    *)
    let msg_prefix = 
      ("provided node for " ^ (Lv6Id.string_of_long false (fst nk)) ^ 
          " is not compatible with its implementation: ")
    in
    let str_of_var = Lic.string_of_var_info in
    let type_is_not_comp v1 v2 = not (Lic.var_are_compatible v1 v2) in

    (* Checking the type profile (w.r.t the body and the provided part) *)
    let ibtypes = List.map (fun v -> v.var_type_eff) body_node_exp_eff.inlist_eff
    and iptypes = List.map (fun v -> v.var_type_eff) prov_node_exp_eff.inlist_eff 
    and obtypes = List.map (fun v -> v.var_type_eff) body_node_exp_eff.outlist_eff
    and optypes = List.map (fun v -> v.var_type_eff) prov_node_exp_eff.outlist_eff 
    in
    let _topt = UnifyType.profile_is_compatible nk
      node_def.src (iptypes,ibtypes) (optypes,obtypes)
    in
    if
      prov_node_exp_eff.node_key_eff <> body_node_exp_eff.node_key_eff
    then
      raise(Compile_error (node_def.src, msg_prefix ^ " ??? "))
    else if
        (* ougth to be checked above: well, it eats no bread to keep that check *)
        (List.exists2 type_is_not_comp 
           prov_node_exp_eff.inlist_eff body_node_exp_eff.inlist_eff) 
    then
      let msg = msg_prefix ^ "bad input profile. \n*** " ^ 
        (String.concat "*" (List.map str_of_var prov_node_exp_eff.inlist_eff)) ^
        " <> " ^
        (String.concat "*" (List.map str_of_var body_node_exp_eff.inlist_eff))
      in
      raise(Compile_error (node_def.src, msg))
    else if 
        (List.exists2 type_is_not_comp
           prov_node_exp_eff.outlist_eff body_node_exp_eff.outlist_eff) 
    (* ougth to be checked above: well, it eats no bread to keep that check *)
    then
      let msg = msg_prefix ^ "bad output profile. \n*** " ^ 
        (String.concat "*" (List.map str_of_var prov_node_exp_eff.outlist_eff)) ^
        " <> " ^
        (String.concat "*" (List.map str_of_var body_node_exp_eff.outlist_eff))
      in
      raise(Compile_error (node_def.src, msg))
    else if     
        prov_node_exp_eff.has_mem_eff <> body_node_exp_eff.has_mem_eff 
    then
      raise(Compile_error (node_def.src, msg_prefix ^ " node or function?"))
    else if 
        prov_node_exp_eff.is_safe_eff <> body_node_exp_eff.is_safe_eff
    then
      raise(Compile_error (node_def.src, msg_prefix ^ "safe or unsafe?"))
    else if 
        match prov_node_exp_eff.def_eff, body_node_exp_eff.def_eff with
          | (AbstractLic _,_) -> false
          | (_,_) -> prov_node_exp_eff.def_eff <> body_node_exp_eff.def_eff
            then
              raise(Compile_error (node_def.src, msg_prefix ^ "abstract or not?"))
            else
              match prov_node_exp_eff.def_eff, body_node_exp_eff.def_eff with
                | AbstractLic None, BodyLic  node_body -> 
                  { prov_node_exp_eff with def_eff = 
                                             AbstractLic (Some body_node_exp_eff) }
                | _,_ -> 
                  prov_node_exp_eff

    (* 
       LE GROS DU BOULOT 
       - suivant "provide_flag" : check d'interface (provide) ou le check 
         de la d�finition
       (n.b. provide_flag influence la r�solution des idents dans l'env local de check)
    *)
    and node_check_do
        (this: t)
          (nk: Lic.node_key)
          (lxm: Lxm.t)
          (symbols: AstTabSymbol.t)
          (provide_flag: bool)
          (pack_name: Lv6Id.pack_name)
          (node_def: AstCore.node_info srcflagged)
          : Lic.node_exp =
      (* START node_check_do *)
      profile_info "node_check_do\n";
      (
        Lv6Verbose.exe ~flag:dbg (fun () -> Printf.printf
               "#DBG: ENTERING node_check_do '%s'\n     (%s)\n"
               (Lic.string_of_node_key nk)
               (Lxm.details lxm)
                                 ); 
        let lxm = node_def.src in
        (* Creates a local_env with just the global bindings,
                local bindinds will be added later (side effect)
         *)
        let local_env = make_local_env nk in
        let _ =
          Lv6Verbose.exe ~flag:dbg (fun () -> 
                 Printf.printf "#  local_env while entering (node_check_do %s):\n" 
                               (Lic.string_of_node_key nk);
                 IdSolver.dump_local_env stderr local_env;
                 flush stdout
                                   )
        in
        let node_id_solver = {
          (* a [node_id_solver] is a [id_solver] where we begin to look
                  into the local environement before looking at the global
                  one.  *)
          id2var = (* var can only be local to the node *)
            (fun id lxm ->
             try IdSolver.lookup_var local_env id lxm
             with Not_found ->
               raise (Unknown_var(lxm,id))
            );
          id2const =
            (fun id lxm ->
             try IdSolver.lookup_const local_env id lxm
             with Not_found ->
               solve_const_idref this symbols provide_flag pack_name id lxm
            );
          id2type  =
            (fun id lxm ->
             try IdSolver.lookup_type local_env id lxm
             with Not_found ->
               Lv6Verbose.exe ~level:3 (
                       fun () ->
                         Printf.printf "*** Dont find type %s in local_env\n"
                           (Lv6Id.string_of_idref false id);
                         Printf.printf "*** local_env.lenv_types contain def for: ";
                         Hashtbl.iter 
                           (fun id t -> 
                             Printf.printf "%s, " (Lv6Id.to_string id) )
                           local_env.lenv_types;
                         Printf.printf "\n";
                         flush stdout);
                     solve_type_idref  this symbols provide_flag pack_name id lxm);
          id2node  =
            (fun id sargs lxm ->
             (try
                 let (node_id,sargs) = IdSolver.lookup_node local_env id lxm in
                 let node_id = Lv6Id.idref_of_long node_id in
                 solve_node_idref this symbols provide_flag pack_name node_id sargs lxm
               (*                node_check this (node_id,[]) lxm   *)
                                  
               with 
                 Not_found -> 
                 solve_node_idref this symbols provide_flag pack_name id sargs lxm
               | _ -> assert false)
            );
          
          (* ATTENTION EN SE SERVANT DE CA !
                  ne tient pas compte des params statiques du noeud ! *)
          global_symbols  = symbols;
          all_srcs  = this.src_tab;
        }
        in
        let make_node_eff id node_def_eff = (
          (* building not aliased nodes *)
          Lv6Verbose.exe ~level:3 
                         (fun () -> Printf.printf
            "*** local_env while entering (make_node_eff %s):\n" (Lv6Id.to_string id);
                                    IdSolver.dump_local_env stderr local_env
                         );
          (********************************************************)
          (* LOCAL CONSTANTS are evaluated and added to local_env *)
          (********************************************************)
          (* init intermediate table *)
          let sz = List.length node_def.it.loc_consts in
          let temp_const_eff_tab : (Lv6Id.long, Lic.const Lic.check_flag) Hashtbl.t =
            Hashtbl.create sz
          in
          let temp_const_def_tab :
                (Lv6Id.t,(Lxm.t * AstCore.type_exp option * AstCore.val_exp)) Hashtbl.t =
            Hashtbl.create sz
          in
          let init_local_const (lxm, cinfo) = (
            match cinfo with
            | DefinedConst (i,topt,ve) -> (
              Lv6Verbose.printf ~level:3 " * local const %s will be treated\n" i;
              Hashtbl.add temp_const_def_tab i (lxm,topt,ve)
            )
            | ExternalConst _ 
            | EnumConst _ -> (
              let msg = "*** abstract constant bot allowed within node "
              in
              raise (Compile_error(lxm, msg))
            )
          ) in
          List.iter init_local_const node_def.it.loc_consts ;
          (* differs from node_id_solver only on id2const *)
          let rec local_id_solver = {
            id2var   = node_id_solver.id2var;
            id2const = local_id2const;
            id2type  = node_id_solver.id2type;
            id2node  = node_id_solver.id2node;
            global_symbols  = node_id_solver.global_symbols;
            all_srcs  = node_id_solver.all_srcs;
          }
          and treat_local_const id = (
            Lv6Verbose.printf ~level:3 " * call treat_local_const %s\n" id;
            let id_key = ("", id) in
            try (
              let ce = lookup_const_eff temp_const_eff_tab id_key lxm in
              Lv6Verbose.exe
                ~level:3 (fun() -> Printf.printf 
                                     " * const %s already treated = %s\n" 
                                     id (LicDump.string_of_const_eff false ce));
              ce
            ) with Not_found -> (
                let (lxmdef, toptdef, vedef) = Hashtbl.find temp_const_def_tab id in
                Lv6Verbose.printf ~level:3 " * const %s not yet treated ...\n" id ;
                (* yes, not yet checked *) 
                Hashtbl.add temp_const_eff_tab id_key Checking ;
                (* computes the value with EvalConst.f id_solver ve ... *)
                let ce = match (EvalConst.f local_id_solver vedef) with
                  | [ceff] -> (
                    match toptdef with
                    | None -> ceff
                    | Some texp -> (
                      let tdecl = Ast2lic.of_type local_id_solver texp in
                      let teff =  Lic.type_of_const ceff in
                      if (tdecl = teff ) then ceff else 
                        raise (Compile_error (
                                   lxmdef, Printf.sprintf " this constant is declared as '%s' but evaluated as '%s'"
                                 (Lic.string_of_type tdecl)
                                 (Lic.string_of_type teff)
                    )))
                  )
                  | [] -> assert false (* should not occur *)
                  | _::_ -> raise (Compile_error(lxmdef, "bad constant value: tuple not allowed"))
                in
                Lv6Verbose.exe
                  ~level:3 (fun() -> Printf.printf " * const %s evaluated to %s\n"
                     id (LicDump.string_of_const_eff false ce));
                Hashtbl.replace temp_const_eff_tab id_key (Checked ce) ;
                ce
              )
          )
          and local_id2const idrf lxm = (
            (* is id a local const ? *)
            try (
              (* certainly NOT if id has a pack *)
              let id = if (Lv6Id.pack_of_idref idrf = None)
                       then Lv6Id.name_of_idref idrf
                       else raise Not_found
              in
              let ce = treat_local_const id in
              ce
            ) with Not_found -> (
                (* not a local constant -> search in global env *)
                Lv6Verbose.printf ~level:3 
                " * %s not a local const, should be global ?" 
                (Lv6Id.string_of_idref false idrf);
                let ce = node_id_solver.id2const idrf lxm in
                Lv6Verbose.exe ~level:3 (fun() -> Printf.printf
                     " YES -> %s\n" (LicDump.string_of_const_eff false ce));
                   ce
              )
          ) in
          (* iters local_id2const n eeach declared constant *)
          Hashtbl.iter (fun id _ -> let _ = treat_local_const id in ())
                       temp_const_def_tab ;
          (* Finally, adds each local const to ICI *)
          let add_local_const idref ceck = (
            Lv6Verbose.exe
              ~level:3
              (fun() -> Printf.printf 
                          " * add_local_const %s = %s\n" (snd idref)
                          (match ceck with
                           | Checking -> "Checking"
                           | Checked ce -> (LicDump.string_of_const_eff false ce)
                           | Incorrect -> "Incorrect"
                          ));
            match ceck with
            | Checked ce -> Hashtbl.add local_env.lenv_const (snd idref) ce
            | _ -> assert false
          ) in
          Hashtbl.iter add_local_const temp_const_eff_tab ;
               
          (********************************************************)
          (* LOCAL FLOWS are added to local_env                   *)
          (********************************************************)
          (* (i.e. ins,outs,locs) *)
          match node_def.it.vars with
          | None -> assert false (* a node with a body should have a profile *)
          | Some vars ->
             (* let is_polymorphic = ref false in *)
             let type_args id =
               let vi = find_var_info lxm vars id in
               let t_eff = Ast2lic.of_type node_id_solver vi.it.var_type in
               (* let _ = if Lic.is_polymorphic t_eff then is_polymorphic := true in *)
               let c_eff = Ast2lic.of_clock node_id_solver vi.it in
               let vi_eff = {
                 var_name_eff   = vi.it.var_name;
                 var_nature_eff = vi.it.var_nature;
                 var_number_eff = vi.it.var_number;
                 var_type_eff  = t_eff;
                 var_clock_eff = c_eff;
               }
               in
               Hashtbl.add local_env.lenv_types id t_eff;
               Hashtbl.add local_env.lenv_vars id vi_eff;
               vi_eff
             in
             let vars_in_sorted  = sort_vars lxm vars vars.inlist
             and vars_out_sorted = sort_vars lxm vars vars.outlist in
             let inlist  = List.map type_args vars_in_sorted
             and outlist = List.map type_args vars_out_sorted
             and loclist = 
               match vars.loclist with
               | None -> None
               | Some loclist -> 
                  let vars_loc_sorted = sort_vars lxm vars loclist in
                  Some (List.map type_args vars_loc_sorted)
             in
             let unsort l_id l_vi =
               let tab = List.map (fun vi -> vi.var_name_eff, vi) l_vi in
               try List.map (fun id -> List.assoc id tab) l_id
               with Not_found -> assert false
             in
             profile_info "LicTab.unsort\n";
             let inlist2 = unsort vars.inlist inlist
             and outlist2 = unsort vars.outlist outlist in
             {
               node_key_eff = nk;
               inlist_eff   = inlist2;
               outlist_eff  = outlist2;
               loclist_eff  = loclist;
               def_eff = node_def_eff ();
               has_mem_eff  = node_def.it.has_mem;
               is_safe_eff  = node_def.it.is_safe;
               lxm = node_def.src;
             (* is_polym_eff = !is_polymorphic *)
             }
        ) in
        (* let's go *)
        let res =
          match node_def.it.def with
          | Abstract -> make_node_eff node_def.it.name (fun () -> AbstractLic None)
          | Extern   -> make_node_eff node_def.it.name (fun () -> ExternLic)
          | Body nb  ->
             make_node_eff node_def.it.name ( 
                             (fun () -> (* trick to force to delay this evaluation 
                                   after the local_env.lenv_vars has been
                                   filled
                                *)
             let eq_eff = List.map (Ast2lic.of_eq node_id_solver) nb.eqs in
             BodyLic {
                 asserts_eff = 
                   List.map (Ast2lic.of_assertion node_id_solver) nb.asserts;
                 eqs_eff = eq_eff; 
               }
                             )
                           )
                           
          | Alias({it= alias;src=lxm}) -> (
            let aliased_node = 
              match alias with
              (* 12/07 SOLUTION INTERMEDIAIRE 
                          - les macros predefs sont trait�es comme des call 
               *)
              | Predef_n(op) -> 
                 let predef_op = op.it in
                 let _ = match predef_op with
                   | AstPredef.NOR_n 
                   | AstPredef.DIESE_n ->
                      raise (Compile_error (lxm, "Can not alias 'nor' nor '#', sorry"))
                   | _ -> ()
                 in
                 let predef_op_eff = LicEvalType.make_node_exp_eff 
                                       node_id_solver (Some node_def.it.has_mem) 
                                       true predef_op lxm
                 in
                 predef_op_eff
                   
              | CALL_n(node_alias) -> 
                 Ast2lic.of_node node_id_solver node_alias 
              | (ARRAY_SLICE_n _|ARRAY_ACCES_n _|STRUCT_ACCESS_n _
                 |IDENT_n _|ARRAY_n|HAT_n|CONCAT_n|WITH_n(_)|TUPLE_n|WHEN_n _
                 |CURRENT_n|FBY_n|ARROW_n|PRE_n)
                -> 
                 raise (Compile_error (lxm, "can not alias this operator, sorry"))
            (* does it make sense to alias when, pre, etc? *)
            in
            let (vil, vol) = 
              match node_def.it.vars with
              | None -> aliased_node.inlist_eff, aliased_node.outlist_eff
              | Some (vars) ->
                 (* a type profile is declared; let's check there are compatible *)
                 let (il,ol) = profile_of_node_exp aliased_node in
                 let (il_decl, ol_decl) = 
                   let vi_il, vi_ol = 
                     List.map (fun id -> find_var_info lxm vars id) vars.AstCore.inlist,
                     List.map (fun id -> find_var_info lxm vars id) vars.AstCore.outlist
                   in
                   let aux vi = Ast2lic.of_type node_id_solver vi.it.var_type in
                   let (il_decl, ol_decl) = List.map aux vi_il, List.map aux vi_ol in
                   let i_unif_res = UnifyType.f il_decl il
                   and o_unif_res = UnifyType.f ol_decl ol
                   in
                   (match i_unif_res with
                    | UnifyType.Ko msg -> raise(Compile_error(lxm, msg))
                    | UnifyType.Equal -> ()
                    | UnifyType.Unif t -> () 
                   (* Ast2lic.dump_polymorphic_nodes t *)
                   );
                   (match o_unif_res with
                    | UnifyType.Ko msg -> raise(Compile_error (lxm, msg))
                    | UnifyType.Equal -> ()
                    | UnifyType.Unif t ->  ()
                   (* Ast2lic.dump_polymorphic_nodes t *)
                   );
                   (* ok, there are compatible. We use the declared profile. *)
                   (il_decl, ol_decl)
                 in
                 let instanciate_var_info vi t = { vi with var_type_eff = t } in
                 let vil = List.map2 instanciate_var_info aliased_node.inlist_eff
                                     il_decl 
                 and vol = List.map2 instanciate_var_info aliased_node.outlist_eff
                                     ol_decl in
                 vil,vol
            in
            let (alias_node : Lic.node_exp) = 
              try make_alias_node  aliased_node nk local_env node_id_solver
                                   vil vol node_def.src
                     with Not_found -> assert false (* defense against List.assoc *)
                   in
                   alias_node
          )
        (* End Alias *)
        in
        L2lCheckOutputs.check_node res;
             (* gen_code provide_flag current_env res; *)
        Lv6Verbose.exe ~flag:dbg (fun() -> Printf.printf
                                             "#DBG: EXITING  node_check_do '%s'\n"
                                             (Lic.string_of_node_key nk)
                                 ); 
        res
      )
    (*END node_check_do *)

    (* 
       [make_alias_node aliased_node alias_nk node_id_solver_vars_opt lxm]
       builds a node that  calls the aliased node. It looks like:
       node  alias_node(ins) returns (outs);  
       let 
       outs = aliased_node(ins); 
       tel
       
       When instanciating models with polymorphic operators, it
       may happen that some exported user nodes become
       polymorphic (via node alias precisely). But in that case,
       a non-polymorphic profile is given in the package provided
       part. In such a case, we can use the types of the provided
       part (itl and otl) instead of the polymorphic ones.  *)

    and make_alias_node
        (aliased_node: node_exp)
          (alias_nk: node_key)
          (local_env: local_env)
          (node_id_solver: IdSolver.t)
          (vil: var_info list)
          (vol: var_info list)
          (lxm: Lxm.t)
          : node_exp
        =
           Lv6Verbose.printf ~level:3 
             "*** Lic.make_alias_node %s \n" (Lv6Id.string_of_long false (fst alias_nk));
      flush stdout;

      let (outs:left list) = List.map  (fun vi -> LeftVarLic (vi, lxm)) vol in
      let tl = List.map type_of_left outs in
      let cl = List.map (fun l -> (var_info_of_left l).var_clock_eff) outs in
      let (aliased_node_call : val_exp) =
        { ve_core = 
            CallByPosLic(
              (Lxm.flagit (CALL(Lxm.flagit aliased_node.node_key_eff lxm)) lxm, 
               (List.map 
                  (fun vi -> (* build operands*)
                    let ve = { 
                      ve_typ = [vi.var_type_eff]; 
                      ve_clk = [snd vi.var_clock_eff];
                      ve_core = CallByPosLic(
                        Lxm.flagit (VAR_REF(vi.var_name_eff)) lxm, []);
                      ve_src = lxm
                    }
                    in
                    ve
                  )
                  vil)));
          ve_typ = tl;
          ve_clk = List.map snd cl;
          ve_src = lxm
        }
      in
      let alias_node = 
        match aliased_node.def_eff with
          | BodyLic _ -> { aliased_node with node_key_eff = alias_nk }
          | _  -> {
            aliased_node with
              node_key_eff = alias_nk;
              inlist_eff = vil;
              outlist_eff = vol;
              loclist_eff = None;
              def_eff = BodyLic(
                { asserts_eff = []; 
                  eqs_eff = [Lxm.flagit (outs, aliased_node_call) lxm] 
                });
            (* is_polym_eff = List.exists is_polymorphic (List.map (fun vi 
                 -> vi.var_type_eff) (vil@vol)); *)
            }
      in
      (* update the local_env table *)
      let _ = 
        let update_local_env_table vi =
          Hashtbl.add local_env.lenv_vars vi.var_name_eff vi
        in
        List.iter update_local_env_table alias_node.inlist_eff;
        List.iter update_local_env_table alias_node.outlist_eff;
        match alias_node.loclist_eff with 
            None -> () | Some l -> List.iter update_local_env_table l;
      in
      alias_node

    (** builds a [node_key] and calls [node_check] *)
    and solve_node_idref
        (this: t)
          (symbols: AstTabSymbol.t)
          (provide_flag: bool)
          (currpack: Lv6Id.pack_name)
          (idr: Lv6Id.idref)
          (sargs: Lic.static_arg list)
          (lxm: Lxm.t)
          : Lic.node_exp =

           solve_x_idref
             node_check_interface node_check AstTabSymbol.find_node "node"
             (fun p id ->
               (* builds a [node_key] from a [pack_name] and a [node] id, 
                  and a Lic.static_arg list *)
               let long = Lv6Id.make_long p id in
               let node_key = long, sargs in
               node_key
             )
             this symbols provide_flag currpack idr sargs lxm

    and node_check (this: t) (nk: Lic.node_key) (lxm: Lxm.t) : Lic.node_exp =
      Lv6Verbose.printf ~flag:dbg 
                        "#DBG: licTab.node_check '%s'\n" (Lic.string_of_node_key nk);
      try (
        let pack_of_x_key = fun nk -> Lv6Id.pack_of_long (fst nk) in
        let name_of_x_key = fun nk -> Lv6Id.of_long (fst nk) in
        x_check this.nodes AstTabSymbol.find_node node_check_do 
                node_builtin lookup_node_exp_eff
                pack_of_x_key
                name_of_x_key
                this nk lxm
      ) with
          Recursion_error (n, stack) -> 
            let msg = "Recursion loop detected in node " ^ 
                        (Lv6Id.string_of_long false (fst nk)) in
            let msg = msg ^ "\n*** "^ (Lv6Id.string_of_long false n) ^
                        " depends on itself\n " ^ (String.concat "\n*****" stack) in
            raise (Compile_error (lxm, msg))

    and node_check_interface
        (this: t)
          (nk: Lic.node_key)
          (lxm: Lxm.t)
          : Lic.node_exp =
           Lv6Verbose.printf ~flag:dbg "#DBG: licTab.node_check_interface '%s'\n" 
                             (Lic.string_of_node_key nk);
      x_check_interface this.prov_nodes AstTabSymbol.find_node node_check
        node_check_interface_do node_builtin lookup_node_exp_eff
        (fun nk -> Lv6Id.pack_of_long (fst nk))
        (fun nk -> Lv6Id.of_long (fst nk)) this nk
        lxm
      

    
(*-------------------------------------------------------------------------
compile all items
  ---------------------------------------------------------------------------*)

let compile_all_item this label  x_check_interface string_of_x_key
    string_of_x_eff to_key id item_def =
  match item_def with
    | AstTabSymbol.Local _item_def ->
        ignore
          (x_check_interface this (to_key id) (Lxm.dummy "compile all items"))
(*        Printf.printf "\t\t%s %s = %s\n" *)
(*          label (string_of_x_key (to_key id)) (string_of_x_eff x_eff) *)

    | AstTabSymbol.Imported(item_def,_) -> ()
(*      Printf.printf "\t\t%s %s = %s (imported)\n" *)
(*        label (string_of_x_key (to_key id)) (Lv6Id.string_of_long item_def) *)
   

let compile_all_types pack_name this =
  compile_all_item this "type" type_check_interface (Lv6Id.string_of_long false)
    Lic.string_of_type (fun id -> Lv6Id.make_long pack_name id)

let compile_all_constants pack_name this = 
  compile_all_item this "const" const_check_interface  (Lv6Id.string_of_long false)
    (LicDump.string_of_const_eff true) (fun id -> Lv6Id.make_long pack_name id)


let (get_static_params : (node_info Lxm.srcflagged) AstTabSymbol.elt -> 
      static_param srcflagged list) =
  fun node_info_flagged -> 
    match node_info_flagged with
      | AstTabSymbol.Local nif -> nif.it.static_params
      | AstTabSymbol.Imported(id,sparams) -> sparams

let compile_all_nodes pack_name this id ni_f =
  let sp = get_static_params ni_f in
    if sp <> [] then () (* we need static arg to compile such kind of things *)
    else
      compile_all_item this "node" node_check_interface 
        (LicDump.string_of_node_key_rec true false)
        Lic.profile_of_node_exp 
        (fun id -> (Lv6Id.make_long pack_name id, [])) id ni_f

(**** to_lic : translate the (finalized) internal structure
  into a proper LicPrg, for forthcoming manip and other prg 2 prg
  transformations
  N.B. items belonging to the "Lustre" virtual pack are not 
  taken into account
*)

let to_lic_prg (this:t) : LicPrg.t =
   (* normally, only checked and correct programs are lic'ified *)
   let unflag = function Checked x -> x | _ -> assert false in
   let add_item add_x k v prg =
      match Lv6Id.pack_of_long k with
      | "Lustre" -> prg
      | _ -> add_x k (unflag v) prg
   in
   let add_node k v prg =
      Lv6Verbose.printf ~flag:dbg "#DBG: licTab.to_lic: node key '%s'\n"
        (Lic.string_of_node_key k);
      match Lv6Id.pack_of_long (fst k) with
(*         | "Lustre" -> prg *)
        | _ -> LicPrg.add_node k (unflag v) prg
   in
   let res = LicPrg.empty in
   let res = Hashtbl.fold (add_item LicPrg.add_type) this.types res in 
   let res = Hashtbl.fold (add_item LicPrg.add_const) this.consts res in 
   let res = Hashtbl.fold add_node this.nodes res in 
   res

(**** Entry points of the module :
   either compile a single node or everithing ...  
*)
let compile_all (this:t) : t =
  let testpack pack_name = (
    Lv6Verbose.printf ~level:3 " * package %s\n" (Lv6Id.pack_name_to_string pack_name);
    let prov_symbols =
      match AstTab.pack_prov_env this.src_tab pack_name with
        | Some tab -> tab
        | None -> AstTab.pack_body_env this.src_tab pack_name
    in
    Lv6Verbose.print_string ~level:3 "\tExported types:\n";
    AstTabSymbol.iter_types prov_symbols (compile_all_types pack_name this);
    flush stdout;
    Lv6Verbose.print_string ~level:3 "\tExported constants:\n";
    AstTabSymbol.iter_consts prov_symbols (compile_all_constants pack_name this);
    flush stdout;
    Lv6Verbose.print_string ~level:3 "\tExported nodes:\n";
    AstTabSymbol.iter_nodes prov_symbols (compile_all_nodes pack_name this);
    flush stdout
  )
  in
  let plist = AstTab.pack_list this.src_tab in
  Lv6Verbose.print_string ~level:3 "*** Dump the exported items of the packages.\n";
  try
    List.iter testpack plist;
    this
  with
      Recursion_error (n, stack) -> 
        let msg = "Recursion loop detected in node " ^ (Lv6Id.string_of_long false n) in
        let msg = msg ^ "\n*****" ^ (String.concat "\n*****" stack) in
        raise (Compile_error (Lxm.dummy "", msg))

let compile_node (this:t) (main_node:Lv6Id.idref) : t =
   (* la cl�e "absolue" du main node (pas d'args statiques) *)
   let main_node_key = node_key_of_idref main_node in
   profile_info "LicTab.compile_node\n";
   Lv6Verbose.printf
      "-- MAIN NODE: \"%s\"\n"
      (LicDump.string_of_node_key_rec true false main_node_key);

   let lxm = match Lv6Id.pack_of_idref main_node with
      | None -> Lxm.dummy ""
      | Some pn  -> Lxm.dummy (Lv6Id.pack_name_to_string pn)
   in
   let _ = node_check this main_node_key lxm in
   this