Skip to content

Commit 9227b91

Browse files
committed
Add support for using Term as query in run_query()
1 parent 5a869e8 commit 9227b91

File tree

2 files changed

+142
-13
lines changed

2 files changed

+142
-13
lines changed

src/machine/lib_machine/mod.rs

+101-13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cell::Cell;
12
use std::cmp::Ordering;
23
use std::collections::BTreeMap;
34

@@ -8,13 +9,14 @@ use crate::machine::mock_wam::CompositeOpDir;
89
use crate::machine::{
910
F64Offset, F64Ptr, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS,
1011
};
11-
use crate::parser::ast::{Var, VarPtr};
12+
use crate::parser::ast::{Literal, Term as AstTerm, Var, VarPtr};
1213
use crate::parser::parser::{Parser, Tokens};
1314
use crate::read::{write_term_to_heap, TermWriteResult};
1415

1516
use dashu::{Integer, Rational};
1617
use indexmap::IndexMap;
1718

19+
use super::AtomTable;
1820
use super::{streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine};
1921

2022
#[cfg(test)]
@@ -401,6 +403,47 @@ impl Term {
401403
debug_assert_eq!(term_stack.len(), 1);
402404
term_stack.pop().unwrap()
403405
}
406+
407+
pub(crate) fn into_ast_term(self, machine: &mut Machine) -> AstTerm {
408+
match self {
409+
Term::Integer(i) => AstTerm::Literal(
410+
Cell::default(),
411+
Literal::Integer(arena_alloc!(i, &mut machine.machine_st.arena)),
412+
),
413+
Term::Rational(r) => AstTerm::Literal(
414+
Cell::default(),
415+
Literal::Rational(arena_alloc!(r, &mut machine.machine_st.arena)),
416+
),
417+
Term::Float(f) => AstTerm::Literal(
418+
Cell::default(),
419+
Literal::Float(float_alloc!(f, &mut machine.machine_st.arena).as_offset()),
420+
),
421+
Term::Atom(a) => AstTerm::Literal(
422+
Cell::default(),
423+
Literal::Atom(AtomTable::build_with(&machine.machine_st.atom_tbl, &a)),
424+
),
425+
Term::String(s) => AstTerm::Literal(
426+
Cell::default(),
427+
Literal::String(AtomTable::build_with(&machine.machine_st.atom_tbl, &s)),
428+
),
429+
Term::List(l) => l.iter().rev().fold(
430+
AstTerm::Literal(Cell::default(), Literal::Atom(atom!("[]"))),
431+
|tail, head| {
432+
AstTerm::Cons(
433+
Cell::default(),
434+
Box::new(head.clone().into_ast_term(machine)),
435+
Box::new(tail),
436+
)
437+
},
438+
),
439+
Term::Compound(f, args) => AstTerm::Clause(
440+
Cell::default(),
441+
AtomTable::build_with(&machine.machine_st.atom_tbl, &f),
442+
args.into_iter().map(|x| x.into_ast_term(machine)).collect(),
443+
),
444+
Term::Var(v) => AstTerm::Var(Cell::default(), VarPtr::from(v)),
445+
}
446+
}
404447
}
405448

406449
/// An iterator though the leaf answers of a query.
@@ -525,6 +568,42 @@ impl Iterator for QueryState<'_> {
525568
}
526569
}
527570

571+
enum QueryInfoInner {
572+
String(String),
573+
Term(Term),
574+
}
575+
576+
/// Information for a query used inside `[Machine::run_query]`.
577+
///
578+
/// See `[IntoQuery]` trait.
579+
pub struct QueryInfo {
580+
inner: QueryInfoInner,
581+
}
582+
583+
/// Something that can be used as a query.
584+
///
585+
/// See `[Machine::run_query]`.
586+
pub trait IntoQuery {
587+
/// Convert to a query.
588+
fn into_query(self) -> QueryInfo;
589+
}
590+
591+
impl<T: Into<String>> IntoQuery for T {
592+
fn into_query(self) -> QueryInfo {
593+
QueryInfo {
594+
inner: QueryInfoInner::String(self.into()),
595+
}
596+
}
597+
}
598+
599+
impl IntoQuery for Term {
600+
fn into_query(self) -> QueryInfo {
601+
QueryInfo {
602+
inner: QueryInfoInner::Term(self),
603+
}
604+
}
605+
}
606+
528607
impl Machine {
529608
/// Loads a module into the [`Machine`] from a string.
530609
pub fn load_module_string(&mut self, module_name: &str, program: impl Into<String>) {
@@ -569,22 +648,31 @@ impl Machine {
569648
}
570649

571650
/// Runs a query.
572-
pub fn run_query(&mut self, query: impl Into<String>) -> QueryState {
573-
let mut parser = Parser::new(
574-
Stream::from_owned_string(query.into(), &mut self.machine_st.arena),
575-
&mut self.machine_st,
576-
);
577-
let op_dir = CompositeOpDir::new(&self.indices.op_dir, None);
578-
let term = parser
579-
.read_term(&op_dir, Tokens::Default)
580-
.expect("Failed to parse query");
651+
pub fn run_query(&mut self, query: impl IntoQuery) -> QueryState {
652+
let ast_term = match query.into_query().inner {
653+
QueryInfoInner::String(query_string) => {
654+
let mut parser = Parser::new(
655+
Stream::from_owned_string(query_string, &mut self.machine_st.arena),
656+
&mut self.machine_st,
657+
);
658+
let op_dir = CompositeOpDir::new(&self.indices.op_dir, None);
659+
660+
parser
661+
.read_term(&op_dir, Tokens::Default)
662+
.expect("Failed to parse query")
663+
}
664+
QueryInfoInner::Term(query_term) => query_term.into_ast_term(self),
665+
};
581666

582667
self.allocate_stub_choice_point();
583668

584669
// Write parsed term to heap
585-
let term_write_result =
586-
write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl)
587-
.expect("couldn't write term to heap");
670+
let term_write_result = write_term_to_heap(
671+
&ast_term,
672+
&mut self.machine_st.heap,
673+
&self.machine_st.atom_tbl,
674+
)
675+
.expect("couldn't write term to heap");
588676

589677
let var_names: IndexMap<_, _> = term_write_result
590678
.var_dict

src/machine/lib_machine/tests.rs

+41
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,44 @@ fn errors_and_exceptions() {
608608
[Ok(LeafAnswer::Exception(Term::atom("a")))]
609609
);
610610
}
611+
612+
#[test]
613+
#[cfg_attr(miri, ignore)]
614+
fn term_as_query() {
615+
let mut machine = MachineBuilder::default().build();
616+
617+
// X = a.
618+
let query = Term::compound("=", [Term::variable("X"), Term::atom("a")]);
619+
620+
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
621+
622+
assert_eq!(
623+
complete_answer,
624+
[LeafAnswer::from_bindings([("X", Term::atom("a"))])]
625+
);
626+
}
627+
628+
#[test]
629+
#[cfg_attr(miri, ignore)]
630+
fn complex_term_as_query() {
631+
let mut machine = MachineBuilder::default().build();
632+
633+
let complex_term = Term::list([
634+
Term::integer(10),
635+
Term::rational(Rational::from_parts(7.into(), 10u32.into())),
636+
Term::float(4.12),
637+
Term::atom("asdf"),
638+
Term::string("fdsa"),
639+
Term::compound("a", [Term::atom("b"), Term::atom("c"), Term::atom("d")]),
640+
Term::variable("Y"),
641+
]);
642+
643+
let query = Term::compound("=", [Term::variable("X"), complex_term.clone()]);
644+
645+
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
646+
647+
assert_eq!(
648+
complete_answer,
649+
[LeafAnswer::from_bindings([("X", complex_term)])]
650+
);
651+
}

0 commit comments

Comments
 (0)