diff --git a/lib/src/metta/runner/mod.rs b/lib/src/metta/runner/mod.rs index 59291afcc..8c367945e 100644 --- a/lib/src/metta/runner/mod.rs +++ b/lib/src/metta/runner/mod.rs @@ -147,6 +147,11 @@ impl Metta { metta } + // Loads stdlib into a separate space and returns it + fn stdlib_space(&self) -> DynSpace { + self.load_module_space(PathBuf::from("stdlib")).unwrap() + } + /// Returns a new MeTTa interpreter intended for use loading MeTTa modules during import fn new_loading_runner(metta: &Metta, path: &Path) -> Self { let space = DynSpace::new(GroundingSpace::new()); @@ -182,16 +187,31 @@ impl Metta { // Load the module to the new space let runner = Metta::new_loading_runner(self, &path); + let mut loading_stdlib = false; let program = match path.to_str() { - Some("stdlib") => METTA_CODE.to_string(), + Some("stdlib") => { + loading_stdlib = true; + METTA_CODE.to_string() + }, _ => std::fs::read_to_string(&path).map_err( |err| format!("Could not read file, path: {}, error: {}", path.display(), err))?, }; + + let stdlib_space = if !loading_stdlib { + let stdlib_space = Atom::gnd(runner.stdlib_space()); + runner.space().borrow_mut().add(stdlib_space.clone()); + Some(stdlib_space) + } else { + None + }; // Make the imported module be immediately available to itself // to mitigate circular imports self.0.modules.borrow_mut().insert(path.clone(), runner.space().clone()); runner.run(SExprParser::new(program.as_str())) .map_err(|err| format!("Cannot import module, path: {}, error: {}", path.display(), err))?; + if let Some(stdlib_space) = stdlib_space { + runner.space().borrow_mut().remove(&stdlib_space); + } Ok(runner.space().clone()) } diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 0d36593b1..1d4d6d220 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -306,7 +306,9 @@ impl Grounded for GetAtomsOp { let arg_error = || ExecError::from("get-atoms expects one argument: space"); let space = args.get(0).ok_or_else(arg_error)?; let space = Atom::as_gnd::(space).ok_or("get-atoms expects a space as its argument")?; - space.borrow().as_space().atom_iter().map(|iter| iter.cloned().collect()).ok_or(ExecError::Runtime("Unsupported Operation. Can't traverse atoms in this space".to_string())) + space.borrow().as_space().atom_iter() + .map(|iter| iter.cloned().map(|a| make_variables_unique(a)).collect()) + .ok_or(ExecError::Runtime("Unsupported Operation. Can't traverse atoms in this space".to_string())) } fn match_(&self, other: &Atom) -> MatchResultIter { diff --git a/lib/src/metta/runner/stdlib2.rs b/lib/src/metta/runner/stdlib2.rs index 7230423d5..d610c252b 100644 --- a/lib/src/metta/runner/stdlib2.rs +++ b/lib/src/metta/runner/stdlib2.rs @@ -405,6 +405,8 @@ pub fn register_runner_tokens(metta: &Metta) { tref.register_token(regex(r"bind!"), move |_| { bind_op.clone() }); let trace_op = Atom::gnd(stdlib::TraceOp{}); tref.register_token(regex(r"trace!"), move |_| { trace_op.clone() }); + let println_op = Atom::gnd(stdlib::PrintlnOp{}); + tref.register_token(regex(r"println!"), move |_| { println_op.clone() }); // &self should be updated // TODO: adding &self might be done not by stdlib, but by MeTTa itself. // TODO: adding &self introduces self referencing and thus prevents space diff --git a/python/tests/scripts/f1_imports.metta b/python/tests/scripts/f1_imports.metta index be40786ac..7aa549bbe 100644 --- a/python/tests/scripts/f1_imports.metta +++ b/python/tests/scripts/f1_imports.metta @@ -22,12 +22,23 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; !(import! &m f1_moduleA.metta) +; Check whether passed expression contains atom for which condition is True +(: contains (-> Expression (-> Atom Bool) Bool)) +(= (contains $list $condition) + (if (== $list ()) False + (let $head (car-atom $list) + (if ($condition $head) True + (let $tail (cdr-atom $list) (contains $tail $condition)) )))) + +; Check whether atom is space comparing its type with type of the &self atom +(: is-space (-> Atom Bool)) +(= (is-space $atom) + (let* (($type (get-type $atom)) ($space (get-type &self))) (== $type $space))) + ; It's first atom is a space !(assertEqual - (let* (($x (collapse (get-atoms &m))) - ($y (car-atom $x))) - (get-type $y)) - (get-type &self)) + (let $x (collapse (get-atoms &m)) (contains $x is-space)) + True) ; FIXME? Now, it is moduleC space. ; Should it be `stdlib` atom for a separately imported space @@ -55,25 +66,24 @@ !(assertEqual (g 2) 102) !(assertEqual (f 2) 103) +; Check whether atom is &m +(: is-m (-> Atom Bool)) +(= (is-m $atom) (== $atom &m)) + ; `&self` contains 3 atoms-spaces now: ; - stdlib ; - moduleC imported by moduleA and removed from A after its import to &self ; - moduleA itself, which is the same as &m -!(assertEqual &m - (let* (($a (collapse (get-atoms &self))) - ($x (cdr-atom $a)) - ($y (cdr-atom $x))) - (car-atom $y))) +!(assertEqual + (let $a (collapse (get-atoms &self)) (contains $a is-m)) + True) ; NOTE: now the first atom, which was a space, is removed from `&m`, ; because we load modules only once, and we collect atoms-spaces to ; prevent duplication !(assertEqual - (== (let* (($x (collapse (get-atoms &m))) - ($y (car-atom $x))) - (get-type $y)) - (get-type &self)) - False) + (let $x (collapse (get-atoms &m)) (contains $x is-space)) + False) ; Let's check that `if` from stdlib is not duplicated and gives only one result !(assertEqual