See the manpage.
(Caml-Light version 0.7 to Objective-Caml version 1.03)
caml2csl is a tool designed to do most of the translation from Caml-Light programs to Objective-Caml programs. It does not pretend to translate any Caml-Light program into a directly compilable OCaml program, but it is supposed to tell all that remains to do by hand.
-
There is no pretty-printing. The source file is modified when necessary. This means that it will not remove comments (except maybe those between
#open
and the name of the module opened). The main drawback is that the indentation of the ouptput file is not very pretty. -
In some errors or warning messages, caml2csl gives locations (2 numbers between parenthesis). It indicates the area where the event occured (in characters from the begining of the Caml-Light file, starting with 0). This is not very explicit, but if you use a good text editor, it should be alright.
Here is a short description of the files caml2csl deals with.
Because of the modularity of Caml-Light, one must remember the translation
information of the imported modules. For example, if module toto
uses values
of module titi
, you need to know how the values of titi
were translated. This
information is stored in the file titi.zc
.
Therefore, when caml2csl translates a module, it also generates a zc
file
(i.e. a file with the same basename as the Caml-Light module name, but with
extension .zc
).
Of course, you must keep these files until all the modules using them are translated.
An mlc
file contains information to guide caml2csl in its translation. You
shall write an mlc
file in 2 cases:
-
when caml2csl generates horrible names, you can force it to choose the new name for a value.
-
when a backtrack is necessary: caml2csl cannot guess the name of the values an
open
directive may import, and an open may hide a name that caml2csl generated previously (example: you define a variableX
that was translated tox
, and then you open a module defining a variablex
). The old value was still reachable with Caml-Light because you could still use name qualification (i.e.mod__x
), but in OCaml, you cannot refer to the module being compiled and the old value would be definitively lost (see test/toto/toto.caml for an example).
The syntax of such files is described below.
-
extension
.mlca
: core conversion script. Written by advanced users. It contains the exhaustive list of translations of the core library of Caml- Light. The syntax of these files is roughly the same as mlc files. -
extension
.zlc
: compiled core conversion file (generated by caml2csl from anmlca
script)
There are 3 categories of features:
- file names: OCaml file names are in lower case, which was not required in Caml-Light.
- name clashes: if you define 2 variables
x
andX
, then the second is tranlated tox_0
, orx_1
ifx_0
is already defined, etc... - changes in the name of the functions of the standard libraries.
Example:
char__char_of_int
-->Char.chr
- name qualification is added if necessary (it may happen when a translation hides a name of an opened module); every qualification in the sources is preserved
- infix, prefix (no more user-defined infixes in OCaml)
- characters: ` becomes ', escaped characters are correctly dealt with
- changes in stream syntax:
[< fct pat >]
-->[< pat = fct >]
- the
where rec
construction no longer exists: becomes alet rec ... in ...
- matrix of patterns are translated to a column of tuples of patterns
- references are implemented with a record
- incomplete destruction of constructors of arity greater than 1 in patterns
- ...
- insert the type, exception, primitive declarations at the right place in the implementation file.
- applying a non-explicit tuple to a constructor of arity greater than 1 in an expression
- changes in the semantics of streams. See the example in test/toto/toto.caml
Moreover,
stream_get
cannot be easily translated in OCaml. Programs usingstream_get
are translated into bad typed programs. - assignments of mutable fields of an arbitrary expression forbidden in OCaml
- uninfix on vales of the standard library (example:
quo
) - ...
- programs using values or types that are not defined or for which caml2csl
cannot find the compiled conversion file (the
.zc
). In general, programs with syntax errors yields very short error messages (caml2csl does not replace the compiler camlc !) - A fatal error
chg_global_id: ...
may occur when you declare an external primitive in a.mli
, because it is not copied in the.ml
. (this is a consequence of the first point). You must change the.caml
: copy the primitive declaration from the.camli
to the.caml
. - lexer and parser specifications (
.mly
and.mll
) are not translated. - ...
Some functions disappeared: a module called Caml__csl
was written, and it
contains these definitions. caml2csl warns you when reference to this module
is made.
You can either let the translator do the job, and you will just have to add
caml__csl.cmo
to the list of objects to link; or (it is certainly better) you
copy the definition somewhere in your source.
The most evident case is that of pair__combine
which is now currified.
caml__csl.combine
was written, but a warning tells you when it is used because
you would better rewrite your program a little.
When caml2csl fails, it generates no ml
file. You may want it to generate a
file, even a little bit incorrect, and then correct it by hand.
Here is what you can do, depending on the error message you get:
-
syntax or lexical error: compile your source with camlc !
-
cannot find file ...: the file was not found in
loadpath
. Add the path of the file in theloadpath
, or give the absolute name of the file. This will also happen if you do not translate the modules in the right order. -
convert .camli first: explicit enough
-
don't know what to do with ...: only files with extension
.caml
,.camli
and.mlc
are accepted. -
_identifier hidden when opening a module ... : as the message explains, you have to write an
mlc
file. caml2csl fails here because it would have to do a backtrack (as explained in the paragraph aboutmlc
files above) -
Fatal errors: find_arity_global, find_arity_local or chg_global_id Happens when you use a value which isn't defined, and when you declare a primitive in the
.mli
(you hadn't to copy it in the.ml
with Caml-Light). The solution is to copy the declaration from the.camli
to the.caml
before the translation. -
Other failures: if the error message isn't explicit enough, it is an error I forgot to document, or it needs to be reported.
caml2csl [options] file1 ... filen
arguments:
file1 ... filen : files to translate.
Allowed extensions are .caml .camli .mlc
options:
-I dir : adds <dir> to the loadpath in order to find the .zc of the
modules used by file1 .. filen
-clib file : name of the core conversion file. std.zlc is used by default.
-o file : name of the output file when compiling a core conversion file
-c file : tells caml2csl to compile <file> as a core conversion file (see
syntax below)
-O set : default open set (cf camlc)
-W : do not display warnings (only those that will make compilation fail are
mentionned)
-verbose : display lots of output, and do not remove object files when fails
renamer file1 ... filen
This command changes the extensions of .ml
and .mli
files:
- files with extension
.ml
will be renamed with extension.caml
- files with extension
.mli
will be renamed with extension.camli
This is one of the first thing to do before the translation, because caml2csl
will generate files with extensions .ml
and .mli
. This avoids overwriting the
original Caml-Light sources, and when the translation fails, it prevents
confusions between Caml-Light and OCaml files.
(see for example test/toto/toto.mlc2 or lib/*.mlc)
-
terminals Ident and Number are that of Caml-Light,
-
non-terminals:
core-conv-file ::= ( module-definition | copy-decl ) *
copy-decl ::= "COPY" ident ident
module-definition ::= "MODULE" ident (equiv-decl)opt "{" bloc * "}"
equiv-decl ::= "IS" ident
| "OPEN" ident
bloc ::= ( "VALUE" | "TYPES" | "LABELS" ) "{" line * "}"
| "CONSTR" "{" constr-line * "}"
line ::= ident new-ident
constr-line ::= ident new-ident Number
new-ident ::= ident "." ident
| "." ident
| "="
ident ::= \" Ident \"
| Ident
An mlc
file is a module-decl entry:
-
the first ident is the name of the Caml-Light module to translate
-
the optional equiv-decl tells the name of the corresponding OCaml module:
- IS ident: ident is the OCaml name but when you open that module in Caml-Light, the equivalent isn't opened in OCaml
- OPEN ident does just the opposite: open the OCaml module when the Caml-Light is opened If there is no equiv-decl, the name will be that of the Caml-Light module, beginning with an upper case and the OCaml module will be opened.
-
Then, a list of blocs between braces. There are 4 kinds of blocs (depending of the keyword introducing it): value, type, constructor or exception, record label.
Each bloc is a list of translation lines (but it does not depend on identation)
A line is composed of one identifier and something caracterizing the new name. It is either:
=
means that the name did not change at all,.
ident means that the module name is the current one, but the basename changed- ident
.
ident when you want to translate to a name that is in another module (the module qualification will be used only when it is qualified in the original program, or when it becomes necessary because of the translation)
The lines of a bloc defining constructors are a little bit different because the arity of the constructors must be specified.
Every identifier may be double-quoted,
e.g. MODULE "MODULE" { .... }
The mlc
files are usefull when you want to enforce some conversions.
For example, when the name that caml2csl would create is not what you want:
if you have defined two record labels X
and x
, caml2csl would translate the
first as x
, and the second as x_0
. You may want a prettier name for x
:
MODULE toto {
...
LABEL{
x .pretty_x
}
...
}
You can translate x
to x
. In that case, X
will not be translated to x
, but to
x_0
.
Note: you do not have to specify the translations of all the values. Just write
the translation you want to enforce. In the example above, X
is not translated.
-
caml2csl does not check if the information of the
mlc
is coherent with the previous information (i.e. anmlc
may make caml2csl translate 2 different values into the same value). This is no laxism: this is necessary, because there are redundancies in Caml-Light libraries that disappeared. -
Translating 2 values into the same value may be dangerous. For example:
=
andeq_string
are both translated into=
becauseeq_string
no longer exists. There is a problem if you have the (weird) idea to redefineeq_string
in your Caml-Light source:
let eq_string x y = (x:string) = (y:string);;
After translation, your program would redefine =
(which is the polymorphic
equality) by a monomorphic equality on strings!
let (=) x y = (x:string) = (y:string);;
Of course, this is not what you want (0 = 0
will not type-check anymore), but
I do not know how to prevent this.
An mlca
file is a conv-file entry (see lib/builtins.mlca)
It is a list of module definitions and copy declarations.
-
The module definitions are described above
-
The declaration:
COPY mod1 mod2
is used when
mod1
andmod2
define the same values with the same translations. You must write the module definition ofmod1
, and the copy declaration will make a copy namedmod2
. This is the case forstring
andfstring
, which differ only on their module names.
Here is a few hacks you may use when you have very specific needs. But do not abuse of them!
- If you want a name not to be used, write a line like the following in each
bloc in which you want to forbid it (
__
is the OCaml module of OCaml keywords):
"" "__".forbidden_name
- If you do not want a name
id
to be qualified, you can tell in the.mlc
to convert id to"__".id
with the line:
id "__".id`
You have the following Makefile:
ZOFILES = toto.zo
all: $(ZOFILES)
clean:
rm -f *.z*
.SUFFIXES: .mli .ml .zo .zi
.mli.zi:
camlc -c $<
.ml.zo:
camlc -c $<
depend::
rm -f Makefile.bak
cp Makefile Makefile.bak
sed -n -e '1,/^### DO NOT DELETE THIS LINE/p' Makefile.bak > Makefile
camldep *.ml *.mli >> Makefile
### DO NOT DELETE THIS LINE
toto.zo: toto.ml toto.zi
toto.zi: toto.mli
-
In the Makefile, replace each occurence of
.zi
and.zo
with.cmi
and.cmo
, and add the suffixes.caml
and.camli
: -
Add some dependencies:
.ml
depends on.mli
-
camldep --> csldep
This yields the following Makefile:
ZOFILES = toto.cmo
all: $(ZOFILES)
clean:
rm -f *.cm*
.SUFFIXES: .mli .ml .cmo .cmi .caml .camli
.caml.ml:
caml2csl $<
.camli.mli:
caml2csl $<
.mli.cmi:
camlc -c $<
.ml.cmo:
camlc -c $<
depend::
rm -f Makefile.bak
cp Makefile Makefile.bak
sed -n -e '1,/^### DO NOT DELETE THIS LINE/p' Makefile.bak > Makefile
csldep *.ml *.mli >> Makefile
### DO NOT DELETE THIS LINE
toto.cmo: toto.ml toto.cmi
toto.cmi: toto.mli
toto.ml: toto.mli
- Apply the renamer to all the
ml
files:
renamer *.ml *.mli
- Make
make all
The translation begins, but the attempt to compile will problably fail if
caml2csl yields -- TO DO --
messages.
Correct the .mli
and .ml
files and retry.
- You can restore the Makefile by removing the
SUFFIX
es.caml
and.camli
. You can delete the.caml
and.camli
if you wish, but keep the.zc
if you have to translate modules using them. You can move.zc
in other directory but make sure to include the latter directory in the caml2cslloadpath
(option-I
).