Report
What is Report ?

The Report tool aims to make generation of XML documents from OCaml applications easier. The main idea is to separate the structure of the document from the information computed by the application and placed in the document. For example, in the following document:

<h1>CrÍpes</h1>
<list>
  <item>flour</item>
  <item>eggs</item>
  <item>sugar</item>
</list>
the document structure is
<h1> </h1>
<list>
  <item> </item>
  <item> </item>
  <item> </item>
</list>
while computed information is "CrÍpes", "flour", "eggs" and "sugar".

To build our XML document, we must therefore describe its structure as well as the way to fill it with information. Then, at runtime, the application will use this description to generate the final XML document.

In practice, Report allows to graphically describe the document (structure + information), and then to generate OCaml code which uses the Report library. In particular, this library contains a function which computes a document description to produce the final XML document. An important point is that the way to compute the information needed in the document is given in the form of OCaml code.

The library
Types

The Report module defines the types used to describe a document :

(** A report element. *)
type 'a report_ele =
  | Leaf of (unit -> string)
  | Tag of 'a tag
  | List of 'a liste
  | Cond of 'a cond
  | Sub of 'a sub

(** A tag. *)
and 'a tag = {
    mutable tag : string ;
    mutable atts : (string * (unit -> string)) list ;
    mutable tag_subs : 'a report_ele list
  }

(** A list of substructures. *)
and 'a liste =
    { mutable list_subs : ('a -> 'a report_ele list) ;
      mutable f : (unit -> 'a list) ;
    }

(** Conditional *)
and 'a cond =
    { mutable cond : unit -> bool ;
      mutable subs_then : 'a report_ele list ;
      mutable subs_else : 'a report_ele list ;
    }

(** Subreport *)
and 'a sub =
    {
      mutable sub_rep : unit -> 'a report ;
    }

(** A report description is a list of report elements. *)
and 'a report = {
    mutable rep_eles : 'a report_ele list ;
  } 
The type report_ele is the type of the tree nodes:
  • Leaf is used for the leafs of the tree and needs the code of a function taking () and returning a string. While computing the document description, () will be applied to this function in order to obtain the string to insert into the final XML document. This way of hiding OCaml code "under" fun () -> allows not to compute the result until the computation of the leaf, thus allowing to use values computed "above" the leaf.
  • Tag is used to define an XML node of the final XML document. It needs the tag name, eventually a list of attributes and values, as well as subtrees of the node. Values of attributes are OCaml code too, as for the leafs.
  • List allows to insert, while computing the final document, a list of subtrees for each element in a list. The field f contains the OCaml function returning the list of elements to iterate on. The field current is used to store the current element while walking through the subtrees. This way, the value of the field current can be used in the functions which appear in the subtrees of the node.
  • Cond allows to use one list of subtrees or another, depending on the boolean value returned by the given function.
  • Sub is used to insert another document in the current one. The given function returns a document description which is computed too.

The document example of the introduction could be described as in following section.

Code example
let rec report  =
  ({
   rep_eles = [
     Tag { tag = "h1" ; atts = [] ;
           tag_subs = [
             Leaf (fun () -> "Crepes");
           ] } ;
     Tag { tag = "list" ; atts = [] ;
           tag_subs = [
             ( let rec ing =
                 { f = (fun () -> ing_of_recipe "CrÍpes") ;
                   current = (Report.coerce 0) ;
                   list_subs = [
                     Tag { tag = "item" ; atts = [] ;
                           tag_subs = [ Leaf (fun () -> ing.current.ing_name) ] }
                   ] }
                in
                List (Report.coerce ing))
           ]
          }
    ]
   } : int report)

The call to Report.coerce is necessary to force the type but type constraints are already satisfied (notably the use of the current field) at this point.

As we can see, this structure can quickly become a pain to define and read. To solve this problem, the report_gui tool allows to graphically define the document description and the report tool generates the OCaml code of this structure.

Moreover, the report value could have had parameters; this is a way to parametrize the final XML document.

Functions

The Report module contains the following functions :

(** Coerce report elements. *)
val coerce : 'a -> 'b

(** Compute a report and print it to the given formatter. *)
val compute : ?html: bool -> Format.formatter -> 'a report -> unit

(** Compute a report and print it in a file. *)
val compute_file : ?html: bool -> string -> 'a report -> unit

The coerce function is used to insert a 'a report_ele into the node of a 'b report_ele, when 'a cannot be used as 'b. This function must only be used for this purpose, as in the example above.

The compute function takes a formatter and a document description and computes this description to generate the final XML document in the given formatter. The html optional parameter allows not to close some tags (like br ) to generate HTML compliant documents.

The compute_file function acts like compute but writes in the given file instead of a formatter.

Invocation of the graphical editor

The report.gui tool allows to describe a document (structure + code to fill it with information), as well as the parameters of this document (these parameters become parameters of the report value in the code example). The document is describe through a graphical user interface with a tree oriented view.

The grapical editor is launched by the following command:

report.gui.{x,byte} [options] file [file2 [file3 ...]]
Invocation of code generator

The report tool generates the OCaml code of the document description, using the types defined in the Report library.

The code generator is launched by the following command:

report.{x,byte} [options] file

The following options are supported:

  • -gen generate code to (chop_extension <file>).ml
  • -o file generate the OCaml code in file file, instead of the default file, when the -gen option is given.