An OCaml program for checking if OCaml code compiles

600 views Asked by At

I would like to write an OCaml module having a compile function which will take a string containing an OCaml program and output the outcome of compilation as either Correct or Error, and in the Error case, information on the line and character of the first error. I don't care about what the error was (at least for now).

A basic module type for this is:

module type Compile =
    sig
        type result = Correct | Error of (int*int)
        val compile : string -> result
    end

There at least two fundamentally different ways to implement it:

  1. the quick hack way -- write the string with the program to a file, use Unix processes to invoke ocamlc on the command line and parse the stderr;
  2. the proper way -- use OCaml tools to analyse the code.

Regarding (1), I'm having trouble getting hold of the contents of stderr. I started by using Unix.open_process_in, which redirects stdout to an in_channel which I can then read. Since the compilation errors output by ocamlc go to stderr (and not to stdout), I tried to use

let ic = Unix.open_process_in "ocamlc test.ml 2>&1" 

so that the content of stderr was redirected at the command line to stdout. This way, I'd expect, ic would contain the compilation errors. Unfortunately, this was not the case and ic contained just End_of_file.

I then moved to the Unix.create_process command, as it allows me to choose new channels for out, in, err. But I'm having trouble choosing these channels. What should the out channel be so that I can then read it in my program?

Regarding (2), what do you think is a (reasonably) simple way to go?

Many thanks for any help!

2

There are 2 answers

2
Daniel Bünzli On BEST ANSWER

Regarding 1) I think you did something wrong. On my machine :

> ocaml unix.cma
        Objective Caml version 3.12.0

# let ic = Unix.open_process_in "ocamlc test.ml 2>&1";;
val ic : in_channel = <abstr>
# input_line ic;;
- : string = "File \"test.ml\", line 1, characters 0-1:"
# input_line ic;;
- : string = "Error: I/O error: test.ml: No such file or directory"
# input_line ic;;
Exception: End_of_file.

For Unix.create_process one way of doing is to pass Unix.stdin for new_stdout so that you can read the output of the process via the standard input of your program (this assumes you don't want to use the stdin of your program for something else). But a simpler way me be to use Unix.open_process_full.

Regarding 2) you can try to use toplevellib.cma (note however that this is completely undocumented and unsupported). Have a look at toplevel/toploop.mli in the distribution, in particular Toploop.execute_phrase.

3
Michael Ekstrand On

Regarding 2), OCaml installations typically have a compiler-lib directory containing many of the modules making up the compiler, including the parser. This is not documented, but may be usable to do the compilation and type-checking. For the parsing, there is also Camlp4, which can be used to do things besides preprocessing (I would imagine you can load Camlp4 into your program and use it as a library, possibly after patching it a bit), but that only contains the grammar and not the typechecker.