Home Prog People

Camlmix [difficulty = 1 camel]
OCaml-stuffed templates

[intro] [example] [advanced] [download]

Introduction

Camlmix is a generic preprocessor which converts text with embedded OCaml into an OCaml program with embedded text. It produces text documents from one or several templates. Camlmix is not a Camlp4 syntax extension.
The basic syntaxic rules are:

Camlmix does not provide a library for text-processing, but all existing libraries and syntax extensions can be used, such as HereDoc, Micmatch or xstrp4. You can also find some examples of code in the Camlmix Toolbox if you are not familiar with I/O in OCaml.

Example

The file example.mlx contains the following:

##
open Printf
let color col s =
  sprintf "<span style=\"color:%s\">%s</span>" col s
##
This is ##= color "red" "red text" ##,
but that is a ##= color "blue" "blue one" ##.

The command

camlmix example.mlx -o example.html

will produce the following example.html file:

This is <span style="color:red">red text</span>,
but that is a <span style="color:blue">blue one</span>.

This piece of HTML will be displayed as: This is red text, but that is a blue one.

You may now have a look at the Camlmix/HTML colorized source of this web page, which was itself made using a Camlmix template.

Camlmix also allows you to convert a template into a regular OCaml source file which is equipped with a single function named render:

camlmix example.mlx -c -fun -co example.ml 

The signature of the render function would be:

val render : ?print:(string -> unit) -> 'a -> unit

where the 'a parameter stands for the type of the argument which is passed to the rendering function. It is available in the template under the name param, and its usage determines its actual type. The print function is the one which is used to handle text. By default, it prints the text onto stdout, but it can be replaced by any other function, as long as the user-defined code of the template uses it.

The result example.ml is a regular OCaml file which would be integrated into a regular OCaml program.

The full set of features and options is described in the output of camlmix -help

Advanced features

Directives

A block of code that starts with an @ character will not be handled as OCaml code but as a sequence of directives for Camlmix.

There is one useful directive: include. This mechanism allows the insertion of templates and subtemplates that inherit the whole OCaml environment, and possibly modify it. These templates are not only parametrized by string variables but also by any type of OCaml data, including functions, and even by types and modules!

The include directive reads a file (e.g. included.mlx) in the Camlmix syntax exactly as if its contents was inserted in the current file (e.g. main.mlx):

main.mlx:

##
let print_counter = (* increments the counter and displays its new value *)
  let n = ref 0 in
  fun () -> incr n; print_int !n
##
Should be 1: ## print_counter () ##
Should be 2: ## @include "included.mlx" ##
Should be 3: ## print_counter () ##

included.mlx:

## print_counter () ##

camlmix main.mlx returns the following:


Should be 1: 1
Should be 2: 2

Should be 3: 3

This is not totally satisfying because two undesired empty lines were inserted. In Camlmix 1.2, you can skip whitespace until the end of the line and the newline character by using .## instead of ## as the closing delimiter. This results in the following program:

##
let print_counter = (* increments the counter and displays its new value *)
  let n = ref 0 in
  fun () -> incr n; print_int !n
.##
Should be 1: ## print_counter () ##
Should be 2: ## @include "included.mlx" .##
Should be 3: ## print_counter () ##

which now produces:

Should be 1: 1
Should be 2: 2
Should be 3: 3

A skip directive also exists: it ignores the next block of text. It is not much useful since the .## delimiter was introduced in Camlmix 1.2. It was useful when a block of OCaml had to follow a block of directives, without printing anything between them:

## @include "somefile.mlx"; skip (* skips the newline character(s) -> *) ##
## print_something () ##

Runtime hooks

The OCaml program that is generated from the Camlmix files first defines a module called Camlmix. This module contains several variables that are updated automatically and possibly explicitely by the user. Its signature is:

module Camlmix :
  sig
    val source : string ref
    val line : int ref
    val char : int ref  

source, line and char refer to the location of the first character of the current block in its source file (source file, line number starting from 1, position in the line starting from 1). They are updated automatically at the beginning of each code block or text block.

    val printer : (string -> unit) ref 

the function that prints the text blocks

    val print_with : (string -> unit) -> unit

print_with f prints the next block of text using f instead of the current printer. Its behavior is undefined if it is called several times in the same OCaml block.

    val print_if : bool -> unit

print_if cond prints the next block of text only if cond is true. It uses print_with.

  end

Command line options

The command line options are returned by camlmix -help.

Download

Camlmix can be installed automatically from GODI.

You can also use the traditional way. Just download the source code: camlmix-1.3.0.tar.bz2 or camlmix-1.3.0.tar.gz. It should work on any platform where OCaml is available.

I would be happy if you send me any positive or negative feedback.

Thanks to Nadji Gauthier, Jean-Baptiste Rouquier, Arthur E., Janne Hellsten and those that I forget, for their contributions.