1use hyperon_atom::*;
2use hyperon_space::*;
3use crate::metta::*;
4use crate::metta::text::Tokenizer;
5use crate::metta::types::{AtomType, get_atom_types, get_meta_type};
6use hyperon_atom::matcher::atoms_are_equivalent;
7use hyperon_common::multitrie::{MultiTrie, TrieKey, TrieToken};
8use super::{grounded_op, regex};
9use hyperon_atom::gnd::number::*;
10
11use std::convert::TryInto;
12use std::hash::{DefaultHasher, Hasher};
13
14#[derive(Clone, Debug)]
15pub struct UniqueAtomOp {}
16
17grounded_op!(UniqueAtomOp, "unique-atom");
18
19impl Grounded for UniqueAtomOp {
20 fn type_(&self) -> Atom {
21 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM])
22 }
23
24 fn as_execute(&self) -> Option<&dyn CustomExecute> {
25 Some(self)
26 }
27}
28
29impl CustomExecute for UniqueAtomOp {
30 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
31 let arg_error = || ExecError::from("unique expects single expression atom as an argument");
32 let expr = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?;
33
34 let mut atoms: Vec<Atom> = expr.children().into();
35 let mut seen: Vec<Atom> = Vec::new();
36 atoms.retain(|x| {
37 let not_contained = !seen.iter().any(|seen_atom| atoms_are_equivalent(seen_atom, x));
38 if not_contained { seen.push(x.clone()) };
39 not_contained
40 });
41 Ok(vec![Atom::expr(atoms)])
42 }
43}
44
45
46#[derive(Clone, Debug)]
47pub struct UnionAtomOp {}
48
49grounded_op!(UnionAtomOp, "union-atom");
50
51impl Grounded for UnionAtomOp {
52 fn type_(&self) -> Atom {
53 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM])
54 }
55
56 fn as_execute(&self) -> Option<&dyn CustomExecute> {
57 Some(self)
58 }
59}
60
61impl CustomExecute for UnionAtomOp {
62 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
63 let arg_error = || ExecError::from("union expects and executable LHS and RHS atom");
64 let mut lhs: Vec<Atom> = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().into();
65 let rhs: Vec<Atom> = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().into();
66
67 lhs.extend(rhs);
68
69 Ok(vec![Atom::expr(lhs)])
70 }
71}
72
73#[derive(Clone, Debug)]
74pub struct IntersectionAtomOp {}
75
76grounded_op!(IntersectionAtomOp, "intersection-atom");
77
78impl Grounded for IntersectionAtomOp {
79 fn type_(&self) -> Atom {
80 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM])
81 }
82
83 fn as_execute(&self) -> Option<&dyn CustomExecute> {
84 Some(self)
85 }
86}
87
88fn atom_to_trie_key(atom: &Atom) -> TrieKey<SymbolAtom> {
89 fn fill_key(atom: &Atom, tokens: &mut Vec<TrieToken<SymbolAtom>>) {
90 match atom {
91 Atom::Symbol(sym) => tokens.push(TrieToken::Exact(sym.clone())),
92 Atom::Expression(expr) => {
93 tokens.push(TrieToken::LeftPar);
94 expr.children().iter().for_each(|child| fill_key(child, tokens));
95 tokens.push(TrieToken::RightPar);
96 },
97 Atom::Grounded(g) if g.as_grounded().as_match().is_none() => {
98 let mut h = DefaultHasher::new();
102 match (*g).serialize(&mut h) {
103 Ok(()) => { tokens.push(TrieToken::Exact(SymbolAtom::new(h.finish().to_string().into()))) }
104 Err(_) => { tokens.push(TrieToken::Wildcard) }
105 }
106 }
107 _ => tokens.push(TrieToken::Wildcard),
108 }
109 }
110
111 let mut tokens = Vec::new();
112 fill_key(atom, &mut tokens);
113 TrieKey::from(tokens)
114}
115
116
117impl CustomExecute for IntersectionAtomOp {
118 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
119 let arg_error = || ExecError::from("intersection expects and executable LHS and RHS atom");
120 let mut lhs: Vec<Atom> = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().into();
121 let rhs = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children();
122
123 let mut rhs_index: MultiTrie<SymbolAtom, Vec<usize>> = MultiTrie::new();
124 for (index, rhs_item) in rhs.iter().enumerate() {
125 let k = atom_to_trie_key(&rhs_item);
126 let r = rhs_index.get(&k).next();
130 match r.cloned() {
131 Some(bucket) => {
132 rhs_index.remove(&k, &bucket);
133 let mut nbucket = bucket;
134 nbucket.push(index);
135 let nbucket = nbucket;
136 rhs_index.insert(k, nbucket);
137 }
138 None => { rhs_index.insert(k, vec![index]) }
139 }
140 }
141
142 lhs.retain(|candidate| {
143 let k = atom_to_trie_key(candidate);
144 let r = rhs_index.get(&k).next();
145 match r.cloned() {
146 None => { false }
147 Some(bucket) => {
148 match bucket.iter().position(|item| &rhs[*item] == candidate) {
149 None => { false }
150 Some(i) => {
151 rhs_index.remove(&k, &bucket);
152 if bucket.len() > 1 {
153 let mut nbucket = bucket;
154 nbucket.remove(i);
155 rhs_index.insert(k, nbucket);
156 }
157 true
158 }
159 }
160 }
161 }
162 });
163
164 Ok(vec![Atom::expr(lhs)])
165 }
166}
167
168#[derive(Clone, Debug)]
169pub struct MaxAtomOp {}
170
171grounded_op!(MaxAtomOp, "max-atom");
172
173impl Grounded for MaxAtomOp {
174 fn type_(&self) -> Atom {
175 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_NUMBER])
176 }
177
178 fn as_execute(&self) -> Option<&dyn CustomExecute> {
179 Some(self)
180 }
181}
182
183impl CustomExecute for MaxAtomOp {
184 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
185 let arg_error = || ExecError::from("max-atom expects one argument: expression");
186 let expr = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?;
187 let children = expr.children();
188 if children.is_empty() {
189 Err(ExecError::from("Empty expression"))
190 } else {
191 children.into_iter().fold(Ok(f64::NEG_INFINITY), |res, x| {
192 match (res, Number::from_atom(x)) {
193 (res @ Err(_), _) => res,
194 (_, None) => Err(ExecError::from(format!("Only numbers are allowed in expression: {}", expr))),
195 (Ok(max), Some(x)) => Ok(f64::max(max, x.into())),
196 }
197 })
198 }.map(|max| vec![Atom::gnd(Number::Float(max))])
199 }
200}
201
202#[derive(Clone, Debug)]
203pub struct MinAtomOp {}
204
205grounded_op!(MinAtomOp, "min-atom");
206
207impl Grounded for MinAtomOp {
208 fn type_(&self) -> Atom {
209 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_NUMBER])
210 }
211
212 fn as_execute(&self) -> Option<&dyn CustomExecute> {
213 Some(self)
214 }
215}
216
217impl CustomExecute for MinAtomOp {
218 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
219 let arg_error = || ExecError::from("min-atom expects one argument: expression");
220 let expr = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?;
221 let children = expr.children();
222 if children.is_empty() {
223 Err(ExecError::from("Empty expression"))
224 } else {
225 children.into_iter().fold(Ok(f64::INFINITY), |res, x| {
226 match (res, Number::from_atom(x)) {
227 (res @ Err(_), _) => res,
228 (_, None) => Err(ExecError::from(format!("Only numbers are allowed in expression: {}", expr))),
229 (Ok(min), Some(x)) => Ok(f64::min(min, x.into())),
230 }
231 })
232 }.map(|min| vec![Atom::gnd(Number::Float(min))])
233 }
234}
235
236#[derive(Clone, Debug)]
237pub struct SizeAtomOp {}
238
239grounded_op!(SizeAtomOp, "size-atom");
240
241impl Grounded for SizeAtomOp {
242 fn type_(&self) -> Atom {
243 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER])
244 }
245
246 fn as_execute(&self) -> Option<&dyn CustomExecute> {
247 Some(self)
248 }
249}
250
251impl CustomExecute for SizeAtomOp {
252 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
253 let arg_error = || ExecError::from("size-atom expects one argument: expression");
254 let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children();
255 let size = children.len();
256 Ok(vec![Atom::gnd(Number::Integer(size as i64))])
257 }
258}
259
260#[derive(Clone, Debug)]
261pub struct IndexAtomOp {}
262
263grounded_op!(IndexAtomOp, "index-atom");
264
265impl Grounded for IndexAtomOp {
266 fn type_(&self) -> Atom {
267 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER, ATOM_TYPE_ATOM])
268 }
269
270 fn as_execute(&self) -> Option<&dyn CustomExecute> {
271 Some(self)
272 }
273}
274
275impl CustomExecute for IndexAtomOp {
276 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
277 let arg_error = || ExecError::from("index-atom expects two arguments: expression and atom");
278 let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children();
279 let index = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?;
280 match children.get(Into::<i64>::into(index) as usize) {
281 Some(atom) => Ok(vec![atom.clone()]),
282 None => Err(ExecError::from("Index is out of bounds")),
283 }
284 }
285}
286
287#[derive(Clone, Debug)]
288pub struct SubtractionAtomOp {}
289
290grounded_op!(SubtractionAtomOp, "subtraction-atom");
291
292impl Grounded for SubtractionAtomOp {
293 fn type_(&self) -> Atom {
294 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM])
295 }
296
297 fn as_execute(&self) -> Option<&dyn CustomExecute> {
298 Some(self)
299 }
300}
301
302impl CustomExecute for SubtractionAtomOp {
303 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
304 let arg_error = || ExecError::from("subtraction expects and executable LHS and RHS atom");
305 let mut lhs: Vec<Atom> = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().into();
306 let rhs = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children();
307
308 let mut rhs_index: MultiTrie<SymbolAtom, Vec<usize>> = MultiTrie::new();
309 for (index, rhs_item) in rhs.iter().enumerate() {
310 let k = atom_to_trie_key(&rhs_item);
311 let r = rhs_index.get(&k).next();
315 match r.cloned() {
316 Some(bucket) => {
317 rhs_index.remove(&k, &bucket);
318 let mut nbucket = bucket;
319 nbucket.push(index);
320 let nbucket = nbucket;
321 rhs_index.insert(k, nbucket);
322 }
323 None => { rhs_index.insert(k, vec![index]) }
324 }
325 }
326
327 lhs.retain(|candidate| {
328 let k = atom_to_trie_key(candidate);
329 let r = rhs_index.get(&k).next();
330 match r.cloned() {
331 None => { true }
332 Some(bucket) => {
333 match bucket.iter().position(|item| &rhs[*item] == candidate) {
334 None => { true }
335 Some(i) => {
336 rhs_index.remove(&k, &bucket);
337 if bucket.len() > 1 {
338 let mut nbucket = bucket;
339 nbucket.remove(i);
340 rhs_index.insert(k, nbucket);
341 }
342 false
343 }
344 }
345 }
346 }
347 });
348
349 Ok(vec![Atom::expr(lhs)])
350 }
351}
352
353#[derive(Clone, Debug)]
354pub struct GetTypeOp {
355 space: DynSpace,
356}
357
358grounded_op!(GetTypeOp, "get-type");
359
360impl GetTypeOp {
361 pub fn new(space: DynSpace) -> Self {
362 Self{ space }
363 }
364}
365
366impl Grounded for GetTypeOp {
367 fn type_(&self) -> Atom {
368 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_UNDEFINED])
369 }
370
371 fn as_execute(&self) -> Option<&dyn CustomExecute> {
372 Some(self)
373 }
374}
375
376impl CustomExecute for GetTypeOp {
377 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
378 let arg_error = || ExecError::from("get-type expects single atom as an argument");
379 let atom = args.get(0).ok_or_else(arg_error)?;
380 let space = match args.get(1) {
381 Some(space) => Atom::as_gnd::<DynSpace>(space)
382 .ok_or("match expects a space as the first argument"),
383 None => Ok(&self.space),
384 }?;
385 let types = get_atom_types(space, atom);
386 if types.iter().all(AtomType::is_error) {
387 Ok(vec![EMPTY_SYMBOL])
388 } else {
389 Ok(types.into_iter().filter(AtomType::is_valid).map(AtomType::into_atom).collect())
390 }
391 }
392}
393
394#[derive(Clone, Debug)]
395pub struct GetMetaTypeOp { }
396
397grounded_op!(GetMetaTypeOp, "get-metatype");
398
399impl Grounded for GetMetaTypeOp {
400 fn type_(&self) -> Atom {
401 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM])
402 }
403
404 fn as_execute(&self) -> Option<&dyn CustomExecute> {
405 Some(self)
406 }
407}
408
409impl CustomExecute for GetMetaTypeOp {
410 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
411 let arg_error = || ExecError::from("get-metatype expects single atom as an argument");
412 let atom = args.get(0).ok_or_else(arg_error)?;
413
414 Ok(vec![get_meta_type(&atom)])
415 }
416}
417
418#[derive(Clone, Debug)]
419pub struct GetTypeSpaceOp {}
420
421grounded_op!(GetTypeSpaceOp, "get-type-space");
422
423impl Grounded for GetTypeSpaceOp {
424 fn type_(&self) -> Atom {
425 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_SPACE, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM])
426 }
427
428 fn as_execute(&self) -> Option<&dyn CustomExecute> {
429 Some(self)
430 }
431}
432
433impl CustomExecute for GetTypeSpaceOp {
434 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
435 let arg_error = || ExecError::from("get-type-space expects two arguments: space and atom");
436 let space = args.get(0).ok_or_else(arg_error)?;
437 let space = Atom::as_gnd::<DynSpace>(space).ok_or("get-type-space expects a space as the first argument")?;
438 let atom = args.get(1).ok_or_else(arg_error)?;
439 log::debug!("GetTypeSpaceOp::execute: space: {}, atom: {}", space, atom);
440 let types = get_atom_types(space, atom);
441 if types.iter().all(AtomType::is_error) {
442 Ok(vec![EMPTY_SYMBOL])
443 } else {
444 Ok(types.into_iter().filter(AtomType::is_valid).map(AtomType::into_atom).collect())
445 }
446 }
447}
448
449pub(super) fn register_context_dependent_tokens(tref: &mut Tokenizer, space: &DynSpace) {
450 let get_type_op = Atom::gnd(GetTypeOp::new(space.clone()));
451 tref.register_token(regex(r"get-type"), move |_| { get_type_op.clone() });
452}
453
454pub(super) fn register_context_independent_tokens(tref: &mut Tokenizer) {
455 let get_type_space_op = Atom::gnd(GetTypeSpaceOp{});
456 tref.register_token(regex(r"get-type-space"), move |_| { get_type_space_op.clone() });
457 let get_meta_type_op = Atom::gnd(GetMetaTypeOp{});
458 tref.register_token(regex(r"get-metatype"), move |_| { get_meta_type_op.clone() });
459 let min_atom_op = Atom::gnd(MinAtomOp{});
460 tref.register_token(regex(r"min-atom"), move |_| { min_atom_op.clone() });
461 let max_atom_op = Atom::gnd(MaxAtomOp{});
462 tref.register_token(regex(r"max-atom"), move |_| { max_atom_op.clone() });
463 let size_atom_op = Atom::gnd(SizeAtomOp{});
464 tref.register_token(regex(r"size-atom"), move |_| { size_atom_op.clone() });
465 let index_atom_op = Atom::gnd(IndexAtomOp{});
466 tref.register_token(regex(r"index-atom"), move |_| { index_atom_op.clone() });
467 let unique_op = Atom::gnd(UniqueAtomOp{});
468 tref.register_token(regex(r"unique-atom"), move |_| { unique_op.clone() });
469 let subtraction_op = Atom::gnd(SubtractionAtomOp{});
470 tref.register_token(regex(r"subtraction-atom"), move |_| { subtraction_op.clone() });
471 let intersection_op = Atom::gnd(IntersectionAtomOp{});
472 tref.register_token(regex(r"intersection-atom"), move |_| { intersection_op.clone() });
473 let union_op = Atom::gnd(UnionAtomOp{});
474 tref.register_token(regex(r"union-atom"), move |_| { union_op.clone() });
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480 use crate::metta::text::SExprParser;
481 use crate::metta::runner::EnvBuilder;
482 use hyperon_atom::gnd::str::Str;
483 use crate::space::grounding::metta_space;
484 use crate::metta::runner::run_program;
485 use crate::metta::runner::Metta;
486 use crate::metta::runner::stdlib::arithmetics::SumOp;
487 use hyperon_common::{assert_eq_metta_results, assert_eq_no_order};
488
489 #[test]
490 fn metta_car_atom() {
491 let result = run_program("!(car-atom (A $b))");
492 assert_eq!(result, Ok(vec![vec![expr!("A")]]));
493 let result = run_program("!(car-atom ($a B))");
494 assert_eq!(result, Ok(vec![vec![expr!(a)]]));
495 let result = run_program("!(car-atom ())");
496 assert_eq!(result, Ok(vec![vec![expr!("Error" ("car-atom" ()) {Str::from_str("car-atom expects a non-empty expression as an argument")})]]));
497 let result = run_program("!(car-atom A)");
498 assert_eq!(result, Ok(vec![vec![expr!("Error" ("car-atom" "A") {Str::from_str("car-atom expects a non-empty expression as an argument")})]]));
499 }
500
501 #[test]
502 fn metta_cdr_atom() {
503 assert_eq!(run_program(&format!("!(cdr-atom (a b c))")), Ok(vec![vec![expr!("b" "c")]]));
504 assert_eq!(run_program(&format!("!(cdr-atom ($a b $c))")), Ok(vec![vec![expr!("b" c)]]));
505 assert_eq!(run_program(&format!("!(cdr-atom ())")), Ok(vec![vec![expr!("Error" ("cdr-atom" ()) {Str::from_str("cdr-atom expects a non-empty expression as an argument")})]]));
506 assert_eq!(run_program(&format!("!(cdr-atom a)")), Ok(vec![vec![expr!("Error" ("cdr-atom" "a") {Str::from_str("cdr-atom expects a non-empty expression as an argument")})]]));
507 assert_eq!(run_program(&format!("!(cdr-atom $a)")), Ok(vec![vec![expr!("Error" ("cdr-atom" a) {Str::from_str("cdr-atom expects a non-empty expression as an argument")})]]));
508 }
509
510 #[test]
511 fn metta_size_atom() {
512 assert_eq!(run_program(&format!("!(size-atom (5 4 3 2 1))")), Ok(vec![vec![expr!({Number::Integer(5)})]]));
513 assert_eq!(run_program(&format!("!(size-atom ())")), Ok(vec![vec![expr!({Number::Integer(0)})]]));
514 }
515
516 #[test]
517 fn metta_min_atom() {
518 assert_eq!(run_program(&format!("!(min-atom (5 4 5.5))")), Ok(vec![vec![expr!({Number::Integer(4)})]]));
519 assert_eq!(run_program(&format!("!(min-atom ())")), Ok(vec![vec![expr!("Error" ({ MinAtomOp{} } ()) "Empty expression")]]));
520 assert_eq!(run_program(&format!("!(min-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ MinAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers are allowed in expression: (3 A B 5)")]]));
521 assert_eq!(run_program(&format!("(= (nums) (5 4 5.5)) !(min-atom (nums))")), Ok(vec![vec![expr!({Number::Integer(4)})]]));
522 }
523
524 #[test]
525 fn metta_max_atom() {
526 assert_eq!(run_program(&format!("!(max-atom (5 4 5.5))")), Ok(vec![vec![expr!({Number::Float(5.5)})]]));
527 assert_eq!(run_program(&format!("!(max-atom ())")), Ok(vec![vec![expr!("Error" ({ MaxAtomOp{} } ()) "Empty expression")]]));
528 assert_eq!(run_program(&format!("!(max-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ MaxAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers are allowed in expression: (3 A B 5)")]]));
529 assert_eq!(run_program(&format!("(= (nums) (5 4 5.5)) !(max-atom (nums))")), Ok(vec![vec![expr!({Number::Float(5.5)})]]));
530 }
531
532 #[test]
533 fn metta_index_atom() {
534 assert_eq!(run_program(&format!("!(index-atom (5 4 3 2 1) 2)")), Ok(vec![vec![expr!({Number::Integer(3)})]]));
535 assert_eq!(run_program(&format!("!(index-atom (A B C D E) 5)")), Ok(vec![vec![expr!("Error" ({ IndexAtomOp{} } ("A" "B" "C" "D" "E") {Number::Integer(5)}) "Index is out of bounds")]]));
536 }
537
538 #[test]
539 fn metta_filter_atom() {
540 assert_eq!(run_program("!(filter-atom () $x (eval (if-error $x False True)))"), Ok(vec![vec![expr!()]]));
541 assert_eq!(run_program("!(filter-atom (a (b) $c) $x (eval (if-error $x False True)))"), Ok(vec![vec![expr!("a" ("b") c)]]));
542 assert_eq!(run_program("!(filter-atom (a (Error (b) \"Test error\") $c) $x (eval (if-error $x False True)))"), Ok(vec![vec![expr!("a" c)]]));
543 }
544
545 #[test]
546 fn metta_map_atom() {
547 assert_eq!(run_program("!(map-atom () $x ($x mapped))"), Ok(vec![vec![expr!()]]));
548 assert_eq!(run_program("!(map-atom (a (b) $c) $x (mapped $x))"), Ok(vec![vec![expr!(("mapped" "a") ("mapped" ("b")) ("mapped" c))]]));
549 }
550
551 #[test]
552 fn metta_foldl_atom() {
553 assert_eq!(run_program("!(foldl-atom () 1 $a $b (eval (+ $a $b)))"), Ok(vec![vec![expr!({Number::Integer(1)})]]));
554 assert_eq!(run_program("!(foldl-atom (1 2 3) 0 $a $b (eval (+ $a $b)))"), Ok(vec![vec![expr!({Number::Integer(6)})]]));
555 }
556
557 #[test]
558 fn size_atom_op() {
559 let res = SizeAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)})]).expect("No result returned");
560 assert_eq!(res, vec![expr!({Number::Integer(5)})]);
561 let res = SizeAtomOp{}.execute(&mut vec![expr!()]).expect("No result returned");
562 assert_eq!(res, vec![expr!({Number::Integer(0)})]);
563 }
564
565 #[test]
566 fn min_atom_op() {
567 let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned");
568 assert_eq!(res, vec![expr!({Number::Integer(4)})]);
569 let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]);
570 assert_eq!(res, Err(ExecError::from("Only numbers are allowed in expression: (5 4 A)")));
571 let res = MinAtomOp{}.execute(&mut vec![expr!()]);
572 assert_eq!(res, Err(ExecError::from("Empty expression")));
573 }
574
575 #[test]
576 fn max_atom_op() {
577 let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned");
578 assert_eq!(res, vec![expr!({Number::Float(5.5)})]);
579 let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]);
580 assert_eq!(res, Err(ExecError::from("Only numbers are allowed in expression: (5 4 A)")));
581 let res = MaxAtomOp{}.execute(&mut vec![expr!()]);
582 assert_eq!(res, Err(ExecError::from("Empty expression")));
583 let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(4)} {Str::from_str("5")})]);
584 assert_eq!(res, Err(ExecError::from("Only numbers are allowed in expression: (4 \"5\")")));
585 }
586
587 #[test]
588 fn index_atom_op() {
589 let res = IndexAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)}), expr!({Number::Integer(2)})]).expect("No result returned");
590 assert_eq!(res, vec![expr!({Number::Integer(3)})]);
591 let res = IndexAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)}), expr!({Number::Integer(5)})]);
592 assert_eq!(res, Err(ExecError::from("Index is out of bounds")));
593 }
594
595 #[test]
596 fn test_error_is_used_as_an_argument() {
597 let metta = Metta::new(Some(EnvBuilder::test_env()));
598 let parser = SExprParser::new(r#"
599 !(get-type Error)
600 !(get-metatype Error)
601 !(get-type (Error Foo Boo))
602 !(Error (+ 1 2) (+ 1 +))
603 "#);
604
605 assert_eq_metta_results!(metta.run(parser), Ok(vec![
606 vec![expr!("->" "Atom" "Atom" "ErrorType")],
607 vec![expr!("Symbol")],
608 vec![expr!("ErrorType")],
609 vec![expr!("Error" ({SumOp{}} {Number::Integer(1)} {Number::Integer(2)}) ({SumOp{}} {Number::Integer(1)} {SumOp{}}))],
610 ]));
611 }
612
613
614 #[test]
615 fn unique_op() {
616 let unique_op = UniqueAtomOp{};
617 let actual = unique_op.execute(&mut vec![expr!(
618 ("A" ("B" "C"))
619 ("A" ("B" "C"))
620 ("f" "g")
621 ("f" "g")
622 ("f" "g")
623 "Z"
624 )]).unwrap();
625 assert_eq_no_order!(actual,
626 vec![expr!(("A" ("B" "C")) ("f" "g") "Z")]);
627 }
628
629 #[test]
630 fn unique_op_() {
631 let unique_op = UniqueAtomOp{};
632 let yonas = VariableAtom::new("yonas");
633 let tol = VariableAtom::new("tol");
634 let name_var = VariableAtom::new("name");
635
636 let input = Atom::expr([
638 Atom::expr([Atom::sym("name"), Atom::Variable(yonas.clone())]),
639 Atom::expr([Atom::sym("name"), Atom::Variable(tol.clone())]),
640 Atom::expr([Atom::Variable(name_var.clone()), Atom::Variable(tol.clone())])
641 ]);
642
643 let actual = unique_op.execute(&[input]).unwrap();
644 let expected = vec![Atom::expr([
647 Atom::expr([Atom::sym("name"), Atom::Variable(yonas.clone())]),
648 Atom::expr([Atom::Variable(name_var.clone()), Atom::Variable(tol.clone())])
649 ])];
650
651 assert_eq!(actual, expected);
652 }
653
654 #[test]
655 fn union_op() {
656 let union_op = UnionAtomOp{};
657 let actual = union_op.execute(&mut vec![expr!(
658 ("A" ("B" "C"))
659 ("A" ("B" "C"))
660 ("f" "g")
661 ("f" "g")
662 ("f" "g")
663 "Z"
664 ), expr!(
665 ("A" ("B" "C"))
666 "p"
667 "p"
668 ("Q" "a")
669 )]).unwrap();
670 assert_eq_no_order!(actual,
671 vec![expr!(("A" ("B" "C")) ("A" ("B" "C"))
672 ("f" "g") ("f" "g") ("f" "g") "Z"
673 ("A" ("B" "C")) "p" "p" ("Q" "a"))]);
674 }
675
676 #[test]
677 fn index_atom_to_key() {
678 assert_eq!(atom_to_trie_key(&Atom::sym("A")), TrieKey::from([TrieToken::Exact(SymbolAtom::new("A".into()))]));
679 assert_eq!(atom_to_trie_key(&Atom::value(1)), TrieKey::from([TrieToken::Wildcard]));
680 assert_eq!(atom_to_trie_key(&Atom::var("a")), TrieKey::from([TrieToken::Wildcard]));
681 assert_eq!(atom_to_trie_key(&expr!("A" "B")), TrieKey::from([
682 TrieToken::LeftPar,
683 TrieToken::Exact(SymbolAtom::new("A".into())),
684 TrieToken::Exact(SymbolAtom::new("B".into())),
685 TrieToken::RightPar
686 ]));
687 }
688
689 #[test]
690 fn intersection_op() {
691 let intersection_op = IntersectionAtomOp{};
692 let actual = intersection_op.execute(&mut vec![expr!(
693 "Z"
694 ("A" ("B" "C"))
695 ("A" ("B" "C"))
696 ("f" "g")
697 ("f" "g")
698 ("f" "g")
699 ("P" "b")
700 ), expr!(
701 ("f" "g")
702 ("f" "g")
703 ("A" ("B" "C"))
704 "p"
705 "p"
706 ("Q" "a")
707 "Z"
708 )]).unwrap();
709 assert_eq_no_order!(actual, vec![expr!("Z" ("A" ("B" "C")) ("f" "g") ("f" "g"))]);
710
711 assert_eq_no_order!(intersection_op.execute(&mut vec![expr!(
712 { Number::Integer(5) }
713 { Number::Integer(4) }
714 { Number::Integer(3) }
715 { Number::Integer(2) }
716 ), expr!(
717 { Number::Integer(5) }
718 { Number::Integer(3) }
719 )]).unwrap(), vec![expr!({Number::Integer(5)} {Number::Integer(3)})]);
720 }
721
722 #[test]
723 fn subtraction_op() {
724 let subtraction_op = SubtractionAtomOp{};
725 let actual = subtraction_op.execute(&mut vec![expr!(
726 "Z"
727 "S"
728 "S"
729 ("A" ("B" "C"))
730 ("A" ("B" "C"))
731 ("f" "g")
732 ("f" "g")
733 ("f" "g")
734 ("P" "b")
735 ), expr!(
736 ("f" "g")
737 ("A" ("B" "C"))
738 "p"
739 "P"
740 ("Q" "a")
741 "Z"
742 "S"
743 "S"
744 "S"
745 )]).unwrap();
746 assert_eq_no_order!(actual,
747 vec![expr!(("A" ("B" "C")) ("f" "g") ("f" "g") ("P" "b"))]);
748 }
749
750 #[test]
751 fn get_type_op() {
752 let space = metta_space("
753 (: B Type)
754 (: C Type)
755 (: A B)
756 (: A C)
757 ");
758
759 let get_type_op = GetTypeOp::new(space.clone());
760 assert_eq_no_order!(get_type_op.execute(&mut vec![sym!("A"), expr!({space.clone()})]).unwrap(),
761 vec![sym!("B"), sym!("C")]);
762 }
763
764 #[test]
765 fn get_type_op_non_valid_atom() {
766 let space = metta_space("
767 (: f (-> Number String))
768 (: 42 Number)
769 (: \"test\" String)
770 ");
771
772 let get_type_op = GetTypeOp::new(space.clone());
773 assert_eq_no_order!(get_type_op.execute(&mut vec![expr!("f" "42"), expr!({space.clone()})]).unwrap(),
774 vec![sym!("String")]);
775 assert_eq_no_order!(get_type_op.execute(&mut vec![expr!("f" "\"test\""), expr!({space.clone()})]).unwrap(),
776 vec![EMPTY_SYMBOL]);
777 }
778}