1#[macro_export]
5macro_rules! disj {
6 () => { $crate::prelude::fail() };
7 ($g:expr) => { $g };
8 ($g0:expr; $($g:expr);*) => { $crate::prelude::disj2($g0, $crate::disj!($($g);*))}
9}
10
11#[macro_export]
13macro_rules! conj {
14 () => { $crate::prelude::succeed() };
15 ($g:expr) => { $g };
16 ($g0:expr, $($g:expr),*) => { $crate::prelude::conj2($g0, $crate::conj!($($g),*))}
17}
18
19#[macro_export]
22macro_rules! defrel {
23
24 ($(#[$outer:meta])* pub $name:ident($($args:ident),*) { $($g:expr),* $(,)? }) => {
25 $(#[$outer])*
26 pub fn $name($($args: impl 'static + Into<$crate::prelude::Value>),*) -> impl $crate::prelude::Goal<$crate::prelude::Substitution<'static>> {
27 defrel!(@body: $($args),* { $($g),* })
28 }
29 };
30
31 ($(#[$outer:meta])* $name:ident($($args:ident),*) { $($g:expr),* $(,)? }) => {
32 $(#[$outer])*
33 fn $name($($args: impl 'static + Into<$crate::prelude::Value>),*) -> impl $crate::prelude::Goal<$crate::prelude::Substitution<'static>> {
34 defrel!(@body: $($args),* { $($g),* })
35 }
36 };
37
38 ($(#[$outer:meta])* pub trace $name:ident($($args:ident),*) { $($g:expr),* $(,)? }) => {
39 $(#[$outer])*
40 pub fn $name($($args: impl 'static + Into<$crate::prelude::Value>),*) -> impl $crate::prelude::Goal<$crate::prelude::Substitution<'static>> {
41 defrel!(@tracebody: $name, $($args),* { $($g),* })
42 }
43 };
44
45 ($(#[$outer:meta])* trace $name:ident($($args:ident),*) { $($g:expr),* $(,)? }) => {
46 $(#[$outer])*
47 fn $name($($args: impl 'static + Into<$crate::prelude::Value>),*) -> impl $crate::prelude::Goal<$crate::prelude::Substitution<'static>> {
48 defrel!(@tracebody: $name, $($args),* { $($g),* })
49 }
50 };
51
52 (pub $name:ident($($args:ident),*) { $($g:expr);* $(;)? }) => {
54 defrel!{pub $name($($args),*) { $($g),* }}
55 };
56
57 ($name:ident($($args:ident),*) { $($g:expr);* $(;)? }) => {
59 defrel!{$name($($args),*) { $($g),* }}
60 };
61
62 (@body: $($args:ident),* { $($g:expr),* }) => {{
63 $(
64 let $args = $args.into();
65 )*
66 move |s| {
67 $(
68 let $args = $args.clone();
69 )*
70 $crate::prelude::Stream::suspension(move || $crate::conj!($($g),*).apply(s))
71 }
72 }};
73
74 (@tracebody: $name:ident, $($args:ident),* { $($g:expr),* }) => {{
75 $(
76 let $args = $args.into();
77 )*
78 move |s: $crate::prelude::Substitution<'static>| {
79 {
80 print!("{} apply:", stringify!($name));
81 let sx = {
82 $(
83 let $args = $args.clone();
84 print!(" {}={:?}", stringify!($args), s.reify(&$args));
85 )*
86 $crate::conj!($($g),*).apply(s.clone())
87 };
88 match sx {
89 $crate::prelude::Stream::Pair(first_sub, next) => {
90 print!(" succeeded with");
91 $(print!(" {}={:?}", stringify!($args), first_sub.reify(&$args));)*
92 if next.is_empty() {
93 println!();
94 } else {
95 println!(" ...");
96 }
97 }
98 $crate::prelude::Stream::Suspension(_) => println!(" ..."),
99 $crate::prelude::Stream::Empty => println!(" failed."),
100 }
101 }
102
103 $(
104 let $args = $args.clone();
105 )*
106
107 $crate::prelude::Stream::suspension(move || {
108 let sx = $crate::conj!($($g),*).apply(s);
109 sx
110 })
111 }
112 }};
113}
114
115#[macro_export]
118macro_rules! defmatch {
119
120 ($(#[$outer:meta])* pub $name:ident($($args:ident),*) { $($body:tt)* }) => {
121 defrel! {
122 $(#[$outer])*
123 pub $name($($args),*) {
124 $crate::matche! { list![$($args.clone()),*],
125 $($body)*
126 }
127 }
128 }
129 };
130
131 ($(#[$outer:meta])* $name:ident($($args:ident),*) { $($body:tt)* }) => {
132 defrel! {
133 $(#[$outer])*
134 $name($($args),*) {
135 $crate::matche! { list![$($args.clone()),*],
136 $($body)*
137 }
138 }
139 }
140 };
141}
142
143#[macro_export]
155macro_rules! run {
156 (*, ($($x:ident),*), $($body:tt)*) => {
157 $crate::run!(@ *, ($($x),*), $($body)*)
158 };
159
160 (*, $q:ident, $($g:expr),* $(,)?) => {
161 $crate::run!(@ *, $q, $($g),*)
162 };
163
164 ($n:expr, ($($x:ident),*), $($body:tt)*) => {
165 $crate::run!(@ $n, ($($x),*), $($body)*)
166 };
167
168 ($n:tt, $q:ident, $($g:expr),* $(,)?) => {
169 $crate::run!(@ $n, $q, $($g),*)
170 };
171
172 (($($x:ident),*), $($body:tt)*) => {
173 $crate::run!(@ iter, ($($x),*), $($body)*)
174 };
175
176 ($q:ident, $($g:expr),* $(,)?) => {
177 $crate::run!(@ iter, $q, $($g),*)
178 };
179
180 (@ $n:tt, ($($x:ident),*), $($g:expr),* $(,)?) => {
181 $crate::run!(@ $n, q, {
182 $crate::fresh!(
183 ($($x),*),
184 $crate::prelude::eq(vec![$($crate::prelude::Value::var($x.clone())),*], q),
185 $($g),*
186 )
187 })
188 };
189
190 (@ *, $q:ident, $($g:expr),* $(,)?) => {{
191 let $q = $crate::prelude::Var::new(stringify!($q));
192 let var = $crate::prelude::Value::var($q.clone());
193 $crate::conj!($($g),*).run_inf().map(move |s| s.reify(&var))
194 }};
195
196 (@ iter, $q:ident, $($g:expr),* $(,)?) => {{
197 let $q = $crate::prelude::Var::new(stringify!($q));
198 let var = $crate::prelude::Value::var($q.clone());
199 $crate::conj!($($g),*).iter().map(move |s| s.reify(&var))
200 }};
201
202 (@ $n:expr, $q:ident, $($g:expr),* $(,)?) => {{
203 let $q = $crate::prelude::Var::new(stringify!($q));
204 let var = $crate::prelude::Value::var($q.clone());
205 $crate::conj!($($g),*).run($n).map(move |s| s.reify(&var))
206 }};
207}
208
209#[macro_export]
211macro_rules! fresh {
212 (($($x:ident),*), $($g:expr),* $(,)?) => {{
213 $( let $x = $crate::prelude::Var::new(stringify!($x)); )*
214 $crate::conj!($($g),*)
215 }}
216}
217
218#[macro_export]
224macro_rules! conde {
225 ( $($($g:expr),*;)* ) => {
226 $crate::disj!($($crate::conj!( $($g),*));*)
227 }
228}
229
230#[macro_export]
236macro_rules! conda {
237 ($($g:expr),*) => { $crate::conj!($($g),*) };
238
239 ($g0:expr, $($g:expr),+; $($rest:tt)*) => {
240 $crate::prelude::ifte($g0, $crate::conj!($($g),*), $crate::conda!($($rest)*))
241 };
242
243 ($g0:expr; $($rest:tt)*) => {
244 $crate::prelude::ifte($g0, $crate::succeed(), $crate::conda!($($rest)*))
245 };
246}
247
248#[macro_export]
251macro_rules! condu {
252 ($g0:expr, $($g:expr),+;) => {
253 $crate::conj!($crate::once($g0), $($g),*)
254 };
255
256 ($g0:expr;) => {
257 $crate::once($g0)
258 };
259
260 ($g0:expr, $($g:expr),+; $($rest:tt)*) => {
261 $crate::prelude::ifte($crate::once($g0), $crate::conj!($($g),*), $crate::condu!($($rest)*))
262 };
263
264 ($g0:expr; $($rest:tt)*) => {
265 $crate::prelude::ifte($crate::once($g0), $crate::succeed(), $crate::condu!($($rest)*))
266 };
267}
268
269#[macro_export]
274macro_rules! matche {
275 ( $val:expr, $( $pat:tt => $($goal:expr),* ; )+ ) => {
276 $crate::disj! {
277 $( $crate::matche!(@match: $val, $pat => $($goal),*) );*
278 }
279 };
280
281 (@match: $val:expr, ($h:tt) => $($goal:expr),*) => {
283 fresh! { (h),
284 $crate::goals::list::conso(h, (), $val.clone()),
285 $crate::matche!(@match: h, $h => $($goal),*)
286 }
287 };
288
289 (@match: $val:expr, ($h:tt ; $t:tt) => $($goal:expr),*) => {
291 fresh! { (h, t),
292 $crate::goals::list::conso(h, t, $val.clone()),
293 $crate::matche!(@match: h, $h => $crate::matche!(@match: t, $t => $($goal),*))
294 }
295 };
296
297 (@match: $val:expr, ($h:tt, $($rest:tt)*) => $($goal:expr),*) => {
299 fresh! { (h, t),
300 $crate::goals::list::conso(h, t, $val.clone()),
301 $crate::matche!(@match: h, $h => $crate::matche!(@match: t, ($($rest)*) => $($goal),*))
302 }
303 };
304
305 (@match: $val:expr, _ => $($goal:expr),*) => {
307 $crate::conj!{
308 $($goal),*
309 }
310 };
311
312 (@match: $val:expr, $v:ident => $($goal:expr),*) => {
314 fresh! { ($v),
315 $crate::prelude::eq($val.clone(), $v),
316 $($goal),*
317 }
318 };
319
320 (@match: $val:expr, $c:expr => $($goal:expr),*) => {
322 $crate::conj!{
323 $crate::prelude::eq($val.clone(), $c),
324 $($goal),*
325 }
326 };
327}
328
329#[cfg(test)]
330mod tests {
331 use crate::testing::{fails, has_unique_solution, succeeds};
332 use crate::{eq, fail, list, succeed};
333 use crate::{RawGoal, Value};
334
335 #[test]
336 fn matching_anything_succeeds_always() {
337 succeeds(matche! { q,
338 _ => ;
339 });
340 }
341
342 #[test]
343 fn matching_fails_if_any_further_goals_fail() {
344 fails(matche! { q,
345 _ => succeed(), fail(), succeed();
346 });
347 }
348
349 #[test]
350 fn matching_a_constant_binds_value() {
351 has_unique_solution(
352 run!(
353 q,
354 matche! { q,
355 1 => ;
356 }
357 ),
358 1.into(),
359 );
360 }
361
362 #[test]
363 fn each_successful_matching_line_contributes_a_value() {
364 assert_eq!(
365 run!(*, q,
366 matche! { q,
367 1 => ;
368 2 => fail();
369 3 => ;
370 }
371 )
372 .into_vec(),
373 vec![1, 3],
374 );
375 }
376
377 #[test]
378 fn matching_deconstructs_lists() {
379 assert_eq!(
380 run!(*, q,
381 matche! { q,
382 (_) => ;
383 (_, _) => ;
384 (_, _ ; _) => ;
385 }
386 )
387 .into_vec(),
388 vec![
389 list![Value::rv(0)],
390 list![Value::rv(0), Value::rv(1)],
391 list![Value::rv(0), Value::rv(1) ; Value::rv(2)],
392 ],
393 );
394 }
395
396 #[test]
397 fn matching_deconstructs_lists_and_binds_fresh_vars() {
398 assert_eq!(
399 run!(*, q,
400 matche! { q,
401 (a) => eq(a, 1);
402 (b, c) => eq(b, c);
403 (a, _ ; d) => eq(d, 2);
404 }
405 )
406 .into_vec(),
407 vec![
408 list![1],
409 list![Value::rv(0), Value::rv(0)],
410 list![Value::rv(0), Value::rv(1) ; 2],
411 ],
412 );
413 }
414
415 #[test]
416 fn matche_can_escape_outside_vars() {
417 let x = 42;
418 has_unique_solution(
419 run!(
420 q,
421 matche! { q,
422 ({x}) => ;
424 }
425 ),
426 list![42],
427 );
428 }
429
430 #[test]
431 fn matche_matches_values() {
432 has_unique_solution(
433 run!(
434 q,
435 matche! { list![1, q],
436 (b, c) => eq(b, c);
437 }
438 ),
439 1.into(),
440 );
441 }
442}