module BatPrint:Formatted output functions (also known as unparsing).sig
..end
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)
The functions of this module produce output, according to BatPrint.directive
s,
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.
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"
.
General-purpose directives start with {%{}
and end with {}}
. By default,
functions of this module recognize the following general-purpose directives:
{%{int}}
or {%{Int.t}}
format an integer with the usual notation (i.e. signed decimal).{%{char}}
or {%{Char.t}}
insert a character{%{string}}
or {%{String.t}}
insert a string{%{float}}
or {%{Float.t}}
format a floating-point number as a decimal, in the style dddd.ddd
{%{bool}}
or {%{Bool.t}}
format a boolean argument to the string "true"
or "false"
{%{int32}}
or {%{Int32.t}}
format an integer with the usual notation (i.e. signed decimal).{%{int64}}
or {%{Int64.t}}
format an integer with the usual notation (i.e. signed decimal).{%{nativeint}}
or {%{Native_int.t}}
format an integer with the usual notation (i.e. signed decimal).{%{Rope.t}}
insert a Rope
{%{foo list}}
or {%{foo List.t}}
, where foo
is the name of another directive, such as int
, char
, string
, int list
. etc., format a list of items (for more control on list formatting, see List.print
{%{foo array}}
or {%{foo Array.t}}
, where foo
is the name of another directive, such as int
, char
, string
, etc., format a array of items (for more control on array formatting, see Array.print
{%{foo maybe}}
, where foo
is the name of another directive, such as int
, char
, string
, etc., format an optional x
item to nothing if x
is None
or to the result of directive foo
applied to y
, if x
is Some y
{%{foo option}}
or {%{foo Option.t}}
, where foo
is the name of another directive, such as int
, char
, string
, etc., format an 'a option
to OCaml syntax, i.e. "None"
or "Some bar"
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:
%d
, %i
, %n
, %l
, %L
, or %N
: format an integer with the
usual notation (i.e. signed decimal). %u
: format an integer as an unsigned decimal.%x
: format an integer as an unsigned hexadecimal, using lowercase letters.%X
: format an integer as an unsigned hexadecimal, using upper letters.%o
: format an integer as an unsigned octal.%s
: insert a string.%S
: insert a quoted string in Caml syntax (double quotes, escapes).%sc
: insert a read-only/read-write string (see String.Cap
).%Sc
: insert a quoted read-only/read-write string in Caml syntax (double quotes, escapes).%rope
: insert a rope (see Rope
).%utf8
: insert a UTF-8 encoded string.%c
: insert a character argument.%C
: insert a character argument in Caml syntax (single quotes, escapes).%f
: format a floating-point number as a decimal,
in the style dddd.ddd
.%F
: format a floating-point number in Caml syntax (dddd.
or dddd.ddd
or d.ddd e+-dd
).%e
or %E
: format a floating-point number as a decimal,
in the style d.ddd e+-dd
(mantissa and exponent).%g
or %G
: format a floating-point number as a decimal
in style %f
or %e
, E
(whichever is more compact).%B
: format a boolean argument to the string "true"
or "false"
%b
: format a boolean argument (for backward compatibility; do not
use in new programs).%ld
, %li
, %lu
, %lx
, %lX
, %lo
: format an int32
respectively as a signed decimal/a signed decimal/an unsigned decimal/
an unsigned hexadecimal in lowercase, an unsigned decimal in uppercase/
an unsigned octal.%nd
, %ni
, %nu
, %nx
, %nX
, %no
: format a nativeint
respectively as a signed decimal/a signed decimal/an unsigned decimal/
an unsigned hexadecimal in lowercase, an unsigned decimal in uppercase/
an unsigned octal.%Ld
, %Li
, %Lu
, %Lx
, %LX
, %Lo
: format an int64
respectively as a signed decimal/a signed decimal/an unsigned decimal/
an unsigned hexadecimal in lowercase, an unsigned decimal in uppercase/
an unsigned octal.%obj
: format an object using its method print: 'a output -> unit
%!
: take no argument and flush the output.%%
: take no argument and output one %
character.In addition to the above default set of directives, you may define your own directives or override existing ones. This is actually quite simple.
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.
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.
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.
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:
-
: left-justify the output (default is right justification).0
: for numerical conversions, pad with zeroes instead of spaces.+
: for numerical conversions, prefix number with a +
sign if positive.#
: request an alternate formatting style for numbers.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
.
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
A directive takes as arguments:
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:
false
):-
with label left_justify
0
with label pad_with_zeros
+
with label prefix_with_plus
with lavel prefix_with_space
#
with label alternate
width
and precision
val literal : string -> ('a, 'a) directive
literal str
create a directive which do not take any argument
from a literal stringval 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
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
.
val fprintf : 'a BatInnerIO.output -> ('b, unit) format -> 'b
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
BatPrint.fprintf
but doesn't actually print anything.
Sometimes useful for debugging.val bprintf : Buffer.t -> ('a, unit) format -> 'a
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
argumentval rprintf : ('a, BatRope.t) format -> 'a
rprintf fmt
returns the result as a ropeval 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.