From: Mark Thom Date: Sun, 17 Mar 2019 01:12:06 +0000 (-0600) Subject: use the readline library at toplevel X-Git-Tag: v0.8.110~177 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=2fad1c724ccfd343698fddf5e2f0017a9600598c;p=scryer-prolog.git use the readline library at toplevel --- diff --git a/Cargo.lock b/Cargo.lock index 6e7aa3a5..a24efcdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,11 @@ dependencies = [ "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "readline-sys" +version = "0.1.0" +source = "git+https://github.com/mthom/readline-sys#e529b03ae3c723fc45b06784b1c67aa0b4c322ae" + [[package]] name = "redox_syscall" version = "0.1.51" @@ -111,12 +116,13 @@ dependencies = [ [[package]] name = "scryer-prolog" -version = "0.8.3" +version = "0.8.4" dependencies = [ "downcast 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "prolog_parser 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "readline-sys 0.1.0 (git+https://github.com/mthom/readline-sys)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -156,6 +162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d" "checksum prolog_parser 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6da85e0cfa5a604edf65f753e629db37bfd04af93a09a1df5576d2197a2f7af3" +"checksum readline-sys 0.1.0 (git+https://github.com/mthom/readline-sys)" = "" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" diff --git a/Cargo.toml b/Cargo.toml index 98d36db4..558ebefb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scryer-prolog" -version = "0.8.3" +version = "0.8.4" authors = ["Mark Thom "] repository = "https://github.com/mthom/scryer-prolog" description = "A modern Prolog implementation written mostly in Rust." @@ -11,6 +11,10 @@ downcast = "0.9.1" num = "0.2" ordered-float = "0.5.0" prolog_parser = "0.8.1" +readline-sys = "0.1.0" + +[patch.crates-io] +readline-sys = { git = 'https://github.com/mthom/readline-sys' } [dependencies.termion] version = "1.4.0" \ No newline at end of file diff --git a/README.md b/README.md index 4e8627fd..007b7edf 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,11 @@ $> cargo install scryer-prolog ``` cargo will download and install the libraries Scryer Prolog uses -automatically. You can find the `scryer-prolog` executable in -`~/.cargo/bin`. +automatically, save for the C library readline. It will search the +system library paths for readline without taking further steps to +install it if it is not found. + +You can find the `scryer-prolog` executable in `~/.cargo/bin`. Note on compatibility: Scryer Prolog should work on Linux, Mac OS X, and FreeBSD. Windows support hinges on the Termion library working in diff --git a/src/main.rs b/src/main.rs index bfbda33f..d04dbc69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ #[macro_use] extern crate downcast; #[macro_use] extern crate prolog_parser; + +extern crate readline_sys; extern crate termion; mod prolog; @@ -11,22 +13,18 @@ use prolog::machine::toplevel::string_to_toplevel; use prolog::read::*; use prolog::write::*; -use std::io::{Write, stdin, stdout}; - #[cfg(test)] mod tests; fn prolog_repl() { let mut wam = Machine::new(); - + loop { - print!("prolog> "); - stdout().flush().unwrap(); - + set_line_mode(LineMode::Single); + match toplevel_read_line() { Ok(Input::TermString(buffer)) => { - let stdin = stdin(); - let result = match string_to_toplevel(stdin.lock(), buffer, &mut wam) { + let result = match string_to_toplevel(buffer, &mut wam) { Ok(packet) => compile_term(&mut wam, packet), Err(e) => EvalSession::from(e) }; @@ -34,9 +32,17 @@ fn prolog_repl() { print(&mut wam, result) }, Ok(Input::Batch) => { - let stdin = stdin(); - let result = compile_user_module(&mut wam, stdin.lock()); + set_line_mode(LineMode::Multi); + + let src = match read_line("") { + Ok(src) => src, + Err(e) => { + println!("{}", e); + continue; + } + }; + let result = compile_user_module(&mut wam, src.as_bytes()); print(&mut wam, result); }, Ok(Input::Quit) => break, @@ -52,5 +58,6 @@ fn prolog_repl() { } fn main() { + readline_initialize(); prolog_repl(); } diff --git a/src/prolog/machine/term_expansion.rs b/src/prolog/machine/term_expansion.rs index a7b8b8c8..d94569fb 100644 --- a/src/prolog/machine/term_expansion.rs +++ b/src/prolog/machine/term_expansion.rs @@ -149,11 +149,6 @@ impl<'a, R: Read> TermStream<'a, R> { self.parser.set_atom_tbl(atom_tbl); } - #[inline] - pub fn add_to_top(&mut self, buf: &str) { - self.parser.add_to_top(buf); - } - #[inline] pub fn eof(&mut self) -> Result { Ok(self.stack.is_empty() && self.parser.eof()?) diff --git a/src/prolog/machine/toplevel.rs b/src/prolog/machine/toplevel.rs index 1be84c37..c3656eba 100644 --- a/src/prolog/machine/toplevel.rs +++ b/src/prolog/machine/toplevel.rs @@ -761,15 +761,12 @@ fn term_to_toplevel<'a, R>(term_stream: &mut TermStream<'a, R>, code_dir: &mut C } pub -fn string_to_toplevel(src: R, buffer: String, wam: &mut Machine) - -> Result +fn string_to_toplevel(buffer: String, wam: &mut Machine) -> Result { - let mut term_stream = TermStream::new(src, wam.indices.atom_tbl(), + let mut term_stream = TermStream::new(buffer.as_bytes(), wam.indices.atom_tbl(), wam.machine_flags(), &mut wam.indices, &mut wam.policies, &mut wam.code_repo); - term_stream.add_to_top(buffer.as_str()); - let term = term_stream.read_term(&OpDir::new())?; let mut code_dir = CodeDir::new(); diff --git a/src/prolog/read.rs b/src/prolog/read.rs index 0c94bc5f..309e0f7d 100644 --- a/src/prolog/read.rs +++ b/src/prolog/read.rs @@ -4,11 +4,14 @@ use prolog_parser::tabled_rc::TabledData; use prolog::forms::*; use prolog::iterators::*; +use prolog::machine::machine_errors::*; use prolog::machine::machine_indices::*; use prolog::machine::machine_state::MachineState; use std::collections::VecDeque; -use std::io::{Read, stdin}; +use std::io::Read; + +use readline_sys::readline::*; type SubtermDeque = VecDeque<(usize, usize)>; @@ -30,21 +33,99 @@ pub enum Input { TermString(String) } -pub fn toplevel_read_line() -> Result { - let mut buffer = String::new(); +#[derive(Clone, Copy)] +pub enum LineMode { + Single, + Multi +} + +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; + } +} + +fn is_directive(buf: &String) -> bool { + match buf.as_str() { + "[user]" | "quit" | "clear" => true, + _ => false + } +} + +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_end_key(_: i32, _: i32) -> i32 { + insert_text_rl("."); + + if let LineMode::Single = LINE_MODE { + END_OF_LINE = true; + } + + 0 +} + +unsafe extern "C" fn bind_cr(_: i32, _: i32) -> i32 { + if END_OF_LINE { + println!(""); + rl_done = 1; + } else { + if let Some(ref buf) = rl_line_buffer_as_string() { + if is_directive(buf) { + println!(""); + rl_done = 1; + return 0; + } + } + + insert_text_rl("\n"); + } + + 0 +} + +pub fn readline_initialize() { + let rc = initialize_rl(); // initialize editline. - let stdin = stdin(); - stdin.read_line(&mut buffer).unwrap(); + if rc != 0 { + panic!("initialize_rl() failed with return code {}", rc); + } + + bind_key_rl('.' as i32, bind_end_key); + bind_key_rl('\n' as i32, bind_cr); + bind_key_rl('\r' as i32, bind_cr); + bind_keyseq_rl("\\C-d", bind_end_chord); +} + +pub fn read_line(prompt: &str) -> Result { + match readline_rl(prompt) { + Some(input) => Ok(input.to_string()), + None => Err(SessionError::UserPrompt) + } +} - match &*buffer.trim() { - "quit" => Ok(Input::Quit), - "clear" => Ok(Input::Clear), +pub fn toplevel_read_line() -> Result { + let buffer = read_line("prolog> ")?; + + Ok(match &*buffer.trim() { + "quit" => Input::Quit, + "clear" => Input::Clear, "[user]" => { println!("(type Enter + Ctrl-D to terminate the stream when finished)"); - Ok(Input::Batch) + Input::Batch }, - _ => Ok(Input::TermString(buffer)) - } + _ => Input::TermString(buffer) + }) } impl MachineState { diff --git a/src/tests.rs b/src/tests.rs index dd27affb..71e19a80 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -6,7 +6,6 @@ use prolog::machine::machine_indices::*; use prolog::machine::toplevel::*; use std::collections::HashSet; -use std::io::empty; use std::mem::swap; use std::ops::{Range, RangeFrom}; @@ -148,7 +147,7 @@ pub fn submit_query(wam: &mut Machine, buffer: &str, result: Vec { wam.reset(); - match string_to_toplevel(empty(), String::from(buffer), wam) { + match string_to_toplevel(String::from(buffer), wam) { Ok(term) => match compile_term(wam, term) { EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => @@ -165,7 +164,7 @@ pub fn submit_query_without_results(wam: &mut Machine, buffer: &str) -> bool { wam.reset(); - match string_to_toplevel(empty(), String::from(buffer), wam) { + match string_to_toplevel(String::from(buffer), wam) { Ok(term) => match compile_term(wam, term) { EvalSession::InitialQuerySuccess(..) @@ -183,7 +182,7 @@ pub fn submit_query_with_limit(wam: &mut Machine, buffer: &str, { wam.reset(); - match string_to_toplevel(empty(), String::from(buffer), wam) { + match string_to_toplevel(String::from(buffer), wam) { Ok(term) => match compile_term(wam, term) { EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) =>