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 "[@[<b0>";
        list_iter_between
          (fun v -> fprintf formatter "@[<b2>"; save formatter v; fprintf formatter "@]")
          (fun () -> fprintf formatter ";@ ")
          l;
        fprintf formatter "@]]"
    | Tuple l ->
        fprintf formatter "(@[<b0>";
        list_iter_between
          (fun v -> fprintf formatter "@[<b2>"; save formatter v; fprintf formatter "@]")
          (fun () -> fprintf formatter ",@ ")
          l;
        fprintf formatter "@])"
    | Section l ->
        fprintf formatter "{@;<0 2>@[<hv0>";
        list_iter_between
          (fun (name,value) ->
             fprintf formatter "@[<hov2>%s =@ @[<b2>" 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 "@[<b2>"; 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