hyperon/metta/runner/stdlib/
module.rs1use hyperon_atom::*;
2use hyperon_space::*;
3use crate::metta::*;
4use crate::metta::text::Tokenizer;
5use hyperon_common::shared::Shared;
6use crate::metta::runner::{Metta, RunContext, ResourceKey};
7use super::{grounded_op, regex, unit_result};
8use hyperon_atom::gnd::str::expect_string_like_atom;
9use hyperon_atom::gnd::GroundedFunctionAtom;
10use crate::space::module::ModuleSpace;
11
12use regex::Regex;
13
14#[derive(Clone, Debug)]
15pub struct ImportOp {
16 context: std::sync::Arc<std::sync::Mutex<Vec<std::sync::Arc<std::sync::Mutex<&'static mut RunContext<'static, 'static>>>>>>,
18}
19
20grounded_op!(ImportOp, "import!");
21
22impl ImportOp {
23 pub fn new(metta: Metta) -> Self {
24 Self{ context: metta.0.context.clone() }
25 }
26}
27
28impl Grounded for ImportOp {
29 fn type_(&self) -> Atom {
30 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, UNIT_TYPE])
34 }
35
36 fn as_execute(&self) -> Option<&dyn CustomExecute> {
37 Some(self)
38 }
39}
40
41impl CustomExecute for ImportOp {
42 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
43 let arg_error = || ExecError::from("import! expects a destination &space and a module name argument");
72 let dest_arg = args.get(0).ok_or_else(arg_error)?;
73 let mod_name = args.get(1).and_then(expect_string_like_atom).ok_or_else(arg_error)?;
74
75 let ctx_ref = self.context.lock().unwrap().last().unwrap().clone();
78 let mut context = ctx_ref.lock().unwrap();
79 let mod_id = context.load_module(&mod_name)?;
80
81 match dest_arg {
83 Atom::Symbol(dest_sym) => {
84 context.import_dependency_as(mod_id, Some(dest_sym.name().to_string()))?;
85 }
86 other_atom => {
87 match &other_atom {
88 Atom::Grounded(_) if Atom::as_gnd::<DynSpace>(other_atom) == Some(&context.module().space()) => {
89 context.import_all_from_dependency(mod_id)?;
90 },
91 _ => {
92 return Err(format!("import! destination argument must be a symbol atom naming a new space, or &self. Found: {other_atom:?}").into());
93 }
94 }
95 }
96 }
102
103 unit_result()
104 }
105}
106
107#[derive(Clone, Debug)]
108pub struct IncludeOp {
109 context: std::sync::Arc<std::sync::Mutex<Vec<std::sync::Arc<std::sync::Mutex<&'static mut RunContext<'static, 'static>>>>>>,
111}
112
113grounded_op!(IncludeOp, "include");
114
115impl IncludeOp {
116 pub fn new(metta: Metta) -> Self {
117 Self{ context: metta.0.context.clone() }
118 }
119}
120
121impl Grounded for IncludeOp {
122 fn type_(&self) -> Atom {
123 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_UNDEFINED])
124 }
125
126 fn as_execute(&self) -> Option<&dyn CustomExecute> {
127 Some(self)
128 }
129}
130
131impl CustomExecute for IncludeOp {
132 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
133 let arg_error = || ExecError::from("include expects a module name argument");
134 let mod_name = args.get(0).and_then(expect_string_like_atom).ok_or_else(arg_error)?;
135
136 let ctx_ref = self.context.lock().unwrap().last().unwrap().clone();
138 let mut context = ctx_ref.lock().unwrap();
139 let resource = context.load_resource_from_module(&mod_name, ResourceKey::MainMettaSrc)?;
140 let parser = crate::metta::text::SExprParser::new(resource);
141 let eval_result = context.run_inline(|context| {
142 context.push_parser(Box::new(parser));
143 Ok(())
144 })?;
145
146 Ok(eval_result.into_iter().last().unwrap_or_else(|| vec![]))
151 }
152}
153
154#[derive(Clone, Debug)]
160pub struct ModSpaceOp {
161 context: std::sync::Arc<std::sync::Mutex<Vec<std::sync::Arc<std::sync::Mutex<&'static mut RunContext<'static, 'static>>>>>>,
163}
164
165grounded_op!(ModSpaceOp, "mod-space!");
166
167impl ModSpaceOp {
168 pub fn new(metta: Metta) -> Self {
169 Self{ context: metta.0.context.clone() }
170 }
171}
172
173impl Grounded for ModSpaceOp {
174 fn type_(&self) -> Atom {
175 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_SPACE])
176 }
177
178 fn as_execute(&self) -> Option<&dyn CustomExecute> {
179 Some(self)
180 }
181}
182
183impl CustomExecute for ModSpaceOp {
184 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
185 let arg_error = "mod-space! expects a module name argument";
186 let mod_name = args.get(0).and_then(expect_string_like_atom).ok_or_else(|| ExecError::from(arg_error))?;
187
188 let ctx_ref = self.context.lock().unwrap().last().unwrap().clone();
191 let mut context = ctx_ref.lock().unwrap();
192 let mod_id = context.load_module(&mod_name)?;
193
194 let space = Atom::gnd(context.metta().module_space(mod_id));
195 Ok(vec![space])
196 }
197}
198
199fn module_space_no_deps(args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
200 let arg_error = "module-space-no-deps expects a space as an argument";
201 let space = args.get(0).ok_or(arg_error)?;
202 let space = Atom::as_gnd::<DynSpace>(space).ok_or(arg_error)?;
203
204 if let Some(space) = space.borrow().as_any().downcast_ref::<ModuleSpace>() {
205 return Ok(vec![Atom::gnd(space.main())]);
206 }
207 Ok(vec![Atom::gnd(space.clone())])
208}
209
210#[derive(Clone, Debug)]
216pub struct PrintModsOp {
217 metta: Metta
218}
219
220grounded_op!(PrintModsOp, "print-mods!");
221
222impl PrintModsOp {
223 pub fn new(metta: Metta) -> Self {
224 Self{ metta }
225 }
226}
227
228impl Grounded for PrintModsOp {
229 fn type_(&self) -> Atom {
230 Atom::expr([ARROW_SYMBOL, UNIT_TYPE])
231 }
232
233 fn as_execute(&self) -> Option<&dyn CustomExecute> {
234 Some(self)
235 }
236}
237
238impl CustomExecute for PrintModsOp {
239 fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
240 self.metta.display_loaded_modules();
241 unit_result()
242 }
243}
244
245#[derive(Clone, Debug)]
246pub struct BindOp {
247 tokenizer: Shared<Tokenizer>,
248}
249
250grounded_op!(BindOp, "bind!");
251
252impl BindOp {
253 pub fn new(tokenizer: Shared<Tokenizer>) -> Self {
254 Self{ tokenizer }
255 }
256}
257
258impl Grounded for BindOp {
259 fn type_(&self) -> Atom {
260 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_SYMBOL, ATOM_TYPE_UNDEFINED, UNIT_TYPE])
261 }
262
263 fn as_execute(&self) -> Option<&dyn CustomExecute> {
264 Some(self)
265 }
266}
267
268impl CustomExecute for BindOp {
269 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
270 let arg_error = || ExecError::from("bind! expects two arguments: token and atom");
271 let token = <&SymbolAtom>::try_from(args.get(0).ok_or_else(arg_error)?).map_err(|_| "bind! expects symbol atom as a token")?.name();
272 let atom = args.get(1).ok_or_else(arg_error)?.clone();
273
274 let token_regex = Regex::new(token).map_err(|err| format!("Could convert token {} into regex: {}", token, err))?;
275 self.tokenizer.borrow_mut().register_token(token_regex, move |_| { atom.clone() });
276 unit_result()
277 }
278}
279
280pub(super) fn register_context_independent_tokens(tref: &mut Tokenizer) {
281 tref.register_function(GroundedFunctionAtom::new(
282 r"module-space-no-deps".into(),
283 expr!("->" "SpaceType" "SpaceType"),
284 module_space_no_deps,
285 ));
286}
287
288pub(super) fn register_context_dependent_tokens(tref: &mut Tokenizer, tokenizer: Shared<Tokenizer>, metta: &Metta) {
289 let import_op = Atom::gnd(ImportOp::new(metta.clone()));
290 tref.register_token(regex(r"import!"), move |_| { import_op.clone() });
291 let include_op = Atom::gnd(IncludeOp::new(metta.clone()));
292 tref.register_token(regex(r"include"), move |_| { include_op.clone() });
293 let bind_op = Atom::gnd(BindOp::new(tokenizer.clone()));
294 tref.register_token(regex(r"bind!"), move |_| { bind_op.clone() });
295 let mod_space_op = Atom::gnd(ModSpaceOp::new(metta.clone()));
296 tref.register_token(regex(r"mod-space!"), move |_| { mod_space_op.clone() });
297 let print_mods_op = Atom::gnd(PrintModsOp::new(metta.clone()));
298 tref.register_token(regex(r"print-mods!"), move |_| { print_mods_op.clone() });
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn bind_new_space_op() {
307 let tokenizer = Shared::new(Tokenizer::new());
308
309 let bind_op = BindOp::new(tokenizer.clone());
310
311 assert_eq!(bind_op.execute(&mut vec![sym!("&my"), sym!("definition")]), unit_result());
312 let borrowed = tokenizer.borrow();
313 let constr = borrowed.find_token("&my");
314 assert!(constr.is_some());
315 assert_eq!(constr.unwrap()("&my"), Ok(sym!("definition")));
316 }
317}