use super::functions::*;
use super::prologgoalbuilder_type::PrologGoalBuilder;
#[derive(Debug, Clone, PartialEq)]
pub enum DcgRhs {
NonTerminal(PrologTerm),
Terminals(Vec<PrologTerm>),
Epsilon,
Goal(Vec<PrologTerm>),
Disjunction(Vec<DcgRhs>, Vec<DcgRhs>),
Seq(Vec<DcgRhs>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum PrologDirective {
Module(String, Vec<String>),
UseModuleLibrary(String),
UseModulePath(String),
UseModuleImports(String, Vec<String>),
Dynamic(String, usize),
Discontiguous(String, usize),
EnsureLoaded(String),
ModuleInfo,
SetPrologFlag(String, String),
Op(u16, String, String),
MetaPredicate(PrologTerm),
Arbitrary(PrologTerm),
}
impl PrologDirective {
pub fn emit(&self) -> String {
match self {
PrologDirective::Module(name, exports) => {
let exp_str = exports.join(", ");
format!(":- module({}, [{}]).", name, exp_str)
}
PrologDirective::UseModuleLibrary(lib) => {
format!(":- use_module(library({})).", lib)
}
PrologDirective::UseModulePath(path) => format!(":- use_module({}).", path),
PrologDirective::UseModuleImports(path, imports) => {
let imp_str = imports.join(", ");
format!(":- use_module({}, [{}]).", path, imp_str)
}
PrologDirective::Dynamic(name, arity) => {
format!(":- dynamic {}/{}.", name, arity)
}
PrologDirective::Discontiguous(name, arity) => {
format!(":- discontiguous {}/{}.", name, arity)
}
PrologDirective::EnsureLoaded(path) => format!(":- ensure_loaded({}).", path),
PrologDirective::ModuleInfo => ":- module_info.".to_string(),
PrologDirective::SetPrologFlag(flag, val) => {
format!(":- set_prolog_flag({}, {}).", flag, val)
}
PrologDirective::Op(prio, op_type, op) => {
format!(":- op({}, {}, {}).", prio, op_type, op)
}
PrologDirective::MetaPredicate(term) => {
format!(":- meta_predicate {}.", term)
}
PrologDirective::Arbitrary(goal) => format!(":- {}.", goal),
}
}
}
#[allow(dead_code)]
pub struct PrologModuleBuilder {
pub(super) module: PrologModule,
}
impl PrologModuleBuilder {
#[allow(dead_code)]
pub fn new(name: impl Into<String>) -> Self {
PrologModuleBuilder {
module: PrologModule::new(name),
}
}
#[allow(dead_code)]
pub fn script() -> Self {
PrologModuleBuilder {
module: PrologModule::script(),
}
}
#[allow(dead_code)]
pub fn export(mut self, indicator: impl Into<String>) -> Self {
self.module.exported_predicates.push(indicator.into());
self
}
#[allow(dead_code)]
pub fn use_library(mut self, lib: impl Into<String>) -> Self {
self.module
.items
.push(PrologItem::Directive(PrologDirective::UseModuleLibrary(
lib.into(),
)));
self
}
#[allow(dead_code)]
pub fn use_module(mut self, path: impl Into<String>) -> Self {
self.module
.items
.push(PrologItem::Directive(PrologDirective::UseModulePath(
path.into(),
)));
self
}
#[allow(dead_code)]
pub fn add_predicate(mut self, pred: PrologPredicate) -> Self {
self.module.items.push(PrologItem::Predicate(pred));
self
}
#[allow(dead_code)]
pub fn add_dcg(mut self, rule: DcgRule) -> Self {
self.module.items.push(PrologItem::Dcg(rule));
self
}
#[allow(dead_code)]
pub fn blank(mut self) -> Self {
self.module.items.push(PrologItem::BlankLine);
self
}
#[allow(dead_code)]
pub fn section(mut self, title: impl Into<String>) -> Self {
self.module
.items
.push(PrologItem::SectionComment(title.into()));
self
}
#[allow(dead_code)]
pub fn comment(mut self, text: impl Into<String>) -> Self {
self.module.items.push(PrologItem::LineComment(text.into()));
self
}
#[allow(dead_code)]
pub fn description(mut self, desc: impl Into<String>) -> Self {
self.module.description = Some(desc.into());
self
}
#[allow(dead_code)]
pub fn build(self) -> PrologModule {
self.module
}
#[allow(dead_code)]
pub fn emit(self) -> String {
PrologBackend::swi().emit_module(&self.module)
}
}
#[allow(dead_code)]
pub struct PrologPredicateBuilder {
pub(super) pred: PrologPredicate,
}
impl PrologPredicateBuilder {
#[allow(dead_code)]
pub fn new(name: impl Into<String>, arity: usize) -> Self {
PrologPredicateBuilder {
pred: PrologPredicate::new(name, arity),
}
}
#[allow(dead_code)]
pub fn dynamic(mut self) -> Self {
self.pred.is_dynamic = true;
self
}
#[allow(dead_code)]
pub fn exported(mut self) -> Self {
self.pred.is_exported = true;
self
}
#[allow(dead_code)]
pub fn discontiguous(mut self) -> Self {
self.pred.is_discontiguous = true;
self
}
#[allow(dead_code)]
pub fn module(mut self, m: impl Into<String>) -> Self {
self.pred.module = Some(m.into());
self
}
#[allow(dead_code)]
pub fn doc(mut self, d: impl Into<String>) -> Self {
self.pred.doc = Some(d.into());
self
}
#[allow(dead_code)]
pub fn clause(mut self, c: PrologClause) -> Self {
self.pred.clauses.push(c);
self
}
#[allow(dead_code)]
pub fn fact(mut self, head: PrologTerm) -> Self {
self.pred.clauses.push(PrologClause::fact(head));
self
}
#[allow(dead_code)]
pub fn rule(mut self, head: PrologTerm, body: Vec<PrologTerm>) -> Self {
self.pred.clauses.push(PrologClause::rule(head, body));
self
}
#[allow(dead_code)]
pub fn build(self) -> PrologPredicate {
self.pred
}
#[allow(dead_code)]
pub fn emit(self) -> String {
self.pred.emit()
}
}
#[allow(dead_code)]
pub struct PrologClauseBuilder {
pub(super) head: PrologTerm,
pub(super) body: Vec<PrologTerm>,
pub(super) comment: Option<String>,
}
impl PrologClauseBuilder {
#[allow(dead_code)]
pub fn head(head: PrologTerm) -> Self {
PrologClauseBuilder {
head,
body: vec![],
comment: None,
}
}
#[allow(dead_code)]
pub fn goal(mut self, g: PrologTerm) -> Self {
self.body.push(g);
self
}
#[allow(dead_code)]
pub fn goals(mut self, builder: PrologGoalBuilder) -> Self {
self.body.extend(builder.build());
self
}
#[allow(dead_code)]
pub fn comment(mut self, c: impl Into<String>) -> Self {
self.comment = Some(c.into());
self
}
#[allow(dead_code)]
pub fn build(self) -> PrologClause {
let mut clause = if self.body.is_empty() {
PrologClause::fact(self.head)
} else {
PrologClause::rule(self.head, self.body)
};
clause.comment = self.comment;
clause
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PrologItem {
Directive(PrologDirective),
Predicate(PrologPredicate),
Clause(PrologClause),
Dcg(DcgRule),
BlankLine,
SectionComment(String),
LineComment(String),
}
impl PrologItem {
pub fn emit(&self) -> String {
match self {
PrologItem::Directive(d) => d.emit(),
PrologItem::Predicate(p) => p.emit(),
PrologItem::Clause(c) => c.emit(),
PrologItem::Dcg(r) => r.emit(),
PrologItem::BlankLine => String::new(),
PrologItem::SectionComment(s) => {
let bar = "=".repeat(s.len() + 8);
format!("% {}\n% === {} ===\n% {}", bar, s, bar)
}
PrologItem::LineComment(s) => format!("% {}", s),
}
}
}
#[allow(dead_code)]
pub struct PrologSnippets;
impl PrologSnippets {
#[allow(dead_code)]
pub fn member_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("member", 2);
pred.add_clause(PrologClause::fact(compound(
"member",
vec![var("X"), PrologTerm::list_partial(vec![var("X")], var("_"))],
)));
pred.add_clause(PrologClause::rule(
compound(
"member",
vec![var("X"), PrologTerm::list_partial(vec![var("_")], var("T"))],
),
vec![compound("member", vec![var("X"), var("T")])],
));
pred
}
#[allow(dead_code)]
pub fn append_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("append", 3);
pred.add_clause(PrologClause::fact(compound(
"append",
vec![PrologTerm::Nil, var("L"), var("L")],
)));
pred.add_clause(PrologClause::rule(
compound(
"append",
vec![
PrologTerm::list_partial(vec![var("H")], var("T")),
var("L"),
PrologTerm::list_partial(vec![var("H")], var("R")),
],
),
vec![compound("append", vec![var("T"), var("L"), var("R")])],
));
pred
}
#[allow(dead_code)]
pub fn length_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("my_length", 2);
pred.add_clause(PrologClause::fact(compound(
"my_length",
vec![PrologTerm::Nil, PrologTerm::Integer(0)],
)));
pred.add_clause(PrologClause::rule(
compound(
"my_length",
vec![PrologTerm::list_partial(vec![var("_")], var("T")), var("N")],
),
vec![
compound("my_length", vec![var("T"), var("N1")]),
is_eval(var("N"), arith_add(var("N1"), PrologTerm::Integer(1))),
],
));
pred
}
#[allow(dead_code)]
pub fn max_list_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("max_list", 2);
pred.add_clause(PrologClause::fact(compound(
"max_list",
vec![PrologTerm::list(vec![var("X")]), var("X")],
)));
pred.add_clause(PrologClause::rule(
compound(
"max_list",
vec![
PrologTerm::list_partial(vec![var("H")], var("T")),
var("Max"),
],
),
vec![
compound("max_list", vec![var("T"), var("Max1")]),
is_eval(var("Max"), compound("max", vec![var("H"), var("Max1")])),
],
));
pred
}
#[allow(dead_code)]
pub fn sum_list_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("sum_list", 2);
pred.add_clause(PrologClause::fact(compound(
"sum_list",
vec![PrologTerm::Nil, PrologTerm::Integer(0)],
)));
pred.add_clause(PrologClause::rule(
compound(
"sum_list",
vec![
PrologTerm::list_partial(vec![var("H")], var("T")),
var("Sum"),
],
),
vec![
compound("sum_list", vec![var("T"), var("Rest")]),
is_eval(var("Sum"), arith_add(var("H"), var("Rest"))),
],
));
pred
}
#[allow(dead_code)]
pub fn last_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("my_last", 2);
pred.add_clause(PrologClause::fact(compound(
"my_last",
vec![PrologTerm::list(vec![var("X")]), var("X")],
)));
pred.add_clause(PrologClause::rule(
compound(
"my_last",
vec![
PrologTerm::list_partial(vec![var("_")], var("T")),
var("Last"),
],
),
vec![compound("my_last", vec![var("T"), var("Last")])],
));
pred
}
#[allow(dead_code)]
pub fn reverse_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("my_reverse", 2);
pred.add_clause(PrologClause::rule(
compound("my_reverse", vec![var("L"), var("R")]),
vec![compound(
"reverse_acc",
vec![var("L"), PrologTerm::Nil, var("R")],
)],
));
pred
}
#[allow(dead_code)]
pub fn msort_dedup_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("msort_dedup", 2);
pred.add_clause(PrologClause::rule(
compound("msort_dedup", vec![var("List"), var("Sorted")]),
vec![
compound("msort", vec![var("List"), var("Tmp")]),
compound("list_to_set", vec![var("Tmp"), var("Sorted")]),
],
));
pred
}
#[allow(dead_code)]
pub fn flatten_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("my_flatten", 2);
pred.add_clause(PrologClause::fact(compound(
"my_flatten",
vec![PrologTerm::Nil, PrologTerm::Nil],
)));
pred.add_clause(PrologClause::rule(
compound(
"my_flatten",
vec![
PrologTerm::list_partial(vec![var("H")], var("T")),
var("Flat"),
],
),
vec![
compound("is_list", vec![var("H")]),
PrologTerm::Cut,
compound("my_flatten", vec![var("H"), var("FH")]),
compound("my_flatten", vec![var("T"), var("FT")]),
compound("append", vec![var("FH"), var("FT"), var("Flat")]),
],
));
pred.add_clause(PrologClause::rule(
compound(
"my_flatten",
vec![
PrologTerm::list_partial(vec![var("H")], var("T")),
var("Flat"),
],
),
vec![
compound("my_flatten", vec![var("T"), var("FT")]),
compound(
"append",
vec![PrologTerm::list(vec![var("H")]), var("FT"), var("Flat")],
),
],
));
pred
}
#[allow(dead_code)]
pub fn nth0_predicate() -> PrologPredicate {
let mut pred = PrologPredicate::new("my_nth0", 3);
pred.add_clause(PrologClause::fact(compound(
"my_nth0",
vec![
PrologTerm::Integer(0),
PrologTerm::list_partial(vec![var("H")], var("_")),
var("H"),
],
)));
pred.add_clause(PrologClause::rule(
compound(
"my_nth0",
vec![
var("N"),
PrologTerm::list_partial(vec![var("_")], var("T")),
var("Elem"),
],
),
vec![
arith_gt(var("N"), PrologTerm::Integer(0)),
is_eval(var("N1"), arith_sub(var("N"), PrologTerm::Integer(1))),
compound("my_nth0", vec![var("N1"), var("T"), var("Elem")]),
],
));
pred
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)]
pub enum PrologMode {
In,
Out,
InOut,
Meta,
NotFurther,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PrologPredicate {
pub name: String,
pub arity: usize,
pub clauses: Vec<PrologClause>,
pub is_dynamic: bool,
pub is_exported: bool,
pub is_discontiguous: bool,
pub module: Option<String>,
pub doc: Option<String>,
}
impl PrologPredicate {
pub fn new(name: impl Into<String>, arity: usize) -> Self {
PrologPredicate {
name: name.into(),
arity,
clauses: vec![],
is_dynamic: false,
is_exported: false,
is_discontiguous: false,
module: None,
doc: None,
}
}
pub fn add_clause(&mut self, clause: PrologClause) {
self.clauses.push(clause);
}
pub fn dynamic(mut self) -> Self {
self.is_dynamic = true;
self
}
pub fn exported(mut self) -> Self {
self.is_exported = true;
self
}
pub fn discontiguous(mut self) -> Self {
self.is_discontiguous = true;
self
}
pub fn indicator(&self) -> String {
format!("{}/{}", self.name, self.arity)
}
pub fn emit(&self) -> String {
let mut out = String::new();
if let Some(doc) = &self.doc {
for line in doc.lines() {
out.push_str(&format!("%% {}\n", line));
}
}
if self.is_dynamic {
out.push_str(&format!(":- dynamic {}/{}.\n", self.name, self.arity));
}
if self.is_discontiguous {
out.push_str(&format!(":- discontiguous {}/{}.\n", self.name, self.arity));
}
for clause in &self.clauses {
out.push_str(&clause.emit());
out.push('\n');
}
out
}
}
#[derive(Debug, Clone)]
pub struct PrologModule {
pub name: Option<String>,
pub exported_predicates: Vec<String>,
pub items: Vec<PrologItem>,
pub description: Option<String>,
}
impl PrologModule {
pub fn new(name: impl Into<String>) -> Self {
PrologModule {
name: Some(name.into()),
exported_predicates: vec![],
items: vec![],
description: None,
}
}
pub fn script() -> Self {
PrologModule {
name: None,
exported_predicates: vec![],
items: vec![],
description: None,
}
}
pub fn with_description(mut self, desc: impl Into<String>) -> Self {
self.description = Some(desc.into());
self
}
pub fn export(&mut self, indicator: impl Into<String>) {
self.exported_predicates.push(indicator.into());
}
pub fn add(&mut self, item: PrologItem) {
self.items.push(item);
}
pub fn directive(&mut self, d: PrologDirective) {
self.items.push(PrologItem::Directive(d));
}
pub fn predicate(&mut self, p: PrologPredicate) {
self.items.push(PrologItem::Predicate(p));
}
pub fn dcg_rule(&mut self, r: DcgRule) {
self.items.push(PrologItem::Dcg(r));
}
pub fn blank(&mut self) {
self.items.push(PrologItem::BlankLine);
}
pub fn section(&mut self, title: impl Into<String>) {
self.items.push(PrologItem::SectionComment(title.into()));
}
}
#[allow(dead_code)]
pub struct PrologDCGBuilder {
pub(super) lhs: PrologTerm,
pub(super) rhs: Vec<DcgRhs>,
pub(super) guards: Vec<PrologTerm>,
pub(super) comment: Option<String>,
}
impl PrologDCGBuilder {
#[allow(dead_code)]
pub fn lhs(lhs: PrologTerm) -> Self {
PrologDCGBuilder {
lhs,
rhs: vec![],
guards: vec![],
comment: None,
}
}
#[allow(dead_code)]
pub fn nonterminal(mut self, t: PrologTerm) -> Self {
self.rhs.push(DcgRhs::NonTerminal(t));
self
}
#[allow(dead_code)]
pub fn terminals(mut self, ts: Vec<PrologTerm>) -> Self {
self.rhs.push(DcgRhs::Terminals(ts));
self
}
#[allow(dead_code)]
pub fn epsilon(mut self) -> Self {
self.rhs.push(DcgRhs::Epsilon);
self
}
#[allow(dead_code)]
pub fn guard(mut self, g: PrologTerm) -> Self {
self.guards.push(g);
self
}
#[allow(dead_code)]
pub fn comment(mut self, c: impl Into<String>) -> Self {
self.comment = Some(c.into());
self
}
#[allow(dead_code)]
pub fn build(self) -> DcgRule {
DcgRule {
lhs: self.lhs,
rhs: self.rhs,
guards: self.guards,
comment: self.comment,
}
}
#[allow(dead_code)]
pub fn emit(self) -> String {
self.build().emit()
}
}
#[derive(Debug, Default)]
pub struct PrologBackend {
pub swi_mode: bool,
pub utf8_encoding: bool,
pub options: PrologBackendOptions,
}
impl PrologBackend {
pub fn new() -> Self {
PrologBackend {
swi_mode: false,
utf8_encoding: false,
options: PrologBackendOptions {
blank_between_predicates: true,
emit_docs: true,
},
}
}
pub fn swi() -> Self {
PrologBackend {
swi_mode: true,
utf8_encoding: true,
options: PrologBackendOptions {
blank_between_predicates: true,
emit_docs: true,
},
}
}
pub fn emit_module(&self, module: &PrologModule) -> String {
let mut out = String::new();
if self.utf8_encoding {
out.push_str(":- encoding(utf8).\n");
}
if let Some(desc) = &module.description {
out.push_str("%% ");
out.push_str(desc);
out.push('\n');
}
if let Some(name) = &module.name {
let exports = module.exported_predicates.join(",\n ");
if module.exported_predicates.is_empty() {
out.push_str(&format!(":- module({}, []).\n", name));
} else {
out.push_str(&format!(
":- module({}, [\n {}\n ]).\n",
name, exports
));
}
out.push('\n');
}
for item in &module.items {
let s = item.emit();
if !s.is_empty() {
out.push_str(&s);
out.push('\n');
} else {
out.push('\n');
}
if self.options.blank_between_predicates && matches!(item, PrologItem::Predicate(_)) {
out.push('\n');
}
}
out
}
pub fn emit_clause(&self, clause: &PrologClause) -> String {
clause.emit()
}
pub fn emit_predicate(&self, pred: &PrologPredicate) -> String {
pred.emit()
}
pub fn emit_dcg(&self, rule: &DcgRule) -> String {
rule.emit()
}
pub fn emit_directive(&self, directive: &PrologDirective) -> String {
directive.emit()
}
pub fn emit_clauses(&self, clauses: &[PrologClause]) -> String {
clauses
.iter()
.map(|c| c.emit())
.collect::<Vec<_>>()
.join("\n")
}
pub fn build_swi_preamble(
&self,
module_name: &str,
exports: &[(&str, usize)],
libraries: &[&str],
) -> String {
let mut out = String::new();
out.push_str(":- encoding(utf8).\n\n");
let exp_strs: Vec<String> = exports
.iter()
.map(|(n, a)| format!("{}/{}", n, a))
.collect();
if exp_strs.is_empty() {
out.push_str(&format!(":- module({}, []).\n", module_name));
} else {
let exp_list = exp_strs.join(",\n ");
out.push_str(&format!(
":- module({}, [\n {}\n ]).\n",
module_name, exp_list
));
}
for lib in libraries {
out.push_str(&format!(":- use_module(library({})).\n", lib));
}
out
}
}
#[allow(dead_code)]
pub struct PrologAssertionBuilder;
impl PrologAssertionBuilder {
#[allow(dead_code)]
pub fn assertz_fact(head: PrologTerm) -> PrologTerm {
compound("assertz", vec![head])
}
#[allow(dead_code)]
pub fn asserta_fact(head: PrologTerm) -> PrologTerm {
compound("asserta", vec![head])
}
#[allow(dead_code)]
pub fn assertz_rule(head: PrologTerm, body: PrologTerm) -> PrologTerm {
let rule = PrologTerm::Op(":-".to_string(), Box::new(head), Box::new(body));
compound("assertz", vec![rule])
}
#[allow(dead_code)]
pub fn retract(head: PrologTerm) -> PrologTerm {
compound("retract", vec![head])
}
#[allow(dead_code)]
pub fn retractall(head: PrologTerm) -> PrologTerm {
compound("retractall", vec![head])
}
#[allow(dead_code)]
pub fn abolish(name: &str, arity: usize) -> PrologTerm {
compound(
"abolish",
vec![PrologTerm::Op(
"/".to_string(),
Box::new(atom(name)),
Box::new(PrologTerm::Integer(arity as i64)),
)],
)
}
}
#[derive(Debug, Default, Clone)]
pub struct PrologBackendOptions {
pub blank_between_predicates: bool,
pub emit_docs: bool,
}
#[allow(dead_code)]
pub struct PrologConstraints;
impl PrologConstraints {
#[allow(dead_code)]
pub fn in_range(x: PrologTerm, low: PrologTerm, high: PrologTerm) -> PrologTerm {
PrologTerm::Op(
"in".to_string(),
Box::new(x),
Box::new(PrologTerm::Op(
"..".to_string(),
Box::new(low),
Box::new(high),
)),
)
}
#[allow(dead_code)]
pub fn clp_eq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("#=".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn clp_neq(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("#\\=".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn clp_lt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("#<".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn clp_gt(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("#>".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn clp_le(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("#=<".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn clp_ge(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("#>=".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn all_different(vars: Vec<PrologTerm>) -> PrologTerm {
compound("all_different", vec![PrologTerm::list(vars)])
}
#[allow(dead_code)]
pub fn label(vars: Vec<PrologTerm>) -> PrologTerm {
compound("label", vec![PrologTerm::list(vars)])
}
#[allow(dead_code)]
pub fn sum_eq(vars: Vec<PrologTerm>, sum: PrologTerm) -> PrologTerm {
compound("sum", vec![PrologTerm::list(vars), atom("#="), sum])
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PrologClause {
pub head: PrologTerm,
pub body: Vec<PrologTerm>,
pub is_fact: bool,
pub comment: Option<String>,
}
impl PrologClause {
pub fn fact(head: PrologTerm) -> Self {
PrologClause {
head,
body: vec![],
is_fact: true,
comment: None,
}
}
pub fn rule(head: PrologTerm, body: Vec<PrologTerm>) -> Self {
PrologClause {
head,
body,
is_fact: false,
comment: None,
}
}
pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
self.comment = Some(comment.into());
self
}
pub fn emit(&self) -> String {
let mut out = String::new();
if let Some(c) = &self.comment {
out.push_str(&format!("% {}\n", c));
}
if self.is_fact || self.body.is_empty() {
out.push_str(&format!("{}.", self.head));
} else {
out.push_str(&format!("{} :-", self.head));
if self.body.len() == 1 {
out.push_str(&format!("\n {}.", self.body[0]));
} else {
for (i, goal) in self.body.iter().enumerate() {
if i < self.body.len() - 1 {
out.push_str(&format!("\n {},", goal));
} else {
out.push_str(&format!("\n {}.", goal));
}
}
}
}
out
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)]
pub enum PrologType {
Integer,
Float,
Atom,
PrologString,
List(Box<PrologType>),
Compound,
Callable,
Term,
Boolean,
Var,
Nonvar,
Number,
Atomic,
PositiveInteger,
NonNeg,
Custom(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct DcgRule {
pub lhs: PrologTerm,
pub rhs: Vec<DcgRhs>,
pub guards: Vec<PrologTerm>,
pub comment: Option<String>,
}
impl DcgRule {
pub fn emit(&self) -> String {
let mut out = String::new();
if let Some(c) = &self.comment {
out.push_str(&format!("% {}\n", c));
}
out.push_str(&format!("{} -->", self.lhs));
let mut parts: Vec<String> = self.rhs.iter().map(|r| format!("{}", r)).collect();
if !self.guards.is_empty() {
let guard_str = self
.guards
.iter()
.map(|g| format!("{}", g))
.collect::<Vec<_>>()
.join(", ");
parts.push(format!("{{{}}}", guard_str));
}
if parts.is_empty() {
out.push_str("\n [].");
} else if parts.len() == 1 {
out.push_str(&format!("\n {}.", parts[0]));
} else {
for (i, p) in parts.iter().enumerate() {
if i < parts.len() - 1 {
out.push_str(&format!("\n {},", p));
} else {
out.push_str(&format!("\n {}.", p));
}
}
}
out
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct PrologTypeSig {
pub name: String,
pub params: Vec<(PrologMode, PrologType)>,
pub description: Option<String>,
}
impl PrologTypeSig {
#[allow(dead_code)]
pub fn emit_pldoc(&self) -> String {
let mut out = String::new();
out.push_str(&format!("%% {}/{}\n", self.name, self.params.len()));
if let Some(desc) = &self.description {
out.push_str(&format!("% {}\n", desc));
}
for (mode, ty) in &self.params {
out.push_str(&format!("% @param {} {}\n", mode, ty));
}
out
}
#[allow(dead_code)]
pub fn emit_pred_directive(&self) -> String {
let param_str: Vec<String> = self
.params
.iter()
.map(|(m, t)| format!("{}({})", m, t))
.collect();
format!(":- pred {}({}).\n", self.name, param_str.join(", "))
}
}
#[allow(dead_code)]
pub struct PrologArith;
impl PrologArith {
#[allow(dead_code)]
pub fn add(x: PrologTerm, y: PrologTerm) -> PrologTerm {
arith_add(x, y)
}
#[allow(dead_code)]
pub fn sub(x: PrologTerm, y: PrologTerm) -> PrologTerm {
arith_sub(x, y)
}
#[allow(dead_code)]
pub fn mul(x: PrologTerm, y: PrologTerm) -> PrologTerm {
arith_mul(x, y)
}
#[allow(dead_code)]
pub fn idiv(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("//".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn mmod(x: PrologTerm, y: PrologTerm) -> PrologTerm {
arith_mod(x, y)
}
#[allow(dead_code)]
pub fn abs(x: PrologTerm) -> PrologTerm {
compound("abs", vec![x])
}
#[allow(dead_code)]
pub fn max(x: PrologTerm, y: PrologTerm) -> PrologTerm {
compound("max", vec![x, y])
}
#[allow(dead_code)]
pub fn min(x: PrologTerm, y: PrologTerm) -> PrologTerm {
compound("min", vec![x, y])
}
#[allow(dead_code)]
pub fn pow(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("^".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn sqrt(x: PrologTerm) -> PrologTerm {
compound("sqrt", vec![x])
}
#[allow(dead_code)]
pub fn truncate(x: PrologTerm) -> PrologTerm {
compound("truncate", vec![x])
}
#[allow(dead_code)]
pub fn round(x: PrologTerm) -> PrologTerm {
compound("round", vec![x])
}
#[allow(dead_code)]
pub fn sign(x: PrologTerm) -> PrologTerm {
compound("sign", vec![x])
}
#[allow(dead_code)]
pub fn bitand(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("/\\".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn bitor(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("\\/".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn xor(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("xor".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn shl(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op("<<".to_string(), Box::new(x), Box::new(y))
}
#[allow(dead_code)]
pub fn shr(x: PrologTerm, y: PrologTerm) -> PrologTerm {
PrologTerm::Op(">>".to_string(), Box::new(x), Box::new(y))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PrologTerm {
Atom(String),
Integer(i64),
Float(f64),
Variable(String),
Compound(String, Vec<PrologTerm>),
List(Vec<PrologTerm>, Option<Box<PrologTerm>>),
Nil,
DcgPhrase(Box<PrologTerm>, Box<PrologTerm>),
Op(String, Box<PrologTerm>, Box<PrologTerm>),
PrefixOp(String, Box<PrologTerm>),
Cut,
Anon,
}
impl PrologTerm {
pub fn atom(s: impl Into<String>) -> Self {
PrologTerm::Atom(s.into())
}
pub fn var(s: impl Into<String>) -> Self {
PrologTerm::Variable(s.into())
}
pub fn compound(functor: impl Into<String>, args: Vec<PrologTerm>) -> Self {
PrologTerm::Compound(functor.into(), args)
}
pub fn list(elems: Vec<PrologTerm>) -> Self {
PrologTerm::List(elems, None)
}
pub fn list_partial(elems: Vec<PrologTerm>, tail: PrologTerm) -> Self {
PrologTerm::List(elems, Some(Box::new(tail)))
}
pub fn op(op: impl Into<String>, lhs: PrologTerm, rhs: PrologTerm) -> Self {
PrologTerm::Op(op.into(), Box::new(lhs), Box::new(rhs))
}
pub fn prefix_op(op: impl Into<String>, arg: PrologTerm) -> Self {
PrologTerm::PrefixOp(op.into(), Box::new(arg))
}
pub fn functor_arity(&self) -> usize {
match self {
PrologTerm::Compound(_, args) => args.len(),
_ => 0,
}
}
pub(super) fn needs_parens_as_arg(&self) -> bool {
matches!(self, PrologTerm::Op(_, _, _) | PrologTerm::PrefixOp(_, _))
}
pub(super) fn needs_quoting(s: &str) -> bool {
if s.is_empty() {
return true;
}
let mut chars = s.chars();
let first = chars
.next()
.expect("s is non-empty; guaranteed by early return on s.is_empty() above");
if s.chars().all(|c| "#&*+-./:<=>?@\\^~".contains(c)) {
return false;
}
if !first.is_lowercase() && first != '_' {
return true;
}
s.chars().any(|c| !c.is_alphanumeric() && c != '_')
}
pub(super) fn fmt_atom(s: &str) -> String {
if Self::needs_quoting(s) {
format!("'{}'", s.replace('\'', "\\'"))
} else {
s.to_string()
}
}
}
#[allow(dead_code)]
pub struct PrologMetaPredicates;
impl PrologMetaPredicates {
#[allow(dead_code)]
pub fn maplist(goal: PrologTerm, list: PrologTerm) -> PrologTerm {
compound("maplist", vec![goal, list])
}
#[allow(dead_code)]
pub fn maplist2(goal: PrologTerm, list: PrologTerm, result: PrologTerm) -> PrologTerm {
compound("maplist", vec![goal, list, result])
}
#[allow(dead_code)]
pub fn include(goal: PrologTerm, list: PrologTerm, result: PrologTerm) -> PrologTerm {
compound("include", vec![goal, list, result])
}
#[allow(dead_code)]
pub fn exclude(goal: PrologTerm, list: PrologTerm, result: PrologTerm) -> PrologTerm {
compound("exclude", vec![goal, list, result])
}
#[allow(dead_code)]
pub fn foldl(goal: PrologTerm, list: PrologTerm, v0: PrologTerm, v: PrologTerm) -> PrologTerm {
compound("foldl", vec![goal, list, v0, v])
}
#[allow(dead_code)]
pub fn aggregate_all(template: PrologTerm, goal: PrologTerm, result: PrologTerm) -> PrologTerm {
compound("aggregate_all", vec![template, goal, result])
}
#[allow(dead_code)]
pub fn call_n(f: PrologTerm, mut args: Vec<PrologTerm>) -> PrologTerm {
let mut all_args = vec![f];
all_args.append(&mut args);
compound("call", all_args)
}
#[allow(dead_code)]
pub fn once(goal: PrologTerm) -> PrologTerm {
compound("once", vec![goal])
}
#[allow(dead_code)]
pub fn ignore(goal: PrologTerm) -> PrologTerm {
compound("ignore", vec![goal])
}
#[allow(dead_code)]
pub fn forall(cond: PrologTerm, action: PrologTerm) -> PrologTerm {
compound("forall", vec![cond, action])
}
#[allow(dead_code)]
pub fn findall(template: PrologTerm, goal: PrologTerm, bag: PrologTerm) -> PrologTerm {
compound("findall", vec![template, goal, bag])
}
#[allow(dead_code)]
pub fn bagof(template: PrologTerm, goal: PrologTerm, bag: PrologTerm) -> PrologTerm {
compound("bagof", vec![template, goal, bag])
}
#[allow(dead_code)]
pub fn setof(template: PrologTerm, goal: PrologTerm, bag: PrologTerm) -> PrologTerm {
compound("setof", vec![template, goal, bag])
}
}