mthom / scryer-prolog

A modern Prolog implementation written mostly in Rust.
BSD 3-Clause "New" or "Revised" License
2.01k stars 118 forks source link

Programmatic interaction with scryer-prolog process #739

Open dcnorris opened 3 years ago

dcnorris commented 3 years ago

Hoping to register a knitr prolog engine for use in R Markdown documents, on something like the same lines as the python engine provided by the reticulate package. To that end, I explored interacting with the scryer-prolog process using R package processx, as follows:

> prolog <- process$new(command = "/usr/local/bin/sicstus", stdin="|", stdout="|", stderr="2>&1")
> prolog$is_alive()
[1] TRUE
> prolog$write_input("member(X,[a,b,c]).\n")
> prolog$read_output()
[1] "SICStus 4.6.0 (x86_64-darwin-17.7.0): Mon Apr  6 18:23:42 CEST 2020\nLicensed to Dr. David C. Norris\nX = a ? "
> prolog$write_input(";\n")
> prolog$read_output()
[1] "\nX = b ? "
> prolog$write_input(";\n")
> prolog$read_output()
[1] "\nX = c ? "
> prolog$write_input(";\n")
> prolog$read_output()
[1] "\nno\n"
> prolog <- process$new(command = "/Users/david/.cargo/bin/scryer-prolog", stdin="|", stdout="|", stderr="2>&1")
> prolog$is_alive()
[1] TRUE
> prolog$write_input("member(X,[a,b,c]).\n")
> prolog$read_output()
[1] "   X = athread 'main' panicked at 'failed to enable raw mode: IoError(Os { code: 102, kind: Other, message: \"Operation not supported on socket\" })', src/machine/system_calls.rs:64:23\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n"
> 

I know @triska's ediprolog.el accomplishes this sort of communication just fine, however, so I have to think the problem is on my part. Any hints/guidance gratefully accepted.

mthom commented 3 years ago

An example you might find helpful is cl-psoatransrun. It communicates with Scryer Prolog as a subprocess by using sockets to connect to a server program on the Scryer Prolog side. It is documented here:

http://wiki.ruleml.org/index.php/The_cl-psoatransrun_System:_An_Efficiently_Executable_Specification_of_PSOA_RuleML_in_Common_Lisp#Communicating_with_Prolog_Engines_as_Subprocesses

triska commented 3 years ago

Network sockets indeed seem to an excellent way to do this, since they are not dependent on the terminal and shell that is used, and also since they allow running client and server on different machines.

I have put together a small self-contained example that shows the interaction with R, based on:

https://www.r-bloggers.com/2013/05/using-r-to-communicate-via-a-socket-connection/

Here are the two files:

Usage:

$ scryer-prolog server.pl 
?- server(6011).
waiting for connections...

R:

> source("client.R")
[1] "Enter text to be encoded, q to quit"
hello
[1] "Base64 encoded text:   aGVsbG8K"
etc.
dcnorris commented 3 years ago

I believe the issue relates to backtracking. I can post deterministic queries without difficulty:

> library(processx)
> prolog <- process$new(command = "/Users/david/.cargo/bin/scryer-prolog",
                        stdin="|", stdout="|", stderr="2>&1",
                        env = c(RUST_BACKTRACE="full"))
> prolog$write_input("a = a.\n")
> prolog$read_output()
[1] "   true.\n"
> prolog$write_input("X = Y, Y = a.\n")
> prolog$read_output()
[1] "   X = a, Y = a.\n"
> prolog$write_input("append([1], [2,3], X).\n")
> prolog$read_output()
[1] "   X = [1,2,3].\n"
> prolog$write_input("append(X, Y, [a,b]).\n")
> prolog$read_output()
[1] "   X = [], Y = \"ab\"thread 'main' panicked at 'failed to enable raw mode:
 IoError(Os { code: 102, kind: Other, message: \"Operation not supported on socket\" })',
 src/machine/system_calls.rs:64:23\nstack backtrace:\n
   0:        0x1088b6054 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hcfc48256a5ab8835\n
   1:        0x1088d77a0 - core::fmt::write::haf3903118f694c48\n
   2:        0x1088ae9e6 - std::io::Write::write_fmt::h7385463ac87804ed\n
   3:        0x1088b7b1f - std::panicking::default_hook::{{closure}}::h91bd4c58cf71392b\n
   4:        0x1088b77ed - std::panicking::default_hook::h7bd29c87df967048\n
   5:        0x1088b80eb - std::panicking::rust_panic_with_hook::hae2b05f08a320721\n
   6:        0x1088b7c6b - std::panicking::begin_panic_handler::{{closure}}::h72d68d3a77e0b718\n
   7:        0x1088b64c8 - std::sys_common::backtrace::__rust_end_short_backtrace::h7c5e286792f94edb\n
   8:        0x1088b7c2a - _rust_begin_unwind\n
   9:        0x1088f073f - core::panicking::panic_fmt::h1b194bb80d76fb10\n
  10:        0x1088f0645 - core::option::expect_none_failed::h75fa4820ade504dc\n
  11:        0x10851f03f - scryer_prolog::machine::system_calls::get_key::heca6cbd40d757309\n
  12:        0x1084c99b3 - scryer_prolog::machine::system_calls::<impl scryer_prolog::machine::machine_state::MachineState>::system_call::hdc75fb3a20aeb737\n
  13:        0x108514826 - scryer_prolog::machine::<impl scryer_prolog::machine::machine_state::MachineState>::dispatch_instr::hd581e3292b2aca36\n
  14:        0x108515fc2 - scryer_prolog::machine::<impl scryer_prolog::machine::machine_state::MachineState>::query_stepper::h5064b85c0c0e4e97\n
  15:        0x108593c8e - scryer_prolog::machine::Machine::run_query::h7b1e248dc7ff3dc5\n
  16:        0x108588b0b - scryer_prolog::machine::Machine::run_top_level::hcd46dec1f2fe1802\n
  17:        0x108681074 - scryer_prolog::main::h9315df94423ffe57\n
  18:        0x108647f7a - std::sys_common::backtrace::__rust_begin_short_backtrace::h6cdc7a2d95e0765b\n
  19:        0x108647f9c - std::rt::lang_start::{{closure}}::h2f9fb6a9d632bb8b\n
  20:        0x1088b8544 - std::rt::lang_start_internal::hd38bb63f9540b327\n
  21:        0x1086812e9 - _main\n"
> prolog$is_alive()
[1] FALSE
> 
ghost commented 3 years ago

The function that panics is get_key, it's for get_single_char/1 and get_single_char/1 is used in toplevel here.

The predicate get_single_char/1 could switch input source (from stdin in raw mode to current_input_stream). For now this happens:

?- use_module(library(charsio)).
   true.
?- read(G), get_single_char(C).
a. b.
   G = a, C = c.
caught: error(existence_error(procedure,b/0),b/0)
?- 

The predicate get_single_char/1 reads something else, not ' ' or b.