migrate to rustyline, add history support
This commit is contained in:
@@ -1,14 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "scryer-prolog"
|
name = "scryer-prolog"
|
||||||
version = "0.8.93"
|
version = "0.8.94"
|
||||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||||
repository = "https://github.com/mthom/scryer-prolog"
|
repository = "https://github.com/mthom/scryer-prolog"
|
||||||
description = "A modern Prolog implementation written mostly in Rust."
|
description = "A modern Prolog implementation written mostly in Rust."
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["readline_rs_compat"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "0.1.7"
|
cfg-if = "0.1.7"
|
||||||
dirs = "2.0.2"
|
dirs = "2.0.2"
|
||||||
@@ -16,9 +13,9 @@ downcast = "0.10.0"
|
|||||||
indexmap = "1.0.2"
|
indexmap = "1.0.2"
|
||||||
ordered-float = "0.5.0"
|
ordered-float = "0.5.0"
|
||||||
prolog_parser = "0.8.29"
|
prolog_parser = "0.8.29"
|
||||||
readline_rs_compat = { version = "0.1.9", optional = true }
|
|
||||||
ref_thread_local = "0.0.0"
|
ref_thread_local = "0.0.0"
|
||||||
rug = "1.4.0"
|
rug = "1.4.0"
|
||||||
|
rustyline = "5.0.3"
|
||||||
|
|
||||||
[dependencies.termion]
|
[dependencies.termion]
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ use prolog::read::*;
|
|||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(feature = "readline_rs_compat")]
|
// #[cfg(feature = "readline_rs_compat")]
|
||||||
readline::readline_initialize();
|
// readline::readline_initialize();
|
||||||
|
|
||||||
let mut wam = Machine::new(readline::input_stream());
|
let mut wam = Machine::new(readline::input_stream());
|
||||||
wam.run_toplevel();
|
wam.run_toplevel();
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ pub enum SystemClauseType {
|
|||||||
NumberToCodes,
|
NumberToCodes,
|
||||||
OpDeclaration,
|
OpDeclaration,
|
||||||
REPL(REPLCodePtr),
|
REPL(REPLCodePtr),
|
||||||
|
ReadQueryTerm,
|
||||||
ReadTerm,
|
ReadTerm,
|
||||||
RedoAttrVarBindings,
|
RedoAttrVarBindings,
|
||||||
RemoveCallPolicyCheck,
|
RemoveCallPolicyCheck,
|
||||||
@@ -322,6 +323,7 @@ impl SystemClauseType {
|
|||||||
&SystemClauseType::GetCurrentBlock => clause_name!("$get_current_block"),
|
&SystemClauseType::GetCurrentBlock => clause_name!("$get_current_block"),
|
||||||
&SystemClauseType::InstallNewBlock => clause_name!("$install_new_block"),
|
&SystemClauseType::InstallNewBlock => clause_name!("$install_new_block"),
|
||||||
&SystemClauseType::ModuleRetractClause => clause_name!("$module_retract_clause"),
|
&SystemClauseType::ModuleRetractClause => clause_name!("$module_retract_clause"),
|
||||||
|
&SystemClauseType::ReadQueryTerm => clause_name!("$read_query_term"),
|
||||||
&SystemClauseType::ReadTerm => clause_name!("$read_term"),
|
&SystemClauseType::ReadTerm => clause_name!("$read_term"),
|
||||||
&SystemClauseType::ResetGlobalVarAtKey => clause_name!("$reset_global_var_at_key"),
|
&SystemClauseType::ResetGlobalVarAtKey => clause_name!("$reset_global_var_at_key"),
|
||||||
&SystemClauseType::RetractClause => clause_name!("$retract_clause"),
|
&SystemClauseType::RetractClause => clause_name!("$retract_clause"),
|
||||||
@@ -416,6 +418,7 @@ impl SystemClauseType {
|
|||||||
("$get_current_block", 1) => Some(SystemClauseType::GetCurrentBlock),
|
("$get_current_block", 1) => Some(SystemClauseType::GetCurrentBlock),
|
||||||
("$get_cp", 1) => Some(SystemClauseType::GetCutPoint),
|
("$get_cp", 1) => Some(SystemClauseType::GetCutPoint),
|
||||||
("$install_new_block", 1) => Some(SystemClauseType::InstallNewBlock),
|
("$install_new_block", 1) => Some(SystemClauseType::InstallNewBlock),
|
||||||
|
("$read_query_term", 2) => Some(SystemClauseType::ReadQueryTerm),
|
||||||
("$read_term", 2) => Some(SystemClauseType::ReadTerm),
|
("$read_term", 2) => Some(SystemClauseType::ReadTerm),
|
||||||
("$reset_block", 1) => Some(SystemClauseType::ResetBlock),
|
("$reset_block", 1) => Some(SystemClauseType::ResetBlock),
|
||||||
("$reset_global_var_at_key", 1) => Some(SystemClauseType::ResetGlobalVarAtKey),
|
("$reset_global_var_at_key", 1) => Some(SystemClauseType::ResetGlobalVarAtKey),
|
||||||
|
|||||||
@@ -354,13 +354,8 @@ impl Machine {
|
|||||||
fn handle_toplevel_command(&mut self, code_ptr: REPLCodePtr, p: LocalCodePtr) {
|
fn handle_toplevel_command(&mut self, code_ptr: REPLCodePtr, p: LocalCodePtr) {
|
||||||
match code_ptr {
|
match code_ptr {
|
||||||
REPLCodePtr::CompileBatch => {
|
REPLCodePtr::CompileBatch => {
|
||||||
#[cfg(feature = "readline_rs_compat")]
|
|
||||||
readline::set_line_mode(readline::LineMode::Multi);
|
|
||||||
|
|
||||||
let src = readline::input_stream();
|
let src = readline::input_stream();
|
||||||
|
readline::set_prompt(false);
|
||||||
#[cfg(feature = "readline_rs_compat")]
|
|
||||||
readline::set_line_mode(readline::LineMode::Single);
|
|
||||||
|
|
||||||
match compile_user_module(self, src) {
|
match compile_user_module(self, src) {
|
||||||
EvalSession::Error(e) => self.throw_session_error(e, (clause_name!("repl"), 0)),
|
EvalSession::Error(e) => self.throw_session_error(e, (clause_name!("repl"), 0)),
|
||||||
|
|||||||
@@ -251,6 +251,63 @@ impl MachineState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_term(&mut self,
|
||||||
|
current_input_stream: &mut PrologStream,
|
||||||
|
indices: &mut IndexStore)
|
||||||
|
-> CallResult
|
||||||
|
{
|
||||||
|
match self.read(
|
||||||
|
current_input_stream,
|
||||||
|
indices.atom_tbl.clone(),
|
||||||
|
&indices.op_dir,
|
||||||
|
) {
|
||||||
|
Ok(term_write_result) => {
|
||||||
|
let a1 = self[temp_v!(1)].clone();
|
||||||
|
self.unify(Addr::HeapCell(term_write_result.heap_loc), a1);
|
||||||
|
|
||||||
|
if self.fail {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut list_of_var_eqs = vec![];
|
||||||
|
|
||||||
|
for (var, binding) in term_write_result.var_dict.into_iter().rev() {
|
||||||
|
let var_atom = clause_name!(var.to_string(), indices.atom_tbl);
|
||||||
|
let var_atom = Constant::Atom(var_atom, None);
|
||||||
|
|
||||||
|
let h = self.heap.h;
|
||||||
|
let spec = fetch_atom_op_spec(clause_name!("="), None, &indices.op_dir);
|
||||||
|
|
||||||
|
self.heap.push(HeapCellValue::NamedStr(2, clause_name!("="), spec));
|
||||||
|
self.heap.push(HeapCellValue::Addr(Addr::Con(var_atom)));
|
||||||
|
self.heap.push(HeapCellValue::Addr(binding));
|
||||||
|
|
||||||
|
list_of_var_eqs.push(Addr::Str(h));
|
||||||
|
}
|
||||||
|
|
||||||
|
let a2 = self[temp_v!(2)].clone();
|
||||||
|
let list_offset =
|
||||||
|
Addr::HeapCell(self.heap.to_list(list_of_var_eqs.into_iter()));
|
||||||
|
|
||||||
|
Ok(self.unify(list_offset, a2))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
if let ParserError::UnexpectedEOF = err {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the input stream after an input failure.
|
||||||
|
*current_input_stream = readline::input_stream();
|
||||||
|
|
||||||
|
let h = self.heap.h;
|
||||||
|
let syntax_error = MachineError::syntax_error(h, err);
|
||||||
|
let stub = MachineError::functor_stub(clause_name!("read_term"), 2);
|
||||||
|
|
||||||
|
Err(self.error_form(syntax_error, stub))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn install_new_block(&mut self, r: RegType) -> usize {
|
fn install_new_block(&mut self, r: RegType) -> usize {
|
||||||
self.block = self.b;
|
self.block = self.b;
|
||||||
@@ -1674,69 +1731,27 @@ impl MachineState {
|
|||||||
&SystemClauseType::InstallNewBlock => {
|
&SystemClauseType::InstallNewBlock => {
|
||||||
self.install_new_block(temp_v!(1));
|
self.install_new_block(temp_v!(1));
|
||||||
}
|
}
|
||||||
|
&SystemClauseType::ReadQueryTerm => {
|
||||||
|
readline::set_prompt(true);
|
||||||
|
let result = self.read_term(current_input_stream, indices);
|
||||||
|
readline::set_prompt(false);
|
||||||
|
|
||||||
|
let _ = result?;
|
||||||
|
}
|
||||||
&SystemClauseType::ReadTerm => {
|
&SystemClauseType::ReadTerm => {
|
||||||
match self.read(
|
readline::set_prompt(false);
|
||||||
current_input_stream,
|
self.read_term(current_input_stream, indices)?;
|
||||||
indices.atom_tbl.clone(),
|
},
|
||||||
&indices.op_dir,
|
|
||||||
) {
|
|
||||||
Ok(term_write_result) => {
|
|
||||||
let a1 = self[temp_v!(1)].clone();
|
|
||||||
self.unify(Addr::HeapCell(term_write_result.heap_loc), a1);
|
|
||||||
|
|
||||||
if self.fail {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut list_of_var_eqs = vec![];
|
|
||||||
|
|
||||||
for (var, binding) in term_write_result.var_dict.into_iter().rev() {
|
|
||||||
let var_atom = clause_name!(var.to_string(), indices.atom_tbl);
|
|
||||||
let var_atom = Constant::Atom(var_atom, None);
|
|
||||||
|
|
||||||
let h = self.heap.h;
|
|
||||||
let spec = fetch_atom_op_spec(clause_name!("="), None, &indices.op_dir);
|
|
||||||
|
|
||||||
self.heap
|
|
||||||
.push(HeapCellValue::NamedStr(2, clause_name!("="), spec));
|
|
||||||
self.heap.push(HeapCellValue::Addr(Addr::Con(var_atom)));
|
|
||||||
self.heap.push(HeapCellValue::Addr(binding));
|
|
||||||
|
|
||||||
list_of_var_eqs.push(Addr::Str(h));
|
|
||||||
}
|
|
||||||
|
|
||||||
let a2 = self[temp_v!(2)].clone();
|
|
||||||
let list_offset =
|
|
||||||
Addr::HeapCell(self.heap.to_list(list_of_var_eqs.into_iter()));
|
|
||||||
|
|
||||||
self.unify(list_offset, a2);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
if let ParserError::UnexpectedEOF = err {
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset the input stream after an input failure.
|
|
||||||
*current_input_stream = readline::input_stream();
|
|
||||||
|
|
||||||
let h = self.heap.h;
|
|
||||||
let syntax_error = MachineError::syntax_error(h, err);
|
|
||||||
let stub = MachineError::functor_stub(clause_name!("read_term"), 2);
|
|
||||||
|
|
||||||
return Err(self.error_form(syntax_error, stub));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&SystemClauseType::ResetBlock => {
|
&SystemClauseType::ResetBlock => {
|
||||||
let addr = self.deref(self[temp_v!(1)].clone());
|
let addr = self.deref(self[temp_v!(1)].clone());
|
||||||
self.reset_block(addr);
|
self.reset_block(addr);
|
||||||
}
|
}
|
||||||
&SystemClauseType::SetBall => self.set_ball(),
|
&SystemClauseType::SetBall =>
|
||||||
&SystemClauseType::SkipMaxList => {
|
self.set_ball(),
|
||||||
|
&SystemClauseType::SkipMaxList =>
|
||||||
if let Err(err) = self.skip_max_list() {
|
if let Err(err) = self.skip_max_list() {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
},
|
||||||
}
|
|
||||||
&SystemClauseType::StoreGlobalVar => {
|
&SystemClauseType::StoreGlobalVar => {
|
||||||
let key = self[temp_v!(1)].clone();
|
let key = self[temp_v!(1)].clone();
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ extern crate dirs;
|
|||||||
extern crate ordered_float;
|
extern crate ordered_float;
|
||||||
extern crate prolog_parser;
|
extern crate prolog_parser;
|
||||||
extern crate rug;
|
extern crate rug;
|
||||||
|
extern crate rustyline;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|||||||
@@ -25,35 +25,60 @@ impl<'a> TermRef<'a> {
|
|||||||
|
|
||||||
pub type PrologStream = ParsingStream<Box<Read>>;
|
pub type PrologStream = ParsingStream<Box<Read>>;
|
||||||
|
|
||||||
#[cfg(feature = "readline_rs_compat")]
|
|
||||||
pub mod readline {
|
pub mod readline {
|
||||||
use prolog_parser::ast::*;
|
use prolog_parser::ast::*;
|
||||||
use readline_rs_compat::readline::*;
|
use prolog::rustyline::error::ReadlineError;
|
||||||
use std::io::{Error, Read};
|
use prolog::rustyline::{Cmd, Editor, KeyPress};
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
static mut PROMPT: bool = false;
|
||||||
pub enum LineMode {
|
|
||||||
Single,
|
pub fn set_prompt(value: bool) {
|
||||||
Multi,
|
unsafe {
|
||||||
|
PROMPT = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_prompt() -> &'static str {
|
||||||
|
unsafe {
|
||||||
|
if PROMPT { "?- " } else { "" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReadlineStream {
|
pub struct ReadlineStream {
|
||||||
|
rl: Editor<()>,
|
||||||
pending_input: String,
|
pending_input: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReadlineStream {
|
impl ReadlineStream {
|
||||||
#[inline]
|
fn input_stream(pending_input: String) -> Self {
|
||||||
fn new(pending_input: String) -> Self {
|
let mut rl = Editor::<()>::new();
|
||||||
ReadlineStream { pending_input }
|
|
||||||
|
rl.bind_sequence(KeyPress::Tab, Cmd::Insert(1, "\t".to_string()));
|
||||||
|
|
||||||
|
ReadlineStream { rl, pending_input }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_readline(&mut self, prompt: &str, buf: &mut [u8]) -> std::io::Result<usize> {
|
fn call_readline(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
match readline_rl(prompt) {
|
match self.rl.readline(get_prompt()) {
|
||||||
Some(text) => {
|
Ok(text) => {
|
||||||
self.pending_input += &text;
|
self.pending_input += &text;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if PROMPT {
|
||||||
|
self.rl.history_mut().add(&self.pending_input);
|
||||||
|
PROMPT = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pending_input += "\n";
|
||||||
Ok(self.write_to_buf(buf))
|
Ok(self.write_to_buf(buf))
|
||||||
}
|
}
|
||||||
None => Err(Error::last_os_error()),
|
Err(ReadlineError::Eof) =>
|
||||||
|
Ok(self.write_to_buf(buf)),
|
||||||
|
Err(e) =>
|
||||||
|
Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,84 +109,16 @@ pub mod readline {
|
|||||||
impl Read for ReadlineStream {
|
impl Read for ReadlineStream {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
if self.pending_input.is_empty() {
|
if self.pending_input.is_empty() {
|
||||||
self.call_readline("", buf)
|
self.call_readline(buf)
|
||||||
} else {
|
} else {
|
||||||
Ok(self.write_to_buf(buf))
|
Ok(self.write_to_buf(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut LINE_MODE: LineMode = LineMode::Single;
|
|
||||||
static mut END_OF_LINE: bool = false;
|
|
||||||
|
|
||||||
pub fn set_line_mode(mode: LineMode) {
|
|
||||||
unsafe {
|
|
||||||
LINE_MODE = mode;
|
|
||||||
END_OF_LINE = false;
|
|
||||||
rl_done = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn bind_end_chord(_: i32, _: i32) -> i32 {
|
|
||||||
if let LineMode::Multi = LINE_MODE {
|
|
||||||
rl_done = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn bind_cr(_: i32, _: i32) -> i32 {
|
|
||||||
if let LineMode::Single = LINE_MODE {
|
|
||||||
insert_text_rl("\n");
|
|
||||||
println!("");
|
|
||||||
rl_done = 1;
|
|
||||||
} else {
|
|
||||||
insert_text_rl("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn readline_initialize() {
|
|
||||||
let rc = initialize_rl(); // initialize editline.
|
|
||||||
|
|
||||||
if rc != 0 {
|
|
||||||
panic!("initialize_rl() failed with return code {}", rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_key_rl('\t' as i32, rl_insert); // just insert tabs when typed.
|
|
||||||
bind_key_rl('\n' as i32, bind_cr);
|
|
||||||
bind_key_rl('\r' as i32, bind_cr);
|
|
||||||
bind_keyseq_rl("\\C-d", bind_end_chord);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn input_stream() -> ::PrologStream {
|
pub fn input_stream() -> ::PrologStream {
|
||||||
let reader: Box<Read> = Box::new(ReadlineStream::new(String::from("")));
|
let reader: Box<Read> = Box::new(ReadlineStream::input_stream(String::from("")));
|
||||||
parsing_stream(reader)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "readline_rs_compat"))]
|
|
||||||
pub mod readline {
|
|
||||||
use prolog_parser::ast::*;
|
|
||||||
use std::io::{stdin, BufReader, Read, Stdin};
|
|
||||||
|
|
||||||
struct StdinWrapper {
|
|
||||||
buf: BufReader<Stdin>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for StdinWrapper {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
||||||
self.buf.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn input_stream() -> ::PrologStream {
|
|
||||||
let reader: Box<Read> = Box::new(StdinWrapper {
|
|
||||||
buf: BufReader::new(stdin()),
|
|
||||||
});
|
|
||||||
parsing_stream(reader)
|
parsing_stream(reader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
'$repl' :- '$repl'.
|
'$repl' :- '$repl'.
|
||||||
|
|
||||||
'$read_and_match' :-
|
'$read_and_match' :-
|
||||||
write_term('?- ', [quoted(false)]),
|
'$read_query_term'(Term, VarList),
|
||||||
read_term(Term, [variable_names(VarList)]),
|
|
||||||
'$instruction_match'(Term, VarList).
|
'$instruction_match'(Term, VarList).
|
||||||
|
|
||||||
'$instruction_match'([user], []) :-
|
'$instruction_match'([user], []) :-
|
||||||
|
|||||||
Reference in New Issue
Block a user