From: Mark Thom Date: Sun, 9 Dec 2018 07:48:28 +0000 (-0700) Subject: add support for user:goal_expansion X-Git-Tag: v0.8.110~317 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=a4406f784a67dd6cd49a14d4fd416d2eecdd6e0f;p=scryer-prolog.git add support for user:goal_expansion --- diff --git a/src/prolog/compile.rs b/src/prolog/compile.rs index 8ef35af2..021488af 100644 --- a/src/prolog/compile.rs +++ b/src/prolog/compile.rs @@ -259,8 +259,8 @@ impl ListingCompiler { -> Result<(), SessionError> { match decl { - Declaration::Hook(CompileTimeHook::TermExpansion, clause, queue) => { - let key = (clause_name!("term_expansion"), 2); + Declaration::Hook(hook, clause, queue) => { + let key = (hook.name(), hook.arity()); let preds = code_repo.term_dir.entry(key) .or_insert((Predicate(vec![]), VecDeque::from(vec![]))); @@ -271,8 +271,13 @@ impl ListingCompiler { let mut code = cg.compile_predicate(&(preds.0).0)?; compile_appendix(&mut code, &preds.1, false, flags)?; - - Ok(code_repo.term_expanders = code) + + match hook { + CompileTimeHook::TermExpansion => + Ok(code_repo.term_expanders = code), + CompileTimeHook::GoalExpansion => + Ok(code_repo.goal_expanders = code) + } }, Declaration::NonCountedBacktracking(name, arity) => Ok(self.add_non_counted_bt_flag(name, arity)), diff --git a/src/prolog/heap_print.rs b/src/prolog/heap_print.rs index 7ce5e482..e86a91c7 100644 --- a/src/prolog/heap_print.rs +++ b/src/prolog/heap_print.rs @@ -262,6 +262,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> printer } + #[inline] + pub fn see_all_locs(&mut self) { + for key in self.heap_locs.keys().cloned() { + self.printed_vars.insert(key); + } + } + fn enqueue_op(&mut self, ct: ClauseType, fixity: Fixity) { match fixity { Fixity::Post => { diff --git a/src/prolog/instructions.rs b/src/prolog/instructions.rs index 013684d8..8672652b 100644 --- a/src/prolog/instructions.rs +++ b/src/prolog/instructions.rs @@ -347,12 +347,23 @@ pub enum BuiltInClauseType { #[derive(Clone, Copy)] pub enum CompileTimeHook { + GoalExpansion, TermExpansion } impl CompileTimeHook { pub fn name(self) -> ClauseName { - clause_name!("term_expansion") + match self { + CompileTimeHook::GoalExpansion => clause_name!("goal_expansion"), + CompileTimeHook::TermExpansion => clause_name!("term_expansion") + } + } + + pub fn arity(self) -> usize { + match self { + CompileTimeHook::GoalExpansion => 2, + CompileTimeHook::TermExpansion => 2 + } } } @@ -873,6 +884,7 @@ impl CodePtr { pub enum LocalCodePtr { DirEntry(usize), // offset. TopLevel(usize, usize), // chunk_num, offset. + UserGoalExpansion(usize), UserTermExpansion(usize) } @@ -927,7 +939,8 @@ impl Add for LocalCodePtr { match self { LocalCodePtr::DirEntry(p) => LocalCodePtr::DirEntry(p + rhs), LocalCodePtr::TopLevel(cn, p) => LocalCodePtr::TopLevel(cn, p + rhs), - LocalCodePtr::UserTermExpansion(p) => LocalCodePtr::UserTermExpansion(p + rhs) + LocalCodePtr::UserTermExpansion(p) => LocalCodePtr::UserTermExpansion(p + rhs), + LocalCodePtr::UserGoalExpansion(p) => LocalCodePtr::UserGoalExpansion(p + rhs), } } } @@ -935,7 +948,8 @@ impl Add for LocalCodePtr { impl AddAssign for LocalCodePtr { fn add_assign(&mut self, rhs: usize) { match self { - &mut LocalCodePtr::UserTermExpansion(ref mut p) + &mut LocalCodePtr::UserGoalExpansion(ref mut p) + | &mut LocalCodePtr::UserTermExpansion(ref mut p) | &mut LocalCodePtr::DirEntry(ref mut p) | &mut LocalCodePtr::TopLevel(_, ref mut p) => *p += rhs } diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 5c478c16..2e5233cf 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -629,13 +629,19 @@ pub(crate) trait CallPolicy: Any { } } - fn compile_hook(&mut self, machine_st: &mut MachineState, _: &CompileTimeHook) -> CallResult + fn compile_hook(&mut self, machine_st: &mut MachineState, hook: &CompileTimeHook) -> CallResult { machine_st.cp = LocalCodePtr::TopLevel(0, 0); - machine_st.num_of_args = 2; + machine_st.num_of_args = hook.arity(); machine_st.b0 = machine_st.b; - machine_st.p = CodePtr::Local(LocalCodePtr::UserTermExpansion(0)); + + machine_st.p = match hook { + CompileTimeHook::TermExpansion => + CodePtr::Local(LocalCodePtr::UserTermExpansion(0)), + CompileTimeHook::GoalExpansion => + CodePtr::Local(LocalCodePtr::UserGoalExpansion(0)) + }; Ok(()) } diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 36df5819..8676d910 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -102,6 +102,7 @@ impl IndexStore { pub struct CodeRepo { cached_query: Option, + pub(super) goal_expanders: Code, pub(super) term_expanders: Code, pub(super) code: Code, pub(super) term_dir: TermDir @@ -112,6 +113,7 @@ impl CodeRepo { fn new() -> Self { CodeRepo { cached_query: None, + goal_expanders: Code::new(), term_expanders: Code::new(), code: Code::new(), term_dir: TermDir::new() @@ -124,11 +126,17 @@ impl CodeRepo { &Some(ref query) => query.len(), _ => 0 } - } + } fn lookup_instr<'a>(&'a self, last_call: bool, p: &CodePtr) -> Option> { match p { + &CodePtr::Local(LocalCodePtr::UserGoalExpansion(p)) => + if p < self.goal_expanders.len() { + Some(RefOrOwned::Borrowed(&self.goal_expanders[p])) + } else { + None + }, &CodePtr::Local(LocalCodePtr::UserTermExpansion(p)) => if p < self.term_expanders.len() { Some(RefOrOwned::Borrowed(&self.term_expanders[p])) @@ -190,6 +198,7 @@ impl Index for CodeRepo { } }, LocalCodePtr::DirEntry(p) => &self.code[p], + LocalCodePtr::UserGoalExpansion(p) => &self.goal_expanders[p], LocalCodePtr::UserTermExpansion(p) => &self.term_expanders[p] } } @@ -502,6 +511,8 @@ impl MachineState { CodePtr::Local(LocalCodePtr::DirEntry(p)) if p < code_repo.code.len() => {}, CodePtr::Local(LocalCodePtr::UserTermExpansion(p)) if p < code_repo.term_expanders.len() => {}, CodePtr::Local(LocalCodePtr::UserTermExpansion(_)) => self.fail = true, + CodePtr::Local(LocalCodePtr::UserGoalExpansion(p)) if p < code_repo.goal_expanders.len() => {}, + CodePtr::Local(LocalCodePtr::UserGoalExpansion(_)) => self.fail = true, CodePtr::Local(_) => break, _ => {} }; diff --git a/src/prolog/machine/term_expansion.rs b/src/prolog/machine/term_expansion.rs index 9314d59c..49152cfe 100644 --- a/src/prolog/machine/term_expansion.rs +++ b/src/prolog/machine/term_expansion.rs @@ -5,7 +5,50 @@ use prolog::instructions::HeapCellValue; use prolog::machine::*; use prolog::read::*; +use std::cell::Cell; +use std::collections::VecDeque; use std::io::Read; +use std::iter::Rev; +use std::vec::IntoIter; + +fn unfold_by_str_once(term: &mut Term, s: &str) -> Option<(Term, Term)> +{ + if let &mut Term::Clause(_, ref name, ref mut subterms, _) = term { + if name.as_str() == s && subterms.len() == 2 { + let snd = *subterms.pop().unwrap(); + let fst = *subterms.pop().unwrap(); + + return Some((fst, snd)); + } + } + + None +} + +pub fn unfold_by_str(mut term: Term, s: &str) -> Vec +{ + let mut terms = vec![]; + + while let Some((fst, snd)) = unfold_by_str_once(&mut term, s) { + terms.push(fst); + term = snd; + } + + terms.push(term); + terms +} + +pub fn fold_by_str(terms: I, mut term: Term, sym: ClauseName) -> Term + where I: DoubleEndedIterator +{ + for prec in terms.rev() { + term = Term::Clause(Cell::default(), sym.clone(), + vec![Box::new(prec), Box::new(term)], + None); + } + + term +} pub struct TermStream<'a, R: Read> { stack: Vec, @@ -14,7 +57,7 @@ pub struct TermStream<'a, R: Read> { pub(crate) code_repo: &'a mut CodeRepo, parser: Parser, in_module: bool, - flags: MachineFlags + flags: MachineFlags } impl<'a, R: Read> TermStream<'a, R> { @@ -49,22 +92,29 @@ impl<'a, R: Read> TermStream<'a, R> { Ok(self.stack.is_empty() && self.parser.eof()?) } + fn extract_from_list(&mut self, head: Box, tail: Box) + -> Result>, ParserError> + { + let mut terms = vec![*head]; + let mut tail = *tail; + + while let Term::Cons(_, head, next_tail) = tail { + terms.push(*head); + tail = *next_tail; + } + + if let Term::Constant(_, Constant::EmptyList) = tail { + Ok(terms.into_iter().rev()) + } else { + Err(ParserError::ExpectedTopLevelTerm) + } + } + fn enqueue_term(&mut self, term: Term) -> Result<(), ParserError> { match term { Term::Cons(_, head, tail) => { - let mut terms = vec![*head]; - let mut tail = *tail; - - while let Term::Cons(_, head, next_tail) = tail { - terms.push(*head); - tail = *next_tail; - } - - if let Term::Constant(_, Constant::EmptyList) = tail { - Ok(self.stack.extend(terms.into_iter().rev())) - } else { - Err(ParserError::ExpectedTopLevelTerm) - } + let iter = self.extract_from_list(head, tail)?; + Ok(self.stack.extend(iter)) }, Term::Clause(..) | Term::Constant(_, Constant::Atom(..)) => Ok(self.stack.push(term)), @@ -72,50 +122,113 @@ impl<'a, R: Read> TermStream<'a, R> { } } - fn parse_expansion_output(&mut self, term_string: &str, op_dir: &OpDir) -> Result { - let mut parser = Parser::new(term_string.trim().as_bytes(), self.indices.atom_tbl.clone(), self.flags); - parser.read_term(composite_op!(self.in_module, - &self.indices.op_dir, - op_dir)) + fn parse_expansion_output(&mut self, term_string: &str, op_dir: &OpDir) + -> Result + { + let mut parser = Parser::new(term_string.trim().as_bytes(), self.indices.atom_tbl.clone(), + self.flags); + parser.read_term(composite_op!(self.in_module, &self.indices.op_dir, op_dir)) } - + pub fn read_term(&mut self, machine_st: &mut MachineState, op_dir: &OpDir) -> Result { loop { while let Some(term) = self.stack.pop() { - match machine_st.try_expand_term(self.indices, self.policies, self.code_repo, &term) + match machine_st.try_expand_term(self.indices, self.policies, self.code_repo, + &term, CompileTimeHook::TermExpansion) { Some(term_string) => { let term = self.parse_expansion_output(term_string.as_str(), op_dir)?; self.enqueue_term(term)? }, - None => return Ok(term) + None => { + let term = self.run_goal_expanders(machine_st, op_dir, term)?; + return Ok(term); + } }; } self.parser.reset(); - let term = self.parser.read_term(composite_op!(self.in_module, - &self.indices.op_dir, + let term = self.parser.read_term(composite_op!(self.in_module, &self.indices.op_dir, op_dir))?; self.stack.push(term); } } + + fn run_goal_expanders(&mut self, machine_st: &mut MachineState, op_dir: &OpDir, term: Term) + -> Result + { + match term { + Term::Clause(cell, name, mut terms, arity) => { + let mut new_terms = { + let old_terms = if name.as_str() == ":-" && terms.len() == 2 { + let comma_term = *terms.pop().unwrap(); + unfold_by_str(comma_term, ",") + } else if name.as_str() == "?-" && terms.len() == 1 { + let comma_term = *terms.pop().unwrap(); + unfold_by_str(comma_term, ",") + } else { + return Ok(Term::Clause(cell, name, terms, arity)); + }; + + self.expand_goals(machine_st, op_dir, VecDeque::from(old_terms))? + }; + + let initial_term = new_terms.pop().unwrap(); + + terms.push(Box::new(fold_by_str(new_terms.into_iter(), initial_term, + clause_name!(",")))); + Ok(Term::Clause(cell, name, terms, None)) + }, + _ => + Ok(term) + } + } + + fn expand_goals(&mut self, machine_st: &mut MachineState, op_dir: &OpDir, + mut terms: VecDeque) + -> Result, ParserError> + { + let mut results = vec![]; + + while let Some(term) = terms.pop_front() { + match machine_st.try_expand_term(self.indices, self.policies, self.code_repo, + &term, CompileTimeHook::GoalExpansion) + { + Some(term_string) => { + let term = self.parse_expansion_output(term_string.as_str(), op_dir)?; + + match term { + Term::Cons(_, head, tail) => + for term in self.extract_from_list(head, tail)? { + terms.push_front(term); + }, + term => + terms.push_front(term) + }; + }, + None => results.push(term) + } + } + + Ok(results) + } } impl MachineState { fn try_expand_term(&mut self, indices: &mut IndexStore, policies: &mut MachinePolicies, - code_repo: &mut CodeRepo, term: &Term) + code_repo: &mut CodeRepo, term: &Term, hook: CompileTimeHook) -> Option { - let term_h = write_term_to_heap(term, self); + let (term_h, var_dict) = write_term_to_heap(term, self); let h = self.heap.h; self[temp_v!(1)] = Addr::HeapCell(term_h); self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self[temp_v!(2)] = Addr::HeapCell(h); - let code = vec![call_clause!(ClauseType::Hook(CompileTimeHook::TermExpansion), 2, 0, true)]; + let code = vec![call_clause!(ClauseType::Hook(hook), 2, 0, true)]; code_repo.cached_query = Some(code); self.run_query(indices, policies, code_repo, &AllocVarDict::new(), &mut HeapVarDict::new()); @@ -125,11 +238,14 @@ impl MachineState { None } else { let mut output = { - let mut printer = HCPrinter::new(&self, PrinterOutputter::new()); - + let output = PrinterOutputter::new(); + let mut printer = HCPrinter::from_heap_locs(&self, output, &var_dict); + printer.quoted = true; printer.numbervars = true; - + + printer.see_all_locs(); + printer.print(Addr::HeapCell(h)) }; diff --git a/src/prolog/read.rs b/src/prolog/read.rs index de6835d2..cde08346 100644 --- a/src/prolog/read.rs +++ b/src/prolog/read.rs @@ -62,7 +62,7 @@ impl MachineState { let mut parser = Parser::new(inner, atom_tbl, self.flags); let term = parser.read_term(composite_op!(op_dir))?; - Ok(write_term_to_heap(&term, self)) + Ok(write_term_to_heap(&term, self).0) } } @@ -82,7 +82,7 @@ fn modify_head_of_queue(machine_st: &mut MachineState, queue: &mut SubtermDeque, } } -pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> usize { +pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> (usize, HeapVarDict) { let h = machine_st.heap.h; let mut queue = SubtermDeque::new(); @@ -151,5 +151,5 @@ pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> modify_head_of_queue(machine_st, &mut queue, term, h); } - h + (h, var_dict) } diff --git a/src/prolog/toplevel.rs b/src/prolog/toplevel.rs index 923fece1..64273263 100644 --- a/src/prolog/toplevel.rs +++ b/src/prolog/toplevel.rs @@ -71,18 +71,25 @@ impl<'a, 'b> CompositeIndices<'a, 'b> } #[inline] -fn is_term_expansion(name: &ClauseName, terms: &Vec>) -> bool { +fn get_compile_time_hook(name: &str, arity: usize) -> Option { + match (name, arity) { + ("term_expansion", 2) => Some(CompileTimeHook::TermExpansion), + ("goal_expansion", 2) => Some(CompileTimeHook::GoalExpansion), + _ => None + } +} + +#[inline] +fn is_compile_time_hook(name: &ClauseName, terms: &Vec>) -> Option { if name.as_str() == ":-" { if let Some(ref term) = terms.first() { if let &Term::Clause(_, ref name, ref terms, None) = term.as_ref() { - return (name.as_str(), terms.len()) == ("term_expansion", 2); + return get_compile_time_hook(name.as_str(), terms.len()); } } - } else if name.as_str() == "term_expansion" { - return terms.len() == 2; } - - false + + get_compile_time_hook(name.as_str(), terms.len()) } type CompileTimeHookCompileInfo = (CompileTimeHook, PredicateClause, VecDeque); @@ -296,44 +303,6 @@ fn append_preds(preds: &mut Vec) -> Predicate { Predicate(mem::replace(preds, vec![])) } -fn unfold_by_str_once(term: &mut Term, s: &str) -> Option<(Term, Term)> -{ - if let &mut Term::Clause(_, ref name, ref mut subterms, _) = term { - if name.as_str() == s && subterms.len() == 2 { - let snd = *subterms.pop().unwrap(); - let fst = *subterms.pop().unwrap(); - - return Some((fst, snd)); - } - } - - None -} - -fn unfold_by_str(mut term: Term, s: &str) -> Vec -{ - let mut terms = vec![]; - - while let Some((fst, snd)) = unfold_by_str_once(&mut term, s) { - terms.push(fst); - term = snd; - } - - terms.push(term); - terms -} - -fn fold_by_str(mut terms: Vec, mut term: Term, sym: ClauseName) -> Term -{ - while let Some(prec) = terms.pop() { - term = Term::Clause(Cell::default(), sym.clone(), - vec![Box::new(prec), Box::new(term)], - None); - } - - term -} - fn mark_cut_variables_as(terms: &mut Vec, name: ClauseName) { for term in terms.iter_mut() { match term { @@ -445,7 +414,7 @@ impl RelationWorker { cut_var_found = mark_cut_variables(&mut subterms); let term = subterms.pop().unwrap(); - fold_by_str(subterms, term, clause_name!(",")) + fold_by_str(subterms.into_iter(), term, clause_name!(",")) }).collect(); if cut_var_found { @@ -480,7 +449,7 @@ impl RelationWorker { let body_term = Term::Clause(Cell::default(), comma_sym.clone(), vec![front_term, back_term], None); - self.fabricate_rule(fold_by_str(prec_seq, body_term, comma_sym)) + self.fabricate_rule(fold_by_str(prec_seq.into_iter(), body_term, comma_sym)) } fn to_query_term(&mut self, indices: &mut CompositeIndices, term: Term) -> Result @@ -622,22 +591,19 @@ impl RelationWorker { Ok(query_terms) } - fn setup_hook(&mut self, indices: &mut CompositeIndices, term: Term) + fn setup_hook(&mut self, hook: CompileTimeHook, indices: &mut CompositeIndices, term: Term) -> Result { match term { Term::Clause(r, name, terms, _) => - if name.as_str() == "term_expansion" && terms.len() == 2 { + if name == hook.name() && terms.len() == hook.arity() { let term = setup_fact(Term::Clause(r, name, terms, None))?; - - Ok((CompileTimeHook::TermExpansion, PredicateClause::Fact(term), - VecDeque::from(vec![]))) + Ok((hook, PredicateClause::Fact(term), VecDeque::from(vec![]))) } else if name.as_str() == ":-" { let rule = self.setup_rule(indices, terms, true)?; let results_queue = self.parse_queue(indices)?; - - Ok((CompileTimeHook::TermExpansion, PredicateClause::Rule(rule), - results_queue)) + + Ok((hook, PredicateClause::Rule(rule), results_queue)) } else { Err(ParserError::InvalidHook) }, @@ -668,9 +634,9 @@ impl RelationWorker { { match term { Term::Clause(r, name, mut terms, fixity) => - if is_term_expansion(&name, &terms) { + if let Some(hook) = is_compile_time_hook(&name, &terms) { let term = Term::Clause(r, name, terms, fixity); - let (hook, clause, queue) = self.setup_hook(indices, term)?; + let (hook, clause, queue) = self.setup_hook(hook, indices, term)?; Ok(TopLevel::Declaration(Declaration::Hook(hook, clause, queue))) } else if name.as_str() == "?-" { @@ -750,12 +716,9 @@ pub struct TopLevelBatchWorker<'a, R: Read> { } impl<'a, R: Read> TopLevelBatchWorker<'a, R> { - pub fn new(inner: R, - atom_tbl: TabledData, - flags: MachineFlags, - indices: &'a mut IndexStore, - policies: &'a mut MachinePolicies, - code_repo: &'a mut CodeRepo) + pub fn new(inner: R, atom_tbl: TabledData, + flags: MachineFlags, indices: &'a mut IndexStore, + policies: &'a mut MachinePolicies, code_repo: &'a mut CodeRepo) -> Self { let term_stream = TermStream::new(inner, atom_tbl, flags,