From 1e2047e72c603826bcfd7afc4851332d8d2d75f9 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 13 Sep 2018 19:44:50 -0600 Subject: [PATCH] streamline inputs. --- Cargo.lock | 6 ++-- Cargo.toml | 4 +-- README.md | 51 ++++++++++-------------------- src/prolog/compile.rs | 17 +++++----- src/prolog/instructions.rs | 4 +-- src/prolog/machine/mod.rs | 31 +++++++++++++----- src/prolog/mod.rs | 2 +- src/prolog/read.rs | 13 ++++---- src/prolog/toplevel.rs | 25 +++++++++++---- src/tests.rs | 64 ++++++++++++++++++++++---------------- 10 files changed, 119 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ed1a05a..194d28b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,6 +87,7 @@ dependencies = [ [[package]] name = "prolog_parser" version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -107,12 +108,12 @@ dependencies = [ [[package]] name = "rusty-wam" -version = "0.7.10" +version = "0.7.11" dependencies = [ "downcast 0.9.1 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", - "prolog_parser 0.7.12", + "prolog_parser 0.7.12 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -151,6 +152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" "checksum ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "58d25b6c0e47b20d05226d288ff434940296e7e2f8b877975da32f862152241f" +"checksum prolog_parser 0.7.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bbf4229f9ff4f5826367591b0c76de86093c9ea8a8c4a8f2ac7cc66668788ce9" "checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0" "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 3ed2e17e..c08a0796 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-wam" -version = "0.7.10" +version = "0.7.11" authors = ["Mark Thom "] repository = "https://github.com/mthom/rusty-wam" description = "The Warren Abstract Machine in Rust." @@ -10,7 +10,7 @@ license = "BSD-3-Clause" downcast = "0.9.1" num = "0.2" ordered-float = "0.5.0" -prolog_parser = { path = "../prolog_parser", version = "0.7.12" } +prolog_parser = "0.7.12" [dependencies.termion] version = "1.4.0" \ No newline at end of file diff --git a/README.md b/README.md index f81e7866..06ebae93 100644 --- a/README.md +++ b/README.md @@ -169,53 +169,36 @@ The following predicates are built-in to rusty-wam. * `writeq/1` ## Tutorial -To enter a multi-clause predicate, the brackets ":{" and "}:" are used -as delimiters. They must be contained entirely within their own lines. +To enter a multi-clause predicate, the directive "[user]" is used. For example, ``` -prolog> :{ +prolog> [user] +(type Enter + Ctrl-D to terminate the stream when finished) p(f(f(X)), h(W), Y) :- g(W), h(W), f(X). p(X, Y, Z) :- h(Y), z(Z). -}: -prolog> :{ -h(x). -h(y). +prolog> [user] +(type Enter + Ctrl-D to terminate the stream when finished) +h(x). h(y). h(z). -}: -``` - -Single clause predicates can be entered without brackets, as in -``` -prolog> p(X) :- q(X). -prolog> f(s). -prolog> z(Z). ``` +In the example, `Enter + Ctrl-D` is used to terminate the standard +input stream. The instructive message is always printed. Queries are issued as ``` prolog> ?- p(X, Y, Z). ``` -Given the above work, the result of the query will be -``` -prolog> ?- p(X, Y, Z). -true -Y = x -X = _0 -Z = _2 -``` - Pressing `SPACE` will backtrack through other possible answers, if any exist. Pressing `.` will abort the search and return to the prompt. Wildcards work as well: ``` -prolog> :{ +prolog> [user] member(X, [X|_]). member(X, [_|Xs]) :- member(X, Xs). -}: prolog> ?- member(X, [a, b, c]). true . X = a ; @@ -225,11 +208,12 @@ false. ``` and so do conjunctive queries: ``` -prolog> f(X) :- g(X). -prolog> :{ +prolog> [user] +f(X) :- g(X). +prolog> [user] g(x). g(y). g(z). -}: -prolog> h(call(f, X)). +prolog> [user] +h(call(f, X)). prolog> ?- h(X), X. true . X = call(f, x) ; @@ -289,16 +273,15 @@ prolog> :- use_module(library(lists)). prolog> :- use_module(library(control)). ``` -To define modules inline at the REPL, use the ":{{" and "}}:" -delimiters: +The [user] prompt can also be used to define modules inline at the +REPL: ``` -prolog> :{{ +prolog> [user] :- module(test, [local_member/2]). :- use_module(library(lists)). local_member(X, Xs) :- member(X, Xs). -}}: ``` `use_module` directives can be qualified by adding a list of imports: diff --git a/src/prolog/compile.rs b/src/prolog/compile.rs index 385eeb19..a8949429 100644 --- a/src/prolog/compile.rs +++ b/src/prolog/compile.rs @@ -9,7 +9,6 @@ use prolog::toplevel::*; use std::collections::{HashMap, HashSet, VecDeque}; use std::io::Read; use std::mem; -use std::ops::DerefMut; #[allow(dead_code)] fn print_code(code: &Code) { @@ -98,10 +97,10 @@ fn compile_query(terms: Vec, queue: Vec, flags: MachineFlag } fn package_term(wam: &mut Machine, term: Term) -> Result { - let mut code_dir = wam.code_dir.borrow_mut(); - let indices = machine_code_indices!(code_dir.deref_mut(), &mut wam.op_dir, &mut wam.modules); - - parse_term(term, indices) + let code_dir = wam.code_dir.clone(); + let indices = machine_code_indices!(&mut CodeDir::new(), &mut wam.op_dir, &mut wam.modules); + + consume_term(code_dir, term, indices) } pub fn compile_term(wam: &mut Machine, term: Term) -> EvalSession @@ -262,10 +261,10 @@ impl ListingCompiler { } } -pub fn compile_listing<'a, R>(wam: &mut Machine, src: R, mut indices: MachineCodeIndices<'a>, - mut toplevel_indices: MachineCodeIndices<'a>) - -> EvalSession - where R: Read +pub +fn compile_listing<'a, R: Read>(wam: &mut Machine, src: R, mut indices: MachineCodeIndices<'a>, + mut toplevel_indices: MachineCodeIndices<'a>) + -> EvalSession { let mut worker = TopLevelBatchWorker::new(src, wam.atom_tbl(), wam.machine_flags(), wam.code_dir.clone()); diff --git a/src/prolog/instructions.rs b/src/prolog/instructions.rs index a19642dc..d2d6636c 100644 --- a/src/prolog/instructions.rs +++ b/src/prolog/instructions.rs @@ -770,7 +770,7 @@ impl HeapCellValue { } } -#[derive(Clone, Copy,PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub enum IndexPtr { Undefined, Index(usize), Module // This is a resolved module call. The module @@ -794,7 +794,7 @@ impl CodeIndex { #[inline] pub fn module_name(&self) -> ClauseName { - self.0.borrow().1.clone() + self.0.borrow().1.clone() } } diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 16dfc389..23490747 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -105,7 +105,6 @@ impl<'a> SubModuleUser for MachineCodeIndices<'a> { } set_code_index!(code_idx, idx.0, idx.1); - return; } @@ -169,14 +168,15 @@ impl Machine { } }; - if idx.module_name().as_str() == "builtins" { - continue; - } - if let Some(ref existing_idx) = self.code_dir.borrow().get(&key) { // ensure we don't try to overwrite an existing predicate from a different module. - if !existing_idx.is_undefined() { - if existing_idx.module_name() != idx.module_name() { + if !existing_idx.is_undefined() && !idx.is_undefined() { + // allow the overwriting of user-level predicates by all other predicates. + if existing_idx.module_name().as_str() == "user" { + continue; + } + + if existing_idx.module_name().as_str() != idx.module_name().as_str() { let err_str = format!("{}/{} from module {}", key.0, key.1, existing_idx.module_name().as_str()); return Err(SessionError::CannotOverwriteImport(err_str)); @@ -185,8 +185,23 @@ impl Machine { } } + // error detection has finished, so update the master index of keys. + for (key, idx) in code_dir { + if let Some(ref mut master_idx) = self.code_dir.borrow_mut().get_mut(&key) { + // ensure we don't double borrow if master_idx == idx. + // we don't need to modify anything in that case. + if !Rc::ptr_eq(&master_idx.0, &idx.0) { + set_code_index!(master_idx, idx.0.borrow().0, idx.module_name()); + } + + continue; + } + + self.code_dir.borrow_mut().insert(key.clone(), idx.clone()); + } + self.code.extend(code.into_iter()); - Ok(self.code_dir.borrow_mut().extend(code_dir.into_iter())) + Ok(()) } #[inline] diff --git a/src/prolog/mod.rs b/src/prolog/mod.rs index 4c77f937..a546e118 100644 --- a/src/prolog/mod.rs +++ b/src/prolog/mod.rs @@ -6,7 +6,7 @@ extern crate prolog_parser; mod and_stack; #[macro_use] mod macros; #[macro_use] mod allocator; -mod toplevel; +pub mod toplevel; pub mod machine; pub mod compile; mod arithmetic; diff --git a/src/prolog/read.rs b/src/prolog/read.rs index 888b4454..a0db1fdc 100644 --- a/src/prolog/read.rs +++ b/src/prolog/read.rs @@ -5,6 +5,7 @@ use prolog::instructions::*; use prolog::iterators::*; use prolog::machine::*; use prolog::machine::machine_state::MachineState; +use prolog::toplevel::*; use std::collections::VecDeque; use std::io::{Read, stdin}; @@ -38,13 +39,11 @@ pub fn read_toplevel(wam: &Machine) -> Result { match &*buffer.trim() { "quit" => Ok(Input::Quit), "clear" => Ok(Input::Clear), - "[user]" => Ok(Input::Batch), - _ => { - let mut parser = Parser::new(stdin.lock(), wam.atom_tbl(), wam.machine_flags()); - - parser.add_to_top(buffer.as_str()); - Ok(Input::Term(parser.read_term(composite_op!(&wam.op_dir))?)) - } + "[user]" => { + println!("(type Enter + Ctrl-D to terminate the stream when finished)"); + Ok(Input::Batch) + }, + _ => Ok(Input::Term(parse_term(wam, buffer.as_bytes())?)) } } diff --git a/src/prolog/toplevel.rs b/src/prolog/toplevel.rs index b4b7f832..198dda1f 100644 --- a/src/prolog/toplevel.rs +++ b/src/prolog/toplevel.rs @@ -36,12 +36,15 @@ impl<'a, 'b : 'a> CompositeIndices<'a, 'b> { fn get_code_index(&mut self, name: ClauseName, arity: usize) -> CodeIndex { let idx_opt = self.local.code_dir.get(&(name.clone(), arity)).cloned() - .or_else(|| self.static_code_dir.clone().and_then(|code_dir| { - code_dir.borrow().get(&(name.clone(), arity)).cloned() - })); + .or_else(|| { + self.static_code_dir.clone().and_then(|code_dir| { + code_dir.borrow().get(&(name.clone(), arity)).cloned() + }) + }); if let Some(idx) = idx_opt { - idx.clone() + self.local.code_dir.insert((name, arity), idx.clone()); + idx } else { let idx = CodeIndex::default(); self.local.code_dir.insert((name, arity), idx.clone()); @@ -666,10 +669,20 @@ impl RelationWorker { } } -pub fn parse_term<'a>(term: Term, mut indices: MachineCodeIndices<'a>) -> Result +// used to parse queries. mostly. +pub fn parse_term(wam: &Machine, buf: R) -> Result +{ + let mut parser = Parser::new(buf, wam.atom_tbl(), wam.machine_flags()); + parser.read_term(composite_op!(&wam.op_dir)) +} + +pub +fn consume_term<'a>(static_code_dir: Rc>, term: Term, + mut indices: MachineCodeIndices<'a>) + -> Result { let mut rel_worker = RelationWorker::new(); - let mut indices = composite_indices!(&mut indices); + let mut indices = composite_indices!(false, &mut indices, static_code_dir); let tl = rel_worker.try_term_to_tl(&mut indices, term, true)?; let results = rel_worker.parse_queue(&mut indices)?; diff --git a/src/tests.rs b/src/tests.rs index e8e40271..34e2c284 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,9 +1,8 @@ -use prolog_parser::ast::*; - use prolog::heap_print::*; use prolog::instructions::*; use prolog::compile::*; use prolog::machine::*; +use prolog::toplevel::*; use std::collections::HashSet; use std::mem::swap; @@ -120,16 +119,12 @@ pub fn submit(wam: &mut Machine, buffer: &str) -> bool { wam.reset(); - match parse_code(wam, buffer) { - Ok(tl) => - match compile_packet(wam, tl) { - EvalSession::InitialQuerySuccess(_, _) | - EvalSession::EntrySuccess | - EvalSession::SubsequentQuerySuccess => - true, - _ => false - }, - Err(e) => panic!("syntax_error({})", e.as_str()) + match compile_user_module(wam, buffer.as_bytes()) { + EvalSession::InitialQuerySuccess(_, _) | + EvalSession::EntrySuccess | + EvalSession::SubsequentQuerySuccess => + true, + _ => false } } @@ -138,9 +133,9 @@ pub fn submit_query(wam: &mut Machine, buffer: &str, result: Vec { wam.reset(); - match parse_code(wam, buffer) { - Ok(tl) => - match compile_packet(wam, tl) { + match parse_term(&wam, buffer.as_bytes()) { + Ok(term) => + match compile_term(wam, term) { EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => result == collect_test_output(wam, alloc_locs, heap_locs), EvalSession::EntrySuccess => true, @@ -150,6 +145,21 @@ pub fn submit_query(wam: &mut Machine, buffer: &str, result: Vec } } +#[allow(dead_code)] +pub fn submit_query_without_results(wam: &mut Machine, buffer: &str) -> bool { + wam.reset(); + + match parse_term(&wam, buffer.as_bytes()) { + Ok(term) => + match compile_term(wam, term) { + EvalSession::InitialQuerySuccess(..) + | EvalSession::EntrySuccess => true, + _ => false + }, + Err(e) => panic!("syntax_error({})", e.as_str()) + } +} + #[allow(dead_code)] pub fn submit_query_with_limit(wam: &mut Machine, buffer: &str, result: Vec>, limit: usize) @@ -157,9 +167,9 @@ pub fn submit_query_with_limit(wam: &mut Machine, buffer: &str, { wam.reset(); - match parse_code(wam, buffer) { - Ok(tl) => - match compile_packet(wam, tl) { + match parse_term(&wam, buffer.as_bytes()) { + Ok(term) => + match compile_term(wam, term) { EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => result == collect_test_output_with_limit(wam, alloc_locs, heap_locs, limit), @@ -187,7 +197,7 @@ macro_rules! assert_prolog_success_with_limit { #[allow(unused_macros)] macro_rules! assert_prolog_failure { ($wam: expr, $buf: expr) => ( - assert_eq!(submit($wam, $buf), false) + assert_eq!(submit_query_without_results($wam, $buf), false) ) } @@ -197,7 +207,7 @@ macro_rules! assert_prolog_success { assert!(submit_query($wam, $query, vec![$(expand_strs!($res)),*])) ); ($wam:expr, $buf:expr) => ( - assert_eq!(submit($wam, $buf), true) + assert_eq!(submit_query_without_results($wam, $buf), true) ) } @@ -1342,7 +1352,7 @@ fn test_queries_on_modules() { let mut wam = Machine::new(); - wam.use_module_in_toplevel(clause_name!("lists")); + submit(&mut wam, ":- use_module(library(lists))."); compile_user_module(&mut wam, " :- module(my_lists, [local_member/2, reverse/2]). @@ -1357,8 +1367,8 @@ reverse(Xs, Ys) :- lists:reverse(Xs, Ys). assert_prolog_success!(&mut wam, "?- my_lists:reverse([a,b,c], [c,b,a])."); compile_user_module(&mut wam, " -:- use_module(library(my_lists), [local_member/2]). :- module(my_lists_2, [local_member/2]). +:- use_module(library(my_lists), [local_member/2]). ".as_bytes()); assert_prolog_success!(&mut wam, "?- my_lists_2:local_member(1, [1,2,3])."); @@ -1371,8 +1381,8 @@ fn test_queries_on_builtins() { let mut wam = Machine::new(); - wam.use_module_in_toplevel(clause_name!("lists")); - wam.use_module_in_toplevel(clause_name!("control")); + submit(&mut wam, ":- use_module(library(lists))."); + submit(&mut wam, ":- use_module(library(control))."); assert_prolog_failure!(&mut wam, "?- atom(X)."); assert_prolog_success!(&mut wam, "?- atom(a)."); @@ -1876,19 +1886,19 @@ fn test_queries_on_string_lists() assert_prolog_success!(&mut wam, "?- X = [a,b,c|\"abc\"].", [["X = [a, b, c, a, b, c]"]]); - submit(&mut wam, "?- set_prolog_flag(double_quotes, atom)."); + assert_prolog_success!(&mut wam, "?- set_prolog_flag(double_quotes, atom)."); assert_prolog_success!(&mut wam, "?- matcher(X, Y).", [["X = [a, b, c | _1]", "Y = _1"]]); assert_prolog_failure!(&mut wam, "?- matcher(\"abcdef\", Y)."); - submit(&mut wam, "?- set_prolog_flag(double_quotes, chars)."); + assert_prolog_success!(&mut wam, "?- set_prolog_flag(double_quotes, chars)."); assert_prolog_success!(&mut wam, "?- X = \"abc\", X = ['a' | Y], set_prolog_flag(double_quotes, atom).", [["X = \"abc\"", "Y = \"bc\""]]); // partial strings. - submit(&mut wam, "?- set_prolog_flag(double_quotes, chars)."); + assert_prolog_success!(&mut wam, "?- set_prolog_flag(double_quotes, chars)."); assert_prolog_failure!(&mut wam, "?- Y = 5, partial_string(\"abc\", Y)."); assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X).", -- 2.54.0