diff --git a/src/coalg/coalg.ml b/src/coalg/coalg.ml index d0f2e010282ea3207324eac1bd422755d05a1477..b1795e25581870280f46dadbbe680aec2af2ce0f 100644 --- a/src/coalg/coalg.ml +++ b/src/coalg/coalg.ml @@ -12,25 +12,6 @@ module FE = FunctorParsing open CoolUtils -(* The type of formulae that are accepted. *) -(* -let sorts = Array.make 1 (CoAlgMisc.MultiModalK, [0]) -*) -(* -let sorts = Array.make 3 (CoAlgMisc.MultiModalK, [0]) -let _ = sorts.(0) <- (CoAlgMisc.Choice, [1; 2]) -*) -(* -let sorts = [| (CoAlgMisc.MultiModalKD, [0]) - (*; (CoAlgMisc.MultiModalKD, [1]) - ; (CoAlgMisc.Fusion, [1; 1]) *) - |] -let sorts = [| (CM.MultiModalK, [0]) - (*; (CoAlgMisc.MultiModalKD, [1]) - ; (CoAlgMisc.Fusion, [1; 1]) *) - |] -*) - (* A partial function mapping nominals to their sorts. *) let nomTable name = assert (CoAlgFormula.isNominal name); @@ -39,11 +20,31 @@ let nomTable name = let _ = Gc.set { (Gc.get()) with Gc.minor_heap_size = 4194304; major_heap_increment = 67108864; space_overhead = 120 } -(* -let _ = - let str = "A . B C" in - print_endline (FE.stringFromFunctorExp (FE.functorExpFromString str)) -*) +type options = + { verbose : bool + } + +let defaultOpts = { verbose = false } + +let options = + [ { Args.long = "verbose" + ; Args.short = Some 'v' + ; Args.description = "print verbose output with statistics" + ; Args.argument = Args.No_arg (fun a -> { verbose = true }) + } + ; { Args.long = "agents" + ; Args.short = None + ; Args.description = "expects the next argument AGLIST to be a list of integers\n" + ^ "which is treaten as the set of agents for CL formulas\n" + ^ "it defaults to 1,2,3,4,5,6,7,8,9,10" + ; Args.argument = + let parse_agents strAglist = + let aglist = List.map int_of_string (Str.split (Str.regexp ",") strAglist) in + CoolUtils.cl_set_agents (Array.of_list aglist) + in + Args.Required_arg ("AGLIST", fun s a -> parse_agents s; a) + } + ] let printUsage name = print_endline ("Usage: \"" ^ name ^ " <task> <functor> [<flags>]\" where"); @@ -61,12 +62,7 @@ let printUsage name = print_endline " or <functor> . <functor> (binds strongest)"; print_endline " (so K+KD.CL*Id stand for (K + ((KD.CL) * Id)))"; print_endline " <flags> = a list of the following items"; - print_endline " --agents AGLIST"; - print_endline " expects the next argument AGLIST to be a list of integers"; - print_endline " which is treaten as the set of agents for CL formulas"; - print_endline " it defaults to 1,2,3,4,5,6,7,8,9,10"; - print_endline " --verbose"; - print_endline " print verbose output for sat task"; + Args.help stdout 11 options; print_endline ""; print_endline "Problems are read from standard input and have the form"; print_endline " [ ass_1; .. ; ass_n |- ] concept"; @@ -90,10 +86,9 @@ let printUsage name = exit 1 let counter = ref 0 -let verbose = ref false -let choiceSat () = - let verb = !verbose in +let choiceSat opts = + let verb = opts.verbose in let sorts = (FE.sortTableFromString Sys.argv.(2)) in (* test if GML or PML occurs in sort table *) @@ -119,8 +114,8 @@ let choiceSat () = done with End_of_file -> () -let choiceProvable () = - let verb = !verbose in +let choiceProvable opts = + let verb = opts.verbose in let sorted_not ((s,f):CF.sortedFormula) : CF.sortedFormula = (s,CF.NOT f) in @@ -144,7 +139,7 @@ let choiceProvable () = done with End_of_file -> () -let choicePrint () = +let choicePrint opts = try while true do let input = read_line () in @@ -158,7 +153,7 @@ let choicePrint () = done with End_of_file -> () -let choiceNom2fix () = +let choiceNom2fix opts = try while true do let input = read_line () in @@ -173,7 +168,7 @@ let choiceNom2fix () = done with End_of_file -> () -let choiceNNF () = +let choiceNNF opts = try while true do let input = read_line () in @@ -187,7 +182,7 @@ let choiceNNF () = done with End_of_file -> () -let choiceGraph () = +let choiceGraph opts = let sorts = (FE.sortTableFromString Sys.argv.(2)) in let input = read_line () in @@ -195,7 +190,7 @@ let choiceGraph () = ignore(CoAlgReasoner.isSat sorts nomTable tbox f); print_endline (CM.graphToDot ()) -let choiceVerify () = +let choiceVerify opts = try while true do print_string "> "; @@ -212,40 +207,21 @@ let choiceVerify () = done with End_of_file -> () -let rec parseFlags arr offs : unit = - let offs = ref (offs) in - let getParam () = - if ((!offs + 1) >= Array.length arr) then - raise (CoAlgFormula.CoAlgException ("Parameter missing for flag »" ^ (arr.(!offs)) ^ "«")) - else - offs := !offs + 1; - arr.(!offs) - in - if (!offs >= Array.length arr) then () - else (ignore (match arr.(!offs) with - | "--verbose" -> verbose := true - | "--agents" -> - let strAglist = getParam () in - let aglist = List.map int_of_string (Str.split (Str.regexp ",") strAglist) in - CoolUtils.cl_set_agents (Array.of_list aglist) - | _ -> - raise (CoAlgFormula.CoAlgException ("Unknown flag »" ^ (arr.(!offs)) ^ "«")) - ) ; parseFlags arr (!offs + 1) - ) - let _ = if Array.length Sys.argv < 3 then printUsage (Sys.argv.(0)) else let choice = Sys.argv.(1) in - parseFlags Sys.argv 3; - match choice with - | "sat" -> choiceSat () - | "prov" -> choiceProvable () - | "print" -> choicePrint () - | "nnf" -> choiceNNF () - | "verify" -> choiceVerify () - | "graph" -> choiceGraph () - | "nom2fix" -> choiceNom2fix() - | _ -> printUsage (Sys.argv.(0)) + match Args.parse Sys.argv 3 options defaultOpts with + | Args.Error e -> Printf.printf "%s\n\n" e; printUsage (Sys.argv.(0)) + | Args.Ok opts -> + match choice with + | "sat" -> choiceSat opts + | "prov" -> choiceProvable opts + | "print" -> choicePrint opts + | "nnf" -> choiceNNF opts + | "verify" -> choiceVerify opts + | "graph" -> choiceGraph opts + | "nom2fix" -> choiceNom2fix opts + | _ -> printUsage (Sys.argv.(0)) (* vim: set et sw=2 sts=2 ts=8 : *) diff --git a/src/lib/CoolUtils.ml b/src/lib/CoolUtils.ml index 92c6481c0cbe922c1e7beadaf616e59fa85c1cbc..ef61ee63a037780cd295ac16f678aa4d38b3f23c 100644 --- a/src/lib/CoolUtils.ml +++ b/src/lib/CoolUtils.ml @@ -43,6 +43,81 @@ module TList = struct List.fold_left (fun acc x -> acc @ List.map (fun lst -> x::lst) tail) [] l end +module Args = struct + type 'a param_parser = 'a -> 'a + type 'a argument = Required_arg of string * (string -> 'a param_parser) + | No_arg of 'a param_parser + + type 'a description = + { long : string + ; short : char option + ; description : string + ; argument : 'a argument + } + + exception ParseError of string + + let find str mapping = + try (List.find (fun arg -> arg.long = str) mapping) + with + Not_found -> raise (ParseError (Printf.sprintf "Unknown option %s" str)) + + let findShort char mapping = + try (List.find (fun arg -> arg.short = Some char) mapping) + with + Not_found -> raise (ParseError (Printf.sprintf "Unknown option %c" char)) + + type 'a result = Ok of 'a | Error of string + + let parse ~argv ~offset ~options ~default = + let rec parseImpl offs args = + let offs = ref (offs) in + let getParam () = + if ((!offs + 1) >= Array.length argv) then + raise (ParseError (Printf.sprintf "Parameter missing for flag »%s«" argv.(!offs))) + else + offs := !offs + 1; + argv.(!offs) + in + if (!offs >= Array.length argv) then args + else + let arg = argv.(!offs) in + let arg_desc = if String.length arg > 2 && String.sub arg 0 2 = "--" then + find (String.sub arg 2 (String.length arg - 2)) options + else if String.length arg > 1 && String.get arg 0 = '-' then + findShort (String.get arg 1) options + else + raise (ParseError (Printf.sprintf "Unknown argument %s" arg)) + in + let args = match arg_desc.argument with + | Required_arg (_,p) -> p (getParam ()) args + | No_arg p -> p args + in + parseImpl (!offs+1) args + + in + try Ok (parseImpl offset default) + with ParseError e -> Error e + + let help channel offset mapping = + let padding offset = String.make offset ' ' in + let help_option desc = + let long = "--" ^ desc.long in + let short = match desc.short with + | Some x -> ",-" ^ Char.escaped x + | None -> "" + in + let var = match desc.argument with + | No_arg _ -> "" + | Required_arg (a,_) -> " " ^ a + in + Printf.fprintf channel "%s%s%s%s\n" (padding offset) long short var; + (* TODO split_on_char is not available in ocaml < 4.04 *) + List.iter (Printf.fprintf channel "%s%s\n" (padding (offset+2))) (String.split_on_char '\n' desc.description) + in + List.iter help_option mapping +end + let agents = ref ([|1;2;3;4;5|]) let cl_get_agents () = !agents diff --git a/src/lib/CoolUtils.mli b/src/lib/CoolUtils.mli index 191eed60dceed1f9e07d0b2529ebe7ec2caeca2b..f045e062ac79fdd744ac585052e1be642571a45f 100644 --- a/src/lib/CoolUtils.mli +++ b/src/lib/CoolUtils.mli @@ -25,6 +25,44 @@ module TList : sig val combinations : 'a list list -> 'a list list end +(* TODO Document, test *) +(** Command line argument parser *) +module Args : sig + type 'a param_parser = 'a -> 'a + + (** Specifies whether the option takes an addition paramter. *) + type 'a argument = Required_arg of string * (string -> 'a param_parser) + (** name of the argument and parser. *) + | No_arg of 'a param_parser + + type 'a description = + { long : string + ; short : char option + ; description : string + ; argument : 'a argument + } + + type 'a result = Ok of 'a | Error of string + + (** parses command line options. + + The user can choose the type 'a, provided they provide parsers and a + default value. + + @param argv The Sys.argv array + @param offset The index in [argv] where option parsing should start + @param options Descriptions of all available options + @param default Default value for the option type + @return The option type or a parse error + *) + val parse : argv:string array -> offset:int -> options:'a description list + -> default:'a -> 'a result + + (** [help out indent options] prints help for [options] to [out] indented + by [indent] characters *) + val help : out_channel -> int -> 'a description list -> unit +end + val cl_get_agents : unit -> int array val cl_set_agents : int array -> unit