From 3ef7797ef6fc605dfafb32523261fe1b023aeecb Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Fri, 28 Apr 2006 14:59:16 +0000 Subject: Imported Upstream version 8.0pl3+8.1alpha --- ide/utils/config_file.ml | 642 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 642 insertions(+) create mode 100644 ide/utils/config_file.ml (limited to 'ide/utils/config_file.ml') diff --git a/ide/utils/config_file.ml b/ide/utils/config_file.ml new file mode 100644 index 00000000..30eb0111 --- /dev/null +++ b/ide/utils/config_file.ml @@ -0,0 +1,642 @@ +(*********************************************************************************) +(* Cameleon *) +(* *) +(* Copyright (C) 2005 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. *) +(* *) +(* This program is free software; you can redistribute it and/or modify *) +(* it under the terms of the GNU Library General Public License as *) +(* published by the Free Software Foundation; either version 2 of the *) +(* License, or any later version. *) +(* *) +(* This program is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Library General Public License for more details. *) +(* *) +(* You should have received a copy of the GNU Library General Public *) +(* License along with this program; if not, write to the Free Software *) +(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *) +(* 02111-1307 USA *) +(* *) +(* Contact: Maxence.Guesdon@inria.fr *) +(* *) +(*********************************************************************************) + +(* $Id: config_file.ml 8618 2006-03-08 11:44:47Z notin $ *) + +(* TODO *) +(* section comments *) +(* better obsoletes: no "{}", line cuts *) + +(* possible improvements: *) +(* use lex/yacc instead of genlex to be more robust, efficient, allow arrays and other types, read comments. *) +(* description and help, level (beginner/advanced/...) for each cp *) +(* find an option from its name and group *) +(* class hooks *) +(* get the sections of a group / of a file *) +(* read file format from inifiles and ConfigParser *) + + +(* Read the mli before reading this file! *) + + +(* ******************************************************************************** *) +(* ******************************** misc utilities ******************************** *) +(* ******************************************************************************** *) +(* This code is intended to be usable without any dependencies. *) + +(* pipeline style, see for instance Raw.of_channel. *) +let (|>) x f = f x + +(* as List.assoc, but applies f to the element matching [key] and returns the list +where this element has been replaced by the result of f. *) +let rec list_assoc_remove key f = function + | [] -> raise Not_found + | (key',value) as elt :: tail -> + if key <> key' + then elt :: list_assoc_remove key f tail + else match f value with + | None -> tail + | Some a -> (key',a) :: tail + +(* reminiscent of String.concat. Same as [Queue.iter f1 queue] + but calls [f2 ()] between each calls to f1. + Does not call f2 before the first call nor after the last call to f2. + Could be more efficient with a richer module interface of Queue. +*) +let queue_iter_between f1 f2 queue = +(* let f flag elt = if flag then f2 (); (f1 elt:unit); true in *) + let f flag elt = if flag then f2 (); f1 elt; true in + ignore (Queue.fold f false queue) + +let list_iter_between f1 f2 = function + [] -> () + | a::[] -> f1 a + | a::tail -> f1 a; List.iter (fun elt -> (f2 ():unit); f1 elt) tail +(* | a::tail -> f1 a; List.iter (fun elt -> f2 (); f1 elt) tail *) +(* !! types ??? *) + +(* to ensure that strings will be parsed correctly by Genlex. +It's more comfortable not to have quotes around the string, but sometimes it's necessary. *) +exception Unsafe_string +let safe_string s = + if s = "" + then "\"\"" + else if ( + try match s.[0] with + | 'a'..'z' | 'A'..'Z' -> + for i = 1 to String.length s - 1 do + match s.[i] with + 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' -> () + | _ -> raise Unsafe_string + done; + false + | _ -> + try + string_of_int (int_of_string s) <> s || + string_of_float (float_of_string s) <> s + with Failure "int_of_string" | Failure "float_of_string" -> true + with Unsafe_string -> true) + then Printf.sprintf "\"%s\"" (String.escaped s) + else s + + +(* ******************************************************************************** *) +(* ************************************* core ************************************* *) +(* ******************************************************************************** *) + +module Raw = struct + type cp = + | String of string + | Int of int + | Float of float + | List of cp list + | Tuple of cp list + | Section of (string * cp) list + +(* code generated by +camlp4 pa_o.cmo pa_op.cmo pr_o.cmo -- -o config_file_parser.ml -impl config_file_parser.ml4 +Unreadable on purpose, edit the file config_file_parser.ml4 rather than editing this (huge) lines. Then manually copy-paste here the content of config_file_parser.ml. +Could be one day rewritten with ocamllex/yacc to be more robust, efficient, allow arrays, read comments...*) + module Parse = struct + let lexer = Genlex.make_lexer ["="; "{"; "}"; "["; "]"; ";"; "("; ")"; ","] + let rec file l (strm__ : _ Stream.t) = match try Some (ident strm__) with Stream.Failure -> None with Some id -> begin match Stream.peek strm__ with Some (Genlex.Kwd "=") -> Stream.junk strm__; let v = try value strm__ with Stream.Failure -> raise (Stream.Error "") in begin try file ((id, v) :: l) strm__ with Stream.Failure -> raise (Stream.Error "") end | _ -> raise (Stream.Error "") end | _ -> List.rev l + and value (strm__ : _ Stream.t) = match Stream.peek strm__ with Some (Genlex.Kwd "{") -> Stream.junk strm__; let v = try file [] strm__ with Stream.Failure -> raise (Stream.Error "") in begin match Stream.peek strm__ with Some (Genlex.Kwd "}") -> Stream.junk strm__; Section v | _ -> raise (Stream.Error "") end | Some (Genlex.Ident s) -> Stream.junk strm__; String s | Some (Genlex.String s) -> Stream.junk strm__; String s | Some (Genlex.Int i) -> Stream.junk strm__; Int i | Some (Genlex.Float f) -> Stream.junk strm__; Float f | Some (Genlex.Char c) -> Stream.junk strm__; String (String.make 1 c) | Some (Genlex.Kwd "[") -> Stream.junk strm__; let v = try list [] strm__ with Stream.Failure -> raise (Stream.Error "") in List v | Some (Genlex.Kwd "(") -> Stream.junk strm__; let v = try list [] strm__ with Stream.Failure -> raise (Stream.Error "") in Tuple v | _ -> raise Stream.Failure + and ident (strm__ : _ Stream.t) = match Stream.peek strm__ with Some (Genlex.Ident s) -> Stream.junk strm__; s | Some (Genlex.String s) -> Stream.junk strm__; s | _ -> raise Stream.Failure + and list l (strm__ : _ Stream.t) = match Stream.peek strm__ with Some (Genlex.Kwd ";") -> Stream.junk strm__; begin try list l strm__ with Stream.Failure -> raise (Stream.Error "") end | Some (Genlex.Kwd ",") -> Stream.junk strm__; begin try list l strm__ with Stream.Failure -> raise (Stream.Error "") end | _ -> match try Some (value strm__) with Stream.Failure -> None with Some v -> begin try list (v :: l) strm__ with Stream.Failure -> raise (Stream.Error "") end | _ -> match Stream.peek strm__ with Some (Genlex.Kwd "]") -> Stream.junk strm__; List.rev l | Some (Genlex.Kwd ")") -> Stream.junk strm__; List.rev l | _ -> raise Stream.Failure + end + + open Format + (* formating convention: the caller has to open the box, close it and flush the output *) + (* remarks on Format: + set_margin impose un appel à set_max_indent + sprintf et bprintf sont flushées à chaque appel*) + + (* pretty print a Raw.cp *) + let rec save formatter = function + | String s -> fprintf formatter "%s" (safe_string s) (* How can I cut lines and *) + | Int i -> fprintf formatter "%d" i (* print backslashes just before the \n? *) + | Float f -> fprintf formatter "%g" f + | List l -> + fprintf formatter "[@["; + list_iter_between + (fun v -> fprintf formatter "@["; save formatter v; fprintf formatter "@]") + (fun () -> fprintf formatter ";@ ") + l; + fprintf formatter "@]]" + | Tuple l -> + fprintf formatter "(@["; + list_iter_between + (fun v -> fprintf formatter "@["; save formatter v; fprintf formatter "@]") + (fun () -> fprintf formatter ",@ ") + l; + fprintf formatter "@])" + | Section l -> + fprintf formatter "{@;<0 2>@["; + list_iter_between + (fun (name,value) -> + fprintf formatter "@[%s =@ @[" name; + save formatter value; + fprintf formatter "@]@]";) + (fun () -> fprintf formatter "@;<2 0>") + l; + fprintf formatter "@]}" + +(* let to_string r = save str_formatter r; flush_str_formatter () *) + let to_channel out_channel r = + let f = formatter_of_out_channel out_channel in + fprintf f "@["; save f r; fprintf f "@]@?" + + let of_string s = s |> Stream.of_string |> Parse.lexer |> Parse.value + + let of_channel in_channel = + let result = in_channel |> Stream.of_channel |> Parse.lexer |> Parse.file [] in + close_in in_channel; + result +end + +(* print the given string in a way compatible with Format. + Truncate the lines when needed, indent the newlines.*) +let print_help formatter = + String.iter (function + | ' ' -> Format.pp_print_space formatter () + | '\n' -> Format.pp_force_newline formatter () + | c -> Format.pp_print_char formatter c) + +type 'a wrappers = { + to_raw : 'a -> Raw.cp; + of_raw : Raw.cp -> 'a} + +class type ['a] cp = object +(* method private to_raw = wrappers.to_raw *) +(* method private of_raw = wrappers.of_raw *) +(* method private set_string s = s |> Raw.of_string |> self#of_raw |> self#set *) + method add_hook : ('a -> 'a -> unit) -> unit + method get : 'a + method get_default : 'a + method set : 'a -> unit + method reset : unit + + method get_formatted : Format.formatter -> unit + method get_default_formatted : Format.formatter -> unit + method get_help_formatted : Format.formatter -> unit + + method get_name : string list + method get_short_name : string option + method set_short_name : string -> unit + method get_help : string + method get_spec : Arg.spec + + method set_raw : Raw.cp -> unit +end + +type groupable_cp = < + get_name : string list; + get_short_name : string option; + get_help : string; + + get_formatted : Format.formatter -> unit; + get_default_formatted : Format.formatter -> unit; + get_help_formatted : Format.formatter -> unit; + get_spec : Arg.spec; + + reset : unit; + set_raw : Raw.cp -> unit; > + +exception Double_name +exception Missing_cp of groupable_cp +exception Wrong_type of (out_channel -> unit) + +(* Two exceptions to stop the iteration on queues. *) +exception Found +exception Found_cp of groupable_cp + +(* The data structure to store the cps. +It's a tree, each node is a section, and a queue of sons with their name. +Each leaf contains a cp. *) +type 'a nametree = + | Immediate of 'a + | Subsection of ((string * 'a nametree) Queue.t) + (* this Queue must be nonempty for group.read.choose *) + +class group = object (self) + val mutable cps = Queue.create () (* hold all the added cps, in a nametree. *) + + method add : 'a. 'a cp -> unit = fun original_cp -> + let cp = (original_cp :> groupable_cp) in + (* function called when we reach the end of the list cp#get_name. *) + let add_immediate name cp queue = + Queue.iter (fun (name',_) -> if name = name' then raise Double_name) queue; + Queue.push (name, Immediate cp) queue in + (* adds the cp with name [first_name::last_name] in section [section]. *) + let rec add_in_section section first_name last_name cp queue = + let sub_add = match last_name with (* what to do once we have find the correct section *) + | [] -> add_immediate first_name + | middle_name :: last_name -> add_in_section first_name middle_name last_name in + try + Queue.iter + (function + | name, Subsection subsection when name = section -> + sub_add cp subsection; raise Found + | _ -> ()) + queue; + let sub_queue = Queue.create () in + sub_add cp sub_queue; + Queue.push (section, Subsection sub_queue) queue + with Found -> () in + (match cp#get_name with + | [] -> failwith "empty name" + | first_name :: [] -> add_immediate first_name cp cps + | first_name :: middle_name :: last_name -> + add_in_section first_name middle_name last_name cp cps) + + method write ?(with_help=true) filename = + let out_channel = open_out filename in + let formatter = Format.formatter_of_out_channel out_channel in + let print = Format.fprintf formatter in + print "@["; + let rec save_queue formatter = + queue_iter_between + (fun (name,nametree) -> save_nametree name nametree) + (Format.pp_print_cut formatter) + and save_nametree name = function + | Immediate cp -> + if with_help && cp#get_help <> "" then + (print "@[(* "; cp#get_help_formatted formatter; + print "@ *)@]@,"); + Format.fprintf formatter "@[%s =@ @[" (safe_string name); + cp#get_formatted formatter; + print "@]@]" + | Subsection queue -> + Format.fprintf formatter "%s = {@;<0 2>@[" (safe_string name); + save_queue formatter queue; + print "@]@,}" in + save_queue formatter cps; + print "@]@."; close_out out_channel + + method read ?obsoletes ?(no_default=false) + ?(on_type_error = fun groupable_cp raw_cp output filename in_channel -> + close_in in_channel; + Printf.eprintf + "Type error while loading configuration parameter %s from file %s.\n%!" + (String.concat "." groupable_cp#get_name) filename; + output stderr; + exit 1) + filename = + (* [filename] is created if it doesn't exist. In this case there is no need to read it. *) + match Sys.file_exists filename with false -> self#write filename | true -> + let in_channel = open_in filename in + (* what to do when a cp is missing: *) + let missing cp default = if no_default then raise (Missing_cp cp) else default in + (* returns a cp contained in the nametree queue, which must be nonempty *) + let choose queue = + let rec iter q = Queue.iter (function + | _, Immediate cp -> raise (Found_cp cp) + | _, Subsection q -> iter q) q in + try iter queue; failwith "choose" with Found_cp cp -> cp in + (* [set_and_remove raw_cps nametree] sets the cp of [nametree] to their value + defined in [raw_cps] and returns the remaining raw_cps. *) + let set_cp cp value = + try cp#set_raw value + with Wrong_type output -> on_type_error cp value output filename in_channel in + let rec set_and_remove raw_cps = function + | name, Immediate cp -> + (try list_assoc_remove name (fun value -> set_cp cp value; None) raw_cps + with Not_found -> missing cp raw_cps) + | name, Subsection queue -> + (try list_assoc_remove name + (function + | Raw.Section l -> + (match remainings l queue with + | [] -> None + | l -> Some (Raw.Section l)) + | r -> missing (choose queue) (Some r)) + raw_cps + with Not_found -> missing (choose queue) raw_cps) + and remainings raw_cps queue = Queue.fold set_and_remove raw_cps queue in + let remainings = remainings (Raw.of_channel in_channel) cps in + (* Handling of cps defined in filename but not belonging to self. *) + if remainings <> [] then match obsoletes with + | Some filename -> + let out_channel = + open_out filename in +(* open_out_gen [Open_wronly; Open_creat; Open_append; Open_text] 0o666 filename in *) + let formatter = Format.formatter_of_out_channel out_channel in + Format.fprintf formatter "@["; + Raw.save formatter (Raw.Section remainings); + Format.fprintf formatter "@]@."; + close_out out_channel + | None -> () + + method command_line_args ~section_separator = + let print = Format.fprintf Format.str_formatter in (* shortcut *) + let result = ref [] in let push x = result := x :: !result in + let rec iter = function + | _, Immediate cp -> + let key = "-" ^ String.concat section_separator cp#get_name in + let spec = cp#get_spec in + let doc = ( + print "@["; + Format.pp_print_as Format.str_formatter (String.length key +3) ""; + if cp#get_help <> "" + then (print "@,@["; cp#get_help_formatted Format.str_formatter; print "@]@ ") + else print "@,"; + print "@[@[current:@;<1 2>@["; cp#get_formatted Format.str_formatter; + print "@]@],@ @[default:@;<1 2>@["; cp#get_default_formatted Format.str_formatter; + print "@]@]@]@]"; + Format.flush_str_formatter ()) in + (match cp#get_short_name with + | None -> () + | Some short_name -> push ("-" ^ short_name,spec,"")); + push (key,spec,doc) + | _, Subsection queue -> Queue.iter iter queue in + Queue.iter iter cps; + List.rev !result +end + + +(* Given wrappers for the type 'a, cp_custom_type defines a class 'a cp. *) +class ['a] cp_custom_type wrappers + ?group:(group:group option) name ?short_name default help = +object (self) + method private to_raw = wrappers.to_raw + method private of_raw = wrappers.of_raw + + val mutable value = default + (* output *) + method get = value + method get_default = default + method get_formatted formatter = self#get |> self#to_raw |> Raw.save formatter + method get_default_formatted formatter = self#get_default |> self#to_raw |> Raw.save formatter + (* input *) + method set v = let v' = value in value <- v; self#exec_hooks v' v + method set_raw v = self#of_raw v |> self#set + method private set_string s = s |> Raw.of_string |> self#of_raw |> self#set + method reset = self#set self#get_default + + (* name *) + val mutable shortname = short_name + method get_name = name + method get_short_name = shortname + method set_short_name s = shortname <- Some s + + (* help *) + method get_help = help + method get_help_formatted formatter = print_help formatter self#get_help + method get_spec = Arg.String self#set_string + + (* hooks *) + val mutable hooks = [] + method add_hook f = hooks <- (f:'a->'a->unit) :: hooks + method private exec_hooks v' v = List.iter (fun f -> f v' v) hooks + + initializer match group with Some g -> g#add (self :> 'a cp) | None -> () +end + + +(* ******************************************************************************** *) +(* ****************************** predefined classes ****************************** *) +(* ******************************************************************************** *) + +let int_wrappers = { + to_raw = (fun v -> Raw.Int v); + of_raw = function + | Raw.Int v -> v + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.Int expected, got %a\n%!" Raw.to_channel r))} +class int_cp ?group name ?short_name default help = object (self) + inherit [int] cp_custom_type int_wrappers ?group name ?short_name default help + method get_spec = Arg.Int self#set +end + +let float_wrappers = { + to_raw = (fun v -> Raw.Float v); + of_raw = function + | Raw.Float v -> v + | Raw.Int v -> float v + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.Float expected, got %a\n%!" Raw.to_channel r)) +} +class float_cp ?group name ?short_name default help = object (self) + inherit [float] cp_custom_type float_wrappers ?group name ?short_name default help + method get_spec = Arg.Float self#set +end + +(* The Pervasives version is too restrictive *) +let bool_of_string s = + match String.lowercase s with + | "false" | "no" | "n" | "0" -> false (* "0" and "1" aren't used. *) + | "true" | "yes" | "y" | "1" -> true + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.Bool expected, got %s\n%!" r)) +let bool_wrappers = { + to_raw = (fun v -> Raw.String (string_of_bool v)); + of_raw = function + | Raw.String v -> bool_of_string v + | Raw.Int v -> v <> 0 + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.Bool expected, got %a\n%!" Raw.to_channel r)) +} +class bool_cp ?group name ?short_name default help = object (self) + inherit [bool] cp_custom_type bool_wrappers ?group name ?short_name default help + method get_spec = Arg.Bool self#set +end + +let string_wrappers = { + to_raw = (fun v -> Raw.String v); + of_raw = function + | Raw.String v -> v + | Raw.Int v -> string_of_int v + | Raw.Float v -> string_of_float v + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.String expected, got %a\n%!" Raw.to_channel r)) +} +class string_cp ?group name ?short_name default help = object (self) + inherit [string] cp_custom_type string_wrappers ?group name ?short_name default help + method private of_string s = s + method get_spec = Arg.String self#set +end + +let list_wrappers wrappers = { + to_raw = (fun l -> Raw.List (List.map wrappers.to_raw l)); + of_raw = function + | Raw.List l -> List.map wrappers.of_raw l + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.List expected, got %a\n%!" Raw.to_channel r)) +} +class ['a] list_cp wrappers = ['a list] cp_custom_type (list_wrappers wrappers) + +let option_wrappers wrappers = { + to_raw = (function + | Some v -> wrappers.to_raw v + | None -> Raw.String ""); + of_raw = function + | Raw.String s as v -> ( + if s = "" || s = "None" then None + else if String.length s >= 5 && String.sub s 0 5 = "Some " + then Some (wrappers.of_raw (Raw.String (String.sub s 5 (String.length s -5)))) + else Some (wrappers.of_raw v)) + | r -> Some (wrappers.of_raw r)} +class ['a] option_cp wrappers = ['a option] cp_custom_type (option_wrappers wrappers) + +let enumeration_wrappers enum = + let switched = List.map (fun (string,cons) -> cons,string) enum in + {to_raw = (fun v -> Raw.String (List.assq v switched)); + of_raw = function + | Raw.String s -> + (try List.assoc s enum + with Not_found -> failwith (Printf.sprintf "%s isn't a known constructor" s)) + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw enumeration expected, got %a\n%!" Raw.to_channel r)) +} +class ['a] enumeration_cp enum ?group name ?short_name default help = object (self) + inherit ['a] cp_custom_type (enumeration_wrappers enum) + ?group name ?short_name default help + method get_spec = Arg.Symbol (List.map fst enum, (fun s -> self#set (List.assoc s enum))) +end + +let tuple2_wrappers wrapa wrapb = { + to_raw = (fun (a,b) -> Raw.Tuple [wrapa.to_raw a; wrapb.to_raw b]); + of_raw = function + | Raw.Tuple [a;b] -> wrapa.of_raw a, wrapb.of_raw b + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.Tuple 2 expected, got %a\n%!" Raw.to_channel r)) +} +class ['a, 'b] tuple2_cp wrapa wrapb = ['a*'b] cp_custom_type (tuple2_wrappers wrapa wrapb) + +let tuple3_wrappers wrapa wrapb wrapc = { + to_raw = (fun (a,b,c) -> Raw.Tuple[wrapa.to_raw a; wrapb.to_raw b; wrapc.to_raw c]); + of_raw = function + | Raw.Tuple [a;b;c] -> wrapa.of_raw a, wrapb.of_raw b, wrapc.of_raw c + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.Tuple 3 expected, got %a\n%!" Raw.to_channel r)) +} +class ['a,'b,'c] tuple3_cp wrapa wrapb wrapc = + ['a*'b*'c] cp_custom_type (tuple3_wrappers wrapa wrapb wrapc) + +let tuple4_wrappers wrapa wrapb wrapc wrapd = { + to_raw=(fun (a,b,c,d)->Raw.Tuple[wrapa.to_raw a;wrapb.to_raw b;wrapc.to_raw c;wrapd.to_raw d]); + of_raw = function + | Raw.Tuple [a;b;c;d] -> wrapa.of_raw a, wrapb.of_raw b, wrapc.of_raw c, wrapd.of_raw d + | r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan + "Raw.Tuple 4 expected, got %a\n%!" Raw.to_channel r)) +} +class ['a,'b,'c,'d] tuple4_cp wrapa wrapb wrapc wrapd = + ['a*'b*'c*'d] cp_custom_type (tuple4_wrappers wrapa wrapb wrapc wrapd) + +class string2_cp = [string,string] tuple2_cp string_wrappers string_wrappers +(* class color_cp = string_cp *) +class font_cp = string_cp +class filename_cp = string_cp + + +(* ******************************************************************************** *) +(******************** Backward compatibility with module Options ****************** *) +(* ******************************************************************************** *) + +type 'a option_class = 'a wrappers +type 'a option_record = 'a cp +type options_file = {mutable filename:string; group:group} + +let create_options_file filename = {filename = filename; group = new group} +let set_options_file options_file filename = options_file.filename <- filename +let load {filename=f; group = g} = g#read f +let append {group=g} filename = g#read filename +let save {filename=f; group = g} = g#write ~with_help:false f +let save_with_help {filename=f; group = g} = g#write ~with_help:true f +let define_option {group=group} name help option_class default = + (new cp_custom_type option_class ~group name default help) +let option_hook cp f = cp#add_hook (fun _ _ -> f ()) + +let string_option = string_wrappers +let color_option = string_wrappers +let font_option = string_wrappers +let int_option = int_wrappers +let bool_option = bool_wrappers +let float_option = float_wrappers +let string2_option = tuple2_wrappers string_wrappers string_wrappers + +let option_option = option_wrappers +let list_option = list_wrappers +let sum_option = enumeration_wrappers +let tuple2_option (a,b) = tuple2_wrappers a b +let tuple3_option (a,b,c) = tuple3_wrappers a b c +let tuple4_option (a,b,c,d) = tuple4_wrappers a b c d + +let ( !! ) cp = cp#get +let ( =:= ) cp value = cp#set value + +let shortname cp = String.concat ":" cp#get_name +let get_help cp = cp#get_help + +type option_value = + Module of option_module +| StringValue of string +| IntValue of int +| FloatValue of float +| List of option_value list +| SmallList of option_value list +and option_module = (string * option_value) list + +let rec value_to_raw = function + | Module a -> Raw.Section (List.map (fun (name,value) -> name, value_to_raw value) a) + | StringValue a -> Raw.String a + | IntValue a -> Raw.Int a + | FloatValue a -> Raw.Float a + | List a -> Raw.List (List.map value_to_raw a) + | SmallList a -> Raw.Tuple (List.map value_to_raw a) +let rec raw_to_value = function + | Raw.String a -> StringValue a + | Raw.Int a -> IntValue a + | Raw.Float a -> FloatValue a + | Raw.List a -> List (List.map raw_to_value a) + | Raw.Tuple a -> SmallList (List.map raw_to_value a) + | Raw.Section a -> Module (List.map (fun (name,value) -> name, raw_to_value value) a) + +let define_option_class _ of_option_value to_option_value = + {to_raw = (fun a -> a |> to_option_value |> value_to_raw); + of_raw = (fun a -> a |> raw_to_value |> of_option_value)} + +let to_value {to_raw = to_raw} a = a |> to_raw |> raw_to_value +let from_value {of_raw = of_raw} a = a |> value_to_raw |> of_raw + +let of_value_w wrappers a = a |> value_to_raw |> wrappers.of_raw +let to_value_w wrappers a = a |> wrappers.to_raw |> raw_to_value +(* fancy indentation when finishing this stub code, not good style :-) *) +let value_to_string : option_value -> string = of_value_w string_option +let string_to_value = to_value_w string_option +let value_to_int = of_value_w int_option +let int_to_value = to_value_w int_option +let value_to_bool = of_value_w bool_option +let bool_to_value = to_value_w bool_option +let value_to_float = of_value_w float_option +let float_to_value = to_value_w float_option +let value_to_string2 = of_value_w string2_option +let string2_to_value = to_value_w string2_option +let value_to_list of_value = + let wrapper = define_option_class "" of_value (fun _ -> failwith "value_to_list") in + of_value_w (list_option wrapper) +let list_to_value to_value = + let wrapper = define_option_class "" (fun _ -> failwith "value_to_list") to_value in + to_value_w (list_option wrapper) -- cgit v1.2.3