Module BatPrint


module BatPrint: sig .. end
Formatted output functions (also known as unparsing).

This module define a printf facility similar to the one defined by ExtPrintf but in a safer and extensible way.

Classical printf implementation (in ocaml and other languages) deals with format strings as regular strings which are parsed at runtime. This approach is problematic considering typing and make it almost impossible to define new directives.

This module attempts to handle the problem in a more functional way. For convenience it can be used with the syntax extension batteries.pa_string.syntax.

Look at the example "examples/snippets/test_printf.ml" for an example of the use of this module.
Author(s): Jeremie Dimino, David Rajchenbach-Teller (documentation)



General overview

The functions of this module produce output, according to BatPrint.directives, as described below. Output may be used to write to the screen, to error channels, to strings, to buffers, to the network, etc.

This module is very powerful and allows concise and extensible definition of output formats. The downside is that this module may be quite confusing at first. Don't worry, it's worth it.

The foremost concept is that of format. A format is a description of how informations should be printed. For instance, a format such as p"%d\n" may be used to display an integer (this is what

%d
means -- d stands for decimal) and will end the line at the end of this integer (this is what
\n
means everywhere in OCaml -- it stands for newline). Similarly, a format such as p"(%d, %d)\n" may be used to display two integers as a pair, and then end the line:
%d
and
\n
have the same meaning as previously, while the other characters are interpreted as regular text. Therefore, when applied to numbers 5 and 10, this format will output "(5, 10)\n".

Formats may be applied using functions such as BatPrint.printf. For instance, Print.printf p"(%d, %d)\n" 5 10 will print on the screen "(5, 10)\n". Similarly, if foo is an BatIO.output which may be used to write on a file, Printf.fprintf foo p"(%d, %d)\n" 5 10 will write "(5, 10)\n" to this file.

Let us consider formats more in detail.

Formats

The simplest format is the empty format, or p"" -- notice the p (for "print"), all formats start with this letter. This format is unable to display any information. Actually, this format is only able to output the empty string, which is not very useful. Its type is ('a, 'a, 'b) format. This 'b states that it can be used with any kind of output.

Slightly more complex is format p"foo". This format is also unable to display any information but, when applied, it will output text "foo". Its type is also ('a, 'a, 'b) format. More generally, any text without special characters may be used as a format, which can be used to display exactly that text, without any additional information.

Formats also support directives, which serve to insert additional pieces of information inside the text. Directives are always introduced with special character %. For instance, format p"%d" may be used to display integers. Its type is (int -> 'a, 'a, 'b) format. Here, type parameter int -> 'a states that this directive expects an integer. A more complex format such as p"some text before the number%d" or, equivalently, p"some text before the number%(d)" would have the same type (int -> 'a, 'a, 'b) format. Again,p"some text before the number%(d)some text after the number" would have the same type -- in this case, parenthesis are compulsory, for reasons we will detail later. When applied with argument 5, this last format will output text p"some text before the number5some text after the number".

Directives

General-purpose directives

General-purpose directives start with {%{} and end with {}}. By default, functions of this module recognize the following general-purpose directives:

Additional directives may be plugged-in.

Short-hand directives

A number of short-hand directives are also provided for most common situations. By default, functions of this module recognize the following short-hand directives:

Additional short-hand directives may also be plugged-in.

Extending formats

In addition to the above default set of directives, you may define your own directives or override existing ones. This is actually quite simple.

Adding general-purpose directives (with a module)

When defining a new data structure Foo.t, it is usually a good idea to implement a function Foo.t_printer, with type Foo.t BatValue_printer. That's it. Once this function is created, you may use directive {%{Foo.t}} in any printing function of this module.

If the data structure has type arguments, as is the case of lists, arrays, etc., you will probably want to be able to print the contents of your data structure. For this purpose, you may define printers which accept as arguments other printers. For instance, the type of List.t_printer is actually 'a BatValue_printer.t -> 'a list BatValue_printer.t, etc. If you have given such a type to Foo.t_printer, you may use {%{Foo.t bar}} in any printing function of this module, where bar is the name of another general-purpose directive.

Adding general-purpose directives (without a module)

An alternative technique for adding general-purpose directives is to define functions outside of their module. For instance, to plug-in a general-purpose directive for elements of type foo, it is sufficient to define a function bar_printer, with type foo BatValue_printer. That's it. Once this function is created, you may use directive {%{bar}} in any printing function of this module.

Again, you may also choose to define functions which take other printer as arguments, as before.

Adding short-hand directives

Short-hands are convenient and easier to write but are also less powerful than general-purpose directives, insofar as they may not accept printers as arguments and may not be passed as arguments to printers.

To define a short-hand directive %foo for formatting elements of type bar, it is sufficient to create a function printer_foo with type (bar -> 'a, 'a) directive. Assuming that you already have a function string_of_bar: bar -> string, printer_foo may be implemented simply as

   let printer_foo k x = k (fun oc -> BatIO.nwrite oc (string_of_bar x))
   

That's it. Once this function is created you may use directive %foo in any printing function of this module.

To improve the flexibility of your newly created directive, you may wish to add an optional argument flags to function printer_foo, to handle flags, as detailed in the following section.

Flags

The general format of directives is

% ( [options] [width] [.precision] name )

name is one of d, i, n, l, L, N, u, x ..., and behaves as explained above.

The optional options are:

The optional width is an integer indicating the minimal width of the result. For instance, %6d prints an integer, prefixing it with spaces to fill at least 6 characters.

The optional precision is a dot . followed by an integer indicating how many digits follow the decimal point in the %f, %e, and %E conversions. For instance, %.4f prints a float with 4 fractional digits.

The integer in a width or precision can also be specified as *, in which case an extra integer argument is taken to specify the corresponding width or precision. This integer argument precedes immediately the argument to print. For instance, %.*f prints a float with as many fractional digits as the value of the argument given before the float.

For more informations on supporting flags in your directives, see the documentation of type BatPrint.directive.

Directives and formats

You can skip this section if you are only interested in using this module and not in extending its behavior or understanding its internal mechanisms.

type ('a, 'b) directive = ((unit BatInnerIO.output -> unit) -> 'b) -> 'a 
The underlying type of a directive. Directives are the basic elements of formats.

A directive takes as arguments:

its goal is to create a printer which prints the arguments of the directives and to pass it to the continuation.

For example, directive %d has the following type

        val printer_d : (int -> 'a, 'a) directive
      

And here is a possible implementation:

        let printer_d k x = k (fun oc -> BatIO.nwrite oc (string_of_int x))
      

Additionally, directives can takes ``flags'' (like in "%2d" where 2 is here a width). Flags are passed to the directive as optional argument before the continuation, for example:

        val printer_d : ?width : int -> (int -> 'a, 'a) directive
      

with:

        let printer_d ?width k x =
          let str = string_of_int x in
          let str = match width with
            | None ->
                str
            | Some n ->
                let len = String.length str in
                if len < n then
                  String.make (n - len) ' ' ^ str
                else
                  str
          in
          k (fun oc -> BatIO.nwrite oc str)
      

Standard flags, i.e. the ones supported by the syntax extension, are:


val literal : string -> ('a, 'a) directive
literal str create a directive which do not take any argument from a literal string

Formatting functions

val printf : ('a, unit) format -> 'a
printf fmt args formats the arguments in args as specified by fmt and prints the result on the standard output BatIO.stdout, i.e. normally to the screen. If you are lost, this is probably the function you're looking for.
val eprintf : ('a, unit) format -> 'a
eprintf fmt args behaves as printf fmt args but prints the result on the error out put BatIO.stderr rather on the screen. This function is typically used to display warnings and errors, which may later be separated from "regular" printouts.
val sprintf : ('a, string) format -> 'a
A function which doesn't print its result but returns it as a string. Useful for building messages, for translation purposes or for display in a window, for instance.

While this function is quite convenient, don't abuse it to create very large strings such as files, that's not its role. For this kind of usage, prefer the more modular and usually faster BatPrint.fprintf.


Generic functions

val fprintf : 'a BatInnerIO.output -> ('b, unit) format -> 'b
General formatting function.

This function behaves mostly as BatPrint.printf or BatPrint.eprintf but, instead of printing to a predefined output, it takes as argument the output to which it should print.

Typically, if you are attempting to build a large output such as a file, this is probably the function you are looking for. If you are writing a pretty-printer, this is probably the function you are looking for. If you are you are looking for a function to combine with directive %a, this is also probably the function you are looking for.

val ifprintf : 'a -> ('b, unit) format -> 'b
As BatPrint.fprintf but doesn't actually print anything. Sometimes useful for debugging.
val bprintf : Buffer.t -> ('a, unit) format -> 'a
This function behaves as BatPrint.fprintf but prints into a buffer rather than into an output.

Functions with continuations

val kfprintf : ('a BatInnerIO.output -> 'b) ->
'a BatInnerIO.output -> ('c, 'b) format -> 'c
kfprintf k oc fmt prints on oc then call k with oc as argument
val rprintf : ('a, BatRope.t) format -> 'a
rprintf fmt returns the result as a rope
val krprintf : (BatRope.t -> 'a) -> ('b, 'a) format -> 'b
krprintf k fmt creates a rope from the format and other arguments and pass it to k
val ksprintf : (string -> 'a) -> ('b, 'a) format -> 'b
ksprintf k fmt creates a string from the format and other arguments and pass it to k
val kbprintf : (Buffer.t -> 'a) -> Buffer.t -> ('b, 'a) format -> 'b
bprintf k buf fmt prints into the buffer buf, then call k with buf as argument.