Skip to content

Commit b083e09

Browse files
authored
Merge pull request #826 from vsbogd/no-module-duplication
Don't copy imported module to filter out its dependencies
2 parents 2f95fd8 + cbe0086 commit b083e09

File tree

8 files changed

+194
-153
lines changed

8 files changed

+194
-153
lines changed

c/src/space.rs

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use hyperon::matcher::*;
77
use crate::atom::*;
88

99
use std::os::raw::*;
10+
use std::borrow::Cow;
1011

1112
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1213
// Space Client Interface
@@ -238,14 +239,9 @@ pub extern "C" fn space_atom_count(space: *const space_t) -> isize {
238239
pub extern "C" fn space_iterate(space: *const space_t,
239240
callback: c_atom_callback_t, context: *mut c_void) -> bool {
240241
let dyn_space = unsafe{ &*space }.borrow();
241-
match dyn_space.borrow().atom_iter() {
242-
Some(atom_iter) => {
243-
for atom in atom_iter {
244-
callback(atom.into(), context);
245-
}
246-
true
247-
},
248-
None => false
242+
match dyn_space.visit(&mut |atom: Cow<Atom>| callback(atom.as_ref().into(), context)) {
243+
Ok(()) => true,
244+
Err(()) => false,
249245
}
250246
}
251247

@@ -736,46 +732,22 @@ impl Space for CSpace {
736732
None
737733
}
738734
}
739-
fn atom_iter(&self) -> Option<SpaceIter> {
740-
struct CSpaceIterator<'a>(&'a CSpace, *mut c_void);
741-
impl<'a> Iterator for CSpaceIterator<'a> {
742-
type Item = &'a Atom;
743-
fn next(&mut self) -> Option<&'a Atom> {
744-
let api = unsafe{ &*self.0.api };
745-
if let Some(next_atom) = api.next_atom {
746-
let atom_ref = next_atom(&self.0.params, self.1);
735+
fn visit(&self, v: &mut dyn SpaceVisitor) -> Result<(), ()> {
736+
let api = unsafe{ &*self.api };
737+
match (api.new_atom_iterator_state, api.next_atom) {
738+
(Some(new_atom_iterator_state), Some(next_atom)) => {
739+
let ctx = new_atom_iterator_state(&self.params);
740+
loop {
741+
let atom_ref = next_atom(&self.params, ctx);
747742
if atom_ref.is_null() {
748-
None
743+
break;
749744
} else {
750-
Some(atom_ref.into_ref())
745+
v.accept(Cow::Borrowed(atom_ref.into_ref()));
751746
}
752-
} else {
753-
panic!("next_atom function must be implemented if new_atom_iterator_state is implemented");
754-
}
755-
}
756-
}
757-
impl Drop for CSpaceIterator<'_> {
758-
fn drop(&mut self) {
759-
let api = unsafe{ &*self.0.api };
760-
if let Some(free_atom_iterator_state) = api.free_atom_iterator_state {
761-
free_atom_iterator_state(&self.0.params, self.1);
762-
} else {
763-
panic!("free_atom_iterator_state function must be implemented if new_atom_iterator_state is implemented");
764747
}
765-
}
766-
}
767-
768-
let api = unsafe{ &*self.api };
769-
if let Some(new_atom_iterator_state) = api.new_atom_iterator_state {
770-
let ctx = new_atom_iterator_state(&self.params);
771-
if ctx.is_null() {
772-
None
773-
} else {
774-
let new_iter = CSpaceIterator(self, ctx);
775-
Some(SpaceIter::new(new_iter))
776-
}
777-
} else {
778-
None
748+
Ok(())
749+
},
750+
_ => Err(()),
779751
}
780752
}
781753
fn as_any(&self) -> Option<&dyn std::any::Any> {
@@ -803,6 +775,7 @@ struct DefaultSpace<'a>(&'a CSpace);
803775
impl Space for DefaultSpace<'_> {
804776
fn common(&self) -> FlexRef<SpaceCommon> { self.0.common() }
805777
fn query(&self, query: &Atom) -> BindingsSet { self.0.query(query) }
778+
fn visit(&self, v: &mut dyn SpaceVisitor) -> Result<(), ()> { self.0.visit(v) }
806779
fn as_any(&self) -> Option<&dyn std::any::Any> { Some(self.0) }
807780
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> { None }
808781
}
@@ -827,7 +800,7 @@ impl SpaceMut for CSpace {
827800
let from: atom_ref_t = from.into();
828801
(api.replace)(&self.params, &from, to.into())
829802
}
830-
fn as_space(&self) -> &dyn Space {
803+
fn as_space<'a>(&self) -> &(dyn Space + 'a) {
831804
self
832805
}
833806
}

lib/src/metta/runner/modules/mod.rs

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::cell::RefCell;
44

55
use crate::metta::*;
66
use crate::metta::runner::*;
7+
use crate::space::module::ModuleSpace;
78

89
use regex::Regex;
910

@@ -56,7 +57,7 @@ impl ModId {
5657
pub struct MettaMod {
5758
mod_path: String,
5859
resource_dir: Option<PathBuf>,
59-
space: DynSpace,
60+
space: Rc<RefCell<ModuleSpace>>,
6061
tokenizer: Shared<Tokenizer>,
6162
imported_deps: Mutex<HashMap<ModId, DynSpace>>,
6263
loader: Option<Box<dyn ModuleLoader>>,
@@ -75,6 +76,7 @@ impl MettaMod {
7576
}
7677
}
7778
}
79+
let space = Rc::new(RefCell::new(ModuleSpace::new(space)));
7880

7981
let new_mod = Self {
8082
mod_path,
@@ -86,8 +88,8 @@ impl MettaMod {
8688
};
8789

8890
// Load the base tokens for the module's new Tokenizer
89-
register_runner_tokens(&mut *new_mod.tokenizer().borrow_mut(), new_mod.tokenizer().clone(), &new_mod.space, metta);
90-
register_common_tokens(&mut *new_mod.tokenizer().borrow_mut(), new_mod.tokenizer().clone(), &new_mod.space, metta);
91+
register_runner_tokens(&mut *new_mod.tokenizer().borrow_mut(), new_mod.tokenizer().clone(), &DynSpace::with_rc(new_mod.space.clone()), metta);
92+
register_common_tokens(&mut *new_mod.tokenizer().borrow_mut(), new_mod.tokenizer().clone(), &DynSpace::with_rc(new_mod.space.clone()), metta);
9193

9294
//Load the stdlib unless this module is no_std
9395
if !no_stdlib {
@@ -192,7 +194,7 @@ impl MettaMod {
192194
let (dep_space, transitive_deps) = mod_ptr.stripped_space();
193195

194196
// Add a new Grounded Space atom to the &self space, so we can access the dependent module
195-
self.insert_dep(mod_id, dep_space)?;
197+
self.insert_dep(mod_id, DynSpace::with_rc(dep_space.clone()))?;
196198

197199
// Add all the transitive deps from the dependency
198200
if let Some(transitive_deps) = transitive_deps {
@@ -228,7 +230,7 @@ impl MettaMod {
228230
fn insert_dep(&self, mod_id: ModId, dep_space: DynSpace) -> Result<(), String> {
229231
let mut deps_table = self.imported_deps.lock().unwrap();
230232
if !deps_table.contains_key(&mod_id) {
231-
self.add_atom(Atom::gnd(dep_space.clone()), false).map_err(|a| a.to_string())?;
233+
self.space.borrow_mut().add_dep(dep_space.clone());
232234
deps_table.insert(mod_id, dep_space);
233235
}
234236
Ok(())
@@ -251,39 +253,9 @@ impl MettaMod {
251253

252254
/// Private function that returns a deep copy of a module's space, with the module's dependency
253255
/// sub-spaces stripped out and returned separately
254-
//
255-
// HACK. This is a terrible design. It is a stop-gap to get around problems caused by transitive
256-
// imports described above, but it has some serious downsides, To name a few:
257-
// - It means we must copy every atom in the space for every import
258-
// - It only works when the dep module's space is a GroundingSpace
259-
fn stripped_space(&self) -> (DynSpace, Option<HashMap<ModId, DynSpace>>) {
256+
fn stripped_space(&self) -> (Rc<RefCell<ModuleSpace>>, Option<HashMap<ModId, DynSpace>>) {
260257
let deps_table = self.imported_deps.lock().unwrap();
261-
if deps_table.len() == 0 {
262-
(self.space.clone(), None)
263-
} else {
264-
if let Some(any_space) = self.space.borrow().as_any() {
265-
if let Some(dep_g_space) = any_space.downcast_ref::<GroundingSpace>() {
266-
267-
// Do a deep-clone of the dep-space atom-by-atom, because `space.remove()` doesn't recognize
268-
// two GroundedAtoms wrapping DynSpaces as being the same, even if the underlying space is
269-
let mut new_space = GroundingSpace::new();
270-
new_space.set_name(self.path().to_string());
271-
for atom in dep_g_space.atom_iter().unwrap() {
272-
if let Some(sub_space) = atom.as_gnd::<DynSpace>() {
273-
if !deps_table.values().any(|space| space == sub_space) {
274-
new_space.add(atom.clone());
275-
}
276-
} else {
277-
new_space.add(atom.clone());
278-
}
279-
}
280-
281-
return (DynSpace::new(new_space), Some(deps_table.clone()));
282-
}
283-
}
284-
log::warn!("import_all_from_dependency: Importing from module based on a non-GroundingSpace is currently unsupported");
285-
(self.space.clone(), None)
286-
}
258+
(self.space.clone(), Some(deps_table.clone()))
287259
}
288260

289261
/// Returns the full path of a loaded module. For example: "top:parent_mod:this_mod"
@@ -302,8 +274,8 @@ impl MettaMod {
302274
self.loader.as_ref().and_then(|loader| loader.pkg_info())
303275
}
304276

305-
pub fn space(&self) -> &DynSpace {
306-
&self.space
277+
pub fn space(&self) -> DynSpace {
278+
DynSpace::with_rc(self.space.clone())
307279
}
308280

309281
pub fn tokenizer(&self) -> &Shared<Tokenizer> {

lib/src/metta/runner/stdlib/module.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ impl CustomExecute for ImportOp {
9090
}
9191
other_atom => {
9292
match &other_atom {
93-
Atom::Grounded(_) if Atom::as_gnd::<DynSpace>(other_atom) == Some(context.module().space()) => {
93+
Atom::Grounded(_) if Atom::as_gnd::<DynSpace>(other_atom) == Some(&context.module().space()) => {
9494
context.import_all_from_dependency(mod_id)?;
9595
},
9696
_ => {

lib/src/metta/runner/stdlib/space.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,10 @@ impl CustomExecute for GetAtomsOp {
160160
let arg_error = || ExecError::from("get-atoms expects one argument: space");
161161
let space = args.get(0).ok_or_else(arg_error)?;
162162
let space = Atom::as_gnd::<DynSpace>(space).ok_or("get-atoms expects a space as its argument")?;
163-
space.borrow().as_space().atom_iter()
164-
.map(|iter| iter.cloned().map(|a| make_variables_unique(a)).collect())
165-
.ok_or(ExecError::Runtime("Unsupported Operation. Can't traverse atoms in this space".to_string()))
163+
let mut result = Vec::new();
164+
space.borrow().as_space().visit(&mut |atom: std::borrow::Cow<Atom>| {
165+
result.push(make_variables_unique(atom.into_owned()))
166+
}).map_or(Err(ExecError::Runtime("Unsupported Operation. Can't traverse atoms in this space".to_string())), |_| Ok(result))
166167
}
167168
}
168169

@@ -259,6 +260,13 @@ mod tests {
259260
assert_eq!(result[2], vec![Atom::gnd(stdlib_space)]);
260261
}
261262

263+
fn collect_atoms(space: &dyn Space) -> Vec<Atom> {
264+
let mut atoms = Vec::new();
265+
space.visit(&mut |atom: std::borrow::Cow<Atom>| atoms.push(atom.into_owned()))
266+
.expect("Space::visit is not implemented");
267+
atoms
268+
}
269+
262270
#[test]
263271
fn remove_atom_op() {
264272
let space = DynSpace::new(metta_space("
@@ -269,7 +277,7 @@ mod tests {
269277
let res = RemoveAtomOp{}.execute(&mut vec![satom, expr!(("foo" "bar"))]).expect("No result returned");
270278
// REM: can return Bool in future
271279
assert_eq!(res, vec![UNIT_ATOM]);
272-
let space_atoms: Vec<Atom> = space.borrow().as_space().atom_iter().unwrap().cloned().collect();
280+
let space_atoms = collect_atoms(space.borrow().as_space());
273281
assert_eq_no_order!(space_atoms, vec![expr!(("bar" "foo"))]);
274282
}
275283

@@ -281,7 +289,7 @@ mod tests {
281289
"));
282290
let satom = Atom::gnd(space.clone());
283291
let res = GetAtomsOp{}.execute(&mut vec![satom]).expect("No result returned");
284-
let space_atoms: Vec<Atom> = space.borrow().as_space().atom_iter().unwrap().cloned().collect();
292+
let space_atoms = collect_atoms(space.borrow().as_space());
285293
assert_eq_no_order!(res, space_atoms);
286294
assert_eq_no_order!(res, vec![expr!(("foo" "bar")), expr!(("bar" "foo"))]);
287295
}
@@ -291,7 +299,7 @@ mod tests {
291299
let res = NewSpaceOp{}.execute(&mut vec![]).expect("No result returned");
292300
let space = res.get(0).expect("Result is empty");
293301
let space = space.as_gnd::<DynSpace>().expect("Result is not space");
294-
let space_atoms: Vec<Atom> = space.borrow().as_space().atom_iter().unwrap().cloned().collect();
302+
let space_atoms = collect_atoms(space.borrow().as_space());
295303
assert_eq_no_order!(space_atoms, Vec::<Atom>::new());
296304
}
297305

@@ -301,7 +309,7 @@ mod tests {
301309
let satom = Atom::gnd(space.clone());
302310
let res = AddAtomOp{}.execute(&mut vec![satom, expr!(("foo" "bar"))]).expect("No result returned");
303311
assert_eq!(res, vec![UNIT_ATOM]);
304-
let space_atoms: Vec<Atom> = space.borrow().as_space().atom_iter().unwrap().cloned().collect();
312+
let space_atoms = collect_atoms(space.borrow().as_space());
305313
assert_eq_no_order!(space_atoms, vec![expr!(("foo" "bar"))]);
306314
}
307315

lib/src/space/grounding.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,8 @@ impl GroundingSpace {
291291
}
292292

293293
/// Returns the iterator over content of the space.
294-
pub fn iter(&self) -> SpaceIter {
295-
SpaceIter::new(GroundingSpaceIter::new(self))
294+
fn iter(&self) -> GroundingSpaceIter<'_> {
295+
GroundingSpaceIter::new(self)
296296
}
297297

298298
/// Sets the name property for the `GroundingSpace` which can be useful for debugging
@@ -316,8 +316,8 @@ impl Space for GroundingSpace {
316316
fn atom_count(&self) -> Option<usize> {
317317
Some(self.iter().count())
318318
}
319-
fn atom_iter(&self) -> Option<SpaceIter> {
320-
Some(self.iter())
319+
fn visit(&self, v: &mut dyn SpaceVisitor) -> Result<(), ()> {
320+
Ok(self.iter().for_each(|atom| v.accept(Cow::Borrowed(atom))))
321321
}
322322
fn as_any(&self) -> Option<&dyn std::any::Any> {
323323
Some(self)
@@ -337,7 +337,7 @@ impl SpaceMut for GroundingSpace {
337337
fn replace(&mut self, from: &Atom, to: Atom) -> bool {
338338
GroundingSpace::replace(self, from, to)
339339
}
340-
fn as_space(&self) -> &dyn Space {
340+
fn as_space<'a>(&self) -> &(dyn Space + 'a) {
341341
self
342342
}
343343
}

0 commit comments

Comments
 (0)