- OCaml 93.2%
- Standard ML 5.1%
- Shell 1%
- Makefile 0.7%
| launch | ||
| lib | ||
| man | ||
| src | ||
| test | ||
| .gitignore | ||
| bootstrap.fr.md | ||
| caml2csl.opam | ||
| COPYRIGHT | ||
| csl.opam | ||
| dune-project | ||
| README | ||
| README.md | ||
Caml to Csl and OCaml version 2.0
See the manpage.
Description
(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.
Remarks
-
There is no pretty-printing. The source file is modified when necessary. This means that it will not remove comments (except maybe those between
#openand 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.
Files introduced by caml2csl
Here is a short description of the files caml2csl deals with.
.zc
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.
.mlc
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
opendirective may import, and an open may hide a name that caml2csl generated previously (example: you define a variableXthat 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.
Other files
-
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 anmlcascript)
Main translated features
There are 3 categories of features:
What it should translate properly (see /test/toto/toto.caml)
- file names: OCaml file names are in lower case, which was not required in Caml-Light.
- name clashes: if you define 2 variables
xandX, then the second is tranlated tox_0, orx_1ifx_0is 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 recconstruction 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
- ...
What it cannot do (but generally yields a warning)
- 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_getcannot be easily translated in OCaml. Programs usingstream_getare 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) - ...
What makes caml2csl fail
- 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.camlito the.caml. - lexer and parser specifications (
.mlyand.mll) are not translated. - ...
Other comments
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.
What to do when caml2csl fails
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,.camliand.mlcare accepted. -
_identifier hidden when opening a module ... : as the message explains, you have to write an
mlcfile. caml2csl fails here because it would have to do a backtrack (as explained in the paragraph aboutmlcfiles 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.mlwith Caml-Light). The solution is to copy the declaration from the.camlito the.camlbefore 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.
Usage of Caml to Csl (see the manpage)
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
The renamer tool
renamer file1 ... filen
This command changes the extensions of .ml and .mli files:
- files with extension
.mlwill be renamed with extension.caml - files with extension
.mliwill 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.
Writing .mlc and core conversion files
(see for example test/toto/toto.mlc2 or lib/*.mlc)
Syntax of translation information
-
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
mlc files
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.
Beware
-
caml2csl does not check if the information of the
mlcis coherent with the previous information (i.e. anmlcmay 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_stringare both translated into=becauseeq_stringno longer exists. There is a problem if you have the (weird) idea to redefineeq_stringin 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.
Core conversion files
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 mod2is used when
mod1andmod2define 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 forstringandfstring, which differ only on their module names.
Hacks
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
idto be qualified, you can tell in the.mlcto convert id to"__".idwith the line:
id "__".id`
A complete example
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
.ziand.zowith.cmiand.cmo, and add the suffixes.camland.camli: -
Add some dependencies:
.mldepends 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
mlfiles:
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
SUFFIXes.camland.camli. You can delete the.camland.camliif you wish, but keep the.zcif you have to translate modules using them. You can move.zcin other directory but make sure to include the latter directory in the caml2cslloadpath(option-I).