hyperon/metta/runner/stdlib/
debug.rs1use hyperon_atom::*;
2use crate::metta::*;
3use crate::metta::text::Tokenizer;
4use hyperon_common::collections::{SliceDisplay, Equality, DefaultEquality};
5use hyperon_common::assert::compare_vec_no_order;
6use hyperon_atom::matcher::atoms_are_equivalent;
7use crate::metta::runner::stdlib::{grounded_op, regex, unit_result};
8use hyperon_atom::gnd::bool::*;
9use hyperon_atom::gnd::str::*;
10use hyperon_atom::gnd::GroundedFunctionAtom;
11
12use std::convert::TryInto;
13
14#[derive(Clone, Debug)]
47pub struct TraceOp {}
48
49grounded_op!(TraceOp, "trace!");
50
51impl Grounded for TraceOp {
52 fn type_(&self) -> Atom {
53 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM, ATOM_TYPE_UNDEFINED])
54 }
55
56 fn as_execute(&self) -> Option<&dyn CustomExecute> {
57 Some(self)
58 }
59}
60
61impl CustomExecute for TraceOp {
62 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
63 let arg_error = || ExecError::from("trace! expects two atoms as arguments");
64 let val = args.get(1).ok_or_else(arg_error)?;
65 let msg = args.get(0).ok_or_else(arg_error)?;
66 eprintln!("{}", msg);
67 Ok(vec![val.clone()])
68 }
69}
70
71#[derive(Clone, Debug)]
72pub struct PrintAlternativesOp {}
73
74grounded_op!(PrintAlternativesOp, "print-alternatives!");
75
76impl Grounded for PrintAlternativesOp {
77 fn type_(&self) -> Atom {
78 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_EXPRESSION, UNIT_TYPE])
79 }
80
81 fn as_execute(&self) -> Option<&dyn CustomExecute> {
82 Some(self)
83 }
84}
85
86impl CustomExecute for PrintAlternativesOp {
87 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
88 let arg_error = || ExecError::from("print-alternatives! expects format string as a first argument and expression as a second argument");
89 let atom = atom_to_string(args.get(0).ok_or_else(arg_error)?);
90 let args = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?;
91 let args: Vec<String> = args.children().iter()
92 .map(|atom| atom_to_string(atom))
93 .collect();
94 println!("{} {}:", args.len(), atom);
95 args.iter().for_each(|arg| println!(" {}", arg));
96 Ok(vec![UNIT_ATOM])
97 }
98}
99
100struct AlphaEquality{}
101
102impl Equality<&Atom> for AlphaEquality {
103 fn eq(a: &&Atom, b: &&Atom) -> bool {
104 atoms_are_equivalent(*a, *b)
105 }
106}
107
108fn assert_results_are_equal<'a, E: Equality<&'a Atom>>(args: &'a [Atom], cmp: E) -> Result<Vec<Atom>, ExecError> {
109 let arg_error = || ExecError::from("Pair of evaluation results with bindings is expected as an argument");
110 let actual = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children();
111 let expected = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children();
112 let assert = args.get(2).ok_or_else(arg_error)?;
113
114
115 let report = format!("\nExpected: {}\nGot: {}", SliceDisplay(expected), SliceDisplay(actual));
116
117 match compare_vec_no_order(actual.iter(), expected.iter(), cmp).as_display() {
118 None => unit_result(),
119 Some(diff) => {
120 let msg = match args.get(3) {
121 None => Atom::gnd(Str::from_string(format!("{}\n{}", report, diff))),
122 Some(m) => m.clone(),
123 };
124 Ok(vec![Atom::expr([ERROR_SYMBOL, assert.clone(), msg])])
125 },
126 }
127}
128
129#[derive(Clone, Debug)]
130pub struct AlphaEqOp {
131}
132grounded_op!(AlphaEqOp, "=alpha");
133
134impl Grounded for AlphaEqOp {
135 fn type_(&self) -> Atom {
136 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_BOOL])
137 }
138
139 fn as_execute(&self) -> Option<&dyn CustomExecute> {
140 Some(self)
141 }
142}
143
144impl CustomExecute for AlphaEqOp {
145 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
146 log::debug!("AlphaEqOp::execute: {:?}", args);
147 let arg_error = || ExecError::from("=alpha expects two atoms as arguments: actual and expected");
148 let actual_atom = args.get(0).ok_or_else(arg_error)?;
149 let expected_atom = args.get(1).ok_or_else(arg_error)?;
150
151 Ok(vec![Atom::gnd(Bool(atoms_are_equivalent(actual_atom, expected_atom)))])
152 }
153}
154
155pub(super) fn register_context_independent_tokens(tref: &mut Tokenizer) {
156 let trace_op = Atom::gnd(TraceOp{});
157 tref.register_token(regex(r"trace!"), move |_| { trace_op.clone() });
158 let print_alternatives_op = Atom::gnd(PrintAlternativesOp{});
159 tref.register_token(regex(r"print-alternatives!"), move |_| { print_alternatives_op.clone() });
160 let alpha_eq_op = Atom::gnd(AlphaEqOp{});
161 tref.register_token(regex(r"=alpha"), move |_| { alpha_eq_op.clone() });
162 tref.register_function(GroundedFunctionAtom::new(
163 r"_assert-results-are-equal".into(),
164 expr!("->" "Atom" "Atom" "Atom" ("->")),
165 |args: &[Atom]| -> Result<Vec<Atom>, ExecError> { assert_results_are_equal(args, DefaultEquality{}) },
166 ));
167 tref.register_function(GroundedFunctionAtom::new(
168 r"_assert-results-are-alpha-equal".into(),
169 expr!("->" "Atom" "Atom" "Atom" ("->")),
170 |args: &[Atom]| -> Result<Vec<Atom>, ExecError> { assert_results_are_equal(args, AlphaEquality{}) },
171 ));
172 tref.register_function(GroundedFunctionAtom::new(
173 r"_assert-results-are-equal-msg".into(),
174 expr!("->" "Atom" "Atom" "Atom" "Atom" ("->")),
175 |args: &[Atom]| -> Result<Vec<Atom>, ExecError> { assert_results_are_equal(args, DefaultEquality{}) },
176 ));
177 tref.register_function(GroundedFunctionAtom::new(
178 r"_assert-results-are-alpha-equal-msg".into(),
179 expr!("->" "Atom" "Atom" "Atom" "Atom" ("->")),
180 |args: &[Atom]| -> Result<Vec<Atom>, ExecError> { assert_results_are_equal(args, AlphaEquality{}) },
181 ));
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use crate::metta::runner::{Metta, EnvBuilder, SExprParser};
188 use crate::metta::runner::run_program;
189
190 #[test]
191 fn metta_assert_equal_op() {
192 let metta = Metta::new(Some(EnvBuilder::test_env()));
193 let program = "
194 (= (foo $x) $x)
195 (= (bar $x) $x)
196 ";
197 assert_eq!(metta.run(SExprParser::new(program)), Ok(vec![]));
198 assert_eq!(metta.run(SExprParser::new("!(assertEqual (foo A) (bar A))")), Ok(vec![
199 vec![UNIT_ATOM],
200 ]));
201 assert_eq!(metta.run(SExprParser::new("!(assertEqual (foo A) (bar B))")), Ok(vec![
202 vec![expr!("Error" ("assertEqual" ("foo" "A") ("bar" "B")) {Str::from_str("\nExpected: [B]\nGot: [A]\nMissed results: B\nExcessive results: A")})],
203 ]));
204 assert_eq!(metta.run(SExprParser::new("!(assertEqual (foo A) Empty)")), Ok(vec![
205 vec![expr!("Error" ("assertEqual" ("foo" "A") "Empty") {Str::from_str("\nExpected: []\nGot: [A]\nExcessive results: A")})]
206 ]));
207 }
208
209 #[test]
210 fn metta_assert_alpha_equal_op() {
211 let metta = Metta::new(Some(EnvBuilder::test_env()));
212 let program = "
213 (= (foo $x) $x)
214 (= (bar $x) $x)
215 ";
216 assert_eq!(metta.run(SExprParser::new(program)), Ok(vec![]));
217 assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo $x) (bar $x))")), Ok(vec![
218 vec![UNIT_ATOM],
219 ]));
220 assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo A) (bar B))")), Ok(vec![
221 vec![expr!("Error" ("assertAlphaEqual" ("foo" "A") ("bar" "B")) {Str::from_str("\nExpected: [B]\nGot: [A]\nMissed results: B\nExcessive results: A")})],
222 ]));
223 assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo A) Empty)")), Ok(vec![
224 vec![expr!("Error" ("assertAlphaEqual" ("foo" "A") "Empty") {Str::from_str("\nExpected: []\nGot: [A]\nExcessive results: A")})]
225 ]));
226 }
227
228 #[test]
229 fn metta_alpha_eq_op() {
230 assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(let $foo (foo) (=alpha $foo (R $x $y)))")), Ok(vec![vec![expr!({Bool(true)})]]));
231 assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(let $foo (foo) (=alpha $foo (R $x $x)))")), Ok(vec![vec![expr!({Bool(false)})]]));
232 }
233
234 #[test]
235 fn metta_assert_equal_to_result_op() {
236 let metta = Metta::new(Some(EnvBuilder::test_env()));
237 let program = "
238 (= (foo) A)
239 (= (foo) B)
240 (= (bar) C)
241 (= (baz) D)
242 (= (baz) D)
243 (= (baz) D)
244 ";
245 assert_eq!(metta.run(SExprParser::new(program)), Ok(vec![]));
246 assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (foo) (A B))")), Ok(vec![
247 vec![UNIT_ATOM],
248 ]));
249 assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (bar) (A))")), Ok(vec![
250 vec![expr!("Error" ("assertEqualToResult" ("bar") ("A")) {Str::from_str("\nExpected: [A]\nGot: [C]\nMissed results: A\nExcessive results: C")})],
251 ]));
252 assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (baz) (D))")), Ok(vec![
253 vec![expr!("Error" ("assertEqualToResult" ("baz") ("D")) {Str::from_str("\nExpected: [D]\nGot: [D, D, D]\nExcessive results: D, D")})]
254 ]));
255 }
256
257 #[test]
258 fn metta_assert_alpha_equal_to_result_op() {
259 let metta = Metta::new(Some(EnvBuilder::test_env()));
260 let program = "
261 (= (foo) $x)
262 (= (bar) C)
263 (= (baz) D)
264 (= (baz) D)
265 (= (baz) D)
266 ";
267 assert_eq!(metta.run(SExprParser::new(program)), Ok(vec![]));
268 assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult (foo) ($x))")), Ok(vec![
269 vec![UNIT_ATOM],
270 ]));
271 assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult ((foo) (foo)) (($x $y)))")), Ok(vec![
272 vec![UNIT_ATOM],
273 ]));
274
275 let res = metta.run(SExprParser::new("!(assertAlphaEqualToResult ((foo) (foo)) (($x $x)))")).unwrap();
276 let res_first_atom = res.get(0).unwrap().get(0);
277 assert_eq!(res_first_atom.unwrap().iter().next().unwrap(), &sym!("Error"));
278 assert_eq!(res.get(0).unwrap().len(), 1);
279
280 assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult (bar) (A))")), Ok(vec![
281 vec![expr!("Error" ("assertAlphaEqualToResult" ("bar") ("A")) {Str::from_str("\nExpected: [A]\nGot: [C]\nMissed results: A\nExcessive results: C")})],
282 ]));
283 assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult (baz) (D))")), Ok(vec![
284 vec![expr!("Error" ("assertAlphaEqualToResult" ("baz") ("D")) {Str::from_str("\nExpected: [D]\nGot: [D, D, D]\nExcessive results: D, D")})]
285 ]));
286 }
287
288 #[test]
289 fn trace_op() {
290 assert_eq!(TraceOp{}.execute(&mut vec![sym!("\"Here?\""), sym!("42")]),
291 Ok(vec![sym!("42")]));
292 }
293}