Skip to content

Commit 476ed8e

Browse files
authored
feat: optimized SolutionHandler and boolean predication support, with… (#4)
* feat: optimized SolutionHandler and boolean predication support, with unit tests * chore: refactor Interpreter to use Default and simplify error handling in eval method * chore: simplify QuerySolver initialization and comment out function in resolver.rs.
1 parent ad6198a commit 476ed8e

File tree

3 files changed

+319
-73
lines changed

3 files changed

+319
-73
lines changed

rulog_vm/src/environment.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
1+
use rulog_core::types::ast::Term;
12
use std::collections::HashMap;
23

3-
use rulog_core::types::ast::Term;
4-
#[derive(Debug, PartialEq, Eq, Clone)]
4+
#[derive(Debug, Clone, Default, PartialEq, Eq)]
55
pub struct Environment {
66
pub bindings: HashMap<String, Term>,
77
}
88

99
impl Environment {
10-
pub fn new() -> Self {
11-
Environment {
12-
bindings: HashMap::new(),
13-
}
14-
}
15-
1610
pub fn bind(&mut self, var: String, term: Term) {
1711
self.bindings.insert(var, term);
1812
}
1913

2014
pub fn lookup(&self, var: &String) -> Option<&Term> {
2115
self.bindings.get(var)
2216
}
17+
18+
pub fn extend(mut self, var: String, term: Term) -> Self {
19+
self.bind(var, term);
20+
self
21+
}
2322
}
2423

2524
impl FromIterator<(std::string::String, Term)> for Environment {
2625
fn from_iter<T: IntoIterator<Item = (std::string::String, Term)>>(iter: T) -> Self {
27-
let mut env = Environment::new();
26+
let mut env = Environment::default();
2827
for (var, term) in iter {
2928
env.bind(var, term);
3029
}

rulog_vm/src/interpreter.rs

+99-39
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,32 @@ use crate::{
99
resolver::{QuerySolution, QuerySolver},
1010
types::InterpretingError,
1111
};
12+
pub trait SolutionHandler {
13+
fn handle_solution(&self, solution: Option<&QuerySolution>) -> bool;
14+
}
1215

16+
#[derive(Default)]
1317
pub struct Interpreter {
1418
clauses: Vec<(Predicate, Vec<Predicate>)>,
1519
operator_definitions: HashMap<String, OperatorDefinition>,
16-
17-
on_solution: Option<Box<dyn Fn(&QuerySolution) -> bool>>,
1820
}
1921

2022
impl Interpreter {
21-
pub fn new() -> Self {
22-
Interpreter {
23-
clauses: Vec::new(),
24-
operator_definitions: HashMap::new(),
25-
on_solution: None,
26-
}
27-
}
28-
29-
pub fn on_solution<F>(&mut self, f: F)
30-
where
31-
F: Fn(&QuerySolution) -> bool + 'static,
32-
{
33-
self.on_solution = Some(Box::new(f));
34-
}
35-
36-
pub fn eval(&mut self, input: &str) -> Result<(), InterpretingError> {
23+
pub fn eval(
24+
&mut self,
25+
input: &str,
26+
handler: Option<&dyn SolutionHandler>,
27+
) -> Result<(), InterpretingError> {
3728
let program = parse(input).map_err(InterpretingError::ParseError)?;
3829
for clause in program.0 {
3930
let ret = match clause {
4031
Clause::Directive(directive) => self.handle_directive(directive),
41-
Clause::Query(query) => self.handle_query(query),
32+
Clause::Query(query) => self.handle_query(query, handler),
4233
Clause::Fact(fact) => self.handle_fact(fact),
4334
Clause::Rule(rule_head, rule_body) => self.handle_rule(rule_head, rule_body),
4435
};
4536

46-
if let Err(e) = ret {
47-
return Err(e);
48-
}
37+
ret?
4938
}
5039

5140
Ok(())
@@ -65,21 +54,27 @@ impl Interpreter {
6554
Ok(())
6655
}
6756

68-
fn handle_query(&mut self, query: Query) -> Result<(), InterpretingError> {
69-
log::trace!("handle query resolved: {:?}", query);
70-
let mut query_solver = QuerySolver::new(self.clauses.clone(), query);
71-
if let Some(ref on_solution) = self.on_solution {
72-
while let Some(solution) = query_solver.next() {
73-
if !on_solution(&solution) {
74-
break;
75-
}
76-
}
77-
} else {
78-
for solution in query_solver {
79-
println!("solution: {:?}", solution);
57+
fn handle_query(
58+
&mut self,
59+
query: Query,
60+
handler: Option<&dyn SolutionHandler>,
61+
) -> Result<(), InterpretingError> {
62+
log::trace!("handle query: {:?}", query);
63+
let handler = handler.unwrap_or(&PrintSolutionHandler);
64+
let query_solver = QuerySolver::new(self.clauses.clone(), query);
65+
66+
let mut has_solution = false;
67+
for solution in query_solver {
68+
has_solution = true;
69+
if !handler.handle_solution(Some(&solution)) {
70+
break;
8071
}
8172
}
8273

74+
if !has_solution {
75+
handler.handle_solution(None);
76+
}
77+
8378
Ok(())
8479
}
8580

@@ -100,74 +95,139 @@ impl Interpreter {
10095
}
10196
}
10297

98+
pub struct PrintSolutionHandler;
99+
100+
impl SolutionHandler for PrintSolutionHandler {
101+
fn handle_solution(&self, solution: Option<&QuerySolution>) -> bool {
102+
println!("solution: {:?}", solution);
103+
true // Continue processing
104+
}
105+
}
106+
103107
#[cfg(test)]
104108
mod tests {
109+
use std::cell::RefCell;
110+
111+
use crate::environment::Environment;
112+
105113
use super::*;
114+
use rulog_core::types::ast::Term;
106115
use rulog_test_util::setup_logger;
116+
struct TestSolutionHandler {
117+
expected_solutions: Vec<Option<QuerySolution>>,
118+
index: RefCell<usize>,
119+
}
120+
121+
impl TestSolutionHandler {
122+
fn new(expected_solutions: Vec<Option<QuerySolution>>) -> Self {
123+
Self {
124+
expected_solutions,
125+
index: RefCell::new(0),
126+
}
127+
}
128+
}
129+
130+
impl SolutionHandler for TestSolutionHandler {
131+
fn handle_solution(&self, solution: Option<&QuerySolution>) -> bool {
132+
let size = self.index.borrow().clone();
133+
if size < self.expected_solutions.len() {
134+
assert_eq!(
135+
solution,
136+
self.expected_solutions[size].as_ref(),
137+
"expected solution: {:?}, actual solution: {:?}",
138+
self.expected_solutions[size],
139+
solution
140+
);
141+
self.index.replace(size + 1);
142+
true
143+
} else {
144+
false
145+
}
146+
}
147+
}
107148

108149
#[test]
109150
fn test_parent_true() {
110151
setup_logger();
111-
let mut vm = Interpreter::new();
152+
let mut vm = Interpreter::default();
112153
let ret = vm.eval(
113154
r#"
114155
parent(tom, liz).
115156
?- parent(tom, liz).
116157
"#,
158+
Some(&TestSolutionHandler::new(vec![Some(
159+
QuerySolution::default(),
160+
)])),
117161
);
118162
assert!(ret.is_ok(), "{:?}", ret);
119163
}
120164

121165
#[test]
122166
fn test_parent_false() {
123167
setup_logger();
124-
let mut vm = Interpreter::new();
168+
let mut vm = Interpreter::default();
125169
let ret = vm.eval(
126170
r#"
127171
parent(tom, liz).
128172
?- parent(liz, tom).
173+
?- parent(tom, liz).
129174
"#,
175+
Some(&TestSolutionHandler::new(vec![
176+
None,
177+
Some(QuerySolution::default()),
178+
])),
130179
);
131180
assert!(ret.is_ok(), "{:?}", ret);
132181
}
133182

134183
#[test]
135184
fn test_parent_var() {
136185
setup_logger();
137-
let mut vm = Interpreter::new();
186+
let mut vm = Interpreter::default();
138187
let ret = vm.eval(
139188
r#"
140189
parent(tom, liz).
141190
?- parent(X, liz).
142191
"#,
192+
Some(&TestSolutionHandler::new(vec![Some(QuerySolution {
193+
env: Environment::default().extend("X".to_string(), Term::Atom("tom".to_string())),
194+
})])),
143195
);
144196
assert!(ret.is_ok(), "{:?}", ret);
145197
}
146198

147199
#[test]
148200
fn test_parent_var_multiple() {
149201
setup_logger();
150-
let mut vm = Interpreter::new();
202+
let mut vm = Interpreter::default();
151203
let ret = vm.eval(
152204
r#"
153205
parent(tom, liz).
154206
parent(tom, bob).
155207
?- parent(X, liz).
156208
"#,
209+
Some(&TestSolutionHandler::new(vec![Some(QuerySolution {
210+
env: Environment::default().extend("X".to_string(), Term::Atom("tom".to_string())),
211+
})])),
157212
);
158213
assert!(ret.is_ok(), "{:?}", ret);
159214
}
160215

161216
#[test]
162217
fn test_parent_var_multiple_children() {
163218
setup_logger();
164-
let mut vm = Interpreter::new();
219+
let mut vm = Interpreter::default();
165220
let ret = vm.eval(
166221
r#"
167222
parent(tom, liz).
168223
parent(tom, bob).
169224
?- parent(tom, X).
170225
"#,
226+
Some(&TestSolutionHandler::new(vec![Some(QuerySolution {
227+
env: Environment::default()
228+
.extend("X".to_string(), Term::Atom("bob".to_string()))
229+
.extend("X".to_string(), Term::Atom("liz".to_string())),
230+
})])),
171231
);
172232
assert!(ret.is_ok(), "{:?}", ret);
173233
}

0 commit comments

Comments
 (0)