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:
Some text ## (* Some OCaml statements *) ... ## More text ...
Some text ##= (* Some OCaml string expression *) ... ## More text ...
This prints actually 2 # symbols: ###
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.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" ##.
camlmix example.mlx -o example.html
will produce the following
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
camlmix example.mlx -c -fun -co example.ml
The signature of the
render function would be:
val render : ?print:(string -> unit) -> 'a -> unit
'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
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
A block of code that starts with an
@ character will not
be handled as OCaml code but as a sequence of directives for
There is one useful directive:
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!
include directive reads a file
in the Camlmix syntax exactly as if its
contents was inserted in the current file (e.g.
## 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 () ##
## 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
skip directive also exists: it ignores the next block of text.
It is not much useful since the
.## delimiter was introduced in
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 () ##
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
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
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
cond is true.
The command line options are returned by
Camlmix can be installed automatically from GODI.
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.