Hi Kenichi,

It is possible to do this kind of thing. If you only want to change the character for the prompt, you can set the Toploop.read_interactive_input function. Then to create a custom toplevel, you need to call [Topmain.main] and link your program with the compiler-libs.toplevel library. Here is a complete example where I copied and adapted the default [read_interactive_input] function from the compiler libraries, and used dune to build a custom toplevel:

$ cat mytop.ml
let read_input prompt buffer len =
  let prompt =
    if prompt <> "" && prompt.[0] = '#' then
      String.mapi (fun i c -> if i = 0 then '%' else c) prompt
    else
      prompt
  in
  output_string stdout prompt; flush stdout;
  let i = ref 0 in
  try
    while true do
      if !i >= len then raise Exit;
      let c = input_char stdin in
      Bytes.set buffer !i c;
      Option.iter (fun b -> Buffer.add_char b c) !Location.input_phrase_buffer;
      incr i;
      if c = '\n' then raise Exit;
    done;
    (!i, false)
  with
  | End_of_file ->
      (!i, true)
  | Exit ->
      (!i, false)

let () =
  Toploop.read_interactive_input := read_input;
  Topmain.main ()
$ cat dune
(executable
 (name mytop)
 (libraries compiler-libs.toplevel)
 (link_flags :standard -linkall)
 (modes byte))
$ dune exec ./mytop.exe
        OCaml version 4.11.1

% 1 + 1;;
- : int = 2

FTR, the compiler invocation to build myutop.exe is:
$ ocamlc -linkall -I +compiler-libs ocamlcommon.cma ocamlbytecomp.cma ocamltoplevel.cma mytop.ml -o mytop.exe
$ ./mytop.exe
        OCaml version 4.11.1

% 1 + 1;;
- : int = 2

If you want to go further, there are few projects that add line edition support to the toplevel, colors etc, using similar methods:
- utop: https://github.com/ocaml-community/utop
- down: https://erratique.ch/software/down

Finally, if you'd like to improve the toplevel API so that doing this kind of thing is easier in the future, a PR is always welcome :)

Happy hacking,

Jeremie



On Fri, Mar 5, 2021 at 5:05 AM Kenichi Asai <asai@is.ocha.ac.jp> wrote:
Is there a simple (but complete) example that uses
compiler-libs.toplevel, like creating an OCaml toplevel that uses a
different character for a prompt?  When I tried:

- copy toplevel/topstart.ml to my_topstart.ml and change Topmain in it
  to My_topmain
- copy toplevel/topmain.ml to my_topmain.ml and change Toploop in it
  to My_toploop
- copy toplevel/toploop.ml to my_toploop.ml and change the prompt
  character in it
- optionally, copy toplevel/topdirs.ml to my_topdirs.ml and change
  Toploop, etc., to My_toploop (this does not affect the story below)

I ran into the following error:

The ocamltoplevel.cma library from compiler-libs cannot be loaded
inside the OCaml toplevel

When I disable it (by removing the expression that raises this error
in my_toploop.ml), I can launch the new toplevel, but I cannot define
a new name:

let a = 3

results in "Fatal error: a unbound at toplevel".  Is it possible to do
this kind of creating a new OCaml toplevel using compiler-libs.toplevel?
Or is the compiler-libs.toplevel intended for something else?

A bit of background.  I am developing an OCaml type debugger:

http://pllab.is.ocha.ac.jp/~asai/TypeDebugger/

and am currently trying to port it to a newer version of OCaml.  For
the current type debugger, I inserted code for type debugging into the
main loop in toploop.ml.  However, inserting code directly into the
OCaml source requires me to recompile whole (or at least the core of)
the OCaml.  If I could do the same with compiler-libs, that would be
great.

Any help is welcome.  (I also welcome the negative information saying
that it is impossible to do this kind of thing.  Then, I would fall
back to the old way of compiling whole the OCaml.)

Thank you in advance.

Sincerely,

--
Kenichi Asai


--
Jeremie