1use crate::builtins::arith::{self, ArithValue};
7use crate::builtins::order::compare_terms;
8use crate::cell::{self, INT_MAX, INT_MIN, Word};
9use crate::machine::Machine;
10use std::cmp::Ordering;
11
12#[inline]
13fn mref<'a>(m: *mut Machine) -> &'a mut Machine {
14 unsafe { &mut *m }
15}
16
17fn value_to_word(m: &mut Machine, v: ArithValue) -> Option<Word> {
24 match v {
25 ArithValue::Int(n) => {
26 if (INT_MIN..=INT_MAX).contains(&n) {
27 Some(cell::make_int(n))
28 } else {
29 let idx = m.heap.len();
31 m.heap.push(n as u64);
32 Some(cell::make(cell::TAG_BIG, idx as u64))
33 }
34 }
35 ArithValue::Float(f) => {
36 let idx = m.heap.len();
37 m.heap.push(f.to_bits());
38 Some(cell::make(cell::TAG_FLT, idx as u64))
39 }
40 }
41}
42
43#[unsafe(no_mangle)]
48pub extern "C" fn plg_rt_b_is(m: *mut Machine, lhs: u64, expr: u64, site_id: u32) -> i32 {
49 let _site = crate::machine::ErrorSiteGuard::enter(m, site_id);
50 let m = mref(m);
51 match arith::eval(m, expr) {
52 Err(()) => 0,
53 Ok(v) => match value_to_word(m, v) {
54 None => 0,
55 Some(w) => crate::unify::unify(m, lhs, w) as i32,
56 },
57 }
58}
59
60#[unsafe(no_mangle)]
63pub extern "C" fn plg_rt_b_arith_cmp(
64 m: *mut Machine,
65 op: i32,
66 a: u64,
67 b: u64,
68 site_id: u32,
69) -> i32 {
70 let _site = crate::machine::ErrorSiteGuard::enter(m, site_id);
71 let m = mref(m);
72 let av = match arith::eval(m, a) {
73 Ok(v) => v,
74 Err(()) => return 0,
75 };
76 let bv = match arith::eval(m, b) {
77 Ok(v) => v,
78 Err(()) => return 0,
79 };
80 let holds = match op {
81 0 => arith::arith_lt(av, bv),
82 1 => arith::arith_gt(av, bv),
83 2 => !arith::arith_gt(av, bv), 3 => !arith::arith_lt(av, bv), 4 => arith::arith_eq(av, bv), 5 => !arith::arith_eq(av, bv), _ => return 0,
88 };
89 holds as i32
90}
91
92#[unsafe(no_mangle)]
96pub extern "C" fn plg_rt_b_neq(m: *mut Machine, a: u64, b: u64) -> i32 {
97 let m = mref(m);
98 let trail_mark = m.trail.len();
99 let unified = crate::unify::unify(m, a, b);
100 while m.trail.len() > trail_mark {
102 let idx = m.trail.pop().unwrap() as usize;
103 m.heap[idx] = cell::make_ref(idx);
104 }
105 (!unified) as i32
106}
107
108#[unsafe(no_mangle)]
111pub extern "C" fn plg_rt_b_term_cmp(m: *mut Machine, op: i32, a: u64, b: u64) -> i32 {
112 let m = mref(m);
113 let ord = compare_terms(m, a, b);
114 let holds = match op {
115 0 => ord == Ordering::Equal, 1 => ord != Ordering::Equal, 2 => ord == Ordering::Less, 3 => ord == Ordering::Greater, 4 => ord != Ordering::Greater, 5 => ord != Ordering::Less, _ => return 0,
122 };
123 holds as i32
124}
125
126#[unsafe(no_mangle)]
129pub extern "C" fn plg_rt_b_compare(m: *mut Machine, order: u64, a: u64, b: u64) -> i32 {
130 let m = mref(m);
131 let name = match compare_terms(m, a, b) {
132 Ordering::Less => "<",
133 Ordering::Equal => "=",
134 Ordering::Greater => ">",
135 };
136 let id = m.atoms.intern(name);
137 crate::unify::unify(m, order, cell::make_atom(id)) as i32
138}
139
140#[unsafe(no_mangle)]
146pub extern "C" fn plg_rt_cut(m: *mut Machine, height: u64) {
147 mref(m).cut_to(height as usize);
149}
150
151#[unsafe(no_mangle)]
153pub extern "C" fn plg_rt_cp_top(m: *mut Machine) -> u64 {
154 mref(m).cps.len() as u64
155}
156
157#[unsafe(no_mangle)]
159pub extern "C" fn plg_rt_deref(m: *mut Machine, w: u64) -> u64 {
160 mref(m).deref(w)
161}
162
163#[unsafe(no_mangle)]
167pub extern "C" fn plg_rt_str_key(m: *mut Machine, w: u64) -> u64 {
168 let m = mref(m);
169 if cell::tag_of(w) == cell::TAG_STR {
170 m.heap[cell::payload(w) as usize]
171 } else {
172 u64::MAX
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use crate::cell::*;
180 use plg_shared::StringInterner;
181
182 fn machine() -> Box<Machine> {
183 Machine::new(StringInterner::new(), Vec::new())
184 }
185
186 fn is_(m: *mut Machine, lhs: u64, expr: u64) -> i32 {
188 plg_rt_b_is(m, lhs, expr, crate::machine::NO_SITE)
189 }
190 fn cmp_(m: *mut Machine, op: i32, a: u64, b: u64) -> i32 {
191 plg_rt_b_arith_cmp(m, op, a, b, crate::machine::NO_SITE)
192 }
193
194 fn bin_str(m: &mut Machine, op: &str, a: Word, b: Word) -> Word {
195 let f = m.atoms.intern(op);
196 let idx = m.heap.len();
197 m.heap.push(pack_functor(f, 2));
198 m.heap.push(a);
199 m.heap.push(b);
200 make(TAG_STR, idx as u64)
201 }
202
203 #[test]
204 fn b_is_unifies_bound_lhs() {
205 let mut m = machine();
206 let mp = &mut *m as *mut Machine;
207 let e = bin_str(&mut m, "+", make_int(2), make_int(3));
209 assert_eq!(is_(mp, make_int(5), e), 1);
210 let e = bin_str(&mut m, "+", make_int(2), make_int(3));
212 assert_eq!(is_(mp, make_int(6), e), 0);
213 }
214
215 #[test]
216 fn b_is_binds_unbound_lhs() {
217 let mut m = machine();
218 let v = m.new_var();
219 let e = bin_str(&mut m, "*", make_int(4), make_int(5));
220 let mp = &mut *m as *mut Machine;
221 assert_eq!(is_(mp, v, e), 1);
222 assert_eq!(int_value(m.deref(v)), 20);
223 }
224
225 #[test]
226 fn b_is_boxes_float() {
227 let mut m = machine();
228 let v = m.new_var();
229 let e = bin_str(&mut m, "/", make_int(1), make_int(2));
231 let mp = &mut *m as *mut Machine;
232 assert_eq!(is_(mp, v, e), 1);
233 let d = m.deref(v);
234 assert_eq!(tag_of(d), TAG_FLT);
235 assert_eq!(f64::from_bits(m.heap[payload(d) as usize]), 0.5);
236 }
237
238 #[test]
239 fn b_is_boxes_big_integers() {
240 let mut m = machine();
243 let v = m.new_var();
244 let big = INT_MAX; let e = bin_str(&mut m, "+", make_int(big), make_int(big));
246 let mp = &mut *m as *mut Machine;
247 assert_eq!(is_(mp, v, e), 1);
248 assert!(m.error.is_none());
249 let d = m.deref(v);
250 assert_eq!(tag_of(d), TAG_BIG);
251 assert_eq!(m.heap[payload(d) as usize] as i64, 2 * big);
252 }
253
254 #[test]
255 fn arith_cmp_ops() {
256 let mut m = machine();
257 let mp = &mut *m as *mut Machine;
258 assert_eq!(cmp_(mp, 0, make_int(1), make_int(2)), 1); assert_eq!(cmp_(mp, 0, make_int(2), make_int(1)), 0);
260 assert_eq!(cmp_(mp, 1, make_int(2), make_int(1)), 1); assert_eq!(cmp_(mp, 2, make_int(1), make_int(1)), 1); assert_eq!(cmp_(mp, 3, make_int(1), make_int(1)), 1); assert_eq!(cmp_(mp, 4, make_int(3), make_int(3)), 1); assert_eq!(cmp_(mp, 5, make_int(3), make_int(4)), 1); }
266
267 #[test]
268 fn arith_cmp_mixed_int_float() {
269 let mut m = machine();
271 let one_f = {
272 let idx = m.heap.len();
273 m.heap.push(1.0f64.to_bits());
274 make(TAG_FLT, idx as u64)
275 };
276 let mp = &mut *m as *mut Machine;
277 assert_eq!(cmp_(mp, 4, make_int(1), one_f), 1);
278 assert_eq!(cmp_(mp, 0, one_f, make_int(1)), 0);
279 }
280
281 #[test]
282 fn arith_cmp_propagates_error() {
283 let mut m = machine();
284 let v = m.new_var();
285 let mp = &mut *m as *mut Machine;
286 assert_eq!(cmp_(mp, 0, make_int(1), v), 0);
287 assert!(m.error.is_some());
288 }
289
290 #[test]
291 fn b_neq_undoes_bindings() {
292 let mut m = machine();
293 let x = m.new_var();
294 let t0 = m.trail.len();
295 let h0 = m.heap.len();
296 let mp = &mut *m as *mut Machine;
297 assert_eq!(plg_rt_b_neq(mp, x, make_atom(7)), 0);
299 assert_eq!(m.deref(x), x, "binding undone");
300 assert_eq!(m.trail.len(), t0, "trail rewound");
301 assert_eq!(m.heap.len(), h0, "heap top untouched");
302 assert_eq!(plg_rt_b_neq(mp, make_atom(7), make_atom(8)), 1);
304 }
305
306 #[test]
307 fn term_cmp_ops() {
308 let mut m = machine();
309 let a = m.atoms.intern("a");
310 let b = m.atoms.intern("b");
311 let mp = &mut *m as *mut Machine;
312 assert_eq!(plg_rt_b_term_cmp(mp, 0, make_atom(a), make_atom(a)), 1); assert_eq!(plg_rt_b_term_cmp(mp, 1, make_atom(a), make_atom(b)), 1); assert_eq!(plg_rt_b_term_cmp(mp, 2, make_atom(a), make_atom(b)), 1); assert_eq!(plg_rt_b_term_cmp(mp, 3, make_atom(b), make_atom(a)), 1); assert_eq!(plg_rt_b_term_cmp(mp, 4, make_atom(a), make_atom(a)), 1); assert_eq!(plg_rt_b_term_cmp(mp, 5, make_atom(a), make_atom(a)), 1); }
319
320 #[test]
321 fn compare3_binds_order_atom() {
322 let mut m = machine();
323 let o = m.new_var();
324 let mp = &mut *m as *mut Machine;
325 assert_eq!(plg_rt_b_compare(mp, o, make_int(1), make_int(2)), 1);
326 let lt = m.atoms.lookup("<").unwrap();
327 assert_eq!(m.deref(o), make_atom(lt));
328
329 let o2 = m.new_var();
330 let mp = &mut *m as *mut Machine;
331 assert_eq!(plg_rt_b_compare(mp, o2, make_int(5), make_int(5)), 1);
332 let eq = m.atoms.lookup("=").unwrap();
333 assert_eq!(m.deref(o2), make_atom(eq));
334 }
335
336 #[test]
337 fn cut_truncates_cps() {
338 let mut m = machine();
339 unsafe extern "C" fn dummy(_m: *mut Machine, _e: u64) -> i32 {
341 0
342 }
343 for _ in 0..3 {
344 m.push_cp(dummy, 0);
345 }
346 let mp = &mut *m as *mut Machine;
347 assert_eq!(plg_rt_cp_top(mp), 3);
348 plg_rt_cut(mp, 1);
349 assert_eq!(plg_rt_cp_top(mp), 1);
350 }
351
352 #[test]
353 fn deref_and_str_key() {
354 let mut m = machine();
355 let f = m.atoms.intern("foo");
356 let idx = m.heap.len();
357 m.heap.push(pack_functor(f, 2));
358 m.heap.push(make_int(1));
359 m.heap.push(make_int(2));
360 let s = make(TAG_STR, idx as u64);
361 let mp = &mut *m as *mut Machine;
362 assert_eq!(plg_rt_str_key(mp, s), pack_functor(f, 2));
363 assert_eq!(plg_rt_str_key(mp, make_int(9)), u64::MAX);
364 assert_eq!(plg_rt_str_key(mp, make_atom(f)), u64::MAX);
365
366 let mut m = machine();
368 let v = m.new_var();
369 m.bind(payload(v) as usize, make_int(42));
370 let mp = &mut *m as *mut Machine;
371 assert_eq!(int_value(plg_rt_deref(mp, v)), 42);
372 }
373}