Setup a new printer

From Gallium

Jump to: navigation, search

NOTE: Thanks to Hendrik Tews for this HOWTO

First a word on side effects: I mean those side effects that newly loaded modules perform inside camlp4 to register their functions in the camlp4 engine.

In the good old times one simply used assignments for that, for instance

      Pcaml.print_implem := f

has always been used to install f as printer for a .ml file.

In the new camlp4 (some of) these side effects are performed not by the user but by camlp4 itself. The user only supplies the structure. It works the following way: The user defines a functor Make that maps some structure to a structure that defines the entities that camlp4 should use:

  module Make (some arg) = struct
    let important_fun ...
  end

The user then uses Make in a functor application with a higher-order, registering functor from camlp4:

 module IgnoreResult = Camlp4.Register.Purpose(Make)

where Purpose is one of the functors that Camlp4.Register provides. On invocation Purpose applies the user supplied Make to the right arguments, obtains a structure with important_fun and, finally, performs the side effect to register important_fun in the right hook. Actually the story is a bit more complicated, because the side effects are delayed: First Make is registered as a loaded module inside camlp4 together with a function that will perform the necessary side effects some time later. See for example functor Printer in camlp4/Camlp4/Register.ml line 78.


In order to install a new camlp4 printer we only have to find the right registering functor and apply it to our arguments, put everything into some file and compile it into a .cmo.

To install a new printer I chose Register.Printer, which takes two arguments: an identification module and a Make functor with my new printer functions inside.

Here is the complete code:

(* identification module *)
module Id = struct
  (* name is printed with the -loaded-modules switch *)
  let name = "Printer HOWTO"
  (* cvs id's seem to be the preferred version string *)
  let version = "$Id: howto verion 1 $"
end


(* the real thing containing the real functions *)
module Make (Syntax : Camlp4.Sig.Syntax) :
  Camlp4.Sig.Printer(Syntax.Ast).S =
struct
  module Ast = Syntax.Ast

  let opt_string = function
    | None -> "<None>"
    | Some s -> s

  let info ?input_file ?output_file name =
    Printf.eprintf
      "printer on %s\n input : %s\n output : %s\n"
      name
      (opt_string input_file)
      (opt_string output_file)

  (* print_interf shall be called on .mli files *)
  let print_interf ?input_file ?output_file ast =
    info ?input_file ?output_file "signature"

  (* print_implem shall be called on .ml files *)
  let print_implem ?input_file ?output_file ast =
    info ?input_file ?output_file "structure"
end

(* apply everything to register the new printer *)
module M = Camlp4.Register.Printer(Id)(Make)

(* end of source *)

Put the source into a file printer.ml and compile with

$ ocamlc -c -I +camlp4 printer.ml

Use examples:

  $ camlp4o printer.cmo printer.ml
  printer on structure
   input : printer.ml
   output : <None>
  $ camlp4o -o output_file printer.cmo printer.ml
  printer on structure
   input : printer.ml
   output : output_file
  
  $ ocamlc -pp 'camlp4o printer.cmo' printer.ml
  printer on structure
   input : printer.ml
   output : <None>


Happy printing,

Personal tools
Espace privé