Skip to main content

oxilean_codegen/prolog_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::prologgoalbuilder_type::PrologGoalBuilder;
6use super::types::{
7    DcgRhs, DcgRule, PrologArith, PrologAssertionBuilder, PrologBackend, PrologClause,
8    PrologClauseBuilder, PrologConstraints, PrologDCGBuilder, PrologDirective,
9    PrologMetaPredicates, PrologMode, PrologModule, PrologModuleBuilder, PrologPredicate,
10    PrologPredicateBuilder, PrologSnippets, PrologTerm, PrologType, PrologTypeSig,
11};
12use std::fmt;
13
14pub(super) fn fmt_dcg_seq(f: &mut fmt::Formatter<'_>, parts: &[DcgRhs]) -> fmt::Result {
15    for (i, p) in parts.iter().enumerate() {
16        if i > 0 {
17            write!(f, ", ")?;
18        }
19        write!(f, "{}", p)?;
20    }
21    Ok(())
22}
23/// `atom(s)` — shorthand for `PrologTerm::Atom`.
24pub fn atom(s: impl Into<String>) -> PrologTerm {
25    PrologTerm::Atom(s.into())
26}
27/// `var(s)` — shorthand for `PrologTerm::Variable`.
28pub fn var(s: impl Into<String>) -> PrologTerm {
29    PrologTerm::Variable(s.into())
30}
31/// `int(n)` — shorthand for `PrologTerm::Integer`.
32pub fn int(n: i64) -> PrologTerm {
33    PrologTerm::Integer(n)
34}
35/// `float(x)` — shorthand for `PrologTerm::Float`.
36pub fn float_term(x: f64) -> PrologTerm {
37    PrologTerm::Float(x)
38}
39/// `compound(f, args)` — shorthand for `PrologTerm::Compound`.
40pub fn compound(functor: impl Into<String>, args: Vec<PrologTerm>) -> PrologTerm {
41    PrologTerm::Compound(functor.into(), args)
42}
43/// `list(elems)` — shorthand for a proper list.
44pub fn list(elems: Vec<PrologTerm>) -> PrologTerm {
45    PrologTerm::List(elems, None)
46}
47/// `op_term(op, l, r)` — shorthand for `PrologTerm::Op`.
48pub fn op_term(op: impl Into<String>, l: PrologTerm, r: PrologTerm) -> PrologTerm {
49    PrologTerm::Op(op.into(), Box::new(l), Box::new(r))
50}
51#[cfg(test)]
52mod tests {
53    use super::*;
54    #[test]
55    pub(super) fn test_atom_display() {
56        assert_eq!(format!("{}", atom("foo")), "foo");
57        assert_eq!(format!("{}", atom("Hello")), "'Hello'");
58        assert_eq!(format!("{}", atom("hello world")), "'hello world'");
59        assert_eq!(format!("{}", PrologTerm::Nil), "[]");
60        assert_eq!(format!("{}", PrologTerm::Cut), "!");
61        assert_eq!(format!("{}", PrologTerm::Anon), "_");
62    }
63    #[test]
64    pub(super) fn test_integer_float_display() {
65        assert_eq!(format!("{}", int(42)), "42");
66        assert_eq!(format!("{}", int(-7)), "-7");
67        assert_eq!(format!("{}", float_term(3.14)), "3.14");
68        assert_eq!(format!("{}", float_term(1.0)), "1.0");
69        let ft = PrologTerm::Float(1.0);
70        let s = format!("{}", ft);
71        assert!(s == "1.0" || s == "1", "got: {}", s);
72    }
73    #[test]
74    pub(super) fn test_compound_display() {
75        let t = compound("f", vec![atom("a"), var("X"), int(1)]);
76        assert_eq!(format!("{}", t), "f(a, X, 1)");
77    }
78    #[test]
79    pub(super) fn test_list_display() {
80        let t = list(vec![int(1), int(2), int(3)]);
81        assert_eq!(format!("{}", t), "[1, 2, 3]");
82        let partial = PrologTerm::list_partial(vec![var("H")], var("T"));
83        assert_eq!(format!("{}", partial), "[H|T]");
84        assert_eq!(format!("{}", PrologTerm::Nil), "[]");
85    }
86    #[test]
87    pub(super) fn test_op_display() {
88        let t = op_term("is", var("X"), op_term("+", var("Y"), int(1)));
89        let s = format!("{}", t);
90        assert!(s.contains("is"), "got: {}", s);
91        assert!(s.contains('+'), "got: {}", s);
92    }
93    #[test]
94    pub(super) fn test_fact_emit() {
95        let f = PrologClause::fact(compound(
96            "member",
97            vec![var("X"), list(vec![var("X"), var("_")])],
98        ));
99        let s = f.emit();
100        assert!(s.ends_with('.'));
101        assert!(s.contains("member"));
102        assert!(!s.contains(":-"));
103    }
104    #[test]
105    pub(super) fn test_rule_emit() {
106        let head = compound(
107            "member",
108            vec![var("X"), compound(".", vec![var("_"), var("T")])],
109        );
110        let body = vec![compound("member", vec![var("X"), var("T")])];
111        let r = PrologClause::rule(head, body);
112        let s = r.emit();
113        assert!(s.contains(":-"), "got: {}", s);
114        assert!(s.ends_with('.'));
115        assert!(s.contains("member(X, T)"));
116    }
117    #[test]
118    pub(super) fn test_rule_multigoal_emit() {
119        let head = compound(
120            "append",
121            vec![
122                compound(".", vec![var("H"), var("T")]),
123                var("L"),
124                compound(".", vec![var("H"), var("R")]),
125            ],
126        );
127        let body = vec![compound("append", vec![var("T"), var("L"), var("R")])];
128        let r = PrologClause::rule(head, body);
129        let s = r.emit();
130        assert!(s.contains(":-"));
131        assert!(s.ends_with('.'));
132    }
133    #[test]
134    pub(super) fn test_predicate_emit() {
135        let mut pred = PrologPredicate::new("length", 2).dynamic().exported();
136        pred.add_clause(PrologClause::fact(compound(
137            "length",
138            vec![PrologTerm::Nil, int(0)],
139        )));
140        pred.add_clause(PrologClause::rule(
141            compound(
142                "length",
143                vec![compound(".", vec![var("_"), var("T")]), var("N")],
144            ),
145            vec![
146                compound("length", vec![var("T"), var("N1")]),
147                op_term("is", var("N"), op_term("+", var("N1"), int(1))),
148            ],
149        ));
150        let s = pred.emit();
151        assert!(s.contains(":- dynamic length/2."));
152        assert!(s.contains("length([], 0)."));
153        assert!(s.contains(":-"));
154        assert_eq!(pred.indicator(), "length/2");
155    }
156    #[test]
157    pub(super) fn test_directive_emit() {
158        let d = PrologDirective::Module("lists".into(), vec!["member/2".into(), "append/3".into()]);
159        let s = d.emit();
160        assert!(s.starts_with(":- module(lists,"));
161        assert!(s.contains("member/2"));
162        assert!(s.contains("append/3"));
163        let d2 = PrologDirective::UseModuleLibrary("lists".into());
164        assert_eq!(d2.emit(), ":- use_module(library(lists)).");
165        let d3 = PrologDirective::Dynamic("fact".into(), 1);
166        assert_eq!(d3.emit(), ":- dynamic fact/1.");
167        let d4 = PrologDirective::Op(700, "xfx".into(), "===".into());
168        assert_eq!(d4.emit(), ":- op(700, xfx, ===).");
169    }
170    #[test]
171    pub(super) fn test_dcg_rule_emit() {
172        let rule = DcgRule {
173            lhs: compound("sentence", vec![var("S")]),
174            rhs: vec![
175                DcgRhs::NonTerminal(compound("np", vec![var("S")])),
176                DcgRhs::NonTerminal(atom("vp")),
177            ],
178            guards: vec![],
179            comment: None,
180        };
181        let s = rule.emit();
182        assert!(s.contains("-->"), "got: {}", s);
183        assert!(s.contains("np(S)"), "got: {}", s);
184        assert!(s.contains("vp"), "got: {}", s);
185    }
186    #[test]
187    pub(super) fn test_dcg_terminals_emit() {
188        let rule = DcgRule {
189            lhs: atom("greeting"),
190            rhs: vec![
191                DcgRhs::Terminals(vec![atom("hello")]),
192                DcgRhs::NonTerminal(atom("name")),
193            ],
194            guards: vec![],
195            comment: Some("Match a greeting phrase".into()),
196        };
197        let s = rule.emit();
198        assert!(s.starts_with("% Match a greeting phrase"));
199        assert!(s.contains("[hello]"), "got: {}", s);
200    }
201    #[test]
202    pub(super) fn test_module_emit() {
203        let backend = PrologBackend::swi();
204        let mut module = PrologModule::new("mylist");
205        module.export("member/2");
206        module.export("append/3");
207        module.directive(PrologDirective::UseModuleLibrary("lists".into()));
208        module.blank();
209        let mut member_pred = PrologPredicate::new("member", 2).exported();
210        member_pred.add_clause(PrologClause::fact(compound(
211            "member",
212            vec![var("X"), list(vec![var("X"), var("_")])],
213        )));
214        member_pred.add_clause(PrologClause::rule(
215            compound(
216                "member",
217                vec![var("X"), compound(".", vec![var("_"), var("T")])],
218            ),
219            vec![compound("member", vec![var("X"), var("T")])],
220        ));
221        module.predicate(member_pred);
222        let s = backend.emit_module(&module);
223        assert!(s.contains(":- module(mylist,"), "got: {}", s);
224        assert!(s.contains("member/2"), "got: {}", s);
225        assert!(s.contains("append/3"), "got: {}", s);
226        assert!(s.contains(":- use_module(library(lists))."), "got: {}", s);
227        assert!(s.contains("member(X, [X, _])."), "got: {}", s);
228    }
229    #[test]
230    pub(super) fn test_swi_preamble() {
231        let backend = PrologBackend::swi();
232        let preamble = backend.build_swi_preamble(
233            "utils",
234            &[("helper", 1), ("transform", 2)],
235            &["lists", "aggregate"],
236        );
237        assert!(preamble.contains(":- module(utils,"));
238        assert!(preamble.contains("helper/1"));
239        assert!(preamble.contains("transform/2"));
240        assert!(preamble.contains(":- use_module(library(lists))."));
241        assert!(preamble.contains(":- use_module(library(aggregate))."));
242    }
243}
244/// Negation-as-failure term: `\+(Goal)`.
245#[allow(dead_code)]
246pub fn not_provable(goal: PrologTerm) -> PrologTerm {
247    PrologTerm::PrefixOp("\\+".to_string(), Box::new(goal))
248}
249/// Conditional term: `(Cond -> Then ; Else)`.
250#[allow(dead_code)]
251pub fn if_then_else(cond: PrologTerm, then_: PrologTerm, else_: PrologTerm) -> PrologTerm {
252    PrologTerm::Op(
253        ";".to_string(),
254        Box::new(PrologTerm::Op(
255            "->".to_string(),
256            Box::new(cond),
257            Box::new(then_),
258        )),
259        Box::new(else_),
260    )
261}
262/// Build `(A, B)` — conjunction term.
263#[allow(dead_code)]
264pub fn conjunction(a: PrologTerm, b: PrologTerm) -> PrologTerm {
265    PrologTerm::Op(",".to_string(), Box::new(a), Box::new(b))
266}
267/// Build `(A ; B)` — disjunction term.
268#[allow(dead_code)]
269pub fn disjunction(a: PrologTerm, b: PrologTerm) -> PrologTerm {
270    PrologTerm::Op(";".to_string(), Box::new(a), Box::new(b))
271}
272/// Build `X = Y` — unification goal.
273#[allow(dead_code)]
274pub fn unify(x: PrologTerm, y: PrologTerm) -> PrologTerm {
275    PrologTerm::Op("=".to_string(), Box::new(x), Box::new(y))
276}
277/// Build `X \= Y` — non-unification goal.
278#[allow(dead_code)]
279pub fn not_unify(x: PrologTerm, y: PrologTerm) -> PrologTerm {
280    PrologTerm::Op("\\=".to_string(), Box::new(x), Box::new(y))
281}
282/// Build `X == Y` — strict equality.
283#[allow(dead_code)]
284pub fn strict_eq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
285    PrologTerm::Op("==".to_string(), Box::new(x), Box::new(y))
286}
287/// Build `X \== Y` — strict inequality.
288#[allow(dead_code)]
289pub fn strict_neq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
290    PrologTerm::Op("\\==".to_string(), Box::new(x), Box::new(y))
291}
292/// Build `X is Expr` — arithmetic evaluation.
293#[allow(dead_code)]
294pub fn is_eval(x: PrologTerm, expr: PrologTerm) -> PrologTerm {
295    PrologTerm::Op("is".to_string(), Box::new(x), Box::new(expr))
296}
297/// Build `X =:= Y` — arithmetic equality.
298#[allow(dead_code)]
299pub fn arith_eq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
300    PrologTerm::Op("=:=".to_string(), Box::new(x), Box::new(y))
301}
302/// Build `X =\= Y` — arithmetic inequality.
303#[allow(dead_code)]
304pub fn arith_neq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
305    PrologTerm::Op("=\\=".to_string(), Box::new(x), Box::new(y))
306}
307/// Build `X < Y`.
308#[allow(dead_code)]
309pub fn arith_lt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
310    PrologTerm::Op("<".to_string(), Box::new(x), Box::new(y))
311}
312/// Build `X > Y`.
313#[allow(dead_code)]
314pub fn arith_gt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
315    PrologTerm::Op(">".to_string(), Box::new(x), Box::new(y))
316}
317/// Build `X @< Y` — term order less-than.
318#[allow(dead_code)]
319pub fn term_lt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
320    PrologTerm::Op("@<".to_string(), Box::new(x), Box::new(y))
321}
322/// Build `X @> Y` — term order greater-than.
323#[allow(dead_code)]
324pub fn term_gt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
325    PrologTerm::Op("@>".to_string(), Box::new(x), Box::new(y))
326}
327/// Build `X + Y`.
328#[allow(dead_code)]
329pub fn arith_add(x: PrologTerm, y: PrologTerm) -> PrologTerm {
330    PrologTerm::Op("+".to_string(), Box::new(x), Box::new(y))
331}
332/// Build `X - Y`.
333#[allow(dead_code)]
334pub fn arith_sub(x: PrologTerm, y: PrologTerm) -> PrologTerm {
335    PrologTerm::Op("-".to_string(), Box::new(x), Box::new(y))
336}
337/// Build `X * Y`.
338#[allow(dead_code)]
339pub fn arith_mul(x: PrologTerm, y: PrologTerm) -> PrologTerm {
340    PrologTerm::Op("*".to_string(), Box::new(x), Box::new(y))
341}
342/// Build `X mod Y`.
343#[allow(dead_code)]
344pub fn arith_mod(x: PrologTerm, y: PrologTerm) -> PrologTerm {
345    PrologTerm::Op("mod".to_string(), Box::new(x), Box::new(y))
346}
347/// Build `X div Y` — integer division.
348#[allow(dead_code)]
349pub fn arith_div(x: PrologTerm, y: PrologTerm) -> PrologTerm {
350    PrologTerm::Op("div".to_string(), Box::new(x), Box::new(y))
351}
352#[cfg(test)]
353mod extended_prolog_tests {
354    use super::*;
355    #[test]
356    pub(super) fn test_not_provable() {
357        let t = not_provable(atom("foo"));
358        let s = format!("{}", t);
359        assert!(s.contains("\\+"));
360        assert!(s.contains("foo"));
361    }
362    #[test]
363    pub(super) fn test_conjunction() {
364        let t = conjunction(atom("a"), atom("b"));
365        let s = format!("{}", t);
366        assert!(s.contains("a"));
367        assert!(s.contains("b"));
368        assert!(s.contains(","));
369    }
370    #[test]
371    pub(super) fn test_disjunction() {
372        let t = disjunction(atom("a"), atom("b"));
373        let s = format!("{}", t);
374        assert!(s.contains(";"));
375    }
376    #[test]
377    pub(super) fn test_unify_ops() {
378        let t = unify(var("X"), int(5));
379        let s = format!("{}", t);
380        assert!(s.contains('='));
381        let t2 = not_unify(var("X"), var("Y"));
382        assert!(format!("{}", t2).contains("\\="));
383    }
384    #[test]
385    pub(super) fn test_strict_eq_neq() {
386        let t = strict_eq(var("X"), var("Y"));
387        assert!(format!("{}", t).contains("=="));
388        let t2 = strict_neq(var("X"), var("Y"));
389        assert!(format!("{}", t2).contains("\\=="));
390    }
391    #[test]
392    pub(super) fn test_arith_ops() {
393        let t = is_eval(var("X"), arith_add(var("Y"), int(1)));
394        let s = format!("{}", t);
395        assert!(s.contains("is"));
396        assert!(s.contains('+'));
397        let sub = arith_sub(var("A"), var("B"));
398        assert!(format!("{}", sub).contains('-'));
399        let mul = arith_mul(var("A"), var("B"));
400        assert!(format!("{}", mul).contains('*'));
401        let md = arith_mod(var("A"), int(2));
402        assert!(format!("{}", md).contains("mod"));
403        let dv = arith_div(var("A"), int(2));
404        assert!(format!("{}", dv).contains("div"));
405    }
406    #[test]
407    pub(super) fn test_if_then_else() {
408        let t = if_then_else(atom("cond"), atom("then"), atom("els"));
409        let s = format!("{}", t);
410        assert!(s.contains("->") || s.contains(';'));
411    }
412    #[test]
413    pub(super) fn test_goal_builder() {
414        let goals = PrologGoalBuilder::new()
415            .is(var("N"), arith_add(var("M"), int(1)))
416            .writeln(var("N"))
417            .cut()
418            .build();
419        assert_eq!(goals.len(), 3);
420        let clause = PrologGoalBuilder::new()
421            .member(var("X"), var("List"))
422            .to_clause(compound("find_member", vec![var("X"), var("List")]));
423        assert!(!clause.body.is_empty());
424    }
425    #[test]
426    pub(super) fn test_goal_builder_list_ops() {
427        let goals = PrologGoalBuilder::new()
428            .length(var("L"), var("N"))
429            .append(var("A"), var("B"), var("C"))
430            .reverse(var("L"), var("R"))
431            .sort(var("L"), var("S"))
432            .msort(var("L"), var("M"))
433            .build();
434        assert_eq!(goals.len(), 5);
435    }
436    #[test]
437    pub(super) fn test_goal_builder_meta() {
438        let goals = PrologGoalBuilder::new()
439            .maplist1(atom("atom"), var("List"))
440            .maplist2(atom("succ"), var("L"), var("R"))
441            .include(atom("integer"), var("L"), var("Ints"))
442            .exclude(atom("integer"), var("L"), var("NonInts"))
443            .build();
444        assert_eq!(goals.len(), 4);
445    }
446    #[test]
447    pub(super) fn test_arith_builder() {
448        let a = PrologArith::add(var("X"), int(1));
449        assert!(format!("{}", a).contains('+'));
450        let b = PrologArith::abs(var("X"));
451        assert!(format!("{}", b).contains("abs"));
452        let m = PrologArith::max(int(3), int(5));
453        assert!(format!("{}", m).contains("max"));
454        let pw = PrologArith::pow(var("X"), int(2));
455        assert!(format!("{}", pw).contains('^'));
456        let ba = PrologArith::bitand(var("X"), var("Y"));
457        assert!(format!("{}", ba).contains("/\\"));
458        let bo = PrologArith::bitor(var("X"), var("Y"));
459        assert!(format!("{}", bo).contains("\\/"));
460    }
461    #[test]
462    pub(super) fn test_prolog_type_display() {
463        assert_eq!(format!("{}", PrologType::Integer), "integer");
464        assert_eq!(format!("{}", PrologType::Atom), "atom");
465        assert_eq!(format!("{}", PrologType::Boolean), "boolean");
466        let list_ty = PrologType::List(Box::new(PrologType::Integer));
467        assert!(format!("{}", list_ty).contains("list(integer)"));
468    }
469    #[test]
470    pub(super) fn test_prolog_mode_display() {
471        assert_eq!(format!("{}", PrologMode::In), "+");
472        assert_eq!(format!("{}", PrologMode::Out), "-");
473        assert_eq!(format!("{}", PrologMode::InOut), "?");
474        assert_eq!(format!("{}", PrologMode::Meta), ":");
475    }
476    #[test]
477    pub(super) fn test_prolog_type_sig_pldoc() {
478        let sig = PrologTypeSig {
479            name: "append".to_string(),
480            params: vec![
481                (PrologMode::In, PrologType::List(Box::new(PrologType::Term))),
482                (PrologMode::In, PrologType::List(Box::new(PrologType::Term))),
483                (
484                    PrologMode::Out,
485                    PrologType::List(Box::new(PrologType::Term)),
486                ),
487            ],
488            description: Some("Append two lists.".to_string()),
489        };
490        let s = sig.emit_pldoc();
491        assert!(s.contains("%% append/3"));
492        assert!(s.contains("list"));
493        let s2 = sig.emit_pred_directive();
494        assert!(s2.contains(":- pred"));
495    }
496    #[test]
497    pub(super) fn test_meta_predicates() {
498        let t = PrologMetaPredicates::maplist(atom("write"), var("L"));
499        assert!(format!("{}", t).contains("maplist"));
500        let t2 = PrologMetaPredicates::findall(var("X"), atom("goal"), var("Bag"));
501        assert!(format!("{}", t2).contains("findall"));
502        let t3 = PrologMetaPredicates::once(atom("goal"));
503        assert!(format!("{}", t3).contains("once"));
504        let t4 = PrologMetaPredicates::forall(atom("cond"), atom("act"));
505        assert!(format!("{}", t4).contains("forall"));
506    }
507    #[test]
508    pub(super) fn test_assertion_builder() {
509        let t = PrologAssertionBuilder::assertz_fact(compound("fact", vec![int(1)]));
510        assert!(format!("{}", t).contains("assertz"));
511        let t2 = PrologAssertionBuilder::retractall(compound("fact", vec![var("_")]));
512        assert!(format!("{}", t2).contains("retractall"));
513        let t3 = PrologAssertionBuilder::abolish("fact", 1);
514        assert!(format!("{}", t3).contains("abolish"));
515    }
516    #[test]
517    pub(super) fn test_constraints() {
518        let c1 = PrologConstraints::clp_eq(var("X"), int(5));
519        assert!(format!("{}", c1).contains("#="));
520        let c2 = PrologConstraints::clp_lt(var("X"), int(10));
521        assert!(format!("{}", c2).contains("#<"));
522        let c3 = PrologConstraints::all_different(vec![var("X"), var("Y"), var("Z")]);
523        assert!(format!("{}", c3).contains("all_different"));
524        let c4 = PrologConstraints::label(vec![var("X")]);
525        assert!(format!("{}", c4).contains("label"));
526    }
527    #[test]
528    pub(super) fn test_snippets_member() {
529        let pred = PrologSnippets::member_predicate();
530        let s = pred.emit();
531        assert!(s.contains("member"));
532        assert_eq!(pred.arity, 2);
533    }
534    #[test]
535    pub(super) fn test_snippets_append() {
536        let pred = PrologSnippets::append_predicate();
537        let s = pred.emit();
538        assert!(s.contains("append"));
539        assert_eq!(pred.arity, 3);
540    }
541    #[test]
542    pub(super) fn test_snippets_length() {
543        let pred = PrologSnippets::length_predicate();
544        let s = pred.emit();
545        assert!(s.contains("my_length"));
546    }
547    #[test]
548    pub(super) fn test_snippets_max_list() {
549        let pred = PrologSnippets::max_list_predicate();
550        let s = pred.emit();
551        assert!(s.contains("max_list"));
552    }
553    #[test]
554    pub(super) fn test_snippets_sum_list() {
555        let pred = PrologSnippets::sum_list_predicate();
556        let s = pred.emit();
557        assert!(s.contains("sum_list"));
558    }
559    #[test]
560    pub(super) fn test_snippets_last() {
561        let pred = PrologSnippets::last_predicate();
562        let s = pred.emit();
563        assert!(s.contains("my_last"));
564    }
565    #[test]
566    pub(super) fn test_module_builder() {
567        let s = PrologModuleBuilder::new("utils")
568            .export("member/2")
569            .use_library("lists")
570            .add_predicate(PrologSnippets::member_predicate())
571            .blank()
572            .section("List utilities")
573            .comment("End of module")
574            .emit();
575        assert!(s.contains(":- module(utils,"));
576        assert!(s.contains("member/2"));
577        assert!(s.contains(":- use_module(library(lists))."));
578    }
579    #[test]
580    pub(super) fn test_clause_builder_fact() {
581        let clause = PrologClauseBuilder::head(compound("hello", vec![atom("world")]))
582            .comment("A greeting fact")
583            .build();
584        let s = clause.emit();
585        assert!(s.contains("hello"));
586        assert!(s.ends_with('.'));
587        assert!(s.contains("A greeting fact"));
588    }
589    #[test]
590    pub(super) fn test_clause_builder_rule() {
591        let clause = PrologClauseBuilder::head(compound("greet", vec![var("X")]))
592            .goal(compound(
593                "format",
594                vec![atom("Hello ~w!~n"), PrologTerm::list(vec![var("X")])],
595            ))
596            .build();
597        let s = clause.emit();
598        assert!(s.contains("greet(X) :-"));
599        assert!(s.ends_with('.'));
600    }
601    #[test]
602    pub(super) fn test_predicate_builder() {
603        let s = PrologPredicateBuilder::new("count", 2)
604            .dynamic()
605            .exported()
606            .doc("Count occurrences.")
607            .fact(compound("count", vec![PrologTerm::Nil, int(0)]))
608            .rule(
609                compound("count", vec![var("L"), var("N")]),
610                vec![compound("length", vec![var("L"), var("N")])],
611            )
612            .emit();
613        assert!(s.contains(":- dynamic count/2."));
614        assert!(s.contains("count([], 0)."));
615    }
616    #[test]
617    pub(super) fn test_dcg_builder() {
618        let s = PrologDCGBuilder::lhs(atom("sentence"))
619            .nonterminal(atom("np"))
620            .nonterminal(atom("vp"))
621            .comment("A simple sentence rule")
622            .emit();
623        assert!(s.contains("-->"));
624        assert!(s.contains("np"));
625        assert!(s.contains("vp"));
626        assert!(s.contains("A simple sentence rule"));
627    }
628    #[test]
629    pub(super) fn test_dcg_builder_terminals() {
630        let s = PrologDCGBuilder::lhs(atom("greeting"))
631            .terminals(vec![atom("hello")])
632            .nonterminal(atom("name"))
633            .emit();
634        assert!(s.contains("[hello]"));
635        assert!(s.contains("name"));
636    }
637    #[test]
638    pub(super) fn test_dcg_builder_with_guard() {
639        let s = PrologDCGBuilder::lhs(compound("number", vec![var("N")]))
640            .nonterminal(compound("digit", vec![var("D")]))
641            .guard(is_eval(var("N"), var("D")))
642            .emit();
643        assert!(s.contains("digit"));
644        assert!(s.contains('{'));
645    }
646    #[test]
647    pub(super) fn test_nth0_predicate() {
648        let pred = PrologSnippets::nth0_predicate();
649        let s = pred.emit();
650        assert!(s.contains("my_nth0"));
651        assert_eq!(pred.arity, 3);
652    }
653    #[test]
654    pub(super) fn test_flatten_predicate() {
655        let pred = PrologSnippets::flatten_predicate();
656        let s = pred.emit();
657        assert!(s.contains("my_flatten"));
658        assert!(s.contains("is_list"));
659    }
660    #[test]
661    pub(super) fn test_term_order_ops() {
662        let lt = term_lt(var("X"), var("Y"));
663        assert!(format!("{}", lt).contains("@<"));
664        let gt = term_gt(var("X"), var("Y"));
665        assert!(format!("{}", gt).contains("@>"));
666    }
667    #[test]
668    pub(super) fn test_arith_eq_neq() {
669        let eq = arith_eq(var("X"), var("Y"));
670        assert!(format!("{}", eq).contains("=:="));
671        let neq = arith_neq(var("X"), var("Y"));
672        assert!(format!("{}", neq).contains("=\\="));
673    }
674    #[test]
675    pub(super) fn test_arith_lt_gt() {
676        let lt = arith_lt(var("X"), int(10));
677        assert!(format!("{}", lt).contains('<'));
678        let gt = arith_gt(var("X"), int(0));
679        assert!(format!("{}", gt).contains('>'));
680    }
681}