Cameleon2: Report

L'outil Report vise à rendre la génération de documents XML en OCaml plus facile. L'idée principale est de séparer la structure du document des informations calculées par l'application et placées dans le document. Par exemple, dans le document suivant:
<h1>Crêpes</h1> <list> <item>flour</item> <item>eggs</item> <item>sugar</item> </list>
<h1> </h1> <list> <item> </item> <item> </item> <item> </item> </list>
Pour construire notre document XML, nous devons donc décrire sa structure et la façon de la remplir d'information. Ensuite, à l'exécution, l'application utilisera la description pour générer le document XML final.
En pratique, Report permet d'diter graphiquement le document (structure + information), et de générer le code OCaml qui utilise la bibliothèque Report. En particulier, cette bibliothèque contient une fonction qui "évalue" une description de document pour produire le document XML final. Un point important est que l'information à mettre dans le document est donnée sous forme de code OCaml.
Le module Report définit les types utilisés pour décrire un 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 ;
}
Le document en exemple de l'introduction peut-être décrit comme dans la section suivante.
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)
L'appel à Report.coerce est nécessaire pour forcer le type mais les contraintes de type sont déjà satisfaites (notamment l'utilisation du champs current ) à cet endroit.
Comme on peut le voir, cette structure peut rapidement devenir difficile à écrire et à lire. Pour pallier à ce problème, l'outil report_gui permet de définir graphiquement la description de document tandis que l'outil report permet de générer le code OCaml correspondant au document décrit.
De plus, la valeur report pourrait avoir des paramètres; c'est une façon de paramétrer le document XML final.
Le module Report contient les fonctions suivantes :
(** 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
La fonction coerce est utilisée pour insérer un 'a report_ele dans le noeud d'un 'b report_ele, quand 'a ne peut être utilisé comme 'b. Cette fonction ne doit être utilisée qu'à cet effet, comme dans l'exemple ci-dessus.
La fonction compute prend en paramètre un formatter et une description de document et évalue cette description pour générer le document XML final dans le formatter donné. Le paramètre optionnel html permet de ne pas fermer certains tags (comme br ) pour générer du HTML correct.
La fonction compute_file agit comme la fonction compute mais écrit dans le fichier donné au lieu d'un formatter.
L'outil report.gui permet de décrire un document (structure + code pour le remplir d'information), ainsi que les paramètres de ce document (ces paramètres deviennent des paramètres de la valeur report dans l'exemple de code). Le contenu du document est édité dans une vue arborescente représentant la structure du document.
L'éditeur graphique est lancé par la commande suivante:
report.gui.{x,byte} [options] file [file2 [file3 ...]]
L'outil report génère le code OCaml de la description de document, en utilisant les types définis dans la bibliothèque Report.
Le générateur de code est lancé par la commande suivante:
report.{x,byte} [options] file
Les options suivantes sont supportées:
Cameleon2: Report
