1use super::functions::*;
6use super::prologgoalbuilder_type::PrologGoalBuilder;
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum DcgRhs {
11 NonTerminal(PrologTerm),
13 Terminals(Vec<PrologTerm>),
15 Epsilon,
17 Goal(Vec<PrologTerm>),
19 Disjunction(Vec<DcgRhs>, Vec<DcgRhs>),
21 Seq(Vec<DcgRhs>),
23}
24#[derive(Debug, Clone, PartialEq)]
26pub enum PrologDirective {
27 Module(String, Vec<String>),
29 UseModuleLibrary(String),
31 UseModulePath(String),
33 UseModuleImports(String, Vec<String>),
35 Dynamic(String, usize),
37 Discontiguous(String, usize),
39 EnsureLoaded(String),
41 ModuleInfo,
43 SetPrologFlag(String, String),
45 Op(u16, String, String),
47 MetaPredicate(PrologTerm),
49 Arbitrary(PrologTerm),
51}
52impl PrologDirective {
53 pub fn emit(&self) -> String {
55 match self {
56 PrologDirective::Module(name, exports) => {
57 let exp_str = exports.join(", ");
58 format!(":- module({}, [{}]).", name, exp_str)
59 }
60 PrologDirective::UseModuleLibrary(lib) => {
61 format!(":- use_module(library({})).", lib)
62 }
63 PrologDirective::UseModulePath(path) => format!(":- use_module({}).", path),
64 PrologDirective::UseModuleImports(path, imports) => {
65 let imp_str = imports.join(", ");
66 format!(":- use_module({}, [{}]).", path, imp_str)
67 }
68 PrologDirective::Dynamic(name, arity) => {
69 format!(":- dynamic {}/{}.", name, arity)
70 }
71 PrologDirective::Discontiguous(name, arity) => {
72 format!(":- discontiguous {}/{}.", name, arity)
73 }
74 PrologDirective::EnsureLoaded(path) => format!(":- ensure_loaded({}).", path),
75 PrologDirective::ModuleInfo => ":- module_info.".to_string(),
76 PrologDirective::SetPrologFlag(flag, val) => {
77 format!(":- set_prolog_flag({}, {}).", flag, val)
78 }
79 PrologDirective::Op(prio, op_type, op) => {
80 format!(":- op({}, {}, {}).", prio, op_type, op)
81 }
82 PrologDirective::MetaPredicate(term) => {
83 format!(":- meta_predicate {}.", term)
84 }
85 PrologDirective::Arbitrary(goal) => format!(":- {}.", goal),
86 }
87 }
88}
89#[allow(dead_code)]
91pub struct PrologModuleBuilder {
92 pub(super) module: PrologModule,
93}
94impl PrologModuleBuilder {
95 #[allow(dead_code)]
97 pub fn new(name: impl Into<String>) -> Self {
98 PrologModuleBuilder {
99 module: PrologModule::new(name),
100 }
101 }
102 #[allow(dead_code)]
104 pub fn script() -> Self {
105 PrologModuleBuilder {
106 module: PrologModule::script(),
107 }
108 }
109 #[allow(dead_code)]
111 pub fn export(mut self, indicator: impl Into<String>) -> Self {
112 self.module.exported_predicates.push(indicator.into());
113 self
114 }
115 #[allow(dead_code)]
117 pub fn use_library(mut self, lib: impl Into<String>) -> Self {
118 self.module
119 .items
120 .push(PrologItem::Directive(PrologDirective::UseModuleLibrary(
121 lib.into(),
122 )));
123 self
124 }
125 #[allow(dead_code)]
127 pub fn use_module(mut self, path: impl Into<String>) -> Self {
128 self.module
129 .items
130 .push(PrologItem::Directive(PrologDirective::UseModulePath(
131 path.into(),
132 )));
133 self
134 }
135 #[allow(dead_code)]
137 pub fn add_predicate(mut self, pred: PrologPredicate) -> Self {
138 self.module.items.push(PrologItem::Predicate(pred));
139 self
140 }
141 #[allow(dead_code)]
143 pub fn add_dcg(mut self, rule: DcgRule) -> Self {
144 self.module.items.push(PrologItem::Dcg(rule));
145 self
146 }
147 #[allow(dead_code)]
149 pub fn blank(mut self) -> Self {
150 self.module.items.push(PrologItem::BlankLine);
151 self
152 }
153 #[allow(dead_code)]
155 pub fn section(mut self, title: impl Into<String>) -> Self {
156 self.module
157 .items
158 .push(PrologItem::SectionComment(title.into()));
159 self
160 }
161 #[allow(dead_code)]
163 pub fn comment(mut self, text: impl Into<String>) -> Self {
164 self.module.items.push(PrologItem::LineComment(text.into()));
165 self
166 }
167 #[allow(dead_code)]
169 pub fn description(mut self, desc: impl Into<String>) -> Self {
170 self.module.description = Some(desc.into());
171 self
172 }
173 #[allow(dead_code)]
175 pub fn build(self) -> PrologModule {
176 self.module
177 }
178 #[allow(dead_code)]
180 pub fn emit(self) -> String {
181 PrologBackend::swi().emit_module(&self.module)
182 }
183}
184#[allow(dead_code)]
186pub struct PrologPredicateBuilder {
187 pub(super) pred: PrologPredicate,
188}
189impl PrologPredicateBuilder {
190 #[allow(dead_code)]
192 pub fn new(name: impl Into<String>, arity: usize) -> Self {
193 PrologPredicateBuilder {
194 pred: PrologPredicate::new(name, arity),
195 }
196 }
197 #[allow(dead_code)]
199 pub fn dynamic(mut self) -> Self {
200 self.pred.is_dynamic = true;
201 self
202 }
203 #[allow(dead_code)]
205 pub fn exported(mut self) -> Self {
206 self.pred.is_exported = true;
207 self
208 }
209 #[allow(dead_code)]
211 pub fn discontiguous(mut self) -> Self {
212 self.pred.is_discontiguous = true;
213 self
214 }
215 #[allow(dead_code)]
217 pub fn module(mut self, m: impl Into<String>) -> Self {
218 self.pred.module = Some(m.into());
219 self
220 }
221 #[allow(dead_code)]
223 pub fn doc(mut self, d: impl Into<String>) -> Self {
224 self.pred.doc = Some(d.into());
225 self
226 }
227 #[allow(dead_code)]
229 pub fn clause(mut self, c: PrologClause) -> Self {
230 self.pred.clauses.push(c);
231 self
232 }
233 #[allow(dead_code)]
235 pub fn fact(mut self, head: PrologTerm) -> Self {
236 self.pred.clauses.push(PrologClause::fact(head));
237 self
238 }
239 #[allow(dead_code)]
241 pub fn rule(mut self, head: PrologTerm, body: Vec<PrologTerm>) -> Self {
242 self.pred.clauses.push(PrologClause::rule(head, body));
243 self
244 }
245 #[allow(dead_code)]
247 pub fn build(self) -> PrologPredicate {
248 self.pred
249 }
250 #[allow(dead_code)]
252 pub fn emit(self) -> String {
253 self.pred.emit()
254 }
255}
256#[allow(dead_code)]
258pub struct PrologClauseBuilder {
259 pub(super) head: PrologTerm,
260 pub(super) body: Vec<PrologTerm>,
261 pub(super) comment: Option<String>,
262}
263impl PrologClauseBuilder {
264 #[allow(dead_code)]
266 pub fn head(head: PrologTerm) -> Self {
267 PrologClauseBuilder {
268 head,
269 body: vec![],
270 comment: None,
271 }
272 }
273 #[allow(dead_code)]
275 pub fn goal(mut self, g: PrologTerm) -> Self {
276 self.body.push(g);
277 self
278 }
279 #[allow(dead_code)]
281 pub fn goals(mut self, builder: PrologGoalBuilder) -> Self {
282 self.body.extend(builder.build());
283 self
284 }
285 #[allow(dead_code)]
287 pub fn comment(mut self, c: impl Into<String>) -> Self {
288 self.comment = Some(c.into());
289 self
290 }
291 #[allow(dead_code)]
293 pub fn build(self) -> PrologClause {
294 let mut clause = if self.body.is_empty() {
295 PrologClause::fact(self.head)
296 } else {
297 PrologClause::rule(self.head, self.body)
298 };
299 clause.comment = self.comment;
300 clause
301 }
302}
303#[derive(Debug, Clone, PartialEq)]
305pub enum PrologItem {
306 Directive(PrologDirective),
308 Predicate(PrologPredicate),
310 Clause(PrologClause),
312 Dcg(DcgRule),
314 BlankLine,
316 SectionComment(String),
318 LineComment(String),
320}
321impl PrologItem {
322 pub fn emit(&self) -> String {
324 match self {
325 PrologItem::Directive(d) => d.emit(),
326 PrologItem::Predicate(p) => p.emit(),
327 PrologItem::Clause(c) => c.emit(),
328 PrologItem::Dcg(r) => r.emit(),
329 PrologItem::BlankLine => String::new(),
330 PrologItem::SectionComment(s) => {
331 let bar = "=".repeat(s.len() + 8);
332 format!("% {}\n% === {} ===\n% {}", bar, s, bar)
333 }
334 PrologItem::LineComment(s) => format!("% {}", s),
335 }
336 }
337}
338#[allow(dead_code)]
340pub struct PrologSnippets;
341impl PrologSnippets {
342 #[allow(dead_code)]
344 pub fn member_predicate() -> PrologPredicate {
345 let mut pred = PrologPredicate::new("member", 2);
346 pred.add_clause(PrologClause::fact(compound(
347 "member",
348 vec![var("X"), PrologTerm::list_partial(vec![var("X")], var("_"))],
349 )));
350 pred.add_clause(PrologClause::rule(
351 compound(
352 "member",
353 vec![var("X"), PrologTerm::list_partial(vec![var("_")], var("T"))],
354 ),
355 vec![compound("member", vec![var("X"), var("T")])],
356 ));
357 pred
358 }
359 #[allow(dead_code)]
361 pub fn append_predicate() -> PrologPredicate {
362 let mut pred = PrologPredicate::new("append", 3);
363 pred.add_clause(PrologClause::fact(compound(
364 "append",
365 vec![PrologTerm::Nil, var("L"), var("L")],
366 )));
367 pred.add_clause(PrologClause::rule(
368 compound(
369 "append",
370 vec![
371 PrologTerm::list_partial(vec![var("H")], var("T")),
372 var("L"),
373 PrologTerm::list_partial(vec![var("H")], var("R")),
374 ],
375 ),
376 vec![compound("append", vec![var("T"), var("L"), var("R")])],
377 ));
378 pred
379 }
380 #[allow(dead_code)]
382 pub fn length_predicate() -> PrologPredicate {
383 let mut pred = PrologPredicate::new("my_length", 2);
384 pred.add_clause(PrologClause::fact(compound(
385 "my_length",
386 vec![PrologTerm::Nil, PrologTerm::Integer(0)],
387 )));
388 pred.add_clause(PrologClause::rule(
389 compound(
390 "my_length",
391 vec![PrologTerm::list_partial(vec![var("_")], var("T")), var("N")],
392 ),
393 vec![
394 compound("my_length", vec![var("T"), var("N1")]),
395 is_eval(var("N"), arith_add(var("N1"), PrologTerm::Integer(1))),
396 ],
397 ));
398 pred
399 }
400 #[allow(dead_code)]
402 pub fn max_list_predicate() -> PrologPredicate {
403 let mut pred = PrologPredicate::new("max_list", 2);
404 pred.add_clause(PrologClause::fact(compound(
405 "max_list",
406 vec![PrologTerm::list(vec![var("X")]), var("X")],
407 )));
408 pred.add_clause(PrologClause::rule(
409 compound(
410 "max_list",
411 vec![
412 PrologTerm::list_partial(vec![var("H")], var("T")),
413 var("Max"),
414 ],
415 ),
416 vec![
417 compound("max_list", vec![var("T"), var("Max1")]),
418 is_eval(var("Max"), compound("max", vec![var("H"), var("Max1")])),
419 ],
420 ));
421 pred
422 }
423 #[allow(dead_code)]
425 pub fn sum_list_predicate() -> PrologPredicate {
426 let mut pred = PrologPredicate::new("sum_list", 2);
427 pred.add_clause(PrologClause::fact(compound(
428 "sum_list",
429 vec![PrologTerm::Nil, PrologTerm::Integer(0)],
430 )));
431 pred.add_clause(PrologClause::rule(
432 compound(
433 "sum_list",
434 vec![
435 PrologTerm::list_partial(vec![var("H")], var("T")),
436 var("Sum"),
437 ],
438 ),
439 vec![
440 compound("sum_list", vec![var("T"), var("Rest")]),
441 is_eval(var("Sum"), arith_add(var("H"), var("Rest"))),
442 ],
443 ));
444 pred
445 }
446 #[allow(dead_code)]
448 pub fn last_predicate() -> PrologPredicate {
449 let mut pred = PrologPredicate::new("my_last", 2);
450 pred.add_clause(PrologClause::fact(compound(
451 "my_last",
452 vec![PrologTerm::list(vec![var("X")]), var("X")],
453 )));
454 pred.add_clause(PrologClause::rule(
455 compound(
456 "my_last",
457 vec![
458 PrologTerm::list_partial(vec![var("_")], var("T")),
459 var("Last"),
460 ],
461 ),
462 vec![compound("my_last", vec![var("T"), var("Last")])],
463 ));
464 pred
465 }
466 #[allow(dead_code)]
468 pub fn reverse_predicate() -> PrologPredicate {
469 let mut pred = PrologPredicate::new("my_reverse", 2);
470 pred.add_clause(PrologClause::rule(
471 compound("my_reverse", vec![var("L"), var("R")]),
472 vec![compound(
473 "reverse_acc",
474 vec![var("L"), PrologTerm::Nil, var("R")],
475 )],
476 ));
477 pred
478 }
479 #[allow(dead_code)]
481 pub fn msort_dedup_predicate() -> PrologPredicate {
482 let mut pred = PrologPredicate::new("msort_dedup", 2);
483 pred.add_clause(PrologClause::rule(
484 compound("msort_dedup", vec![var("List"), var("Sorted")]),
485 vec![
486 compound("msort", vec![var("List"), var("Tmp")]),
487 compound("list_to_set", vec![var("Tmp"), var("Sorted")]),
488 ],
489 ));
490 pred
491 }
492 #[allow(dead_code)]
494 pub fn flatten_predicate() -> PrologPredicate {
495 let mut pred = PrologPredicate::new("my_flatten", 2);
496 pred.add_clause(PrologClause::fact(compound(
497 "my_flatten",
498 vec![PrologTerm::Nil, PrologTerm::Nil],
499 )));
500 pred.add_clause(PrologClause::rule(
501 compound(
502 "my_flatten",
503 vec![
504 PrologTerm::list_partial(vec![var("H")], var("T")),
505 var("Flat"),
506 ],
507 ),
508 vec![
509 compound("is_list", vec![var("H")]),
510 PrologTerm::Cut,
511 compound("my_flatten", vec![var("H"), var("FH")]),
512 compound("my_flatten", vec![var("T"), var("FT")]),
513 compound("append", vec![var("FH"), var("FT"), var("Flat")]),
514 ],
515 ));
516 pred.add_clause(PrologClause::rule(
517 compound(
518 "my_flatten",
519 vec![
520 PrologTerm::list_partial(vec![var("H")], var("T")),
521 var("Flat"),
522 ],
523 ),
524 vec![
525 compound("my_flatten", vec![var("T"), var("FT")]),
526 compound(
527 "append",
528 vec![PrologTerm::list(vec![var("H")]), var("FT"), var("Flat")],
529 ),
530 ],
531 ));
532 pred
533 }
534 #[allow(dead_code)]
536 pub fn nth0_predicate() -> PrologPredicate {
537 let mut pred = PrologPredicate::new("my_nth0", 3);
538 pred.add_clause(PrologClause::fact(compound(
539 "my_nth0",
540 vec![
541 PrologTerm::Integer(0),
542 PrologTerm::list_partial(vec![var("H")], var("_")),
543 var("H"),
544 ],
545 )));
546 pred.add_clause(PrologClause::rule(
547 compound(
548 "my_nth0",
549 vec![
550 var("N"),
551 PrologTerm::list_partial(vec![var("_")], var("T")),
552 var("Elem"),
553 ],
554 ),
555 vec![
556 arith_gt(var("N"), PrologTerm::Integer(0)),
557 is_eval(var("N1"), arith_sub(var("N"), PrologTerm::Integer(1))),
558 compound("my_nth0", vec![var("N1"), var("T"), var("Elem")]),
559 ],
560 ));
561 pred
562 }
563}
564#[derive(Debug, Clone, PartialEq)]
566#[allow(dead_code)]
567pub enum PrologMode {
568 In,
570 Out,
572 InOut,
574 Meta,
576 NotFurther,
578}
579#[derive(Debug, Clone, PartialEq)]
581pub struct PrologPredicate {
582 pub name: String,
584 pub arity: usize,
586 pub clauses: Vec<PrologClause>,
588 pub is_dynamic: bool,
590 pub is_exported: bool,
592 pub is_discontiguous: bool,
594 pub module: Option<String>,
596 pub doc: Option<String>,
598}
599impl PrologPredicate {
600 pub fn new(name: impl Into<String>, arity: usize) -> Self {
602 PrologPredicate {
603 name: name.into(),
604 arity,
605 clauses: vec![],
606 is_dynamic: false,
607 is_exported: false,
608 is_discontiguous: false,
609 module: None,
610 doc: None,
611 }
612 }
613 pub fn add_clause(&mut self, clause: PrologClause) {
615 self.clauses.push(clause);
616 }
617 pub fn dynamic(mut self) -> Self {
619 self.is_dynamic = true;
620 self
621 }
622 pub fn exported(mut self) -> Self {
624 self.is_exported = true;
625 self
626 }
627 pub fn discontiguous(mut self) -> Self {
629 self.is_discontiguous = true;
630 self
631 }
632 pub fn indicator(&self) -> String {
634 format!("{}/{}", self.name, self.arity)
635 }
636 pub fn emit(&self) -> String {
638 let mut out = String::new();
639 if let Some(doc) = &self.doc {
640 for line in doc.lines() {
641 out.push_str(&format!("%% {}\n", line));
642 }
643 }
644 if self.is_dynamic {
645 out.push_str(&format!(":- dynamic {}/{}.\n", self.name, self.arity));
646 }
647 if self.is_discontiguous {
648 out.push_str(&format!(":- discontiguous {}/{}.\n", self.name, self.arity));
649 }
650 for clause in &self.clauses {
651 out.push_str(&clause.emit());
652 out.push('\n');
653 }
654 out
655 }
656}
657#[derive(Debug, Clone)]
659pub struct PrologModule {
660 pub name: Option<String>,
662 pub exported_predicates: Vec<String>,
664 pub items: Vec<PrologItem>,
666 pub description: Option<String>,
668}
669impl PrologModule {
670 pub fn new(name: impl Into<String>) -> Self {
672 PrologModule {
673 name: Some(name.into()),
674 exported_predicates: vec![],
675 items: vec![],
676 description: None,
677 }
678 }
679 pub fn script() -> Self {
681 PrologModule {
682 name: None,
683 exported_predicates: vec![],
684 items: vec![],
685 description: None,
686 }
687 }
688 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
690 self.description = Some(desc.into());
691 self
692 }
693 pub fn export(&mut self, indicator: impl Into<String>) {
695 self.exported_predicates.push(indicator.into());
696 }
697 pub fn add(&mut self, item: PrologItem) {
699 self.items.push(item);
700 }
701 pub fn directive(&mut self, d: PrologDirective) {
703 self.items.push(PrologItem::Directive(d));
704 }
705 pub fn predicate(&mut self, p: PrologPredicate) {
707 self.items.push(PrologItem::Predicate(p));
708 }
709 pub fn dcg_rule(&mut self, r: DcgRule) {
711 self.items.push(PrologItem::Dcg(r));
712 }
713 pub fn blank(&mut self) {
715 self.items.push(PrologItem::BlankLine);
716 }
717 pub fn section(&mut self, title: impl Into<String>) {
719 self.items.push(PrologItem::SectionComment(title.into()));
720 }
721}
722#[allow(dead_code)]
724pub struct PrologDCGBuilder {
725 pub(super) lhs: PrologTerm,
726 pub(super) rhs: Vec<DcgRhs>,
727 pub(super) guards: Vec<PrologTerm>,
728 pub(super) comment: Option<String>,
729}
730impl PrologDCGBuilder {
731 #[allow(dead_code)]
733 pub fn lhs(lhs: PrologTerm) -> Self {
734 PrologDCGBuilder {
735 lhs,
736 rhs: vec![],
737 guards: vec![],
738 comment: None,
739 }
740 }
741 #[allow(dead_code)]
743 pub fn nonterminal(mut self, t: PrologTerm) -> Self {
744 self.rhs.push(DcgRhs::NonTerminal(t));
745 self
746 }
747 #[allow(dead_code)]
749 pub fn terminals(mut self, ts: Vec<PrologTerm>) -> Self {
750 self.rhs.push(DcgRhs::Terminals(ts));
751 self
752 }
753 #[allow(dead_code)]
755 pub fn epsilon(mut self) -> Self {
756 self.rhs.push(DcgRhs::Epsilon);
757 self
758 }
759 #[allow(dead_code)]
761 pub fn guard(mut self, g: PrologTerm) -> Self {
762 self.guards.push(g);
763 self
764 }
765 #[allow(dead_code)]
767 pub fn comment(mut self, c: impl Into<String>) -> Self {
768 self.comment = Some(c.into());
769 self
770 }
771 #[allow(dead_code)]
773 pub fn build(self) -> DcgRule {
774 DcgRule {
775 lhs: self.lhs,
776 rhs: self.rhs,
777 guards: self.guards,
778 comment: self.comment,
779 }
780 }
781 #[allow(dead_code)]
783 pub fn emit(self) -> String {
784 self.build().emit()
785 }
786}
787#[derive(Debug, Default)]
791pub struct PrologBackend {
792 pub swi_mode: bool,
794 pub utf8_encoding: bool,
796 pub options: PrologBackendOptions,
798}
799impl PrologBackend {
800 pub fn new() -> Self {
802 PrologBackend {
803 swi_mode: false,
804 utf8_encoding: false,
805 options: PrologBackendOptions {
806 blank_between_predicates: true,
807 emit_docs: true,
808 },
809 }
810 }
811 pub fn swi() -> Self {
813 PrologBackend {
814 swi_mode: true,
815 utf8_encoding: true,
816 options: PrologBackendOptions {
817 blank_between_predicates: true,
818 emit_docs: true,
819 },
820 }
821 }
822 pub fn emit_module(&self, module: &PrologModule) -> String {
824 let mut out = String::new();
825 if self.utf8_encoding {
826 out.push_str(":- encoding(utf8).\n");
827 }
828 if let Some(desc) = &module.description {
829 out.push_str("%% ");
830 out.push_str(desc);
831 out.push('\n');
832 }
833 if let Some(name) = &module.name {
834 let exports = module.exported_predicates.join(",\n ");
835 if module.exported_predicates.is_empty() {
836 out.push_str(&format!(":- module({}, []).\n", name));
837 } else {
838 out.push_str(&format!(
839 ":- module({}, [\n {}\n ]).\n",
840 name, exports
841 ));
842 }
843 out.push('\n');
844 }
845 for item in &module.items {
846 let s = item.emit();
847 if !s.is_empty() {
848 out.push_str(&s);
849 out.push('\n');
850 } else {
851 out.push('\n');
852 }
853 if self.options.blank_between_predicates && matches!(item, PrologItem::Predicate(_)) {
854 out.push('\n');
855 }
856 }
857 out
858 }
859 pub fn emit_clause(&self, clause: &PrologClause) -> String {
861 clause.emit()
862 }
863 pub fn emit_predicate(&self, pred: &PrologPredicate) -> String {
865 pred.emit()
866 }
867 pub fn emit_dcg(&self, rule: &DcgRule) -> String {
869 rule.emit()
870 }
871 pub fn emit_directive(&self, directive: &PrologDirective) -> String {
873 directive.emit()
874 }
875 pub fn emit_clauses(&self, clauses: &[PrologClause]) -> String {
877 clauses
878 .iter()
879 .map(|c| c.emit())
880 .collect::<Vec<_>>()
881 .join("\n")
882 }
883 pub fn build_swi_preamble(
885 &self,
886 module_name: &str,
887 exports: &[(&str, usize)],
888 libraries: &[&str],
889 ) -> String {
890 let mut out = String::new();
891 out.push_str(":- encoding(utf8).\n\n");
892 let exp_strs: Vec<String> = exports
893 .iter()
894 .map(|(n, a)| format!("{}/{}", n, a))
895 .collect();
896 if exp_strs.is_empty() {
897 out.push_str(&format!(":- module({}, []).\n", module_name));
898 } else {
899 let exp_list = exp_strs.join(",\n ");
900 out.push_str(&format!(
901 ":- module({}, [\n {}\n ]).\n",
902 module_name, exp_list
903 ));
904 }
905 for lib in libraries {
906 out.push_str(&format!(":- use_module(library({})).\n", lib));
907 }
908 out
909 }
910}
911#[allow(dead_code)]
913pub struct PrologAssertionBuilder;
914impl PrologAssertionBuilder {
915 #[allow(dead_code)]
917 pub fn assertz_fact(head: PrologTerm) -> PrologTerm {
918 compound("assertz", vec![head])
919 }
920 #[allow(dead_code)]
922 pub fn asserta_fact(head: PrologTerm) -> PrologTerm {
923 compound("asserta", vec![head])
924 }
925 #[allow(dead_code)]
927 pub fn assertz_rule(head: PrologTerm, body: PrologTerm) -> PrologTerm {
928 let rule = PrologTerm::Op(":-".to_string(), Box::new(head), Box::new(body));
929 compound("assertz", vec![rule])
930 }
931 #[allow(dead_code)]
933 pub fn retract(head: PrologTerm) -> PrologTerm {
934 compound("retract", vec![head])
935 }
936 #[allow(dead_code)]
938 pub fn retractall(head: PrologTerm) -> PrologTerm {
939 compound("retractall", vec![head])
940 }
941 #[allow(dead_code)]
943 pub fn abolish(name: &str, arity: usize) -> PrologTerm {
944 compound(
945 "abolish",
946 vec![PrologTerm::Op(
947 "/".to_string(),
948 Box::new(atom(name)),
949 Box::new(PrologTerm::Integer(arity as i64)),
950 )],
951 )
952 }
953}
954#[derive(Debug, Default, Clone)]
956pub struct PrologBackendOptions {
957 pub blank_between_predicates: bool,
959 pub emit_docs: bool,
961}
962#[allow(dead_code)]
964pub struct PrologConstraints;
965impl PrologConstraints {
966 #[allow(dead_code)]
968 pub fn in_range(x: PrologTerm, low: PrologTerm, high: PrologTerm) -> PrologTerm {
969 PrologTerm::Op(
970 "in".to_string(),
971 Box::new(x),
972 Box::new(PrologTerm::Op(
973 "..".to_string(),
974 Box::new(low),
975 Box::new(high),
976 )),
977 )
978 }
979 #[allow(dead_code)]
981 pub fn clp_eq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
982 PrologTerm::Op("#=".to_string(), Box::new(x), Box::new(y))
983 }
984 #[allow(dead_code)]
986 pub fn clp_neq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
987 PrologTerm::Op("#\\=".to_string(), Box::new(x), Box::new(y))
988 }
989 #[allow(dead_code)]
991 pub fn clp_lt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
992 PrologTerm::Op("#<".to_string(), Box::new(x), Box::new(y))
993 }
994 #[allow(dead_code)]
996 pub fn clp_gt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
997 PrologTerm::Op("#>".to_string(), Box::new(x), Box::new(y))
998 }
999 #[allow(dead_code)]
1001 pub fn clp_le(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1002 PrologTerm::Op("#=<".to_string(), Box::new(x), Box::new(y))
1003 }
1004 #[allow(dead_code)]
1006 pub fn clp_ge(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1007 PrologTerm::Op("#>=".to_string(), Box::new(x), Box::new(y))
1008 }
1009 #[allow(dead_code)]
1011 pub fn all_different(vars: Vec<PrologTerm>) -> PrologTerm {
1012 compound("all_different", vec![PrologTerm::list(vars)])
1013 }
1014 #[allow(dead_code)]
1016 pub fn label(vars: Vec<PrologTerm>) -> PrologTerm {
1017 compound("label", vec![PrologTerm::list(vars)])
1018 }
1019 #[allow(dead_code)]
1021 pub fn sum_eq(vars: Vec<PrologTerm>, sum: PrologTerm) -> PrologTerm {
1022 compound("sum", vec![PrologTerm::list(vars), atom("#="), sum])
1023 }
1024}
1025#[derive(Debug, Clone, PartialEq)]
1027pub struct PrologClause {
1028 pub head: PrologTerm,
1030 pub body: Vec<PrologTerm>,
1032 pub is_fact: bool,
1034 pub comment: Option<String>,
1036}
1037impl PrologClause {
1038 pub fn fact(head: PrologTerm) -> Self {
1040 PrologClause {
1041 head,
1042 body: vec![],
1043 is_fact: true,
1044 comment: None,
1045 }
1046 }
1047 pub fn rule(head: PrologTerm, body: Vec<PrologTerm>) -> Self {
1049 PrologClause {
1050 head,
1051 body,
1052 is_fact: false,
1053 comment: None,
1054 }
1055 }
1056 pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
1058 self.comment = Some(comment.into());
1059 self
1060 }
1061 pub fn emit(&self) -> String {
1063 let mut out = String::new();
1064 if let Some(c) = &self.comment {
1065 out.push_str(&format!("% {}\n", c));
1066 }
1067 if self.is_fact || self.body.is_empty() {
1068 out.push_str(&format!("{}.", self.head));
1069 } else {
1070 out.push_str(&format!("{} :-", self.head));
1071 if self.body.len() == 1 {
1072 out.push_str(&format!("\n {}.", self.body[0]));
1073 } else {
1074 for (i, goal) in self.body.iter().enumerate() {
1075 if i < self.body.len() - 1 {
1076 out.push_str(&format!("\n {},", goal));
1077 } else {
1078 out.push_str(&format!("\n {}.", goal));
1079 }
1080 }
1081 }
1082 }
1083 out
1084 }
1085}
1086#[derive(Debug, Clone, PartialEq)]
1088#[allow(dead_code)]
1089pub enum PrologType {
1090 Integer,
1092 Float,
1094 Atom,
1096 PrologString,
1098 List(Box<PrologType>),
1100 Compound,
1102 Callable,
1104 Term,
1106 Boolean,
1108 Var,
1110 Nonvar,
1112 Number,
1114 Atomic,
1116 PositiveInteger,
1118 NonNeg,
1120 Custom(String),
1122}
1123#[derive(Debug, Clone, PartialEq)]
1125pub struct DcgRule {
1126 pub lhs: PrologTerm,
1128 pub rhs: Vec<DcgRhs>,
1130 pub guards: Vec<PrologTerm>,
1132 pub comment: Option<String>,
1134}
1135impl DcgRule {
1136 pub fn emit(&self) -> String {
1138 let mut out = String::new();
1139 if let Some(c) = &self.comment {
1140 out.push_str(&format!("% {}\n", c));
1141 }
1142 out.push_str(&format!("{} -->", self.lhs));
1143 let mut parts: Vec<String> = self.rhs.iter().map(|r| format!("{}", r)).collect();
1144 if !self.guards.is_empty() {
1145 let guard_str = self
1146 .guards
1147 .iter()
1148 .map(|g| format!("{}", g))
1149 .collect::<Vec<_>>()
1150 .join(", ");
1151 parts.push(format!("{{{}}}", guard_str));
1152 }
1153 if parts.is_empty() {
1154 out.push_str("\n [].");
1155 } else if parts.len() == 1 {
1156 out.push_str(&format!("\n {}.", parts[0]));
1157 } else {
1158 for (i, p) in parts.iter().enumerate() {
1159 if i < parts.len() - 1 {
1160 out.push_str(&format!("\n {},", p));
1161 } else {
1162 out.push_str(&format!("\n {}.", p));
1163 }
1164 }
1165 }
1166 out
1167 }
1168}
1169#[derive(Debug, Clone)]
1171#[allow(dead_code)]
1172pub struct PrologTypeSig {
1173 pub name: String,
1175 pub params: Vec<(PrologMode, PrologType)>,
1177 pub description: Option<String>,
1179}
1180impl PrologTypeSig {
1181 #[allow(dead_code)]
1183 pub fn emit_pldoc(&self) -> String {
1184 let mut out = String::new();
1185 out.push_str(&format!("%% {}/{}\n", self.name, self.params.len()));
1186 if let Some(desc) = &self.description {
1187 out.push_str(&format!("% {}\n", desc));
1188 }
1189 for (mode, ty) in &self.params {
1190 out.push_str(&format!("% @param {} {}\n", mode, ty));
1191 }
1192 out
1193 }
1194 #[allow(dead_code)]
1196 pub fn emit_pred_directive(&self) -> String {
1197 let param_str: Vec<String> = self
1198 .params
1199 .iter()
1200 .map(|(m, t)| format!("{}({})", m, t))
1201 .collect();
1202 format!(":- pred {}({}).\n", self.name, param_str.join(", "))
1203 }
1204}
1205#[allow(dead_code)]
1207pub struct PrologArith;
1208impl PrologArith {
1209 #[allow(dead_code)]
1211 pub fn add(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1212 arith_add(x, y)
1213 }
1214 #[allow(dead_code)]
1216 pub fn sub(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1217 arith_sub(x, y)
1218 }
1219 #[allow(dead_code)]
1221 pub fn mul(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1222 arith_mul(x, y)
1223 }
1224 #[allow(dead_code)]
1226 pub fn idiv(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1227 PrologTerm::Op("//".to_string(), Box::new(x), Box::new(y))
1228 }
1229 #[allow(dead_code)]
1231 pub fn mmod(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1232 arith_mod(x, y)
1233 }
1234 #[allow(dead_code)]
1236 pub fn abs(x: PrologTerm) -> PrologTerm {
1237 compound("abs", vec![x])
1238 }
1239 #[allow(dead_code)]
1241 pub fn max(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1242 compound("max", vec![x, y])
1243 }
1244 #[allow(dead_code)]
1246 pub fn min(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1247 compound("min", vec![x, y])
1248 }
1249 #[allow(dead_code)]
1251 pub fn pow(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1252 PrologTerm::Op("^".to_string(), Box::new(x), Box::new(y))
1253 }
1254 #[allow(dead_code)]
1256 pub fn sqrt(x: PrologTerm) -> PrologTerm {
1257 compound("sqrt", vec![x])
1258 }
1259 #[allow(dead_code)]
1261 pub fn truncate(x: PrologTerm) -> PrologTerm {
1262 compound("truncate", vec![x])
1263 }
1264 #[allow(dead_code)]
1266 pub fn round(x: PrologTerm) -> PrologTerm {
1267 compound("round", vec![x])
1268 }
1269 #[allow(dead_code)]
1271 pub fn sign(x: PrologTerm) -> PrologTerm {
1272 compound("sign", vec![x])
1273 }
1274 #[allow(dead_code)]
1276 pub fn bitand(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1277 PrologTerm::Op("/\\".to_string(), Box::new(x), Box::new(y))
1278 }
1279 #[allow(dead_code)]
1281 pub fn bitor(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1282 PrologTerm::Op("\\/".to_string(), Box::new(x), Box::new(y))
1283 }
1284 #[allow(dead_code)]
1286 pub fn xor(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1287 PrologTerm::Op("xor".to_string(), Box::new(x), Box::new(y))
1288 }
1289 #[allow(dead_code)]
1291 pub fn shl(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1292 PrologTerm::Op("<<".to_string(), Box::new(x), Box::new(y))
1293 }
1294 #[allow(dead_code)]
1296 pub fn shr(x: PrologTerm, y: PrologTerm) -> PrologTerm {
1297 PrologTerm::Op(">>".to_string(), Box::new(x), Box::new(y))
1298 }
1299}
1300#[derive(Debug, Clone, PartialEq)]
1302pub enum PrologTerm {
1303 Atom(String),
1305 Integer(i64),
1307 Float(f64),
1309 Variable(String),
1311 Compound(String, Vec<PrologTerm>),
1313 List(Vec<PrologTerm>, Option<Box<PrologTerm>>),
1315 Nil,
1317 DcgPhrase(Box<PrologTerm>, Box<PrologTerm>),
1319 Op(String, Box<PrologTerm>, Box<PrologTerm>),
1321 PrefixOp(String, Box<PrologTerm>),
1323 Cut,
1325 Anon,
1327}
1328impl PrologTerm {
1329 pub fn atom(s: impl Into<String>) -> Self {
1331 PrologTerm::Atom(s.into())
1332 }
1333 pub fn var(s: impl Into<String>) -> Self {
1335 PrologTerm::Variable(s.into())
1336 }
1337 pub fn compound(functor: impl Into<String>, args: Vec<PrologTerm>) -> Self {
1339 PrologTerm::Compound(functor.into(), args)
1340 }
1341 pub fn list(elems: Vec<PrologTerm>) -> Self {
1343 PrologTerm::List(elems, None)
1344 }
1345 pub fn list_partial(elems: Vec<PrologTerm>, tail: PrologTerm) -> Self {
1347 PrologTerm::List(elems, Some(Box::new(tail)))
1348 }
1349 pub fn op(op: impl Into<String>, lhs: PrologTerm, rhs: PrologTerm) -> Self {
1351 PrologTerm::Op(op.into(), Box::new(lhs), Box::new(rhs))
1352 }
1353 pub fn prefix_op(op: impl Into<String>, arg: PrologTerm) -> Self {
1355 PrologTerm::PrefixOp(op.into(), Box::new(arg))
1356 }
1357 pub fn functor_arity(&self) -> usize {
1359 match self {
1360 PrologTerm::Compound(_, args) => args.len(),
1361 _ => 0,
1362 }
1363 }
1364 pub(super) fn needs_parens_as_arg(&self) -> bool {
1366 matches!(self, PrologTerm::Op(_, _, _) | PrologTerm::PrefixOp(_, _))
1367 }
1368 pub(super) fn needs_quoting(s: &str) -> bool {
1370 if s.is_empty() {
1371 return true;
1372 }
1373 let mut chars = s.chars();
1374 let first = chars
1375 .next()
1376 .expect("s is non-empty; guaranteed by early return on s.is_empty() above");
1377 if s.chars().all(|c| "#&*+-./:<=>?@\\^~".contains(c)) {
1378 return false;
1379 }
1380 if !first.is_lowercase() && first != '_' {
1381 return true;
1382 }
1383 s.chars().any(|c| !c.is_alphanumeric() && c != '_')
1384 }
1385 pub(super) fn fmt_atom(s: &str) -> String {
1387 if Self::needs_quoting(s) {
1388 format!("'{}'", s.replace('\'', "\\'"))
1389 } else {
1390 s.to_string()
1391 }
1392 }
1393}
1394#[allow(dead_code)]
1396pub struct PrologMetaPredicates;
1397impl PrologMetaPredicates {
1398 #[allow(dead_code)]
1400 pub fn maplist(goal: PrologTerm, list: PrologTerm) -> PrologTerm {
1401 compound("maplist", vec![goal, list])
1402 }
1403 #[allow(dead_code)]
1405 pub fn maplist2(goal: PrologTerm, list: PrologTerm, result: PrologTerm) -> PrologTerm {
1406 compound("maplist", vec![goal, list, result])
1407 }
1408 #[allow(dead_code)]
1410 pub fn include(goal: PrologTerm, list: PrologTerm, result: PrologTerm) -> PrologTerm {
1411 compound("include", vec![goal, list, result])
1412 }
1413 #[allow(dead_code)]
1415 pub fn exclude(goal: PrologTerm, list: PrologTerm, result: PrologTerm) -> PrologTerm {
1416 compound("exclude", vec![goal, list, result])
1417 }
1418 #[allow(dead_code)]
1420 pub fn foldl(goal: PrologTerm, list: PrologTerm, v0: PrologTerm, v: PrologTerm) -> PrologTerm {
1421 compound("foldl", vec![goal, list, v0, v])
1422 }
1423 #[allow(dead_code)]
1425 pub fn aggregate_all(template: PrologTerm, goal: PrologTerm, result: PrologTerm) -> PrologTerm {
1426 compound("aggregate_all", vec![template, goal, result])
1427 }
1428 #[allow(dead_code)]
1430 pub fn call_n(f: PrologTerm, mut args: Vec<PrologTerm>) -> PrologTerm {
1431 let mut all_args = vec![f];
1432 all_args.append(&mut args);
1433 compound("call", all_args)
1434 }
1435 #[allow(dead_code)]
1437 pub fn once(goal: PrologTerm) -> PrologTerm {
1438 compound("once", vec![goal])
1439 }
1440 #[allow(dead_code)]
1442 pub fn ignore(goal: PrologTerm) -> PrologTerm {
1443 compound("ignore", vec![goal])
1444 }
1445 #[allow(dead_code)]
1447 pub fn forall(cond: PrologTerm, action: PrologTerm) -> PrologTerm {
1448 compound("forall", vec![cond, action])
1449 }
1450 #[allow(dead_code)]
1452 pub fn findall(template: PrologTerm, goal: PrologTerm, bag: PrologTerm) -> PrologTerm {
1453 compound("findall", vec![template, goal, bag])
1454 }
1455 #[allow(dead_code)]
1457 pub fn bagof(template: PrologTerm, goal: PrologTerm, bag: PrologTerm) -> PrologTerm {
1458 compound("bagof", vec![template, goal, bag])
1459 }
1460 #[allow(dead_code)]
1462 pub fn setof(template: PrologTerm, goal: PrologTerm, bag: PrologTerm) -> PrologTerm {
1463 compound("setof", vec![template, goal, bag])
1464 }
1465}