From 7500465b38aaf1afcaf614f7e865ca1a363b41d2 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 10 Mar 2018 15:26:35 -0700 Subject: [PATCH] add atom predicate. --- README.md | 1 + src/main.rs | 1 - src/prolog/ast.rs | 4 + src/prolog/builtins.rs | 7 +- src/prolog/codegen.rs | 17 +- src/prolog/io.rs | 2 + src/prolog/machine/machine_state_impl.rs | 8 + src/prolog/macros.rs | 6 + src/test_utils.rs | 196 --------------------- src/tests.rs | 211 ++++++++++++++++++++++- 10 files changed, 248 insertions(+), 205 deletions(-) delete mode 100644 src/test_utils.rs diff --git a/README.md b/README.md index 9dd97819..7dfb1378 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ The following predicates are built-in to rusty-wam. * `(;)/2` * `append/3` * `arg/3` +* `atom/1` * `atomic/1` * `between/3` * `call/1..63` diff --git a/src/main.rs b/src/main.rs index e08e5301..cde9536e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ extern crate termion; mod prolog; -#[macro_use] mod test_utils; use prolog::ast::*; use prolog::io::*; diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index 45810283..2ab59feb 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -551,6 +551,7 @@ pub enum Term { #[derive(Clone, Copy)] pub enum InlinedClauseType { CompareNumber(CompareNumberQT), + IsAtom, IsAtomic, IsCompound, IsInteger, @@ -565,6 +566,7 @@ impl InlinedClauseType { pub fn name(&self) -> &'static str { match self { &InlinedClauseType::CompareNumber(qt) => qt.name(), + &InlinedClauseType::IsAtom => "atom", &InlinedClauseType::IsAtomic => "atomic", &InlinedClauseType::IsCompound => "compound", &InlinedClauseType::IsInteger => "integer", @@ -584,6 +586,7 @@ impl InlinedClauseType { ("<=", 2) => Some(InlinedClauseType::CompareNumber(CompareNumberQT::LessThanOrEqual)), ("=\\=", 2) => Some(InlinedClauseType::CompareNumber(CompareNumberQT::NotEqual)), ("=:=", 2) => Some(InlinedClauseType::CompareNumber(CompareNumberQT::Equal)), + ("atom", 1) => Some(InlinedClauseType::IsAtom), ("atomic", 1) => Some(InlinedClauseType::IsAtomic), ("compound", 1) => Some(InlinedClauseType::IsCompound), ("integer", 1) => Some(InlinedClauseType::IsInteger), @@ -1203,6 +1206,7 @@ pub enum BuiltInInstruction { InstallInferenceCounter(RegType, RegType, RegType), InstallNewBlock, InternalCallN, + IsAtom(RegType), IsAtomic(RegType), IsCompound(RegType), IsFloat(RegType), diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index 6f4572dd..5da93945 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -620,7 +620,9 @@ fn get_builtins() -> Code { query![put_value!(temp_v!(5), 1)], reset_block!(), fail!(), - compare_execute!() // compare/3, 464. + compare_execute!(), // compare/3, 464. + is_atom!(temp_v!(1)), // atom/1, 465. + proceed!() ] } @@ -733,7 +735,8 @@ pub fn build_code_and_op_dirs() -> (CodeDir, OpDir) code_dir.insert((clause_name!("=@="), 2), (391, builtin.clone())); code_dir.insert((clause_name!("\\=@="), 2), (392, builtin.clone())); code_dir.insert((clause_name!("compare"), 3), (464, builtin.clone())); - + code_dir.insert((clause_name!("atom"), 1), (465, builtin.clone())); + (code_dir, op_dir) } diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index 15299eda..b770a693 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -320,6 +320,19 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator at_1.unwrap_or(interm!(1)), at_2.unwrap_or(interm!(2)))); }, + InlinedClauseType::IsAtom => + match terms[0].as_ref() { + &Term::Constant(_, Constant::Atom(_)) => { + code.push(succeed!()); + }, + &Term::Var(ref vr, ref name) => { + let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code); + code.push(is_atom!(r)); + } + _ => { + code.push(fail!()); + } + }, InlinedClauseType::IsAtomic => match terms[0].as_ref() { &Term::AnonVar | &Term::Clause(..) | &Term::Cons(..) => { @@ -608,12 +621,12 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator vs.populate_restricting_sets(); self.marker.drain_var_data(vs); - + let mut code = Vec::new(); if let &Term::Clause(_, _, ref args, _) = term { self.marker.reset_at_head(args); - + let iter = FactInstruction::iter(term); let mut compiled_fact = self.compile_target(iter, GenContext::Head, false); diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 7fcdfe34..912b605d 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -249,6 +249,8 @@ impl fmt::Display for BuiltInInstruction { write!(f, "unwind_stack"), &BuiltInInstruction::Unify => write!(f, "unify"), + &BuiltInInstruction::IsAtom(r) => + write!(f, "is_atom {}", r), &BuiltInInstruction::IsAtomic(r) => write!(f, "is_atomic {}", r), &BuiltInInstruction::IsCompound(r) => diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index c8d169cd..39d1ad46 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -1350,6 +1350,14 @@ impl MachineState { _ => self.throw_exception(functor!("type_error", 1, [heap_atom!("integer_expected")])) }; }, + &BuiltInInstruction::IsAtom(r) => { + let d = self.store(self.deref(self[r].clone())); + + match d { + Addr::Con(Constant::Atom(_)) => self.p += 1, + _ => self.fail = true + }; + }, &BuiltInInstruction::IsAtomic(r) => { let d = self.store(self.deref(self[r].clone())); diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 01e075f3..d78c7b76 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -167,6 +167,12 @@ macro_rules! retry_me_else { ) } +macro_rules! is_atom { + ($reg:expr) => ( + Line::BuiltIn(BuiltInInstruction::IsAtom($reg)) + ) +} + macro_rules! is_atomic { ($reg:expr) => ( Line::BuiltIn(BuiltInInstruction::IsAtomic($reg)) diff --git a/src/test_utils.rs b/src/test_utils.rs deleted file mode 100644 index f464c072..00000000 --- a/src/test_utils.rs +++ /dev/null @@ -1,196 +0,0 @@ -use prolog::ast::*; -use prolog::heap_print::*; -use prolog::io::*; -use prolog::machine::*; - -use std::collections::HashSet; -use std::mem::swap; - -pub struct TestOutputter { - results: Vec>, - contents: HashSet, - focus: String -} - -impl TestOutputter { - fn cache(&mut self) { - self.begin_new_var(); - - let mut contents = HashSet::new(); - swap(&mut contents, &mut self.contents); - - self.results.push(contents); - } -} - -impl HeapCellValueOutputter for TestOutputter { - type Output = Vec>; - - fn new() -> Self { - TestOutputter { results: vec![], - contents: HashSet::new(), - focus: String::new() } - } - - fn append(&mut self, focus: &str) { - self.focus += focus; - } - - fn begin_new_var(&mut self) { - if !self.focus.is_empty() { - let mut focus = String::new(); - swap(&mut focus, &mut self.focus); - - self.contents.insert(focus); - } - } - - fn result(self) -> Self::Output { - self.results - } - - fn ends_with(&self, s: &str) -> bool { - self.focus.ends_with(s) - } - - fn len(&self) -> usize { - self.focus.len() - } - - fn truncate(&mut self, len: usize) { - self.focus.truncate(len); - } -} - -pub fn collect_test_output(wam: &mut Machine, alloc_locs: AllocVarDict, mut heap_locs: HeapVarDict) - -> Vec> -{ - let mut output = TestOutputter::new(); - - output = wam.heap_view(&heap_locs, output); - output.cache(); - - while let EvalSession::SubsequentQuerySuccess = wam.continue_query(&alloc_locs, &mut heap_locs) - { - output = wam.heap_view(&heap_locs, output); - output.cache(); - } - - output.result() -} - -pub fn collect_test_output_with_limit(wam: &mut Machine, alloc_locs: AllocVarDict, - mut heap_locs: HeapVarDict, limit: usize) - -> Vec> -{ - let mut output = TestOutputter::new(); - - output = wam.heap_view(&heap_locs, output); - output.cache(); - - let mut count = 1; - - if count == limit { - return output.result(); - } - - while let EvalSession::SubsequentQuerySuccess = wam.continue_query(&alloc_locs, &mut heap_locs) - { - output = wam.heap_view(&heap_locs, output); - output.cache(); - - count += 1; - - if count == limit { - break; - } - } - - output.result() -} - -#[allow(dead_code)] -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!("parse error: {:?}", e) - } -} - -#[allow(dead_code)] -pub fn submit_query(wam: &mut Machine, buffer: &str, result: Vec>) -> bool -{ - wam.reset(); - - match parse_code(wam, buffer) { - Ok(tl) => - match compile_packet(wam, tl) { - EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => - result == collect_test_output(wam, alloc_locs, heap_locs), - EvalSession::EntrySuccess => true, - _ => false - }, - Err(e) => panic!("parse error: {:?}", e) - } -} - -#[allow(dead_code)] -pub fn submit_query_with_limit(wam: &mut Machine, buffer: &str, - result: Vec>, limit: usize) - -> bool -{ - wam.reset(); - - match parse_code(wam, buffer) { - Ok(tl) => - match compile_packet(wam, tl) { - EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => - result == collect_test_output_with_limit(wam, alloc_locs, - heap_locs, limit), - EvalSession::EntrySuccess => true, - _ => false - }, - Err(e) => panic!("parse error: {:?}", e) - } -} - -#[allow(unused_macros)] -macro_rules! expand_strs { - ($arr:expr) => ( - $arr.into_iter().map(|s| String::from(*s)).collect() - ) -} - -#[allow(unused_macros)] -macro_rules! assert_prolog_success_with_limit { - ($wam:expr, $buf:expr, [$($res:expr),*], $limit:expr) => ( - assert!(submit_query_with_limit($wam, $buf, vec![$(expand_strs!($res)),*], $limit)) - ) -} - -#[allow(unused_macros)] -macro_rules! assert_prolog_failure { - ($wam: expr, $buf: expr) => ( - assert_eq!(submit($wam, $buf), false) - ) -} - -#[allow(unused_macros)] -macro_rules! assert_prolog_success { - ($wam:expr, $query:expr, [$($res:expr),*]) => ( - assert!(submit_query($wam, $query, vec![$(expand_strs!($res)),*])) - ); - ($wam:expr, $buf:expr) => ( - assert_eq!(submit($wam, $buf), true) - ) -} diff --git a/src/tests.rs b/src/tests.rs index 5b29072e..6640fd1b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,5 +1,199 @@ -use super::*; -use test_utils::*; +use prolog::ast::*; +use prolog::heap_print::*; +use prolog::io::*; +use prolog::machine::*; + +use std::collections::HashSet; +use std::mem::swap; + +pub struct TestOutputter { + results: Vec>, + contents: HashSet, + focus: String +} + +impl TestOutputter { + fn cache(&mut self) { + self.begin_new_var(); + + let mut contents = HashSet::new(); + swap(&mut contents, &mut self.contents); + + self.results.push(contents); + } +} + +impl HeapCellValueOutputter for TestOutputter { + type Output = Vec>; + + fn new() -> Self { + TestOutputter { results: vec![], + contents: HashSet::new(), + focus: String::new() } + } + + fn append(&mut self, focus: &str) { + self.focus += focus; + } + + fn begin_new_var(&mut self) { + if !self.focus.is_empty() { + let mut focus = String::new(); + swap(&mut focus, &mut self.focus); + + self.contents.insert(focus); + } + } + + fn result(self) -> Self::Output { + self.results + } + + fn ends_with(&self, s: &str) -> bool { + self.focus.ends_with(s) + } + + fn len(&self) -> usize { + self.focus.len() + } + + fn truncate(&mut self, len: usize) { + self.focus.truncate(len); + } +} + +pub fn collect_test_output(wam: &mut Machine, alloc_locs: AllocVarDict, mut heap_locs: HeapVarDict) + -> Vec> +{ + let mut output = TestOutputter::new(); + + output = wam.heap_view(&heap_locs, output); + output.cache(); + + while let EvalSession::SubsequentQuerySuccess = wam.continue_query(&alloc_locs, &mut heap_locs) + { + output = wam.heap_view(&heap_locs, output); + output.cache(); + } + + output.result() +} + +pub fn collect_test_output_with_limit(wam: &mut Machine, alloc_locs: AllocVarDict, + mut heap_locs: HeapVarDict, limit: usize) + -> Vec> +{ + let mut output = TestOutputter::new(); + + output = wam.heap_view(&heap_locs, output); + output.cache(); + + let mut count = 1; + + if count == limit { + return output.result(); + } + + while let EvalSession::SubsequentQuerySuccess = wam.continue_query(&alloc_locs, &mut heap_locs) + { + output = wam.heap_view(&heap_locs, output); + output.cache(); + + count += 1; + + if count == limit { + break; + } + } + + output.result() +} + +#[allow(dead_code)] +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!("parse error: {:?}", e) + } +} + +#[allow(dead_code)] +pub fn submit_query(wam: &mut Machine, buffer: &str, result: Vec>) -> bool +{ + wam.reset(); + + match parse_code(wam, buffer) { + Ok(tl) => + match compile_packet(wam, tl) { + EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => + result == collect_test_output(wam, alloc_locs, heap_locs), + EvalSession::EntrySuccess => true, + _ => false + }, + Err(e) => panic!("parse error: {:?}", e) + } +} + +#[allow(dead_code)] +pub fn submit_query_with_limit(wam: &mut Machine, buffer: &str, + result: Vec>, limit: usize) + -> bool +{ + wam.reset(); + + match parse_code(wam, buffer) { + Ok(tl) => + match compile_packet(wam, tl) { + EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => + result == collect_test_output_with_limit(wam, alloc_locs, + heap_locs, limit), + EvalSession::EntrySuccess => true, + _ => false + }, + Err(e) => panic!("parse error: {:?}", e) + } +} + +#[allow(unused_macros)] +macro_rules! expand_strs { + ($arr:expr) => ( + $arr.into_iter().map(|s| String::from(*s)).collect() + ) +} + +#[allow(unused_macros)] +macro_rules! assert_prolog_success_with_limit { + ($wam:expr, $buf:expr, [$($res:expr),*], $limit:expr) => ( + assert!(submit_query_with_limit($wam, $buf, vec![$(expand_strs!($res)),*], $limit)) + ) +} + +#[allow(unused_macros)] +macro_rules! assert_prolog_failure { + ($wam: expr, $buf: expr) => ( + assert_eq!(submit($wam, $buf), false) + ) +} + +#[allow(unused_macros)] +macro_rules! assert_prolog_success { + ($wam:expr, $query:expr, [$($res:expr),*]) => ( + assert!(submit_query($wam, $query, vec![$(expand_strs!($res)),*])) + ); + ($wam:expr, $buf:expr) => ( + assert_eq!(submit($wam, $buf), true) + ) +} #[test] fn test_queries_on_facts() @@ -1172,7 +1366,6 @@ fn test_queries_on_conditionals() assert_prolog_success!(&mut wam, "?- catch(test(a, [a]), type_error(E), true).", [["E = _6"], ["E = _6"]]); - //TODO: write tests for calling ;, ->, to confirm behavior is correct. assert_prolog_success!(&mut wam, "?- f(X), call(->, atomic(X), true).", [["X = a"], ["X = b"]]); } @@ -1182,6 +1375,16 @@ fn test_queries_on_builtins() { let mut wam = Machine::new(); + assert_prolog_failure!(&mut wam, "?- atom(X)."); + assert_prolog_success!(&mut wam, "?- atom(a)."); + assert_prolog_failure!(&mut wam, "?- atom(\"string\")."); + assert_prolog_failure!(&mut wam, "?- atom([])."); + assert_prolog_failure!(&mut wam, "?- atom(1)."); + assert_prolog_failure!(&mut wam, "?- atom(0)."); + assert_prolog_failure!(&mut wam, "?- atom(0.0)."); + assert_prolog_failure!(&mut wam, "?- atom([a,b,c])."); + assert_prolog_failure!(&mut wam, "?- atom(atop(the_trees))."); + assert_prolog_failure!(&mut wam, "?- atomic(X)."); assert_prolog_success!(&mut wam, "?- atomic(a)."); assert_prolog_success!(&mut wam, "?- atomic(\"string\")."); @@ -1570,5 +1773,5 @@ fn test_queries_on_call_with_inference_limit() [["R = !", "X = 6"]]); assert_prolog_success!(&mut wam, "?- call_with_inference_limit(g(X), 1, R), call_with_inference_limit(g(X), 1, R).", [["R = inference_limit_exceeded", "X = _1"]]); - + } -- 2.54.0