use prolog::allocator::*; use prolog::arithmetic::*; use prolog::ast::*; use prolog::fixtures::*; use prolog::indexing::*; use prolog::iterators::*; use prolog::targets::*; use std::collections::HashMap; use std::vec::Vec; pub struct CodeGenerator<'a, TermMarker> { marker: TermMarker, var_count: HashMap<&'a Var, usize> } pub enum EvalSession<'a> { OpIsInfixAndPostFix, NamelessEntry, ParserError(ParserError), ImpermissibleEntry(String), EntrySuccess, InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>), QueryFailure, QueryFailureWithException(String), SubsequentQuerySuccess, } pub struct ConjunctInfo<'a> { pub perm_vs: VariableFixtures<'a>, pub num_of_chunks: usize, pub has_deep_cut: bool } impl<'a> ConjunctInfo<'a> { fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool) -> Self { ConjunctInfo { perm_vs, num_of_chunks, has_deep_cut } } fn allocates(&self) -> bool { self.perm_vs.size() > 0 || self.num_of_chunks > 1 || self.has_deep_cut } fn perm_vars(&self) -> usize { self.perm_vs.size() + self.perm_var_offset() } fn perm_var_offset(&self) -> usize { self.has_deep_cut as usize } } impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> { pub fn new() -> Self { CodeGenerator { marker: Allocator::new(), var_count: HashMap::new() } } pub fn take_vars(self) -> AllocVarDict<'a> { self.marker.take_bindings() } fn update_var_count(&mut self, iter: Iter) where Iter: Iterator> { for term in iter { if let TermRef::Var(_, _, var) = term { let entry = self.var_count.entry(var).or_insert(0); *entry += 1; } } } fn get_var_count(&self, var: &'a Var) -> usize { *self.var_count.get(var).unwrap() } fn add_or_increment_void_instr(target: &mut Vec) where Target: CompilationTarget<'a> { if let Some(ref mut instr) = target.last_mut() { if Target::is_void_instr(&*instr) { Target::incr_void_instr(instr); return; } } target.push(Target::to_void(1)); } fn subterm_to_instr(&mut self, subterm: &'a Term, term_loc: GenContext, is_exposed: bool, target: &mut Vec) where Target: CompilationTarget<'a> { match subterm { &Term::AnonVar if is_exposed => self.marker.mark_anon_var(Level::Deep, target), &Term::AnonVar => Self::add_or_increment_void_instr(target), &Term::Cons(ref cell, _, _) | &Term::Clause(ref cell, _, _) => { self.marker.mark_non_var(Level::Deep, term_loc, cell, target); target.push(Target::clause_arg_to_instr(cell.get())); }, &Term::Constant(_, ref constant) => target.push(Target::constant_subterm(constant.clone())), &Term::Var(ref cell, ref var) => if is_exposed || self.get_var_count(var) > 1 { self.marker.mark_var(var, Level::Deep, cell, term_loc, target); } else { Self::add_or_increment_void_instr(target); } }; } fn compile_clause(&mut self, ct: ClauseType<'a>, term_loc: GenContext, is_exposed: bool, terms: &'a Vec>, target: &mut Vec) where Target: CompilationTarget<'a> { match ct { ClauseType::Deep(lvl, cell, atom) => { self.marker.mark_non_var(lvl, term_loc, cell, target); target.push(Target::to_structure(lvl, atom.clone(), terms.len(), cell.get())); for subterm in terms { self.subterm_to_instr(subterm.as_ref(), term_loc, is_exposed, target); } }, _ => {} } } fn compile_target(&mut self, iter: Iter, term_loc: GenContext, is_exposed: bool) -> Vec where Target: CompilationTarget<'a>, Iter: Iterator> { let mut target = Vec::new(); for term in iter { match term { TermRef::Clause(ct, terms) => self.compile_clause(ct, term_loc, is_exposed, terms, &mut target), TermRef::Cons(lvl, cell, head, tail) => { self.marker.mark_non_var(lvl, term_loc, cell, &mut target); target.push(Target::to_list(lvl, cell.get())); self.subterm_to_instr(head, term_loc, is_exposed, &mut target); self.subterm_to_instr(tail, term_loc, is_exposed, &mut target); }, TermRef::Constant(lvl @ Level::Shallow, cell, constant) => { self.marker.mark_non_var(lvl, term_loc, cell, &mut target); target.push(Target::to_constant(lvl, constant.clone(), cell.get())); }, TermRef::AnonVar(lvl @ Level::Shallow) => if let GenContext::Head = term_loc { self.marker.advance_arg(); } else { self.marker.mark_anon_var(lvl, &mut target); }, TermRef::Var(lvl @ Level::Shallow, ref cell, ref var) => self.marker.mark_var(var, lvl, cell, term_loc, &mut target), _ => {} }; } target } fn collect_var_data(&mut self, mut iter: ChunkedIterator<'a>) -> ConjunctInfo<'a> { let mut vs = VariableFixtures::new(); let at_rule_head = iter.at_rule_head(); while let Some((chunk_num, last_term_arity, terms)) = iter.next() { for (i, term_or_cut_ref) in terms.iter().enumerate() { let term_loc = if chunk_num == 0 && i == 0 && at_rule_head { GenContext::Head } else if i < terms.len() - 1 { GenContext::Mid(chunk_num) } else { GenContext::Last(chunk_num) }; self.update_var_count(term_or_cut_ref.post_order_iter()); vs.mark_vars_in_chunk(term_or_cut_ref.post_order_iter(), last_term_arity, chunk_num, term_loc); } } let num_of_chunks = iter.chunk_num(); let has_deep_cut = iter.encountered_deep_cut(); vs.populate_restricting_sets(); vs.set_perm_vals(has_deep_cut); let vs = self.marker.drain_var_data(vs); ConjunctInfo::new(vs, num_of_chunks, has_deep_cut) } fn add_conditional_call(compiled_query: &mut Code, qt: QueryTermRef, pvs: usize) { match qt { QueryTermRef::CallN(terms) => { let call = ControlInstruction::CallN(terms.len()); compiled_query.push(Line::Control(call)); }, QueryTermRef::Catch(_) => compiled_query.push(Line::Control(ControlInstruction::CatchCall)), QueryTermRef::IsAtomic(_) => compiled_query.push(proceed!()), QueryTermRef::IsVar(_) => compiled_query.push(proceed!()), QueryTermRef::Term(&Term::Constant(_, Constant::Atom(ref atom))) => { let call = ControlInstruction::Call(atom.clone(), 0, pvs); compiled_query.push(Line::Control(call)); }, QueryTermRef::Term(&Term::Clause(_, ref atom, ref terms)) => { let call = ControlInstruction::Call(atom.clone(), terms.len(), pvs); compiled_query.push(Line::Control(call)); }, QueryTermRef::Throw(_) => compiled_query.push(Line::Control(ControlInstruction::ThrowCall)), _ => {} } } fn lco(code: &mut Code, toc: QueryTermRef<'a>) -> usize { let last_arity = toc.arity(); let mut dealloc_index = code.len() - 1; match toc { QueryTermRef::Term(&Term::Clause(_, ref name, _)) | QueryTermRef::Term(&Term::Constant(_, Constant::Atom(ref name))) => if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() { *ctrl = ControlInstruction::Execute(name.clone(), last_arity); }, QueryTermRef::CallN(terms) => if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() { *ctrl = ControlInstruction::ExecuteN(terms.len()); }, QueryTermRef::Catch(_) => if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() { *ctrl = ControlInstruction::CatchExecute; }, QueryTermRef::Cut => {}, QueryTermRef::Throw(_) => if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() { *ctrl = ControlInstruction::ThrowExecute; }, QueryTermRef::Is(_) => if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() { *ctrl = ControlInstruction::UnifyExecute; }, QueryTermRef::IsAtomic(_) | QueryTermRef::IsVar(_) => { dealloc_index = code.len(); code.push(proceed!()); }, _ => dealloc_index = code.len() }; dealloc_index } fn compile_seq(&mut self, iter: ChunkedIterator<'a>, conjunct_info: &ConjunctInfo<'a>, code: &mut Code, is_exposed: bool) -> Result<(), ParserError> { for (chunk_num, _, terms) in iter { for (i, term) in terms.iter().enumerate() { let term_loc = if i + 1 < terms.len() { GenContext::Mid(chunk_num) } else { GenContext::Last(chunk_num) }; match term { &QueryTermRef::Cut if i + 1 < terms.len() => { code.push(if chunk_num == 0 { Line::Cut(CutInstruction::NeckCut(Terminal::Non)) } else { Line::Cut(CutInstruction::Cut(Terminal::Non)) }); }, &QueryTermRef::Cut => { code.push(if chunk_num == 0 { Line::Cut(CutInstruction::NeckCut(Terminal::Terminal)) } else { Line::Cut(CutInstruction::Cut(Terminal::Terminal)) }); }, &QueryTermRef::Is(terms) => { let mut arith_code = { let mut evaluator = ArithmeticEvaluator::new(self.marker.bindings()); evaluator.eval(terms[1].as_ref())? }; code.append(&mut arith_code); match terms[0].as_ref() { &Term::Var(ref vr, ref name) => { let mut target = Vec::new(); self.marker.advance(term_loc, *term); self.marker.mark_var(name, Level::Shallow, vr, term_loc, &mut target); code.push(Line::Query(target)); code.push(unify_call!()); }, &Term::Constant(_, Constant::Float(fl)) => { code.push(query![put_constant!(Level::Shallow, Constant::Float(fl), temp_v!(1))]); code.push(unify_call!()); }, &Term::Constant(_, Constant::Integer(ref bi)) => { let bi = bi.clone(); code.push(query![put_constant!(Level::Shallow, Constant::Integer(bi), temp_v!(1))]); code.push(unify_call!()); }, _ => { return Err(ParserError::from(ArithmeticError::InvalidTerm)); } } }, &QueryTermRef::IsAtomic(inner_term) => match inner_term { &Term::AnonVar | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => { code.push(fail!()); }, &Term::Constant(_, _) => { code.push(succeed!()); }, &Term::Var(ref vr, ref name) => match self.marker.bindings().get(name) { Some(&VarData::Temp(_, t, _)) if t != 0 => code.push(is_atomic!(RegType::Temp(t))), Some(&VarData::Perm(p)) if p != 0 => code.push(is_atomic!(RegType::Perm(p))), _ => { let mut target = Vec::new(); // reset self.marker.arg_c to 1. self.marker.advance(term_loc, *term); self.marker.mark_var(name, Level::Shallow, vr, term_loc, &mut target); code.push(Line::Query(target)); code.push(is_atomic!(vr.get().norm())); } }, }, &QueryTermRef::IsVar(inner_term) => match inner_term { &Term::Constant(_, _) | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => { code.push(fail!()); }, &Term::AnonVar => { code.push(succeed!()); }, &Term::Var(ref vr, ref name) => match self.marker.bindings().get(name) { Some(&VarData::Temp(_, t, _)) if t != 0 => code.push(is_var!(RegType::Temp(t))), Some(&VarData::Perm(p)) if p != 0 => code.push(is_var!(RegType::Perm(p))), _ => { let mut target = Vec::new(); // reset self.marker.arg_c to 1. self.marker.advance(term_loc, *term); self.marker.mark_var(name, Level::Shallow, vr, term_loc, &mut target); code.push(Line::Query(target)); code.push(is_var!(vr.get().norm())); } } }, _ if chunk_num == 0 => { self.marker.advance(GenContext::Head, *term); let iter = term.post_order_iter(); code.push(Line::Query(self.compile_target(iter, term_loc, is_exposed))); Self::add_conditional_call(code, *term, conjunct_info.perm_vars()); }, _ => { let num_vars = conjunct_info.perm_vs.vars_above_threshold(i + 1); self.compile_query_line(*term, term_loc, code, num_vars, is_exposed); }, }; self.marker.reset_contents(); } } Ok(()) } fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code) { if conjunct_info.allocates() { let perm_vars = conjunct_info.perm_vars(); body.push(Line::Control(ControlInstruction::Allocate(perm_vars))); if conjunct_info.has_deep_cut { body.push(Line::Cut(CutInstruction::GetLevel)); } } } fn compile_cleanup(body: &mut Code, conjunct_info: &ConjunctInfo, toc: QueryTermRef<'a>) { let dealloc_index = Self::lco(body, toc); if conjunct_info.allocates() { body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate)); } } pub fn compile_rule<'b: 'a>(&mut self, rule: &'b Rule) -> Result { let iter = ChunkedIterator::from_rule(rule); let conjunct_info = self.collect_var_data(iter); let &Rule { head: (ref p0, ref p1), ref clauses } = rule; let mut code = Vec::new(); self.marker.advance(GenContext::Head, QueryTermRef::Term(p0)); self.compile_seq_prelude(&conjunct_info, &mut code); if p0.is_clause() { let iter = FactInstruction::iter(p0); code.push(Line::Fact(self.compile_target(iter, GenContext::Head, false))); } let iter = ChunkedIterator::from_rule_body(p1, clauses); try!(self.compile_seq(iter, &conjunct_info, &mut code, false)); if conjunct_info.allocates() { let index = if let &Line::Control(_) = code.last().unwrap() { code.len() - 2 } else { code.len() - 1 }; if let &mut Line::Query(ref mut query) = &mut code[index] { conjunct_info.perm_vs.mark_unsafe_vars_in_rule(p0, query); } } Self::compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1).to_ref()); Ok(code) } fn mark_unsafe_fact_vars(&self, fact: &mut CompiledFact) { let mut unsafe_vars = HashMap::new(); for var_status in self.marker.bindings().values() { unsafe_vars.insert(var_status.as_reg_type(), false); } for fact_instr in fact.iter_mut() { match fact_instr { &mut FactInstruction::UnifyValue(reg) => if let Some(found) = unsafe_vars.get_mut(®) { if !*found { *found = true; *fact_instr = FactInstruction::UnifyLocalValue(reg); } }, &mut FactInstruction::UnifyVariable(reg) => { if let Some(found) = unsafe_vars.get_mut(®) { *found = true; } }, _ => {} }; } } pub fn compile_fact<'b: 'a>(&mut self, term: &'b Term) -> Code { let iter = ChunkedIterator::from_fact(term); self.collect_var_data(iter); self.marker.advance(GenContext::Head, QueryTermRef::Term(term)); let mut code = Vec::new(); if term.is_clause() { let iter = FactInstruction::iter(term); let mut compiled_fact = self.compile_target(iter, GenContext::Head, false); self.mark_unsafe_fact_vars(&mut compiled_fact); code.push(Line::Fact(compiled_fact)); } code.push(proceed!()); code } fn compile_query_line(&mut self, term: QueryTermRef<'a>, term_loc: GenContext, code: &mut Code, index: usize, is_exposed: bool) { self.marker.advance(term_loc, term); let iter = term.post_order_iter(); let compiled_query = Line::Query(self.compile_target(iter, term_loc, is_exposed)); code.push(compiled_query); Self::add_conditional_call(code, term, index); } pub fn compile_query(&mut self, query: &'a Vec) -> Result { let iter = ChunkedIterator::from_term_sequence(query); let conjunct_info = self.collect_var_data(iter); let mut code = Vec::new(); self.compile_seq_prelude(&conjunct_info, &mut code); let iter = ChunkedIterator::from_term_sequence(query); try!(self.compile_seq(iter, &conjunct_info, &mut code, true)); if conjunct_info.allocates() { let index = if let &Line::Control(_) = code.last().unwrap() { code.len() - 2 } else { code.len() - 1 }; if let &mut Line::Query(ref mut query) = &mut code[index] { conjunct_info.perm_vs.mark_unsafe_vars_in_query(query); } } Self::compile_cleanup(&mut code, &conjunct_info, query.last().unwrap().to_ref()); Ok(code) } fn split_predicate(clauses: &Vec) -> Vec<(usize, usize)> { let mut subseqs = Vec::new(); let mut left_index = 0; for (right_index, clause) in clauses.iter().enumerate() { match clause.first_arg() { Some(&Term::Var(_, _)) | Some(&Term::AnonVar) => { if left_index < right_index { subseqs.push((left_index, right_index)); } subseqs.push((right_index, right_index + 1)); left_index = right_index + 1; }, _ => {} } } if left_index < clauses.len() { subseqs.push((left_index, clauses.len())); } subseqs } fn compile_pred_subseq<'b: 'a>(&mut self, clauses: &'b [PredicateClause]) -> Result { let mut code_body = Vec::new(); let mut code_offsets = CodeOffsets::new(); let num_clauses = clauses.len(); for (i, clause) in clauses.iter().enumerate() { self.marker.reset(); let mut clause_code = match clause { &PredicateClause::Fact(ref fact) => self.compile_fact(fact), &PredicateClause::Rule(ref rule) => try!(self.compile_rule(rule)) }; if num_clauses > 1 { let choice = match i { 0 => ChoiceInstruction::TryMeElse(clause_code.len() + 1), _ if i == num_clauses - 1 => ChoiceInstruction::TrustMe, _ => ChoiceInstruction::RetryMeElse(clause_code.len() + 1) }; code_body.push(Line::Choice(choice)); } clause.first_arg().map(|arg| { let index = code_body.len(); code_offsets.index_term(arg, index); }); code_body.append(&mut clause_code); } let mut code = Vec::new(); code_offsets.add_indices(&mut code, code_body); Ok(code) } pub fn compile_predicate<'b: 'a>(&mut self, clauses: &'b Vec) -> Result { let mut code = Vec::new(); let split_pred = Self::split_predicate(&clauses); let multi_seq = split_pred.len() > 1; for (l, r) in split_pred { let mut code_segment = try!(self.compile_pred_subseq(&clauses[l .. r])); if multi_seq { let choice = match l { 0 => ChoiceInstruction::TryMeElse(code_segment.len() + 1), _ if r == clauses.len() => ChoiceInstruction::TrustMe, _ => ChoiceInstruction::RetryMeElse(code_segment.len() + 1) }; code.push(Line::Choice(choice)); } code.append(&mut code_segment); } Ok(code) } }