Skip to content

Caml Light to Caml Special Light (almost OCaml) translator, resurrected from INRIA archives

Notifications You must be signed in to change notification settings

ghuysmans/caml2csl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 #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.

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 open directive may import, and an open may hide a name that caml2csl generated previously (example: you define a variable X that was translated to x, and then you open a module defining a variable x). 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 an mlca script)

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 x and X, then the second is tranlated to x_0, or x_1 if x_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 a let 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_get cannot be easily translated in OCaml. Programs using stream_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)
  • ...

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 .camli to the .caml.
  • lexer and parser specifications (.mly and .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 the loadpath, 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 about mlc 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.

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 .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.

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:

    1. = means that the name did not change at all,
    2. .ident means that the module name is the current one, but the basename changed
    3. 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 mlc is coherent with the previous information (i.e. an mlc 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: = and eq_string are both translated into = because eq_string no longer exists. There is a problem if you have the (weird) idea to redefine eq_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.

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 mod2

    is used when mod1 and mod2 define the same values with the same translations. You must write the module definition of mod1, and the copy declaration will make a copy named mod2. This is the case for string and fstring, 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 id to be qualified, you can tell in the .mlc to convert id to "__".id with 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
  1. In the Makefile, replace each occurence of .zi and .zo with .cmi and .cmo, and add the suffixes .caml and .camli:

  2. Add some dependencies: .ml depends on .mli

  3. 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
  1. Apply the renamer to all the ml files:
renamer *.ml *.mli
  1. 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.

  1. You can restore the Makefile by removing the SUFFIXes .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 caml2csl loadpath (option -I).

About

Caml Light to Caml Special Light (almost OCaml) translator, resurrected from INRIA archives

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published