Looking for an example of a REPL

I want to wrap a simple REPL around my app. It should:

  • Read lines from the terminal
  • Parse the lines using a DCG
  • Process the resulting requests and display the results
  • Have command-line editing and persistent history

All the bits appear to be in the standard libraries but I’m struggling to find a worked example of how to glue them together. Does anyone have a pointer? Thanks!

Where would I find that?

Sorry, I don’t see what any of that has to do with the question I asked.

Why are you posting Rust code as the solution to a SWI Prolog question?

I have a sort of terminal emulator thing here that might be of some use to you. See loop/3 executing.pl for the entry point to the “REPL”.

Except for the command line editing, this is all pretty specific to your application. Best SWI-Prolog option for commandline editing is library(editline), providing el_wrap/4 and friends. Normally, the terminal is already wrapped though. Then use read_line_to_codes/3 to read lines, add your DCG and process the output thereof.

I have tried to make my own REPL more than once and it turned out every time that I should be using the top level instead. There must be a very good reason not to; for example if the language is syntactically incompatible with (SWI-)Prolog.

Thanks, I found read_line_to_codes, have tinkered with it and I noticed that it seemed to do editing and history, but I wasn’t sure if that was something I could rely on. What’s the relationship between editline and readline? I see mentions of them both in the docs.

I would like to do “backslash continuation”, i.e. if the last char of a line before the CR is \ then read another line. I couldn’t find a way of doing that with the difflist version of read_line_to_codes and strings seem easier to munge than codes, so I was thinking of using read_line to build up a list of strings, collapsing it down to a single string, converting it to codes and then passing it to my DCG.

There is a good reason, it’s that the users are syntactically incompatible with Prolog :laughing: The language is trivial, just a list of field specifiers and search terms.

Actually I can’t rely on it, all that’s in the command history is whatever was typed into the prolog top level. A call to el_add_history addresses that, thanks for the pointer to editline, I don’t think I would have found it otherwise.

Thanks. I was wondering how to do that.

Bash glues continuation lines into one long line in its history, the SWI top-level also accepts \ continuation lines but preserves the \ + linebreak, That’s ideal but looks like it might be a bit of a faff to do so I’ve just done “glue all the lines together in the history” as it’s trivial to do. Here’s what I ended up with:

repl(Action) :-
  prompt(P, 'p2k> '),
  readContLines([], RevLines),
  prompt(_, P),
  foldl(string_concat, RevLines, "", Line),
  el_add_history(user_input, Line),
  call(Action, Line),
  repl(Action)
.

readContLines(PrevLines, Lines) :-
  read_line_to_string(user_input, Line),
  Line \= end_of_file, Line \= "exit",
  (
  sub_string(Line, Pos, 1, 0, "\\")
  ->
    sub_string(Line, 0, Pos, 1, L1),
    readContLines([L1 | PrevLines], Lines)
  ;
    Lines = [Line | PrevLines]
  )
.

They are two line editing libraries. Originally, SWI-Prolog used GNU libreadline, but the license change to BSD makes this a bit cumbersome (you effectively get GPL), so editline was added as alternative. As is, the editline has several advantages, such as a single process can have multiple iistances of editline, but not of readline. This is handy for e.g., the SSH server that is in an extension package.

I don’t expect there to be more than 2-3 lines of input and it’s for interactive use, so I won’t be worrying about any quadratic behaviour in this case.

The last time I touched Prolog was when I was working on the Alvey Project. :older_person: