Skip to content
Snippets Groups Projects
l2lCheckLoops.ml 4.33 KiB
(* Time-stamp: <modified the 11/07/2017 (at 11:52) by Erwan Jahier> *)

open Lxm
open Lv6errors
open Lic
open Lv6Misc

module IdMap = Map.Make(struct type t = Lv6Id.t let compare = compare end)
module IdSet = Set.Make(struct type t = Lv6Id.t let compare = compare end)

(* Associate to an ident the set of idents it depends on *)
type dependencies = (Lxm.t * IdSet.t) IdMap.t

(*********************************************************************************)
(* Compute the set of vars appearing in an expression *)
let rec (vars_of_exp : Lic.val_exp -> IdSet.t) =
  fun ve ->
  let rec aux s ve =
    vars_of_val_exp_core s ve.ve_core
    and
      vars_of_val_exp_core s = function
      | CallByPosLic ({ it=FBY }, _) 
      | CallByPosLic ({ it=PRE }, _) 
        -> s (* pre is not a dependance! *)
      | CallByPosLic (by_pos_op, vel) -> 
         let s = vars_of_by_pos_op s by_pos_op.it in
         List.fold_left aux s vel
      | Merge(ce, l) -> 
         let s = aux s ce in
         List.fold_left (fun s (_,ve) -> aux s ve) s l
      | CallByNameLic(_, _) -> s
    and 
      vars_of_by_pos_op s = function
      | VAR_REF id -> IdSet.add id s
      | PREDEF_CALL(_)
      | ARRAY_SLICE _ | ARRAY_ACCES _  | ARROW | FBY | CURRENT _ | WHEN _ 
      | ARRAY | HAT(_) | STRUCT_ACCESS _
      | TUPLE | CONCAT | CONST_REF _ | CALL _ | CONST _ -> s
      | PRE -> assert false
    and 
      vars_of_static_arg s = function
      | ConstStaticArgLic(id,_)
      | TypeStaticArgLic(id,_)
      | NodeStaticArgLic(id,_) -> IdSet.add id s
  in
  aux IdSet.empty ve
(*********************************************************************************)

exception DepLoop of (Lxm.t * string)
exception Error of (Lxm.t * string * LicPrg.t)

type visit_status = Todo | Doing | Done
type visit_info = visit_status IdMap.t
let (status : Lv6Id.t -> visit_info -> visit_status) = IdMap.find
    
(* At init, all the idents are 'Todo' *)
let (visit_init: dependencies -> visit_info) =
  fun deps ->
    let f id _ acc = IdMap.add id Todo acc in
    IdMap.fold f deps IdMap.empty

let rec (visit : dependencies -> visit_info -> Lv6Id.t -> Lv6Id.t list -> visit_info) =
  fun deps vi id path ->
    assert (IdMap.mem id deps);
    let path = id::path in
    let lxm, iddeps = IdMap.find id deps in
    let iddeps =  (* filter out id that have no deps (inputs, pre, const)*)
      IdSet.filter (fun id -> IdMap.mem id deps) iddeps
    in
    let doing,iddeps = IdSet.partition (fun id -> status id vi = Doing) iddeps in
    let _ =
      if (not (IdSet.is_empty doing)) then 
        let id = IdSet.choose doing in
        let idl = List.rev (id::path) in
        let msg = "Dependency loop on "^id^": " ^ (String.concat "->" idl) ^ "\n" in
        raise (DepLoop (lxm,msg))
    in
    let to_visit,_ = IdSet.partition (fun id -> status id vi = Todo) iddeps in
    let vi = IdSet.fold 
      (fun id vi -> 
        let vi = IdMap.add id Doing vi in
        let vi = visit deps vi id path in
        let vi = IdMap.add id Done vi in
        vi
      ) 
      to_visit 
      vi 
    in
    vi

(* Update the dependency graph according to the information contained
   in the equation.  *)
let (update_dependencies : dependencies -> Lic.eq_info srcflagged -> dependencies) =
  fun deps { it = (ll,exp) ; src = lxm } ->
    let lvars = List.map (fun l -> (Lic.var_info_of_left l).var_name_eff) ll in
    let rvars = vars_of_exp  exp in
    let deps =
      List.fold_left
        (fun deps v -> 
          let deps = 
            try IdMap.add v (lxm,(IdSet.union (snd (IdMap.find v deps)) rvars)) deps 
            with Not_found -> IdMap.add v (lxm,rvars) deps
          in
          deps
        )
        deps
        lvars
    in
    deps

let (check_node : Lic.node_exp -> unit) =
  fun node -> 
    match node.def_eff with  
	   | ExternLic | MetaOpLic | AbstractLic _ -> ()
	   | BodyLic{ eqs_eff = eql } ->
        let dependencies_init = IdMap.empty in
        let deps = List.fold_left update_dependencies dependencies_init eql in
        let vi = visit_init deps in
          let f id _ vi = visit deps vi id [] in
          ignore (IdMap.fold f deps vi)


(* exported *)
let (doit :  LicPrg.t -> unit) =
  fun inprg -> 
    let rec (do_node : Lic.node_key -> Lic.node_exp -> unit) = 
      fun _nk ne -> 
        check_node ne
    in
    try LicPrg.iter_nodes do_node inprg 
    with DepLoop(lxm,msg) -> raise (Error(lxm,msg,inprg))