From 4fcdeca24c2c3977a85cc45497ddd36a4c533923 Mon Sep 17 00:00:00 2001 From: Pascal Raymond <Pascal.Raymond@imag.fr> Date: Tue, 10 Jul 2012 11:24:40 +0200 Subject: [PATCH] reutilise le MainArgs de lutin, plus joli ... --- ALIRE.compil-des-nodes | 6 +- Makefile | 2 + overload.lus | 13 +- src/compile.ml | 2 +- src/lazyCompiler.ml | 27 +++- src/main.ml | 164 ++--------------------- src/mainArgs.ml | 288 +++++++++++++++++++++++++++++++++++++++++ src/mainArgs.mli | 12 ++ src/name.ml | 7 +- src/verbose.ml | 43 ++++-- src/verbose.mli | 24 +++- 11 files changed, 401 insertions(+), 187 deletions(-) create mode 100644 src/mainArgs.ml create mode 100644 src/mainArgs.mli diff --git a/ALIRE.compil-des-nodes b/ALIRE.compil-des-nodes index bd61621d..cb7aedf9 100644 --- a/ALIRE.compil-des-nodes +++ b/ALIRE.compil-des-nodes @@ -8,7 +8,5 @@ compile_all_item node_check_interface -> node_check_interface_do -node_check - -node_check_do - + node_check: cache on node_check_do + + node_check_do en plus pour l'interface (prov) diff --git a/Makefile b/Makefile index 94d7dd62..69e40a46 100644 --- a/Makefile +++ b/Makefile @@ -100,6 +100,8 @@ SOURCES = \ $(OBJDIR)/lazyCompiler.ml \ $(OBJDIR)/lazyCompiler.mli \ $(OBJDIR)/compile.ml \ + $(OBJDIR)/mainArgs.ml \ + $(OBJDIR)/mainArgs.mli \ $(OBJDIR)/main.ml # diff --git a/overload.lus b/overload.lus index b20d1c8e..fe73618a 100644 --- a/overload.lus +++ b/overload.lus @@ -1,9 +1,12 @@ -node titi = map<<+,4>>; +node overplus = map<<+,4>>; -node toto(x,y: int^4) returns (o: int^4); -let o = titi(x,y); tel +node do_int(x,y: int^4) returns (o: int^4); +let o = overplus(x,y); tel -node tutu(x,y: real^4) returns (o: real^4); -let o = titi(x,y); tel +node do_real(x,y: real^4) returns (o: real^4); +let o = overplus(x,y); tel + +node do_bool(x,y: real^4) returns (o: real^4); +let o = overplus(x,y); tel diff --git a/src/compile.ml b/src/compile.ml index 948be3a4..19f0f403 100644 --- a/src/compile.ml +++ b/src/compile.ml @@ -28,7 +28,7 @@ let (doit : SyntaxTree.pack_or_model list -> Ident.idref option -> unit) = priorité dans l'ordre *) let lzcomp = LazyCompiler.create syntax_tab in - if Verbose.get_level () > 2 then SyntaxTab.dump syntax_tab; + Verbose.exe ~level:2 (fun () -> SyntaxTab.dump syntax_tab); Ident.set_dft_pack_name (first_pack_in srclist); let zelic = match main_node with diff --git a/src/lazyCompiler.ml b/src/lazyCompiler.ml index 70e718ee..cb389265 100644 --- a/src/lazyCompiler.ml +++ b/src/lazyCompiler.ml @@ -623,6 +623,11 @@ and (node_check_interface_do: t -> Eff.node_key -> Lxm.t -> SymbolTab.t -> Ident.pack_name -> SyntaxTreeCore.node_info srcflagged -> Eff.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., @@ -698,10 +703,22 @@ and (node_check_interface_do: t -> Eff.node_key -> Lxm.t -> | _,_ -> prov_node_exp_eff -and (node_check_do: t -> Eff.node_key -> Lxm.t -> SymbolTab.t -> - bool -> Ident.pack_name -> SyntaxTreeCore.node_info srcflagged -> - Eff.node_exp) = - fun this nk lxm symbols provide_flag pack_name node_def -> +(* +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: Eff.node_key) + (lxm: Lxm.t) + (symbols: SymbolTab.t) + (provide_flag: bool) + (pack_name: Ident.pack_name) + (node_def: SyntaxTreeCore.node_info srcflagged) + : Eff.node_exp = +(* START node_check_do *) +( let lxm = node_def.src in (* Creates a local_env with just the global bindings, local bindinds will be added later (side effect) @@ -1071,6 +1088,8 @@ and (node_check_do: t -> Eff.node_key -> Lxm.t -> SymbolTab.t -> let _ = UniqueOutput.check res node_def.src in (* gen_code provide_flag current_env res; *) res +) +(*END node_check_do *) (* [make_alias_node aliased_node alias_nk node_id_solver_vars_opt lxm] diff --git a/src/main.ml b/src/main.ml index 61cf8db4..b798a221 100644 --- a/src/main.ml +++ b/src/main.ml @@ -59,157 +59,6 @@ let test_lex ( lexbuf ) = ( done ) -(*--------------------------------------------------------- -Les args sont des variables GLOBALES ----------------------------------------------------------*) - -let print_version = function (x: unit) -> ( - print_string (Version.str ^ "\n") -) - -let usage_msg = - "usage: "^(Version.tool)^" [options] <lustre files>\nwhere [options] can be:" - -let rec arg_list = [ - ( "--node", Arg.String(fun x -> Global.main_node := x; Global.compile_all_items := false), - "<node>" - ); - ( "-n", Arg.String(fun x -> Global.main_node := x; Global.compile_all_items := false), - "<node>\n\t Set the main node (all items are compiled if unset)" - ); - - ( "--output-file", Arg.String(fun x -> Global.outfile := x), "<file>" - ); - ( "-o", Arg.String(fun x -> Global.outfile := x), - "<file>\n\t Set the output file name." - ); - - ( "--keep-nested-calls", Arg.Unit (fun _ -> Global.one_op_per_equation := false), - "" - ); - ( "-knc", Arg.Unit (fun _ -> Global.one_op_per_equation := false), - "\n\t Keep nested calls. By default, only one node per equation is generated." - ); - - ( "--expand-iterators", Arg.Unit (fun _ -> Global.inline_iterator := true), - "" - ); - ( "-ei", Arg.Unit (fun _ -> Global.inline_iterator := true), - "\n\t Expand array iterators (i.e., generate iterator-free code)." - ); - - ( "--expand-enums", Arg.Unit (fun _ -> Global.expand_enums := true), - "" - ); - ( "-ee", Arg.Unit (fun _ -> Global.expand_enums := true), - "\n\t Translate enums into integers." - ); - - ( "--expand-structs-and-arrays", Arg.Unit - (fun _ -> Global.expand_structs := true;Global.inline_iterator := true), - "" - ); - ( "-esa", Arg.Unit - (fun _ -> Global.expand_structs := true;Global.inline_iterator := true), - "\n\t Expand structures and arrays using as many variables as necessary (automatically impose '-ei')." - ); - - ( "--expand-nodes", Arg.Unit (fun _ -> Global.expand_nodes := true), - "" - ); - ( "-en", Arg.Unit (fun _ -> Global.expand_nodes := true), - "\n\t Expand the main node (use the first node if no one is specified)." - ); - - ( "--do-not-expand-node", Arg.String add_dont_expand_nodes, - "<node>" - ); - ( "-den", Arg.String add_dont_expand_nodes, - "<node>\n\t Do not expand node (useful in the expand mode only of course)." - ); - - ( "--lustre-v4", Arg.Unit - (fun _ -> set_v4_options ()), - "\t" - ); - - ( "-lv4", Arg.Unit - (fun _ -> set_v4_options ()), - "\n\t Use Lustre V4 syntax (automatically impose '-ei -ee -esa')." - ); - - ( "--expanded-code", Arg.Unit - (fun _ -> set_ec_options ()), - "" - ); - ( "-ec", Arg.Unit - (fun _ -> set_ec_options ()), - "\n\t Generate ec (actually just an alias for '-en -lv4 --no-prefix')." - ); - - ( "-np", Arg.Set Global.no_prefix, ""); - ( "--no-prefix", Arg.Set Global.no_prefix, - "\n\t Do not prefix variable names by their module (beware: variable names may clash with this option)." - ); - - ("--test-lexer",Arg.Set Global.tlex,"Internal option used to test the lexer"); - ("-tlex",Arg.Set Global.tlex,""); - - ( "--verbose-level", Arg.Int(fun vl -> Verbose.set_level vl ), "<int>" - ); - ( "-vl", Arg.Int(fun vl -> Verbose.set_level vl ), - "<int>\n\t Set the verbose level." - ); - - ( "--verbose", Arg.Unit (fun vl -> Verbose.set_level 1 ), - "" - ); - ( "-v", Arg.Unit (fun vl -> Verbose.set_level 1 ), - "\n\t Set the verbose level to 1." - ); - - ( "--version", Arg.Unit(fun x -> print_version () ; exit 0), - "" - ); - ( "-version", Arg.Unit(fun x -> print_version () ; exit 0), - "\n\t Display the current version of the tool." - ); - - ( "-unit", Arg.Unit (fun x -> Global.run_unit_test := true), - "\n\t Run some (internal) unit tests" - ); - - ("--nonreg-test", Arg.Unit(fun _ -> Global.nonreg_test := true), - ""); - - ("-h", Arg.Unit (fun _ -> (Arg.usage arg_list usage_msg; exit 0)), "" ); - ("-help", Arg.Unit (fun _ -> (Arg.usage arg_list usage_msg; exit 0)),"" ); - ("--help", Arg.Unit (fun _ -> (Arg.usage arg_list usage_msg; exit 0)), - "\n\t Display this message." ) -] -and set_v4_options () = - Global.lv4 := true; - Global.inline_iterator := true; - Global.expand_enums := true; - Global.expand_structs := true -and set_ec_options () = - set_v4_options (); - Global.ec := true; - Global.no_prefix := true; - Global.expand_nodes := true - -and add_dont_expand_nodes str = - Global.dont_expand_nodes := str::!Global.dont_expand_nodes -and - parse_args () = ( - Arg.parse arg_list (* liste des options *) - Global.add_infile (* arg par defaut = fichier d'entree *) - usage_msg (* message d'erreur *) - ; - () - ) - - (* Retourne un SyntaxTree.t *) let lus_load lexbuf = let tree = Parser.program Lexer.lexer lexbuf in @@ -373,14 +222,17 @@ let my_exit i = let main = ( (* Compile.init_appli () ; *) - parse_args (); - if Verbose.get_level() > 2 then Gc.set { (Gc.get ()) with Gc.verbose = 0x01 }; + (* parse_args (); *) + let args = MainArgs.parse Sys.argv in + Verbose.exe ~level:3 (fun () -> + Gc.set { (Gc.get ()) with Gc.verbose = 0x01 } + ); if !Global.run_unit_test then ( UnifyType.unit_test (); exit 0 ); if (!Global.infiles = []) then ( - Arg.usage arg_list usage_msg ; + MainArgs.usage stderr args; exit 1 ); try ( @@ -396,7 +248,9 @@ let main = ( (* OBSOLETE LicDump.dump_type_alias !Global.oc; *) - if Verbose.get_level() > 2 then Gc.print_stat stdout; + Verbose.exe ~level:3 (fun () -> + Gc.print_stat stdout + ); close_out !Global.oc ) with Sys_error(s) -> diff --git a/src/mainArgs.ml b/src/mainArgs.ml new file mode 100644 index 00000000..56f09d4c --- /dev/null +++ b/src/mainArgs.ml @@ -0,0 +1,288 @@ +(* +Le manager d'argument adapté de celui de lutin, plus joli +N.B. solution un peu batarde : les options sont stockées, comme avant, dans Global, +du coup, le fait de rendre un type "t" est une koketerie ! +*) +open Version +open Verbose +open Arg + +let tool_name = Version.tool +let usage_msg = "usage: "^tool_name^" [options] <file> | "^tool_name^" -help" + +type t = { + mutable _opts : (string * Arg.spec * string) list; (* classical Arg option tab used by Arg.parse *) + mutable _user_man : (string * string list) list; (* ad hoc tab for pretty prtting usage *) + mutable _hidden_man: (string * string list) list; (* ad hoc tab for pretty prtting usage *) + mutable _others: string list; + mutable _margin : int; + +} + +let (make_opt : unit -> t) = + fun () -> + { + _opts = []; + _user_man = []; + _hidden_man = []; + _others = []; + _margin = 12; +} + + +(* all unrecognized options are accumulated *) +let (add_other : t -> string -> unit) = + fun opt s -> + opt._others <- s::opt._others + +let pspec os opt (c, ml) = ( + let (m1, oth) = match ml with + | h::t -> (h,t) + | _ -> ("",[]) + in + let t2 = String.make opt._margin ' ' in + let cl = String.length c in + let t1 = if (cl < opt._margin ) then + String.make (opt._margin - cl) ' ' + else + "\n"^t2 + in + Printf.fprintf os "%s%s%s" c t1 m1; + List.iter (function x -> Printf.fprintf os "\n%s%s" t2 x) oth ; + Printf.fprintf os "\n" ; +) + +let usage os opt = ( + let l = List.rev opt._user_man in + Printf.fprintf os "%s\n\n" usage_msg; + List.iter (pspec os opt) l +) +let help opt ()= ( + usage stdout opt; + exit 0 +) +let full_usage os opt = ( + Printf.fprintf os "%s\n" usage_msg; + let l = List.rev opt._user_man in + List.iter (pspec os opt) l; + let l = List.rev (opt._hidden_man) in + List.iter (pspec os opt) l +) +let full_help opt ()= ( + full_usage stdout opt; + exit 0 +) + +let unexpected s opt = ( + prerr_string ("unexpected argument \""^s^"\""); + prerr_newline (); + usage stderr opt; + exit 1 +) +let file_notfound f opt = ( + prerr_string ("File not found: \""^f^"\""); + prerr_newline (); + usage stderr opt; + exit 1 +) + +let debug_options_list = + [ + "CkType"; + "Expand"; + "LutExe"; + "Run"; + "LutProg"; + "LucProg"; + "AutoGen"; "AutoExplore"; "Guard" + ] + +let (mkopt : t -> string list -> ?hide:bool -> ?arg:string -> Arg.spec -> string list -> unit) = + fun opt ol ?(hide=false) ?(arg="") se ml -> + let treto o = opt._opts <- (o, se, "")::opt._opts in + List.iter treto ol ; + let col1 = (String.concat ", " ol)^arg in + if hide + then opt._hidden_man <- (col1, ml)::opt._hidden_man + else opt._user_man <- (col1, ml)::opt._user_man + (* + let tabs = String.make (col - (String.length o) - (String.length arg)) ' ' in + (* (o, se, arg^tabs^m) *) + (o, se, arg^"\n "^m) + *) + +(* utils *) +let set_v4_options () = + Global.lv4 := true; + Global.inline_iterator := true; + Global.expand_enums := true; + Global.expand_structs := true +let set_ec_options () = + set_v4_options (); + Global.ec := true; + Global.no_prefix := true; + Global.expand_nodes := true + +(*** USER OPTIONS TAB **) +let mkoptab (opt:t) : unit = ( + mkopt opt + ["-n";"-node"] + ~arg:" <string>" + (Arg.String(function x -> + Global.main_node := x; + Global.compile_all_items := false)) + ["Set the main node (all items are compiled if unset)"] + ; + mkopt opt + ["-o";"--output-file"] + ~arg:" <string>" + (Arg.String(function x -> + Global.outfile := x)) + ["Set the output file name"] + ; + mkopt opt + ["-knc"; "--keep-nested-calls"] + (Arg.Unit (fun _ -> Global.one_op_per_equation := false)) + ["Keep nested calls. By default, only one node per equation is generated."] + ; + mkopt opt + ["-ei"; "--expand-iterators"] + (Arg.Unit (fun _ -> Global.inline_iterator := true)) + ["Expand array iterators (i.e., generate iterator-free code)."] + ; + mkopt opt + ["-ee"; "--expand-enums"] + (Arg.Unit (fun _ -> Global.expand_enums := true)) + [" Translate enums into integers."] + ; + mkopt opt + ["-esa"; "--expand-structs-and-arrays"] + (Arg.Unit (fun _ -> + Global.expand_structs := true; + Global.inline_iterator := true)) + ["Expand structures and arrays using as many variables as necessary (automatically impose '-ei')"] + ; + mkopt opt + ["-en"; "--expand-nodes"] + (Arg.Unit (fun _ -> Global.expand_nodes := true)) + ["Expand the main node (use the first node if no one is specified)."] + ; + mkopt opt + ["-den"; "--do_not-expand-nodes"] + ~arg:" <string>" + (Arg.String (fun str -> + Global.dont_expand_nodes := str::!Global.dont_expand_nodes + )) + ["Do not expand node (useful in the expand mode only of course)."] + ; + mkopt opt + ["-lv4"; "--lustre-v4"] + (Arg.Unit (fun _ -> set_v4_options ())) + ["Use Lustre V4 syntax (automatically impose '-ei -ee -esa')."] + ; + mkopt opt + ["-ec"; "--expanded-code"] + (Arg.Unit (fun _ -> set_ec_options ())) + ["Generate ec (actually just an alias for '-en -lv4 --no-prefix')."] + ; + mkopt opt + ["-np"; "--no-prefix"] + (Arg.Set Global.no_prefix) + ["Do not prefix variable names by their module (beware: variable names may clash with this option)."] + ; + + mkopt opt + ["-version"] + (Arg.Unit(function _ -> Printf.fprintf stderr "%s\n" Version.str; exit 0)) + ["Print the current version and exit"] + ; + (* verbose *) + mkopt opt + ["-v"; "--verbose"] + (Arg.Unit(function _ -> Verbose.on () )) + ["Set the verbose level to 1"] + ; + mkopt opt + ["-vl"] + ~arg:" <int>" + (Arg.Int(function i -> Verbose.set i)) + ["Set the verbose level"] + ; + + (* to show Hidden opt *) + mkopt opt + ["-more"] + (* (Arg.Unit(fun _ -> opt._see_all_options <- true)) *) + (Arg.Unit (full_help opt)) + ["Show hidden options (for dev purposes)"]; + (* HIDDEN *) + + (* test lexical *) + mkopt opt ~hide:true + ["-tlex"; "--test-lexer"] + (Arg.Set Global.tlex) + ["Test the lexical analysis"] + ; + (* test syntaxique + mkopt opt ~hide:true + ["-tparse"] + (Arg.Unit(function _ -> opt._gen_mode <- GenLuc ; opt._test_parse <- true ; ())) + ["Test the syntactic analysis"] + ; + *) + mkopt opt ~hide:true + ["-unit"] + (Arg.Set Global.run_unit_test) + ["Run some (internal) unit tests"] + ; + mkopt opt ~hide:true + ["--nonreg-test"] + (Arg.Set Global.nonreg_test) + ["(internal)"] + ; + (* misc degub flag *) + mkopt opt ~hide:true + ["-dbg"; "--debug"] + (Arg.Symbol (debug_options_list, + (function s -> let x = Verbose.get_flag s in Verbose.set_flag x))) + [ "<dbg_flag>"; + "Possible dbg_flag are: " ^(String.concat ", " debug_options_list) ] +) + + +let first_line b = ( + try ( + let f = String.index b '\n' in + String.sub b 0 f + ) with Not_found -> b +) + + +let current = ref 0;; + +(* La ``méthode'' principale *) +let parse argv = ( + let opt = make_opt() in + let save_current = !current in + try ( + mkoptab opt; + Arg.parse_argv ~current:current argv opt._opts (add_other opt) usage_msg; + (List.iter + (fun f -> + if (String.sub f 0 1 = "-") then + unexpected f opt + else if not (Sys.file_exists f) then + file_notfound f opt + else () + ) + opt._others + ); + Global.infiles := (List.rev opt._others); + current := save_current; + opt + ) with + (* only 1rst line is interesting ! *) + | Bad msg -> Printf.fprintf stderr "%s\n" (first_line msg); usage stderr opt; exit 2; + | Help msg -> help opt (); +) + diff --git a/src/mainArgs.mli b/src/mainArgs.mli new file mode 100644 index 00000000..b00eae1d --- /dev/null +++ b/src/mainArgs.mli @@ -0,0 +1,12 @@ + +(* koketeri, vu qu'on continu à ranger concetement + les options dans des var. globales ! (cf Global +*) +type t + +(* La ``méthode'' principale *) +val parse : string array -> t + +val usage : out_channel -> t -> unit +val full_usage : out_channel -> t -> unit + diff --git a/src/name.ml b/src/name.ml index b64d0c18..b3d21733 100644 --- a/src/name.ml +++ b/src/name.ml @@ -121,9 +121,10 @@ let (update_fresh_var_prefix : unit -> unit) = else ( let new_prefix = ("_" ^ (string_of_int index)) in fresh_var_prefix := new_prefix ; - if (Verbose.get_level()>1) then ( - print_string ("I use " ^ new_prefix ^ " as prefix for fresh var names.\n"); - flush stdout + Verbose.exe ~level:1 ( + fun () -> + prerr_string ("I use " ^ new_prefix ^ " as prefix for fresh var names.\n"); + flush stderr ) ) diff --git a/src/verbose.ml b/src/verbose.ml index ed10721b..0f2c6ea9 100644 --- a/src/verbose.ml +++ b/src/verbose.ml @@ -2,6 +2,8 @@ module : Verbose date : ------------------------------------------------------------------------ +-- New: 2010/11, debug flag system + description : Affichage verbeux avec appel "printf-like" : @@ -21,23 +23,48 @@ N.B. VERSION GORE : le kprintf n'est appel ----------------------------------------------------------------------*) +type flag = bool ref + +let _no_flag = ref false + +let _flag_tab : (string, flag) Hashtbl.t = Hashtbl.create 10 +(* warning: last in first ! *) +let _flag_list : string list ref = ref [] + +let get_flag s = ( + try ( + Hashtbl.find _flag_tab s + ) with Not_found -> ( + let res = ref false in + Hashtbl.add _flag_tab s res; + _flag_list := s::!_flag_list; + res + ) +) +let set_flag f = (f := true) +let flag_list () = !_flag_list (* type msg = string Lazy.t *) let _level = ref 0 -let set_level (l:int) = ( _level := l ) -let get_level () = !_level +let on () = ( _level := 1 ) +let off () = ( _level := 0 ) +let set (l:int) = ( _level := l ) + +let level () = !_level (**** VERSION PAS TROP GORE *****) -let printf ?(level=1) s = Printf.kprintf - (fun t -> if (!_level >= level) then (print_string t; flush stdout) else ()) s +let printf ?(level=1) ?(flag=_no_flag) s = + Printf.kprintf (fun t -> if (!flag || (!_level >= level)) then (prerr_string t; flush stderr) else ()) s + +let print_string ?(level=1) ?(flag=_no_flag) s = + if (!flag || (!_level >= level)) then (prerr_string s; flush stderr) + -let print_string ?(level=1) s = - if (!_level >= level) then (print_string s; flush stdout) +let exe ?(level=1) ?(flag=_no_flag) f = + if (!flag || (!_level >= level)) then f () else () -let exe ?(level=1) p = - if (!_level >= level) then p () (**** VERSION GORE *****) (* diff --git a/src/verbose.mli b/src/verbose.mli index e08817db..7484b16a 100644 --- a/src/verbose.mli +++ b/src/verbose.mli @@ -1,5 +1,3 @@ -(** Time-stamp: <modified the 28/01/2008 (at 14:13) by Erwan Jahier> *) - (*---------------------------------------------------------------------- module : Verbose date : @@ -12,10 +10,22 @@ Verbose.put "format" args... ----------------------------------------------------------------------*) +val on : unit -> unit +val off : unit -> unit +val set : int -> unit + +type flag +val get_flag : string -> flag +val set_flag : flag -> unit +val flag_list : unit -> string list + +(* val level : unit -> int *) -val set_level : int -> unit -val get_level : unit -> int +(* print/execute if EITHER: + - level (dflt=1) is >= than the set level + - flag is set +*) -val printf : ?level:int -> ('a, unit, string, unit) format4 -> 'a -val exe : ?level:int -> (unit -> unit) -> unit -val print_string : ?level:int -> string -> unit +val printf : ?level:int -> ?flag:flag -> ('a, unit, string, unit) format4 -> 'a +val print_string : ?level:int -> ?flag:flag -> string -> unit +val exe : ?level:int -> ?flag:flag -> (unit -> unit) -> unit -- GitLab