From 86a2c5e2ed86c2dc47b01168dd865b1147e2a64c Mon Sep 17 00:00:00 2001 From: bakaq Date: Wed, 14 Aug 2024 18:39:59 -0300 Subject: [PATCH] Add run_query_iter() --- src/machine/lib_machine.rs | 173 ++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-) diff --git a/src/machine/lib_machine.rs b/src/machine/lib_machine.rs index 8480950b..f068c908 100644 --- a/src/machine/lib_machine.rs +++ b/src/machine/lib_machine.rs @@ -8,7 +8,7 @@ use crate::machine::mock_wam::CompositeOpDir; use crate::machine::{BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS}; use crate::parser::ast::{Var, VarPtr}; use crate::parser::parser::{Parser, Tokens}; -use crate::read::write_term_to_heap; +use crate::read::{write_term_to_heap, TermWriteResult}; use indexmap::IndexMap; use super::{ @@ -16,6 +16,124 @@ use super::{ QueryResolution, QueryResolutionLine, QueryResult, Value, }; +pub struct QueryState<'a> { + machine: &'a mut Machine, + term: TermWriteResult, + stub_b: usize, + var_names: IndexMap, + called: bool, +} + +impl Drop for QueryState<'_> { + fn drop(&mut self) { + // This may be wrong if the iterator is not fully consumend, but from testing it seems + // fine. + self.machine.trust_me(); + } +} + +impl Iterator for QueryState<'_> { + type Item = Result; + + fn next(&mut self) -> Option { + let var_names = &self.var_names; + let term_write_result = &self.term; + let machine = &mut self.machine; + + // No more choicepoints, end iteration + if self.called && machine.machine_st.b <= self.stub_b { + return None; + } + + machine.dispatch_loop(); + + self.called = true; + + if !machine.machine_st.ball.stub.is_empty() { + // NOTE: this means an exception was thrown, at which + // point we backtracked to the stub choice point. + // this should halt the search for solutions as it + // does in the Scryer top-level. the exception term is + // contained in self.machine_st.ball. + let error_string = self + .machine + .machine_st + .ball + .stub + .iter() + .filter(|h| { + matches!( + h.get_tag(), + HeapCellValueTag::Atom | HeapCellValueTag::Fixnum + ) + }) + .map(|h| match h.get_tag() { + HeapCellValueTag::Atom => { + let (name, _) = cell_as_atom_cell!(h).get_name_and_arity(); + name.as_str().to_string() + } + HeapCellValueTag::Fixnum => h.get_value().clone().to_string(), + _ => unreachable!(), + }) + .collect::>() + .join(" "); + + return Some(Err(error_string)); + } + + if machine.machine_st.p == LIB_QUERY_SUCCESS { + if term_write_result.var_dict.is_empty() { + self.machine.machine_st.backtrack(); + return Some(Ok(QueryResolutionLine::True)); + } + } else if machine.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC { + return Some(Ok(QueryResolutionLine::False)); + } + + let mut bindings: BTreeMap = BTreeMap::new(); + + for (var_key, term_to_be_printed) in &term_write_result.var_dict { + if var_key.to_string().starts_with('_') { + continue; + } + let mut printer = HCPrinter::new( + &mut machine.machine_st.heap, + Arc::clone(&machine.machine_st.atom_tbl), + &mut machine.machine_st.stack, + &machine.indices.op_dir, + PrinterOutputter::new(), + *term_to_be_printed, + ); + + printer.ignore_ops = false; + printer.numbervars = true; + printer.quoted = true; + printer.max_depth = 1000; // NOTE: set this to 0 for unbounded depth + printer.double_quotes = true; + printer.var_names.clone_from(var_names); + + let outputter = printer.print(); + + let output: String = outputter.result(); + + if var_key.to_string() != output { + bindings.insert( + var_key.to_string(), + Value::try_from(output).expect("Couldn't convert Houtput to Value"), + ); + } + } + + // NOTE: there are outstanding choicepoints, backtrack + // through them for further solutions. if + // self.machine_st.b == stub_b we've backtracked to the stub + // choice point, so we should break. + self.machine.machine_st.backtrack(); + + Some(Ok(QueryResolutionLine::Match(bindings))) + } +} + impl Machine { pub fn new_lib() -> Self { Machine::new(MachineConfig::in_memory()) @@ -228,6 +346,59 @@ impl Machine { Ok(QueryResolution::from(matches)) } + + pub fn run_query_iter(&mut self, query: String) -> QueryState { + let mut parser = Parser::new( + Stream::from_owned_string(query, &mut self.machine_st.arena), + &mut self.machine_st, + ); + let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); + let term = parser + .read_term(&op_dir, Tokens::Default) + .expect("Failed to parse query"); + + self.allocate_stub_choice_point(); + + // Write parsed term to heap + let term_write_result = + write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl) + .expect("couldn't write term to heap"); + + let var_names: IndexMap<_, _> = term_write_result + .var_dict + .iter() + .map(|(var_key, cell)| match var_key { + // NOTE: not the intention behind Var::InSitu here but + // we can hijack it to store anonymous variables + // without creating problems. + VarKey::AnonVar(h) => (*cell, VarPtr::from(Var::InSitu(*h))), + VarKey::VarPtr(var_ptr) => (*cell, var_ptr.clone()), + }) + .collect(); + + // Write term to heap + self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc]; + + self.machine_st.cp = LIB_QUERY_SUCCESS; // BREAK_FROM_DISPATCH_LOOP_LOC; + let call_index_p = self + .indices + .code_dir + .get(&(atom!("call"), 1)) + .expect("couldn't get code index") + .local() + .unwrap(); + + self.machine_st.execute_at_index(1, call_index_p); + + let stub_b = self.machine_st.b; + QueryState { + machine: self, + term: term_write_result, + stub_b, + var_names, + called: false, + } + } } #[cfg(test)] -- 2.54.0