use the readline library at toplevel

This commit is contained in:
Mark Thom
2019-03-16 19:12:06 -06:00
parent bc0f0719fb
commit 2fad1c724c
8 changed files with 132 additions and 39 deletions

9
Cargo.lock generated
View File

@@ -96,6 +96,11 @@ dependencies = [
"ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.1.51" version = "0.1.51"
@@ -111,12 +116,13 @@ dependencies = [
[[package]] [[package]]
name = "scryer-prolog" name = "scryer-prolog"
version = "0.8.3" version = "0.8.4"
dependencies = [ dependencies = [
"downcast 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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 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 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 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)" = "<none>"
"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "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 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" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "scryer-prolog" name = "scryer-prolog"
version = "0.8.3" version = "0.8.4"
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."
@@ -11,6 +11,10 @@ downcast = "0.9.1"
num = "0.2" num = "0.2"
ordered-float = "0.5.0" ordered-float = "0.5.0"
prolog_parser = "0.8.1" prolog_parser = "0.8.1"
readline-sys = "0.1.0"
[patch.crates-io]
readline-sys = { git = 'https://github.com/mthom/readline-sys' }
[dependencies.termion] [dependencies.termion]
version = "1.4.0" version = "1.4.0"

View File

@@ -101,8 +101,11 @@ $> cargo install scryer-prolog
``` ```
cargo will download and install the libraries Scryer Prolog uses cargo will download and install the libraries Scryer Prolog uses
automatically. You can find the `scryer-prolog` executable in automatically, save for the C library readline. It will search the
`~/.cargo/bin`. 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 Note on compatibility: Scryer Prolog should work on Linux, Mac OS X, and
FreeBSD. Windows support hinges on the Termion library working in FreeBSD. Windows support hinges on the Termion library working in

View File

@@ -1,5 +1,7 @@
#[macro_use] extern crate downcast; #[macro_use] extern crate downcast;
#[macro_use] extern crate prolog_parser; #[macro_use] extern crate prolog_parser;
extern crate readline_sys;
extern crate termion; extern crate termion;
mod prolog; mod prolog;
@@ -11,22 +13,18 @@ use prolog::machine::toplevel::string_to_toplevel;
use prolog::read::*; use prolog::read::*;
use prolog::write::*; use prolog::write::*;
use std::io::{Write, stdin, stdout};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
fn prolog_repl() { fn prolog_repl() {
let mut wam = Machine::new(); let mut wam = Machine::new();
loop { loop {
print!("prolog> "); set_line_mode(LineMode::Single);
stdout().flush().unwrap();
match toplevel_read_line() { match toplevel_read_line() {
Ok(Input::TermString(buffer)) => { Ok(Input::TermString(buffer)) => {
let stdin = stdin(); let result = match string_to_toplevel(buffer, &mut wam) {
let result = match string_to_toplevel(stdin.lock(), buffer, &mut wam) {
Ok(packet) => compile_term(&mut wam, packet), Ok(packet) => compile_term(&mut wam, packet),
Err(e) => EvalSession::from(e) Err(e) => EvalSession::from(e)
}; };
@@ -34,9 +32,17 @@ fn prolog_repl() {
print(&mut wam, result) print(&mut wam, result)
}, },
Ok(Input::Batch) => { Ok(Input::Batch) => {
let stdin = stdin(); set_line_mode(LineMode::Multi);
let result = compile_user_module(&mut wam, stdin.lock());
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); print(&mut wam, result);
}, },
Ok(Input::Quit) => break, Ok(Input::Quit) => break,
@@ -52,5 +58,6 @@ fn prolog_repl() {
} }
fn main() { fn main() {
readline_initialize();
prolog_repl(); prolog_repl();
} }

View File

@@ -149,11 +149,6 @@ impl<'a, R: Read> TermStream<'a, R> {
self.parser.set_atom_tbl(atom_tbl); self.parser.set_atom_tbl(atom_tbl);
} }
#[inline]
pub fn add_to_top(&mut self, buf: &str) {
self.parser.add_to_top(buf);
}
#[inline] #[inline]
pub fn eof(&mut self) -> Result<bool, ParserError> { pub fn eof(&mut self) -> Result<bool, ParserError> {
Ok(self.stack.is_empty() && self.parser.eof()?) Ok(self.stack.is_empty() && self.parser.eof()?)

View File

@@ -761,15 +761,12 @@ fn term_to_toplevel<'a, R>(term_stream: &mut TermStream<'a, R>, code_dir: &mut C
} }
pub pub
fn string_to_toplevel<R: Read>(src: R, buffer: String, wam: &mut Machine) fn string_to_toplevel(buffer: String, wam: &mut Machine) -> Result<TopLevelPacket, SessionError>
-> Result<TopLevelPacket, SessionError>
{ {
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, wam.machine_flags(), &mut wam.indices,
&mut wam.policies, &mut wam.code_repo); &mut wam.policies, &mut wam.code_repo);
term_stream.add_to_top(buffer.as_str());
let term = term_stream.read_term(&OpDir::new())?; let term = term_stream.read_term(&OpDir::new())?;
let mut code_dir = CodeDir::new(); let mut code_dir = CodeDir::new();

View File

@@ -4,11 +4,14 @@ use prolog_parser::tabled_rc::TabledData;
use prolog::forms::*; use prolog::forms::*;
use prolog::iterators::*; use prolog::iterators::*;
use prolog::machine::machine_errors::*;
use prolog::machine::machine_indices::*; use prolog::machine::machine_indices::*;
use prolog::machine::machine_state::MachineState; use prolog::machine::machine_state::MachineState;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io::{Read, stdin}; use std::io::Read;
use readline_sys::readline::*;
type SubtermDeque = VecDeque<(usize, usize)>; type SubtermDeque = VecDeque<(usize, usize)>;
@@ -30,21 +33,99 @@ pub enum Input {
TermString(String) TermString(String)
} }
pub fn toplevel_read_line() -> Result<Input, ParserError> { #[derive(Clone, Copy)]
let mut buffer = String::new(); pub enum LineMode {
Single,
Multi
}
let stdin = stdin(); static mut LINE_MODE: LineMode = LineMode::Single;
stdin.read_line(&mut buffer).unwrap(); static mut END_OF_LINE: bool = false;
match &*buffer.trim() { pub fn set_line_mode(mode: LineMode) {
"quit" => Ok(Input::Quit), unsafe {
"clear" => Ok(Input::Clear), 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.
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<String, SessionError> {
match readline_rl(prompt) {
Some(input) => Ok(input.to_string()),
None => Err(SessionError::UserPrompt)
}
}
pub fn toplevel_read_line() -> Result<Input, SessionError> {
let buffer = read_line("prolog> ")?;
Ok(match &*buffer.trim() {
"quit" => Input::Quit,
"clear" => Input::Clear,
"[user]" => { "[user]" => {
println!("(type Enter + Ctrl-D to terminate the stream when finished)"); 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 { impl MachineState {

View File

@@ -6,7 +6,6 @@ use prolog::machine::machine_indices::*;
use prolog::machine::toplevel::*; use prolog::machine::toplevel::*;
use std::collections::HashSet; use std::collections::HashSet;
use std::io::empty;
use std::mem::swap; use std::mem::swap;
use std::ops::{Range, RangeFrom}; use std::ops::{Range, RangeFrom};
@@ -148,7 +147,7 @@ pub fn submit_query(wam: &mut Machine, buffer: &str, result: Vec<HashSet<String>
{ {
wam.reset(); wam.reset();
match string_to_toplevel(empty(), String::from(buffer), wam) { match string_to_toplevel(String::from(buffer), wam) {
Ok(term) => Ok(term) =>
match compile_term(wam, term) { match compile_term(wam, term) {
EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) =>
@@ -165,7 +164,7 @@ pub fn submit_query_without_results(wam: &mut Machine, buffer: &str) -> bool
{ {
wam.reset(); wam.reset();
match string_to_toplevel(empty(), String::from(buffer), wam) { match string_to_toplevel(String::from(buffer), wam) {
Ok(term) => Ok(term) =>
match compile_term(wam, term) { match compile_term(wam, term) {
EvalSession::InitialQuerySuccess(..) EvalSession::InitialQuerySuccess(..)
@@ -183,7 +182,7 @@ pub fn submit_query_with_limit(wam: &mut Machine, buffer: &str,
{ {
wam.reset(); wam.reset();
match string_to_toplevel(empty(), String::from(buffer), wam) { match string_to_toplevel(String::from(buffer), wam) {
Ok(term) => Ok(term) =>
match compile_term(wam, term) { match compile_term(wam, term) {
EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) =>