popham
popham

Reputation: 592

Printing enumerated and record types within an ocamldebug custom printer

I'm trying to create a custom printer for Map types for use in ocamldebug. I would like to leverage the debugger's pretty printer for enumerated and record types within my custom printer. Is this possible?

I've got a module, m.mli, with a couple of types, and I'm mapping integers to gammas:

type alpha = A | B
type gamma = { m: alpha }
module IMap : Map.S with type key = int
val print_map: gamma IMap.t -> unit

The implementation looks like so:

open Format

type alpha = A | B
type gamma = { m: alpha }

module IMap = Map.Make(struct 
  type t = int 
  let compare = (-) 
end)

let rec ig_pairs = function
  | [] -> ()
  | [(k,g)] ->
     open_hvbox 0;
     printf "%u->" k;
     print_break 0 2;
     print_string (stringifier_that_saves_my_ass g);
     close_box ()
  | (k,g)::xs ->
     open_hvbox 0;
     printf "%u->" k;
     print_break 0 2;
     print_string (stringifier_that_saves_my_ass g);
     print_string ",";
     close_box ();
     ig_pairs xs;
     ()

let print_map m =
  print_string "{";
  open_hvbox 0;
  ig_pairs (IMap.bindings m);
  close_box ();
  print_string "}"

And finally, I have script.ml:

open M

let key = 2
let some_gamma_value = { m = B }
let old = IMap.empty
let b = IMap.add key some_gamma_value old

Stepping through my script with ocamldebug (after stubbing stringifier_that_saves_my_ass),

  1. I can print some_gamma_value to obtain a nice representation of some_gamma_value and
  2. I can load_printer m.cmo and install_printer print_map to get something other than <abstr> out of print b.

I want stringify_that_saves_my_ass to provide the representation from (1) within my custom printer to facilitate printing map values.

Upvotes: 1

Views: 1711

Answers (2)

popham
popham

Reputation: 592

The Deriving library provides preprocessors that build formatters for record and variant types automatically. You just have to suffix the type with show (or deriving (Show)). The generated formatters require that any contained types have been with show-suffixed also. Given type a = A | B with show, the preprocessor generates a module Show_a = ... such that Show_a.format: Format.formatter -> t -> unit. This format function is a good substitute for the debugger's pretty printer.

You can with show-suffix a Map also to save yourself the trouble of writing a print_map method.

Upvotes: 0

ivg
ivg

Reputation: 35210

To be installed in ocamldebug or ocaml toplevel the printer need to have the following signature:

Format.formatter -> t -> unit

where t is the type you're goint to print.

That means, that your printer should also accept one more argument, namely formatter, i.e., an abstraction of a channel. In other words, instead of outputting just to standard output, you need to output to the specified channel. That means, that you need to use fprintf and pp_ family of functions that accepts the formatter.

What concerning the stringifier problem, then since your map is polymorphic over the value type, you need to make your printers to be a functions from the printer that prints your value type into the printers that prints the whole map. This just means, that you need to add an extra parameter to your printer, namely pp_value, that will print the value type.

To demonstrate this in a full fledged example, take a look at this Trie (from which I forget to remove the debugging code) module,

let rec pp pp_val fmt t =
    let pp_some_data fmt = function
      | None -> ()
      | Some v -> fprintf fmt "data =@ %a@," pp_val v in
    let pp_table fmt cs =
      Tokens.iter cs (fun ~key ~data ->
          let toks = Key.sexp_of_token key in
          fprintf fmt "@[%a ->@ %a@]"
            Sexp.pp toks (pp pp_val) data) in
    fprintf fmt "{@;@[%a@ %a@]}@;"
      pp_some_data t.data pp_table t.subs

Tries are little bit more complex maps, so the printer reflects this, but let's go to the usage of the printer. But before we need to concretize our Trie to a particular key type, let it be string:

module String = Make(struct
    type t = string
    type token = char with bin_io, compare, sexp
    let length = String.length
    let nth_token = String.unsafe_get
    let token_hash = Char.to_int
  end)

Now let's print it. Again, we can print only concrete structures, so suppose that we're going to use int Trie, i.e., our value type is int. We create a printer for the int tries, and install it

let pp_int_trie = pp pp_print_int
#install_printer pp_int_trie;;

After that, all string tries with int value type will be printed, and here is the full example:

open String
let pp_int_trie = pp pp_print_int
let t = create ();;
add t "hell is my life" 1;;
add t "hello" 2;;
add t "hello my darling Clementine" 3;;
#install_printer pp_int_trie;;
t;;

I hope, that this will be enough, to get the idea.

Upvotes: 0

Related Questions