1use hyperon_atom::*;
2use crate::metta::*;
3use crate::metta::text::Tokenizer;
4use super::regex;
5use hyperon_atom::gnd::number::*;
6use hyperon_atom::gnd::bool::*;
7
8use std::fmt::Display;
9
10macro_rules! def_binary_number_op {
11 ($name:ident, $op:tt, $r:ident, $ret_type:ident) => {
12 #[derive(Clone, PartialEq, Debug)]
13 pub struct $name{}
14
15 impl Display for $name {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, stringify!($op))
18 }
19 }
20
21 impl Grounded for $name {
22 fn type_(&self) -> Atom {
23 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, $r])
24 }
25
26 fn as_execute(&self) -> Option<&dyn CustomExecute> {
27 Some(self)
28 }
29 }
30
31 impl CustomExecute for $name {
32 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
33 let arg_error = || ExecError::IncorrectArgument;
34 let a = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
35 let b = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?;
36
37 let (a, b) = Number::promote(a, b);
38 let res: $ret_type = match (a, b) {
39 (Number::Integer(a), Number::Integer(b)) => (a $op b).into(),
40 (Number::Float(a), Number::Float(b)) => (a $op b).into(),
41 _ => panic!("Unexpected state"),
42 };
43
44 Ok(vec![Atom::gnd(res)])
45 }
46 }
47 }
48}
49
50def_binary_number_op!(SumOp, +, ATOM_TYPE_NUMBER, Number);
51def_binary_number_op!(SubOp, -, ATOM_TYPE_NUMBER, Number);
52def_binary_number_op!(MulOp, *, ATOM_TYPE_NUMBER, Number);
53def_binary_number_op!(ModOp, %, ATOM_TYPE_NUMBER, Number);
54def_binary_number_op!(LessOp, <, ATOM_TYPE_BOOL, Bool);
55def_binary_number_op!(GreaterOp, >, ATOM_TYPE_BOOL, Bool);
56def_binary_number_op!(LessEqOp, <=, ATOM_TYPE_BOOL, Bool);
57def_binary_number_op!(GreaterEqOp, >=, ATOM_TYPE_BOOL, Bool);
58
59macro_rules! def_binary_bool_op {
60 ($name:ident, $disp:ident, $op:tt) => {
61 #[derive(Clone, PartialEq, Debug)]
62 pub struct $name{}
63
64 impl Display for $name {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, stringify!($disp))
67 }
68 }
69
70 impl Grounded for $name {
71 fn type_(&self) -> Atom {
72 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL])
73 }
74
75 fn as_execute(&self) -> Option<&dyn CustomExecute> {
76 Some(self)
77 }
78 }
79
80 impl CustomExecute for $name {
81 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
82 let arg_error = || ExecError::IncorrectArgument;
83 let Bool(a) = args.get(0).and_then(Bool::from_atom).ok_or_else(arg_error)?;
84 let Bool(b) = args.get(1).and_then(Bool::from_atom).ok_or_else(arg_error)?;
85
86 Ok(vec![Atom::gnd(Bool(a $op b))])
87 }
88 }
89 }
90}
91
92def_binary_bool_op!(AndOp, and, &&);
93def_binary_bool_op!(OrOp, or, ||);
94
95def_binary_bool_op!(XorOp, xor, ^);
97
98
99#[derive(Clone, PartialEq, Debug)]
100pub struct NotOp{}
101
102impl Display for NotOp {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 write!(f, "not")
105 }
106}
107
108impl Grounded for NotOp {
109 fn type_(&self) -> Atom {
110 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL])
111 }
112
113 fn as_execute(&self) -> Option<&dyn CustomExecute> {
114 Some(self)
115 }
116}
117
118impl CustomExecute for NotOp {
119 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
120 let arg_error = || ExecError::IncorrectArgument;
121 let Bool(a) = args.get(0).and_then(Bool::from_atom).ok_or_else(arg_error)?;
122
123 Ok(vec![Atom::gnd(Bool(!a))])
124 }
125}
126
127#[derive(Clone, PartialEq, Debug)]
128pub struct DivOp{}
129
130impl Display for DivOp {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 write!(f, "/")
133 }
134}
135
136impl Grounded for DivOp {
137 fn type_(&self) -> Atom {
138 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
139 }
140
141 fn as_execute(&self) -> Option<&dyn CustomExecute> {
142 Some(self)
143 }
144}
145
146impl CustomExecute for DivOp {
147 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
148 let arg_error = || ExecError::from("Divide expects two numbers: dividend and divisor");
149 let dividend = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
150 let divisor = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?;
151
152 let (dividend, divisor) = Number::promote(dividend, divisor);
153 match (dividend, divisor) {
154 (Number::Integer(_), Number::Integer(0)) => Err(ExecError::from("DivisionByZero")),
155 (Number::Integer(a), Number::Integer(b)) => Ok(vec![Atom::gnd(Number::Integer(a / b))]),
156 (Number::Float(a), Number::Float(b)) => Ok(vec![Atom::gnd(Number::Float(a / b))]),
157 _ => panic!("Unexpected state")
158 }
159 }
160}
161
162pub(super) fn register_context_independent_tokens(tref: &mut Tokenizer) {
163 tref.register_fallible_token(regex(r"[\-\+]?\d+"),
164 |token| { Ok(Atom::gnd(Number::from_int_str(token)?)) });
165 tref.register_fallible_token(regex(r"[\-\+]?\d+\.\d+"),
166 |token| { Ok(Atom::gnd(Number::from_float_str(token)?)) });
167 tref.register_fallible_token(regex(r"[\-\+]?\d+(\.\d+)?[eE][\-\+]?\d+"),
168 |token| { Ok(Atom::gnd(Number::from_float_str(token)?)) });
169 tref.register_token(regex(r"True|False"),
170 |token| { Atom::gnd(Bool::from_str(token)) });
171
172 let sum_op = Atom::gnd(SumOp{});
173 tref.register_token(regex(r"\+"), move |_| { sum_op.clone() });
174 let sub_op = Atom::gnd(SubOp{});
175 tref.register_token(regex(r"\-"), move |_| { sub_op.clone() });
176 let mul_op = Atom::gnd(MulOp{});
177 tref.register_token(regex(r"\*"), move |_| { mul_op.clone() });
178 let div_op = Atom::gnd(DivOp{});
179 tref.register_token(regex(r"/"), move |_| { div_op.clone() });
180 let mod_op = Atom::gnd(ModOp{});
181 tref.register_token(regex(r"%"), move |_| { mod_op.clone() });
182 let lt_op = Atom::gnd(LessOp{});
183 tref.register_token(regex(r"<"), move |_| { lt_op.clone() });
184 let gt_op = Atom::gnd(GreaterOp{});
185 tref.register_token(regex(r">"), move |_| { gt_op.clone() });
186 let le_op = Atom::gnd(LessEqOp{});
187 tref.register_token(regex(r"<="), move |_| { le_op.clone() });
188 let ge_op = Atom::gnd(GreaterEqOp{});
189 tref.register_token(regex(r">="), move |_| { ge_op.clone() });
190 let and_op = Atom::gnd(AndOp{});
191 tref.register_token(regex(r"and"), move |_| { and_op.clone() });
192 let or_op = Atom::gnd(OrOp{});
193 tref.register_token(regex(r"or"), move |_| { or_op.clone() });
194 let not_op = Atom::gnd(NotOp{});
195 tref.register_token(regex(r"not"), move |_| { not_op.clone() });
196 let xor_op = Atom::gnd(XorOp{});
198 tref.register_token(regex(r"xor"), move |_| { xor_op.clone() });
199}
200
201#[cfg(test)]
202mod tests {
203 use crate::metta::runner::run_program;
204 use super::*;
205
206 macro_rules! assert_binary_op {
207 ($name:ident, $a: expr, $b: expr, $r: expr) => {
208 assert_eq!($name{}.execute(&mut vec![Atom::gnd($a), Atom::gnd($b)]), Ok(vec![Atom::gnd($r)]));
209 }
210 }
211
212 macro_rules! assert_unary_op {
213 ($name:ident, $a: expr, $r: expr) => {
214 assert_eq!($name{}.execute(&mut vec![Atom::gnd($a)]), Ok(vec![Atom::gnd($r)]));
215 }
216 }
217
218 #[test]
219 fn div_errors() {
220 assert_eq!(run_program(&format!("!(assertEqual (/ 5 0) (Error (/ 5 0) DivisionByZero))")), Ok(vec![vec![UNIT_ATOM]]));
221 assert_eq!(run_program(&format!("!(assertEqual (let $div (/ 5.0 0.0) (isinf-math $div)) True)")), Ok(vec![vec![UNIT_ATOM]]));
222 }
223
224 #[test]
225 fn and() {
226 assert_binary_op!(AndOp, Bool(true), Bool(true), Bool(true));
227 assert_binary_op!(AndOp, Bool(true), Bool(false), Bool(false));
228 assert_binary_op!(AndOp, Bool(false), Bool(true), Bool(false));
229 assert_binary_op!(AndOp, Bool(false), Bool(false), Bool(false));
230 }
231
232 #[test]
233 fn or() {
234 assert_binary_op!(OrOp, Bool(true), Bool(true), Bool(true));
235 assert_binary_op!(OrOp, Bool(true), Bool(false), Bool(true));
236 assert_binary_op!(OrOp, Bool(false), Bool(true), Bool(true));
237 assert_binary_op!(OrOp, Bool(false), Bool(false), Bool(false));
238 }
239
240 #[test]
241 fn not() {
242 assert_unary_op!(NotOp, Bool(true), Bool(false));
243 assert_unary_op!(NotOp, Bool(false), Bool(true));
244 }
245
246 #[test]
247 fn sum_op() {
248 assert_binary_op!(SumOp, Number::Integer(40), Number::Integer(2), Number::Integer(42));
249 assert_binary_op!(SumOp, Number::Integer(40), Number::Float(2.42), Number::Float(42.42));
250 assert_binary_op!(SumOp, Number::Float(40.42), Number::Integer(2), Number::Float(42.42));
251 assert_binary_op!(SumOp, Number::Float(40.40), Number::Float(2.02), Number::Float(42.42));
252 }
253
254 #[test]
255 fn sub_op() {
256 assert_binary_op!(SubOp, Number::Integer(44), Number::Integer(2), Number::Integer(42));
257 assert_binary_op!(SubOp, Number::Integer(44), Number::Float(2.42), Number::Float(41.58));
258 assert_binary_op!(SubOp, Number::Float(44.42), Number::Integer(2), Number::Float(42.42));
259 assert_binary_op!(SubOp, Number::Float(44.5), Number::Float(2.5), Number::Float(42.0));
260 }
261
262 #[test]
263 fn mul_op() {
264 assert_binary_op!(MulOp, Number::Integer(6), Number::Integer(7), Number::Integer(42));
265 assert_binary_op!(MulOp, Number::Integer(4), Number::Float(10.5), Number::Float(42.0));
266 assert_binary_op!(MulOp, Number::Float(10.5), Number::Integer(4), Number::Float(42.0));
267 assert_binary_op!(MulOp, Number::Float(2.5), Number::Float(16.8), Number::Float(42.0));
268 }
269
270 #[test]
271 fn div_op() {
272 assert_binary_op!(DivOp, Number::Integer(84), Number::Integer(2), Number::Integer(42));
273 assert_binary_op!(DivOp, Number::Integer(441), Number::Float(10.5), Number::Float(42.0));
274 assert_binary_op!(DivOp, Number::Float(84.0), Number::Integer(2), Number::Float(42.0));
275 assert_binary_op!(DivOp, Number::Float(430.5), Number::Float(10.25), Number::Float(42.0));
276 }
277
278 #[test]
279 fn mod_op() {
280 assert_binary_op!(ModOp, Number::Integer(85), Number::Integer(43), Number::Integer(42));
281 assert_binary_op!(ModOp, Number::Integer(85), Number::Float(43.5), Number::Float(41.5));
282 assert_binary_op!(ModOp, Number::Float(85.5), Number::Integer(43), Number::Float(42.5));
283 assert_binary_op!(ModOp, Number::Float(85.5), Number::Float(43.5), Number::Float(42.0));
284 }
285}