(** Time-stamp: <modified the 09/12/2008 (at 17:36) by Erwan Jahier> *)

(** 
    Table des infos sources : une couche au dessus de SyntaxTree pour mieux
    ranger les packages et les mod�les et faciliter la r�solution des
    identificateurs.

    1) expansion des mod�les

    2) pour chaque package instanci�, 2 tables de symboles :

    - une pour la vision "export�e"
    - une pour la vision interne. Chaque table de symbole, 3 "espaces"
    de noms (par nature d'items, type/const/node)

    Ces tables sont destin�es � r�soudre les r�f�rences simples, elle
    associent � une string :

    - la definition syntaxique de l'item associ� s'il est local
    - l'identificateur absolu (package+nom) si il est externe
*)

open Lxm
open SyntaxTree
open SyntaxTreeCore
open Errors

(** Package manager

    Un package manager (pack_mng) contient les infos ``source'' du
    package + DEUX tables de symboles, correspondant aux deux contextes
    possibles de compilation :

    - compilation du provide
    - compilation du body

    En effet, un identificateur de type, de constante ou de noeud
    n'est pas interpr�t� de la m�me mani�re suivant qu'il appara�t
    dans la partie provide ou body.

    Il contient aussi une table des items export�s pour faciliter le
    traitement des "use" du package. C'est une correspondance 

        nature + nom simple -> nom complet 

    (c.a.d. ??? + SyntaxTree.item_ident -> Ident.long)
*)

type pack_mng = {
  (* le lexeme associ� au package? *)
  pm_lxm : Lxm.t;
  (* le source brut *)
  pm_raw_src : SyntaxTree.pack_info;
  (* le source expans� *)
  pm_actual_src : SyntaxTree.pack_given;
  (* table "brute" des items provided *)
  (* pour les "user" du pack *)
  pm_user_items : (SyntaxTreeCore.item_ident, Ident.long Lxm.srcflagged) Hashtbl.t;
  (* les tables de symboles pour compil ult�rieure *)
  pm_body_stab : SymbolTab.t;
  (* la table pour provide n'est cr��e que si besoin ... *)
  pm_provide_stab : SymbolTab.t option;
}

(** TYPE PRINCIPAL : t

    Packages et mod�les sont rang�s dans des tables, ce qui permet
    notamment de traiter les erreurs de multi-d�clarations
    (st_raw_mod_tab et st_raw_pack_tab)

    Les instances de modeles sont trait�es pour n'avoir plus que des
    ``pack_given'' (i.e. pack avec provide + body)

    � chaque package (�ventuellement expans�) est associ� un manager
    pour faciliter l'acc�s � ses infos (pack_mng)
*)
type t = {
  (* liste + tables des sources bruts *)
  st_list : SyntaxTree.pack_or_model list ;
  st_raw_mod_tab  : (Ident.t , model_info srcflagged) Hashtbl.t ;
  st_raw_pack_tab : (Ident.pack_name , pack_info srcflagged) Hashtbl.t ;
  (* table des managers de packs *)
  st_pack_mng_tab : (Ident.pack_name , pack_mng)  Hashtbl.t; 
}

(* exported *)
let (pack_list:t -> Ident.pack_name list) =
  fun this -> 
    Hashtbl.fold (fun n p l -> n::l) this.st_pack_mng_tab []


(* exported *)
let (pack_body_env: t -> Ident.pack_name -> SymbolTab.t) =
  fun this p -> 
    try  (Hashtbl.find this.st_pack_mng_tab p).pm_body_stab
    with Not_found -> 
      print_string ("*** Can not find package '" ^ 
		      (Ident.pack_name_to_string p) ^ "' in the following packages: ");
      Hashtbl.iter 
	(fun pn pm -> print_string ("\n***\t '"^(Ident.pack_name_to_string pn)^ "'"))
	this.st_pack_mng_tab;
      print_string "\n";
      flush stdout;
      exit 2


(* exported *)
let (pack_prov_env: t -> Ident.pack_name -> Lxm.t -> SymbolTab.t option) =
  fun this p lxm -> 
    try (Hashtbl.find this.st_pack_mng_tab p).pm_provide_stab
    with Not_found -> 
(*       let msg =  *)
(*         ("\n*** Could not find package " ^(Ident.pack_name_to_string p) ^  *)
(*            " in the package table" ) *)
(*       in *)
	None  
(*         raise(Compile_error(lxm, msg))   *)
      



(****************************************************************************
init de la table des items provided (pour les users)
****************************************************************************)
let init_user_items (this: pack_mng) = (
  let pname = Ident.pack_name_of_string (Lxm.str this.pm_lxm) in

  (** Exportation D'une const_info *)
  let export_const (s:Ident.t) (xci: SyntaxTreeCore.const_info srcflagged) =
	Verbose.printf ~level:3 "       export const %s\n" (Ident.to_string s);
	SyntaxTabUtils.put_in_tab "const" this.pm_user_items
	  (ConstItem s)
	  (Lxm.flagit (Ident.make_long pname s) xci.src)
  in
    
  (** Exportation D'un type_info *)
  let export_type (s: Ident.t) (xti: SyntaxTreeCore.type_info srcflagged) = 
    ( match (xti.it) with
	| EnumType (_, ecl) -> (
	    (* Cas particulier des types enums *)
	    (* on exporte les constantes ... *)
	    let treat_enum_const ec = 
	      let s = ec.it in
	      let lxm = ec.src in
		Verbose.printf ~level:3 "       export enum const %s\n" (Ident.to_string s);
		SyntaxTabUtils.put_in_tab "const" this.pm_user_items
		  (ConstItem s)
		  (Lxm.flagit (Ident.make_long pname s) lxm)
	    in
	      List.iter treat_enum_const ecl
	  )
	| ExternalType _
	| AliasedType _ 
	| StructType _ 
	| ArrayType _ 
	  -> ()
    );
    Verbose.printf ~level:3 "       export type %s\n" (Ident.to_string s);
    SyntaxTabUtils.put_in_tab "type" this.pm_user_items
      (TypeItem s)
      (Lxm.flagit (Ident.make_long pname  s) xti.src)
  in

  (** Exportation D'un node_info *)
  let export_node (s: Ident.t) (xoi: SyntaxTreeCore.node_info srcflagged) = 
    Verbose.printf ~level:3 "       export node %s\n" (Ident.to_string s);
    SyntaxTabUtils.put_in_tab "node" this.pm_user_items
      (NodeItem (s,xoi.it.static_params))
      (Lxm.flagit (Ident.make_long pname s) xoi.src)
  in

  let pg = this.pm_actual_src in
    match pg.pg_provides with
      | None ->
	  (* On Exporte Tout Tel Quel *)
	  Hashtbl.iter export_type  pg.pg_body.pk_type_table ;
	  Hashtbl.iter export_const pg.pg_body.pk_const_table ;
	  Hashtbl.iter export_node  pg.pg_body.pk_node_table ;
      | Some spflg ->
	  (* On Exporte Les Provides *)
	  let treat_prov x = 
	    let lxm = x.src in
	    let s = Lxm.id lxm in
	      match (x.it) with
		  TypeInfo xti -> export_type s (Lxm.flagit xti lxm)
		| ConstInfo xci -> export_const s (Lxm.flagit xci lxm)
		| NodeInfo xoi -> export_node s (Lxm.flagit xoi lxm)
	  in
	    List.iter treat_prov spflg
)

(*
Cr�ation/initialisation d'un pack_mng :
On pr�pare juste la table des items provided
pour pouvoir traiter les �ventuels "use" des autres pack.
Les tables de symboles sont cr��es plus tard.
*)
let create_pack_mng 
    (pdata : SyntaxTree.pack_info srcflagged)
    (pgiven : SyntaxTree.pack_given)
    = (
      (* la table pm_provide_stab n'est cr��e que si besoin *)
      let ppstab = match pgiven.pg_provides with
	  None -> None 
	| Some _ -> Some (SymbolTab.create ())
      in
      let res =
	{
	  pm_lxm = pdata.src ;
	  pm_raw_src = pdata.it;
	  pm_actual_src = pgiven;
	  pm_user_items = Hashtbl.create 50;
	  pm_provide_stab = ppstab;
	  pm_body_stab = SymbolTab.create ();
	} 
      in
	init_user_items res;
	res
    )



(****************************************************************************
CREATION
-----------------------------------------------------------------------------
Se fait en plusieurs passes :

1) mise en place des tables "raw" mod et pack (string -> source pack/mod)
2) instanciations �ventuelle des packs (voir InstanciateModel)
   et initialisation des pack_mng (en particulier des infos pour les users)
3) pour chaque pack, cr�ation des symbol_table contextuelles
   (pour la partie provide et pour la partie body)
****************************************************************************)

let rec (create  : SyntaxTree.pack_or_model list -> t) =
  fun sl -> 
    (* liste + tables des sources bruts *)
    let res = {
      st_list = sl ;
      st_raw_mod_tab = Hashtbl.create 50;
      st_raw_pack_tab = Hashtbl.create 50; 
      st_pack_mng_tab = Hashtbl.create 50; 
    } 
    in
      Verbose.printf ~level:3 "*** SyntaxTab.create pass 1\n";
      (* passe 1 *)
      init_raw_tabs res sl ;
      (* passe 2 *)
      Verbose.printf ~level:3 "*** SyntaxTab.create pass 2\n";
      let init_pack_mng pname pdata = (
	Verbose.printf ~level:3 "    init pack %s\n" (Ident.pack_name_to_string pname);
	let pg = InstanciateModel.f res.st_raw_mod_tab pdata in
	  Hashtbl.add res.st_pack_mng_tab 
	    pname
	    (create_pack_mng pdata pg)
      ) in
	Hashtbl.iter init_pack_mng res.st_raw_pack_tab ;
	(* passe 3 *)
	Verbose.printf ~level:3 "*** SyntaxTab.create pass 3\n";
	Hashtbl.iter (init_pack_mng_stabs res) res.st_pack_mng_tab ;
	(* resultat *)
	Verbose.printf ~level:3 "*** SyntaxTab.create done\n";
	res
 and
(***** PASSE 1 *****)
(* init des tables string -> mod ou pack *)
    init_raw_tabs (this : t) (sl : SyntaxTree.pack_or_model list) =
    (* on it�re pour chaque pack_or_model : *)
  let treat_ns ns = 
      match ns with
	  (* cas d'un package *)
	| SyntaxTree.NSPack  pi ->
	    let lxm = pi.Lxm.src in
	    let nme = (Ident.pack_name_of_string (Lxm.str lxm)) in
	      SyntaxTabUtils.put_in_tab "package" this.st_raw_pack_tab nme pi
	   	
	| SyntaxTree.NSModel mi -> (* cas d'un modele *)
	    let lxm = mi.Lxm.src in
	    let nme = (Lxm.id lxm) in
	      SyntaxTabUtils.put_in_tab "model" this.st_raw_mod_tab nme mi
    in
      List.iter treat_ns sl
 and
(***** PASSE 3 *****)
(* Essentiellement le remplissage des champs de pack_mng : 

   pm_provide_stab : SymbolTab.t
   table qui permettra de r�soudre les refs simples
   � l'int�rieur de la partie provides.

   pm_body_stab : SymbolTab.t ;
   table qui permettra de r�soudre les refs simples
   � l'int�rieur de la partie body.

   N.B. s'il n'y a pas de provides explicite, on construit
   une unique table qui sert pour les deux !

   Comment �a marche :
   - on traite en premier les �ventuels "use", (= open de ocaml)
   - puis les d�clarations locales qui peuvent �ventuellement
   masquer les pr�c�dentes (warning ?)
*)
init_pack_mng_stabs (this: t) (pname: Ident.pack_name) (pm: pack_mng) = (
  let pg = pm.pm_actual_src in

    Verbose.printf ~level:3 "   init symbol tables for pack %s\n"
      (Ident.pack_name_to_string pname);
    (* ON COMMENCE PAR TRAITER LE PG_USES *)
    let treat_uses (px:Ident.pack_name srcflagged) = (
      let pname = px.it in
      let lxm = px.src in
      let pum = 
	try Hashtbl.find this.st_pack_mng_tab pname
	with Not_found -> raise(Compile_error(lxm, "unknown package"))
      in
      let fill_used_item
	  (ii: SyntaxTreeCore.item_ident)
	  (iks: Ident.long Lxm.srcflagged) =
	(match ii with
	   | ConstItem n -> (
	       SymbolTab.add_import_const pm.pm_body_stab px.it n iks.it;
	       match pm.pm_provide_stab with
		   Some pt -> SymbolTab.add_import_const pt px.it n iks.it
		 | None -> ()
	     )
	   | TypeItem n -> (
	       SymbolTab.add_import_type pm.pm_body_stab n iks.it;
	       match pm.pm_provide_stab with
		   Some pt -> SymbolTab.add_import_type pt n iks.it
		 | None -> ()
	     )
	   | NodeItem (n,sparams) -> (
	       SymbolTab.add_import_node pm.pm_body_stab n iks.it sparams;
	       match pm.pm_provide_stab with
		   Some pt -> SymbolTab.add_import_node pt n iks.it sparams
		 | None -> ()
	     )
	) 
      in
	Hashtbl.iter fill_used_item pum.pm_user_items
    )
    in
      List.iter treat_uses pg.pg_uses ;

      (* PUIS LES DECLARATION LOCALES *)
      (* ... dans le body : *)
      Hashtbl.iter (SymbolTab.add_type pm.pm_body_stab pname) pg.pg_body.pk_type_table;
      Hashtbl.iter (SymbolTab.add_const pm.pm_body_stab pname) pg.pg_body.pk_const_table;
      Hashtbl.iter (SymbolTab.add_node pm.pm_body_stab)	 pg.pg_body.pk_node_table;
      (* ... dans le provide : *)
      match pg.pg_provides with
	| None -> () 
	| Some spflg -> (
	    let pptab = match pm.pm_provide_stab with
		Some pt -> pt
	      | None -> assert false
	    in
	    let treat_prov x = 
	      let lxm = x.src in
	      let s = Lxm.id lxm in
		match (x.it) with
		  | TypeInfo xti  -> SymbolTab.add_type pptab pname s (Lxm.flagit xti lxm) 
		  | ConstInfo xci -> SymbolTab.add_const pptab pname s (Lxm.flagit xci lxm)
		  | NodeInfo xoi  -> SymbolTab.add_node pptab s (Lxm.flagit xoi lxm)
	    in
	      List.iter treat_prov spflg
	  )
)

(****************************************************************************
Associations :
--------------
- Ident.t -> Ident.long * SyntaxTreeCore.xxxx_info

****************************************************************************)

(* associations idref -> Ident.long *)
let find_type (genv: t) (pck: string) (idr: Ident.t) = 
  print_string "*** not implemented.\n";
  assert false

let find_const (genv: t) (pck: string) (idr: Ident.t) = 
  print_string "*** not implemented.\n";
  assert false

let find_node (genv: t) (pck: string) (idr: Ident.t) =
  print_string "*** not implemented.\n";
  assert false



(* exported *)
let (dump : t -> unit) =
  fun x -> 
    let p = Verbose.print_string ~level:3 in
      p "*** � Syntax table dump:\n";
    
      p " \t - Package or model list:\n\t\t";
      (* st_list : SyntaxTree.pack_or_model list ; *)
      List.iter 
	(fun pm -> p (SyntaxTree.pack_or_model_to_string pm); p "\n\t\t") 
	x.st_list ;
      
      p "\n\t - Raw model table: ";
      (* st_raw_mod_tab  : (Ident.t , model_info srcflagged) Hashtbl.t ; *)
      Hashtbl.iter 
	(fun id _mi -> p ((Ident.to_string id) ^ " "))
	x.st_raw_mod_tab;
      
      p "\n\t - Raw Package table: ";
      (* st_raw_pack_tab : (Ident.pack_name , pack_info srcflagged) Hashtbl.t ; *)
      Hashtbl.iter 
	(fun pn pi -> p ((Ident.pack_name_to_string pn) ^ " ")) 
	x.st_raw_pack_tab;
      
      p "\n\t - Package manager table: ";
      (* st_pack_mng_tab : (Ident.pack_name , pack_mng)  Hashtbl.t;  *)
      Hashtbl.iter 
	(fun pn pm -> p ((Ident.pack_name_to_string pn) ^ " ")) 
	x.st_pack_mng_tab;
      
      p "\nEnd of Syntax table dump. �\n"