Added when and merge to heptagon. Need tests !
Clocking is still done in minils since it is way simpler.
This commit is contained in:
		
							parent
							
								
									bdcdbddb09
								
							
						
					
					
						commit
						a22f7216f2
					
				
					 18 changed files with 150 additions and 35 deletions
				
			
		|  | @ -33,7 +33,7 @@ let index = ref 0 | |||
| let gen_index () = (incr index; !index) | ||||
| 
 | ||||
| (** returns a new clock variable *) | ||||
| let new_var () = Cvar { contents = Cindex (gen_index ()); } | ||||
| let fresh_clock () = Cvar { contents = Cindex (gen_index ()); } | ||||
| 
 | ||||
| (** returns the canonic (short) representant of a [ck] | ||||
|     and update it to this value. *) | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ and static_exp_desc funs acc sd = match sd with | |||
|       let f_se_l, acc = mapfold aux acc f_se_l in | ||||
|       Srecord f_se_l, acc | ||||
| 
 | ||||
| 
 | ||||
| and ty_it funs acc t = try funs.ty funs acc t with Fallback -> ty funs acc t | ||||
| and ty funs acc t = match t with | ||||
|   | Tid _ -> t, acc | ||||
|  | @ -58,7 +59,6 @@ and ty funs acc t = match t with | |||
|       let t, acc = ty_it funs acc t in | ||||
|       let se, acc = static_exp_it funs acc se in | ||||
|       Tarray (t, se), acc | ||||
| 
 | ||||
| (* | ||||
| and ct_it funs acc c = try funs.ct funs acc c with Fallback -> ct funs acc t | ||||
| and ct funs acc c = match c with | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| open Names | ||||
| open Idents | ||||
| open Signature | ||||
| open Types | ||||
| open Clocks | ||||
|  | @ -107,3 +108,17 @@ let print_interface ff = | |||
|     (fun key sigtype -> print_interface_value ff key sigtype) m.m_values; | ||||
|   Format.fprintf ff "@." | ||||
| 
 | ||||
| let print_ident ff id = Format.fprintf ff "%s" (name id) | ||||
| 
 | ||||
|  let rec print_ck ff = function | ||||
|   | Cbase -> fprintf ff "base" | ||||
|   | Con (ck, c, n) -> | ||||
|       fprintf ff "%a on %a(%a)" print_ck ck print_qualname c print_ident n | ||||
|   | Cvar { contents = Cindex _ } -> fprintf ff "base" | ||||
|   | Cvar { contents = Clink ck } -> print_ck ff ck | ||||
| 
 | ||||
| let rec print_clock ff = function | ||||
|   | Ck ck -> print_ck ff ck | ||||
|   | Cprod ct_list -> | ||||
|       fprintf ff "@[<2>(%a)@]" (print_list_r print_clock """ *""") ct_list | ||||
| 
 | ||||
|  |  | |||
|  | @ -46,6 +46,3 @@ sig | |||
|   include (Set.S with type elt = ident) | ||||
|   val fprint_t : Format.formatter -> t -> unit | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| val print_ident : Format.formatter -> ident -> unit | ||||
|  |  | |||
|  | @ -108,6 +108,13 @@ let rec typing e = | |||
|         candlist l | ||||
|     | Eiterator (_, _, _, e_list, _) -> | ||||
|         ctuplelist (List.map typing e_list) | ||||
|     | Ewhen (e, c, n) -> | ||||
|         let t = typing e in | ||||
|         cseq (read n) t | ||||
|     | Emerge (n, c_e_list) -> | ||||
|         let tl = List.map (fun (_,e) -> typing e) c_e_list in | ||||
|         cseq (read n) (candlist tl) | ||||
| 
 | ||||
| 
 | ||||
| (** Typing an application *) | ||||
| and apply op e_list = | ||||
|  |  | |||
|  | @ -169,6 +169,7 @@ let rec less left_ty right_ty = | |||
| module Printer = struct | ||||
|   open Format | ||||
|   open Pp_tools | ||||
|   open Global_printer | ||||
| 
 | ||||
|   let rec print_init ff i = match !i with | ||||
|     | Izero -> fprintf ff "initialized" | ||||
|  | @ -223,7 +224,7 @@ let less_exp e actual_ty expected_ty = | |||
| (** Main typing function *) | ||||
| let rec typing h e = | ||||
|   match e.e_desc with | ||||
|     | Econst c -> skeleton izero e.e_ty | ||||
|     | Econst _ -> skeleton izero e.e_ty | ||||
|     | Evar(x) -> IEnv.find_var_typ x h | ||||
|     | Elast(x) -> IEnv.find_last_typ x h | ||||
|     | Epre(None, e1) -> | ||||
|  | @ -248,6 +249,16 @@ let rec typing h e = | |||
|     | Eiterator (_, _, _, e_list, _) -> | ||||
|         List.iter (fun e -> initialized_exp h e) e_list; | ||||
|         skeleton izero e.e_ty | ||||
|     | Ewhen (e, _, n) -> | ||||
|         let i = imax (itype (IEnv.find_var_typ n h)) (itype (typing h e)) in | ||||
|         skeleton i e.e_ty | ||||
|     | Emerge (n, c_e_list) -> | ||||
|         let i = | ||||
|           List.fold_left | ||||
|             (fun acc (_, e) -> imax acc (itype (typing h e))) izero c_e_list in | ||||
|         let i = imax (itype (IEnv.find_var_typ n h)) i in | ||||
|         skeleton i e.e_ty | ||||
| 
 | ||||
| 
 | ||||
| (** Typing an application *) | ||||
| and apply h app e_list = | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ open Types | |||
| open Global_printer | ||||
| open Heptagon | ||||
| open Hept_mapfold | ||||
| open Pp_tools | ||||
| 
 | ||||
| type value = { ty: ty; mutable last: bool } | ||||
| 
 | ||||
|  | @ -46,6 +47,9 @@ type error = | |||
|   | Eempty_record | ||||
|   | Eempty_array | ||||
|   | Efoldi_bad_args of ty | ||||
|   | Emerge_missing_constrs of qualname list | ||||
|   | Emerge_not_enum of name * ty | ||||
|   | Emerge_unexpected_constr of qualname | ||||
| 
 | ||||
| exception Unify | ||||
| exception TypingError of error | ||||
|  | @ -91,6 +95,21 @@ let message loc kind = | |||
|         Format.eprintf "%aThe name %s is already defined.@." | ||||
|           print_location loc | ||||
|           s | ||||
|     | Emerge_missing_constrs cl -> | ||||
|         Format.eprintf "%aSome constructors are missing in this merge :@\n\ | ||||
|             @[%a@]@." | ||||
|           print_location loc | ||||
|           (print_list_r print_qualname """,""") cl | ||||
|     | Emerge_not_enum (n,t) -> | ||||
|         Format.eprintf | ||||
|           "%aThe merge variable %a should be of an enum type (found %a)@." | ||||
|           print_location loc | ||||
|           print_name n | ||||
|           print_type t | ||||
|     | Emerge_unexpected_constr c -> | ||||
|         Format.eprintf "%aThe constructor %a is unexpected.@." | ||||
|           print_location loc | ||||
|           print_qualname c | ||||
|     | Enon_exaustive -> | ||||
|         Format.eprintf "%aSome constructors are missing in this \ | ||||
|                         pattern/matching.@." | ||||
|  | @ -166,9 +185,9 @@ let find_with_error find_fun f = | |||
|   try find_fun f | ||||
|   with Not_found -> error (Eundefined(fullname f)) | ||||
| 
 | ||||
| let find_value = find_with_error find_value | ||||
| let find_constrs = find_with_error find_constrs | ||||
| let find_field = find_with_error find_field | ||||
| let find_value v = find_with_error find_value v | ||||
| let find_constrs c = Tid (find_with_error find_constrs c) | ||||
| let find_field f = find_with_error find_field f | ||||
| 
 | ||||
| (** Constraints related functions *) | ||||
| let (curr_size_constr : size_constraint list ref) = ref [] | ||||
|  | @ -393,7 +412,7 @@ and typing_static_exp const_env se = | |||
|              Svar ln, cd.Signature.c_type | ||||
|          with Not_found -> (* or a static parameter *) | ||||
|            Svar ln, QualEnv.find ln const_env) | ||||
|     | Sconstructor c -> Sconstructor c, Tid (find_constrs c) | ||||
|     | Sconstructor c -> Sconstructor c, find_constrs c | ||||
|     | Sfield c -> Sfield c, Tid (find_field c) | ||||
|     | Sop (op, se_list) -> | ||||
|         let ty_desc = find_value op in | ||||
|  | @ -423,10 +442,9 @@ and typing_static_exp const_env se = | |||
|              | [] -> error (Eempty_record) | ||||
|              | (f,_)::_ -> struct_info_from_field f | ||||
|           ) in | ||||
| 
 | ||||
|           check_static_field_unicity f_se_list; | ||||
|           if List.length f_se_list <> List.length fields then | ||||
|             message se.se_loc Esome_fields_are_missing; | ||||
|           check_static_field_unicity f_se_list; | ||||
|           let f_se_list = | ||||
|             List.map (typing_static_field const_env fields | ||||
|                         (Tid q)) f_se_list in | ||||
|  | @ -539,6 +557,40 @@ let rec typing const_env h e = | |||
|                         , typed_n, typed_e_list, reset), ty | ||||
| 
 | ||||
|       | Eiterator _ -> assert false | ||||
| 
 | ||||
|       | Ewhen (e, c, n) -> | ||||
|           let typed_e, t = typing const_env h e in | ||||
|           let tn_expected = find_constrs c in | ||||
|           let tn_actual = typ_of_name h n in | ||||
|           unify tn_actual tn_expected; | ||||
|           Ewhen (typed_e, c, n), t | ||||
| 
 | ||||
|       | Emerge (n, (c1,e1)::c_e_list) -> | ||||
|           let tn_actual = typ_of_name h n in | ||||
|           unify tn_actual (find_constrs c1); | ||||
|           let typed_e1, t = typing const_env h e1 in | ||||
|           let expected_c_list = (* constructors from the type of n *) | ||||
|             (match tn_actual with | ||||
|               | Tid tc -> | ||||
|                   (match find_type tc with | ||||
|                     | Tenum cl -> cl | ||||
|                     | _ -> message e.e_loc (Emerge_not_enum (name n,tn_actual))) | ||||
|               | _ -> message e.e_loc (Emerge_not_enum (name n,tn_actual))) in | ||||
|           let expected_c_set = | ||||
|             List.fold_left | ||||
|               (fun acc c -> QualSet.add c acc) QualSet.empty expected_c_list in | ||||
|           let typed_c_e_list, left_c_set = | ||||
|             let check_c_e expected_c_set (c,e) = | ||||
|               if not (QualSet.mem c expected_c_set) | ||||
|               then message e.e_loc (Emerge_unexpected_constr c); | ||||
|               (c, expect const_env h t e), QualSet.remove c expected_c_set in | ||||
|             mapfold check_c_e expected_c_set c_e_list in | ||||
|           if not (QualSet.is_empty left_c_set) | ||||
|           then message e.e_loc | ||||
|                        (Emerge_missing_constrs (QualSet.elements left_c_set)); | ||||
|           Emerge (n, (c1,typed_e1)::typed_c_e_list), t | ||||
|       | Emerge (_, []) -> assert false | ||||
| 
 | ||||
|     in | ||||
|       { e with e_desc = typed_desc; e_ty = ty; }, ty | ||||
|   with | ||||
|  | @ -1017,8 +1069,7 @@ let typing_const_dec cd = | |||
|     { cd with c_value = se; c_type = ty } | ||||
| 
 | ||||
| let program | ||||
|     ({ p_opened = opened; p_types = p_type_list; | ||||
|        p_nodes = p_node_list; p_consts = p_consts_list } as p) = | ||||
|     ({ p_nodes = p_node_list; p_consts = p_consts_list } as p) = | ||||
|   let typed_cd_list = List.map typing_const_dec p_consts_list in | ||||
|   let typed_node_list = List.map node p_node_list in | ||||
|     { p with p_nodes = typed_node_list; p_consts = typed_cd_list } | ||||
|  |  | |||
|  | @ -135,6 +135,16 @@ and edesc funs acc ed = match ed with | |||
|       let args, acc = mapfold (exp_it funs) acc args in | ||||
|       let reset, acc = optional_wacc (exp_it funs) acc reset in | ||||
|       Eiterator (i, app, param, args, reset), acc | ||||
|   | Ewhen (e, c, n) -> | ||||
|       let e, acc = exp_it funs acc e in | ||||
|       Ewhen (e, c, n), acc | ||||
|   | Emerge (n, c_e_list) -> | ||||
|       let aux acc (c,e) = | ||||
|         let e, acc = exp_it funs acc e in | ||||
|         (c,e), acc in | ||||
|       let c_e_list, acc = mapfold aux acc c_e_list in | ||||
|       Emerge (n, c_e_list), acc | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| and app_it funs acc a = funs.app funs acc a | ||||
|  |  | |||
|  | @ -96,14 +96,14 @@ and print_exp_desc ff = function | |||
|   | Econst c -> print_static_exp ff c | ||||
|   | Epre(None, e) -> fprintf ff "pre %a" print_exp e | ||||
|   | Epre(Some c, e) -> | ||||
|     fprintf ff "@[<2>%a fby@ %a@]" print_static_exp c  print_exp e | ||||
|       fprintf ff "@[<2>%a fby@ %a@]" print_static_exp c  print_exp e | ||||
|   | Efby(e1, e2) -> | ||||
|     fprintf ff "@[<2>%a fby@ %a@]" print_exp e1  print_exp e2 | ||||
|       fprintf ff "@[<2>%a fby@ %a@]" print_exp e1  print_exp e2 | ||||
|   | Eapp(app, args, reset) -> | ||||
|     fprintf ff "@[<2>%a@,%a@]" | ||||
|       fprintf ff "@[<2>%a@,%a@]" | ||||
|         print_app (app, args) print_every reset | ||||
|   | Estruct(f_e_list) -> | ||||
|     print_record (print_couple print_qualname print_exp """ = """) ff f_e_list | ||||
|       print_record (print_couple print_qualname print_exp """ = """) ff f_e_list | ||||
|   | Eiterator (it, f, param, args, reset) -> | ||||
|       fprintf ff "@[<2>(%s (%a)<<%a>>)@,%a@]%a" | ||||
|         (iterator_to_string it) | ||||
|  | @ -111,6 +111,18 @@ and print_exp_desc ff = function | |||
|         print_static_exp param | ||||
|         print_exp_tuple args | ||||
|         print_every reset | ||||
|   | Ewhen (e, c, n) -> | ||||
|       fprintf ff "@[<2>(%a@ when %a(%a))@]" | ||||
|         print_exp e print_qualname c print_ident n | ||||
|   | Emerge (x, tag_e_list) -> | ||||
|       fprintf ff "@[<2>merge %a@ %a@]" | ||||
|         print_ident x print_tag_e_list tag_e_list | ||||
| 
 | ||||
| and print_handler ff c = | ||||
|   fprintf ff "@[<2>%a@]" (print_couple print_qualname print_exp "("" -> "")") c | ||||
| 
 | ||||
| and print_tag_e_list ff tag_e_list = | ||||
|   fprintf ff "@[%a@]" (print_list print_handler """""") tag_e_list | ||||
| 
 | ||||
| and print_every ff reset = | ||||
|   print_opt (fun ff id -> fprintf ff " every %a" print_exp id) ff reset | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ open Idents | |||
| open Static | ||||
| open Signature | ||||
| open Types | ||||
| open Clocks | ||||
| open Initial | ||||
| 
 | ||||
| type state_name = name | ||||
|  | @ -36,6 +37,10 @@ and desc = | |||
|   | Epre of static_exp option * exp | ||||
|   | Efby of exp * exp | ||||
|   | Estruct of (field_name * exp) list | ||||
|   | Ewhen of exp * constructor_name * var_ident | ||||
|     (** exp when Constructor(ident) *) | ||||
|   | Emerge of var_ident * (constructor_name * exp) list | ||||
|     (** merge ident (Constructor -> exp)+ *) | ||||
|   | Eapp of app * exp list * exp option | ||||
|   | Eiterator of iterator_type * app * static_exp * exp list * exp option | ||||
| 
 | ||||
|  | @ -106,6 +111,7 @@ and present_handler = { | |||
| and var_dec = { | ||||
|   v_ident : var_ident; | ||||
|   v_type  : ty; | ||||
|   v_clock : ck; | ||||
|   v_last  : last; | ||||
|   v_loc   : location } | ||||
| 
 | ||||
|  | @ -187,16 +193,18 @@ let mk_type_dec name desc = | |||
| let mk_equation ?(statefull = true) desc = | ||||
|   { eq_desc = desc; eq_statefull = statefull; eq_loc = no_location; } | ||||
| 
 | ||||
| let mk_var_dec ?(last = Var) name ty  = | ||||
|   { v_ident = name; v_type = ty; | ||||
| let mk_var_dec ?(last = Var) ?(ck = fresh_clock()) name ty = | ||||
|   { v_ident = name; v_type = ty; v_clock = ck; | ||||
|     v_last = last; v_loc = no_location } | ||||
| 
 | ||||
| let mk_block ?(statefull = true) ?(defnames = Env.empty) eqs = | ||||
|   { b_local = []; b_equs = eqs; b_defnames = defnames; | ||||
|     b_statefull = statefull; b_loc = no_location } | ||||
| 
 | ||||
| let dfalse = mk_exp (Econst (mk_static_bool false)) (Tid Initial.pbool) | ||||
| let dtrue = mk_exp (Econst (mk_static_bool true)) (Tid Initial.pbool) | ||||
| let dfalse = | ||||
|   mk_exp (Econst (mk_static_bool false)) (Tid Initial.pbool) | ||||
| let dtrue = | ||||
|   mk_exp (Econst (mk_static_bool true)) (Tid Initial.pbool) | ||||
| 
 | ||||
| let mk_ifthenelse e1 e2 e3 = | ||||
|   { e3 with e_desc = mk_op_app Eifthenelse [e1; e2; e3] } | ||||
|  |  | |||
|  | @ -36,8 +36,8 @@ let parse_interface lexbuf = | |||
| 
 | ||||
| let compile_impl pp p = | ||||
|   (* Typing *) | ||||
|   let p = pass "Typing" true Typing.program p pp in | ||||
|   let p = silent_pass "Statefullness check" true Statefull.program p in | ||||
|   let p = pass "Typing" true Typing.program p pp in | ||||
| 
 | ||||
|   if !print_types then print_interface Format.std_formatter; | ||||
| 
 | ||||
|  |  | |||
|  | @ -368,6 +368,7 @@ and translate_var_dec local_const env vd = | |||
|     { Heptagon.v_ident = Rename.var vd.v_loc env vd.v_name; | ||||
|       Heptagon.v_type = translate_type vd.v_loc local_const vd.v_type; | ||||
|       Heptagon.v_last = translate_last local_const vd.v_last; | ||||
|       Heptagon.v_clock = Clocks.fresh_clock(); (* TODO add clock annotations *) | ||||
|       Heptagon.v_loc = vd.v_loc } | ||||
| 
 | ||||
| and translate_vd_list local_const env = | ||||
|  |  | |||
|  | @ -45,10 +45,8 @@ open Initial | |||
|    e1 -> e2 is translated into if (true fby false) then e1 else e2 | ||||
| *) | ||||
| 
 | ||||
| let mk_bool_var n = | ||||
|   mk_exp (Evar n) (Tid Initial.pbool) | ||||
| let mk_bool_param n = | ||||
|   mk_var_dec n (Tid Initial.pbool) | ||||
| let mk_bool_var n = mk_exp (Evar n) (Tid Initial.pbool) | ||||
| let mk_bool_param n = mk_var_dec n (Tid Initial.pbool) | ||||
| 
 | ||||
| let or_op_call e_list = mk_op_app (Efun Initial.por) e_list | ||||
| 
 | ||||
|  |  | |||
|  | @ -247,6 +247,11 @@ let rec translate env | |||
|     | Heptagon.Efby _ | ||||
|     | Heptagon.Elast _ -> | ||||
|         Error.message loc Error.Eunsupported_language_construct | ||||
|     | Heptagon.Ewhen (e, c, n) -> | ||||
|         mk_exp ~loc:loc ~ty:ty (Ewhen (translate env e, c, n)) | ||||
|     | Heptagon.Emerge (n, c_e_list) -> | ||||
|         mk_exp ~loc:loc ~ty:ty | ||||
|           (Emerge (n, List.map (fun (c,e) -> c, translate env e) c_e_list)) | ||||
| 
 | ||||
| let rec translate_pat = function | ||||
|   | Heptagon.Evarpat(n) -> Evarpat n | ||||
|  |  | |||
|  | @ -422,8 +422,6 @@ let translate_contract map mem_vars = | |||
|         { | ||||
|           Minils.c_eq = eq_list; | ||||
|           Minils.c_local = d_list; | ||||
|           Minils.c_assume = e_a; | ||||
|           Minils.c_enforce = e_c | ||||
|         } -> | ||||
|         let (v, si, j, s_list) = translate_eq_list map | ||||
|           empty_call_context eq_list in | ||||
|  |  | |||
|  | @ -35,17 +35,17 @@ let typ_of_name h x = Env.find x h | |||
| 
 | ||||
| let rec typing h e = | ||||
|   let ct = match e.e_desc with | ||||
|     | Econst se -> skeleton (new_var ()) se.se_ty | ||||
|     | Econst se -> skeleton (fresh_clock ()) se.se_ty | ||||
|     | Evar x -> Ck (typ_of_name h x) | ||||
|     | Efby (_, e) -> typing h e | ||||
|     | Eapp({a_op = op}, args, r) -> | ||||
|         let ck = match r with | ||||
|           | None -> new_var () | ||||
|           | None -> fresh_clock () | ||||
|           | Some(reset) -> typ_of_name h reset in | ||||
|         typing_op op args h e ck | ||||
|     | Eiterator (_, _, _, args, r) -> (* Typed exactly as a fun or a node... *) | ||||
|         let ck = match r with | ||||
|           | None -> new_var() | ||||
|           | None -> fresh_clock() | ||||
|           | Some(reset) -> typ_of_name h reset | ||||
|         in (List.iter (expect h (Ck ck)) args; skeleton ck e.e_ty) | ||||
|     | Ewhen (e, c, n) -> | ||||
|  | @ -55,14 +55,14 @@ let rec typing h e = | |||
|         let ck_c = typ_of_name h n in | ||||
|         (typing_c_e_list h ck_c n c_e_list; skeleton ck_c e.e_ty) | ||||
|     | Estruct l -> | ||||
|         let ck = new_var () in | ||||
|         let ck = fresh_clock () in | ||||
|         (List.iter | ||||
|            (fun (_, e) -> let ct = skeleton ck e.e_ty in expect h ct e) l; | ||||
|          Ck ck) | ||||
|   in (e.e_ck <- ckofct ct; ct) | ||||
| 
 | ||||
| and typing_op op e_list h e ck = match op with | ||||
|   | (Eequal | Efun _ | Enode _) -> | ||||
|   | (Eequal | Efun _ | Enode _) -> (*LA*) | ||||
|       List.iter (fun e -> expect h (skeleton ck e.e_ty) e) e_list; | ||||
|       skeleton ck e.e_ty | ||||
|   | Etuple -> | ||||
|  | @ -128,7 +128,7 @@ let typing_eqs h eq_list = (*TODO FIXME*) | |||
|   in List.iter typing_eq eq_list | ||||
| 
 | ||||
| let build h dec = | ||||
|   List.fold_left (fun h { v_ident = n } -> Env.add n (new_var ()) h) h dec | ||||
|   List.fold_left (fun h { v_ident = n } -> Env.add n (fresh_clock ()) h) h dec | ||||
| 
 | ||||
| let sbuild h dec base = | ||||
|   List.fold_left (fun h { v_ident = n } -> Env.add n base h) h dec | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ open Signature | |||
| open Minils | ||||
| open Mls_utils | ||||
| open Mls_printer | ||||
| open Global_printer | ||||
| open Types | ||||
| open Clocks | ||||
| open Pp_tools | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ open Signature | |||
| open Minils | ||||
| open Mls_utils | ||||
| open Mls_printer | ||||
| open Global_printer | ||||
| open Types | ||||
| open Clocks | ||||
| open Pp_tools | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Léonard Gérard
						Léonard Gérard