Scryer-prolog: A predicate for reading a single character from the terminal would be useful

Created on 10 Apr 2020  路  22Comments  路  Source: mthom/scryer-prolog

Please provide a predicate get_single_char(C) that waits for input of a single key press, and C is the character that was entered.

As far as I know, this is surprisingly difficult in general (due to various scan codes, modifier keys etc. that can be pressed). However, the immediate use case would be to simplify the toplevel interaction code and port more of it to Prolog.

So, even if only a small number of ASCII characters are supported initially, the predicate would still be quite useful already.

Most helpful comment

I added the predicate at the branch get_single_char. What do you think?

All 22 comments

This depends on the terminal type, doesn't it?

It is my hope that Rust can somehow abstract this away, at least for ASCII characters.

Terminal types are fundamental.

Is it something like what the crate console provide?

Scryer uses the crossterm crate for terminal IO, and ideally that can be used for this feature.

For instance, the following code should ideally be simplified by reading a single character, promoting it to a Prolog term, and making it available for reasoning within Prolog (as a character, i.e., a one-char atom):

https://github.com/mthom/scryer-prolog/blob/57d739673a67337aa43384b54c58ba93496aeb21/src/prolog/machine/system_calls.rs#L45

So, instead of this low-level explicit case distinction formulated in Rust, the top-level could simply invoke get_single_char(C), and make the necessary distinction within Prolog.

It is good practice to shift as much of the work as possible to the Prolog level, so that the core engine is kept lean, stressed and tested.

I added the predicate at the branch get_single_char. What do you think?

I'm extremely impressed!

As regards placement, since this predicate is not a standard predicate, I think library(charsio) would be a better fit.

Please keep up the great work!

Once get_single_char/1 is available, it can be used to replace this call:

https://github.com/mthom/scryer-prolog/blob/57d739673a67337aa43384b54c58ba93496aeb21/src/prolog/toplevel.pl#L148

And everything that has to do with ContinueResult can simply be removed from src/prolog/machine/system_calls.rs.

There are some characters that can't be read this way like \n. Do I make a single PR where $raw_input_read_char is replaced with $get_single_char?

Is there a way to make get_single_char/1 work also for \n? That would be the ideal solution.

I think $raw_input_read_char should be removed completely from the Rust code, and instead of the internal $raw_input_read_char, the new Prolog predicate get_single_char/1 from library(charsio) should be used by the toplevel. So, in toplevel.pl, I suggest :- use_module(library(charsio))., and using the new "normal" Prolog predicate.

This way, as much as possible stays within Prolog.

Yes, there is a way. The other character that is missing is also \t, the tab. In KeyCode, some keycode could be a character. Make it for \n only or the others too?

\t and \n would be perfect!

Done but replacing $raw_input_read_char is hard in toplevel.pl.

Updated get_single_char. Not sure if it is the way this should be done.

Thank you a lot, I will look into the integration!

If possible, could you please have a look at #351 ? This would be incredibly useful to write parsers with Scryer Prolog, and it would be a very valuable addition.

It works perfectly, this is a great contribution!

I have filed https://github.com/notoria/scryer-prolog/pull/1 with two small changes.

Please file this as a pull request. Thank you a lot!

Unfortunately, I have now found the following problem with the change:

They apparently break the interaction when Scryer Prolog is used from other programs.

I have constructed the following test case:

Using Emacs, place point at the end of this form, and press C-x C-e to evaluate it:

(let* ((scryer "~/scryer-prolog/target/release/scryer-prolog")
       (p (start-process "scryer" (current-buffer) scryer)))
  (process-send-string p "true ; true.\n")
  (sit-for 0.5)
  (process-send-string p " "))

With the current master branch of Scryer Prolog, the following ouput appears at the end of the buffer:

?-    true
;  true.
?- 

But when using the new get_single_char/1 predicate in the toplevel, we now get:

?-    true

So, Scryer seems not to see that SPACE was sent to the process! Is there a way to correct this, could you please have a look? Thank you a lot! This is needed for ediprolog and other Emacs packages that run Scryer.

I don't have Emacs. What is the error message? In a case like:
Bash $ cat main.pl get_single_char(C). $ scryer-prolog < main.pl $ cat main.pl | scryer-prolog
The program fails. It seems it depends on how the program is executed. Not sure if this example reproduces that issue.

Could you check if it solved? I added space in toplevel.pl.

Now it's perfect, thank you again!

I have filed https://github.com/notoria/scryer-prolog/pull/2. Please review, merge if applicable, and then file your branch as a pull request for the main repository.

Due to your work, better interaction is now possible with the system in addition to the new get_single_char/1 predicate that may also be useful in other contexts, and which has simplified the Rust code. Thank you very much!

I'm looking forward to working with you on #351, which would also be a great addition for library(charsio).

This is now brilliantly implemented via https://github.com/mthom/scryer-prolog/pull/360, thank you a lot!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

triska picture triska  路  3Comments

Qqwy picture Qqwy  路  3Comments

UWN picture UWN  路  3Comments

notoria picture notoria  路  4Comments

UWN picture UWN  路  3Comments