use crate::jbo_prop::{JboFragment, JboMex, JboModalOp, JboOperator, JboQuantifier, JboRel, JboTerm, SideType, Texticule};
use crate::jbo_syntax::{Lerfu, SumtiAtom};
use crate::logic::Prop;
pub type JboProp = Prop<JboRel, JboTerm, String, JboModalOp, JboQuantifier>;
#[derive(Clone, Copy, PartialEq, Eq)]
#[allow(clippy::enum_variant_names)]
enum ShowBinding {
SVar(i32),
SAss(i32),
SRel(i32),
SRVar(i32),
SRAss(i32),
}
fn non_jbo_quote_delimiter(s: &str) -> String {
let mut n = 0;
loop {
let delimiter = if n == 0 {
"zoi".to_string()
} else {
format!("zoik{}", "yk".repeat(n - 1))
};
if !s.contains(&delimiter) {
return delimiter;
}
n += 1;
}
}
pub fn jbonum(n: i32) -> String {
match n {
0 => "no".to_string(),
1 => "pa".to_string(),
2 => "re".to_string(),
3 => "ci".to_string(),
4 => "vo".to_string(),
5 => "mu".to_string(),
6 => "xa".to_string(),
7 => "ze".to_string(),
8 => "bi".to_string(),
9 => "so".to_string(),
_ if n >= 10 => format!("{}{}", jbonum(n / 10), jbonum(n % 10)),
_ => format!("{}", n),
}
}
pub fn vowelnum(n: i32) -> &'static str {
match n {
1 => "a",
2 => "e",
3 => "i",
4 => "o",
5 => "u",
_ => "a",
}
}
pub fn se_to_str(n: i32) -> String {
match n {
2 => "se".to_string(),
3 => "te".to_string(),
4 => "ve".to_string(),
5 => "xe".to_string(),
_ => format!("se xi {}", jbonum(n)),
}
}
pub fn fa_to_str(n: i32) -> String {
match n {
1 => "fa".to_string(),
2 => "fe".to_string(),
3 => "fi".to_string(),
4 => "fo".to_string(),
5 => "fu".to_string(),
_ => format!("fa xi {}", jbonum(n)),
}
}
pub fn bracket(c: char, s: &str) -> String {
let end = match c {
'(' => ")",
'[' => "]",
'{' => "}",
'<' => ">",
'«' => "»",
'"' => "\"",
'\'' => "'",
' ' => " ",
_ => "",
};
format!("{}{}{}", c, s, end)
}
pub fn jbobracket(l: &str, r: &str, s: &str) -> String {
format!("{} {} {}", l, s, r)
}
fn jboshow_operator(op: &JboOperator) -> String {
jboshow_operator_with_context(op, None, &[])
}
fn jboshow_operator_with_context(op: &JboOperator, rel_var: Option<ShowBinding>, bindings: &[(i32, ShowBinding)]) -> String {
match op {
JboOperator::ConnectedOperator(_, con, o1, o2) => format!(
"ke {} ke'e {} ke {} ke'e",
jboshow_operator_with_context(o1, rel_var, bindings),
jboshow_connective(".", con),
jboshow_operator_with_context(o2, rel_var, bindings)
),
JboOperator::OpPermuted(s, op) => format!("{} {}", se_to_str(*s), jboshow_operator_with_context(op, rel_var, bindings)),
JboOperator::OpScalarNegated(n, op) => format!("{} {}", n, jboshow_operator_with_context(op, rel_var, bindings)),
JboOperator::OpMex(m) => jbobracket("ma'o", "te'u", &jboshow_mex_with_context(m, rel_var, bindings)),
JboOperator::OpSelbri(s) => jbobracket(
"na'u",
"te'u",
&jboshow_pred_prop_with_rel_context(&s(&[JboTerm::BoundVar(0)]), 0, rel_var),
),
JboOperator::OpVUhU(s) => s.clone(),
}
}
fn logshow_operator(op: &JboOperator) -> String {
match op {
JboOperator::ConnectedOperator(_, con, o1, o2) => format!(
"<{}>{}<{}>",
logshow_operator(o1),
logshow_connective(".", con),
logshow_operator(o2)
),
JboOperator::OpPermuted(s, op) => format!("{} {}", se_to_str(*s), logshow_operator(op)),
JboOperator::OpScalarNegated(n, op) => format!("{{{}}}({})", n, logshow_operator(op)),
JboOperator::OpMex(m) => bracket('[', &logshow_mex(m)),
JboOperator::OpSelbri(s) => bracket('[', &logshow_prop(&s(&[JboTerm::BoundVar(0)]))),
JboOperator::OpVUhU(s) => format!("{{{}}}", s),
}
}
fn jboshow_mex_number(mex: &JboMex) -> String {
match mex {
JboMex::MexNumeralString(ns) => ns
.iter()
.map(|n| match n {
crate::jbo_syntax::Numeral::PA(s) => s.clone(),
crate::jbo_syntax::Numeral::NumeralLerfu(l) => jboshow_lerfu(l),
})
.collect::<Vec<_>>()
.join(" "),
JboMex::MexLerfuString(ls) => jboshow_lerfu_string(ls),
JboMex::MexInt(n) => jbonum(*n),
_ => "<mex>".to_string(),
}
}
fn jboshow_mex(mex: &JboMex) -> String {
jboshow_mex_with_context(mex, None, &[])
}
fn jboshow_mex_with_context(mex: &JboMex, rel_var: Option<ShowBinding>, bindings: &[(i32, ShowBinding)]) -> String {
match mex {
JboMex::MexNumeralString(_) | JboMex::MexInt(_) => format!("{} boi", jboshow_mex_number(mex)),
JboMex::MexLerfuString(ls) => format!("{} boi", jboshow_lerfu_string(ls)),
JboMex::Operation(op, args) => {
let args = args.iter().map(|m| jboshow_mex_with_context(m, rel_var, bindings)).collect::<Vec<_>>().join(" ");
format!("pe'o {} {} ku'e", jboshow_operator_with_context(op, rel_var, bindings), args)
}
JboMex::ConnectedMex(_, con, m1, m2) => format!(
"vei {} ve'o {} vei {} ve'o",
jboshow_mex_with_context(m1, rel_var, bindings),
jboshow_connective(".", con),
jboshow_mex_with_context(m2, rel_var, bindings)
),
JboMex::QualifiedMex(qual, m) => {
let qualifier = match qual {
crate::jbo_syntax::SumtiQualifier::LAhE(l) => l.clone(),
crate::jbo_syntax::SumtiQualifier::NAhE_BO(n) => format!("{} bo", n),
};
format!("{} {} lu'u", qualifier, jboshow_mex_with_context(m, rel_var, bindings))
}
JboMex::MexSelbri(s) => jbobracket(
"ni'e",
"te'u",
&jboshow_pred_prop_with_rel_context(&s(&[JboTerm::BoundVar(0)]), 0, rel_var),
),
JboMex::MexArray(ms) => format!(
"jo'i {} te'u",
ms.iter().map(|m| jboshow_mex_with_context(m, rel_var, bindings)).collect::<Vec<_>>().join(" ")
),
JboMex::MexSumti(t) => format!("mo'e {} te'u", jboshow_term_with_context(t, &[], rel_var, bindings)),
}
}
fn jboshow_lerfu_string(ls: &[Lerfu]) -> String {
ls.iter().map(jboshow_lerfu).collect::<Vec<_>>().join(" ")
}
fn jboshow_lerfu(l: &Lerfu) -> String {
match l {
Lerfu::LerfuChar(c) if matches!(c, 'a' | 'e' | 'i' | 'o' | 'u') => format!("{}bu", c),
Lerfu::LerfuChar('y') => "y bu".to_string(),
Lerfu::LerfuChar('h') => "y'y".to_string(),
Lerfu::LerfuChar(c) if c.is_ascii_digit() => jbonum(c.to_digit(10).unwrap() as i32),
Lerfu::LerfuChar(c) => format!("{}y", c),
Lerfu::LerfuPA(p) => p.clone(),
Lerfu::LerfuValsi(v) => format!("{} bu", v),
Lerfu::LerfuShift(s) => s.clone(),
Lerfu::LerfuShifted(lau, l) => format!("{} {}", lau, jboshow_lerfu(l)),
Lerfu::LerfuComposite(ls) => jbobracket("tei", "foi", &jboshow_lerfu_string(ls)),
}
}
fn logshow_lerfu_string(ls: &[Lerfu]) -> String {
ls.iter().map(logshow_lerfu).collect::<String>()
}
fn logshow_lerfu(l: &Lerfu) -> String {
match l {
Lerfu::LerfuChar(c) => c.to_string(),
Lerfu::LerfuPA(p) | Lerfu::LerfuValsi(p) | Lerfu::LerfuShift(p) => bracket('{', p),
Lerfu::LerfuShifted(lau, l) => format!("{}{}", bracket('{', lau), bracket('(', &logshow_lerfu(l))),
Lerfu::LerfuComposite(ls) => bracket('[', &ls.iter().map(logshow_lerfu).collect::<Vec<_>>().join(","))
}
}
fn logshow_lerfu_debug(l: &Lerfu) -> String {
match l {
Lerfu::LerfuChar(c) => format!("LerfuChar '{}'", c),
Lerfu::LerfuPA(p) => format!("LerfuPA {:?}", p),
Lerfu::LerfuValsi(v) => format!("LerfuValsi {:?}", v),
Lerfu::LerfuShift(s) => format!("LerfuShift {:?}", s),
Lerfu::LerfuShifted(lau, l) => format!("LerfuShifted {:?} ({})", lau, logshow_lerfu_debug(l)),
Lerfu::LerfuComposite(ls) => format!("LerfuComposite [{}]", ls.iter().map(logshow_lerfu_debug).collect::<Vec<_>>().join(",")),
}
}
fn jboshow_sumti_atom(atom: &SumtiAtom) -> String {
match atom {
SumtiAtom::Assignable(n) if *n <= 5 => format!("ko'{}", vowelnum(*n)),
SumtiAtom::Assignable(n) if *n <= 10 => format!("fo'{}", vowelnum(*n - 5)),
SumtiAtom::Assignable(n) => format!("ko'a xi {}", jbonum(*n)),
SumtiAtom::Ri(0 | 1) => "ri".to_string(),
SumtiAtom::Ri(n) => format!("ri xi {}", jbonum(*n)),
SumtiAtom::Ra(r) => r.clone(),
SumtiAtom::MainBridiSumbasti(n) if *n <= 5 => format!("vo'{}", vowelnum(*n)),
SumtiAtom::MainBridiSumbasti(n) => format!("vo'a xi {}", jbonum(*n)),
SumtiAtom::LerfuString(ls) => jboshow_lerfu_string(ls),
_ => format!("{:?}", atom),
}
}
fn logshow_sumti_atom(atom: &SumtiAtom) -> String {
match atom {
SumtiAtom::LerfuString(ls) => logshow_lerfu_string(ls),
_ => bracket('{', &jboshow_sumti_atom(atom)),
}
}
fn logshow_syntax_operator(op: &crate::jbo_syntax::Operator) -> String {
match op {
crate::jbo_syntax::Operator::OpVUhU(s) => format!("OpVUhU \"{}\"", s),
_ => format!("{:?}", op),
}
}
fn logshow_syntax_mex(mex: &crate::jbo_syntax::Mex) -> String {
match mex {
crate::jbo_syntax::Mex::Operation(op, args) => {
let args = args.iter().map(logshow_syntax_mex).collect::<Vec<_>>().join(",");
format!("Operation ({}) [{}]", logshow_syntax_operator(op), args)
}
crate::jbo_syntax::Mex::MexNumeralString(ns) => {
let digits = ns
.iter()
.map(|n| match n {
crate::jbo_syntax::Numeral::PA(s) => s.clone(),
crate::jbo_syntax::Numeral::NumeralLerfu(l) => format!("{:?}", l),
})
.collect::<Vec<_>>()
.join("");
match digits.as_str() {
"no" => "MexInt 0".to_string(),
"pa" => "MexInt 1".to_string(),
"re" => "MexInt 2".to_string(),
"ci" => "MexInt 3".to_string(),
"vo" => "MexInt 4".to_string(),
"mu" => "MexInt 5".to_string(),
"xa" => "MexInt 6".to_string(),
"ze" => "MexInt 7".to_string(),
"bi" => "MexInt 8".to_string(),
"so" => "MexInt 9".to_string(),
_ => format!("MexNumeralString {:?}", ns),
}
}
crate::jbo_syntax::Mex::MexInt(n) => format!("MexInt {}", n),
crate::jbo_syntax::Mex::MexLerfuString(ls) => format!("MexLerfuString [{}]", ls.iter().map(logshow_lerfu_debug).collect::<Vec<_>>().join(",")),
_ => format!("{:?}", mex),
}
}
fn logshow_mex(mex: &JboMex) -> String {
match mex {
JboMex::MexNumeralString(ns) => bracket('(', &ns
.iter()
.map(|n| match n {
crate::jbo_syntax::Numeral::PA(s) => s.clone(),
crate::jbo_syntax::Numeral::NumeralLerfu(l) => jboshow_lerfu(l),
})
.collect::<Vec<_>>()
.join(" ")),
JboMex::MexInt(n) => n.to_string(),
JboMex::MexLerfuString(ls) => bracket('(', &jboshow_lerfu_string(ls)),
JboMex::Operation(op, args) => {
let args = args.iter().map(logshow_mex).collect::<Vec<_>>().join(",");
format!("{}({})", logshow_operator(op), args)
}
JboMex::ConnectedMex(_, con, m1, m2) => format!(
"({}){}({})",
logshow_mex(m1),
logshow_connective(".", con),
logshow_mex(m2)
),
JboMex::QualifiedMex(qual, m) => {
let qualifier = match qual {
crate::jbo_syntax::SumtiQualifier::LAhE(l) => l.clone(),
crate::jbo_syntax::SumtiQualifier::NAhE_BO(n) => format!("{} bo", n),
};
format!("{{{}}}({})", qualifier, logshow_mex(m))
}
JboMex::MexSelbri(s) => bracket('[', &logshow_prop(&s(&[JboTerm::BoundVar(0)]))),
JboMex::MexArray(ms) => bracket(
'[',
&ms.iter().map(logshow_mex).collect::<Vec<_>>().join(","),
),
_ => "<mex>".to_string(),
}
}
fn jboshow_syntax_operator(op: &crate::jbo_syntax::Operator) -> String {
match op {
crate::jbo_syntax::Operator::OpVUhU(s) => s.clone(),
_ => "<operator>".to_string(),
}
}
fn jboshow_syntax_mex(mex: &crate::jbo_syntax::Mex) -> String {
match mex {
crate::jbo_syntax::Mex::MexNumeralString(ns) => ns
.iter()
.map(|n| match n {
crate::jbo_syntax::Numeral::PA(s) => s.clone(),
crate::jbo_syntax::Numeral::NumeralLerfu(l) => format!("{:?}", l),
})
.collect::<Vec<_>>()
.join(" "),
crate::jbo_syntax::Mex::MexInt(n) => jbonum(*n),
crate::jbo_syntax::Mex::Operation(op, args) => {
let args = args.iter().map(jboshow_syntax_mex).collect::<Vec<_>>().join(" boi ");
format!("pe'o {} {} boi ku'e", jboshow_syntax_operator(op), args)
}
_ => "<mex>".to_string(),
}
}
fn jboshow_quantifier(q: &JboQuantifier) -> String {
match q {
JboQuantifier::QuestionQuantifier => "ma".to_string(),
JboQuantifier::LojQuantifier(crate::logic::LojQuantifier::Exists) => "su'o".to_string(),
JboQuantifier::LojQuantifier(crate::logic::LojQuantifier::Forall) => "ro".to_string(),
JboQuantifier::LojQuantifier(crate::logic::LojQuantifier::Exactly(n)) => jbonum(*n),
JboQuantifier::MexQuantifier(m) => jboshow_mex(m),
JboQuantifier::RelQuantifier(q) => jboshow_quantifier(q),
}
}
fn logshow_quantifier(q: &JboQuantifier) -> String {
match q {
JboQuantifier::QuestionQuantifier => "?".to_string(),
JboQuantifier::LojQuantifier(q) => q.to_string(),
JboQuantifier::MexQuantifier(m) => logshow_mex(m),
JboQuantifier::RelQuantifier(q) => format!("RelQuantifier({})", logshow_quantifier(q)),
}
}
fn format_quantified_prefix(q: &JboQuantifier, name: &str) -> String {
match q {
JboQuantifier::LojQuantifier(crate::logic::LojQuantifier::Exists)
| JboQuantifier::LojQuantifier(crate::logic::LojQuantifier::Forall) => {
format!("{}{}", logshow_quantifier(q), name)
}
_ => format!("{} {}", logshow_quantifier(q), name),
}
}
fn jboshow_lambda_var(n: i32) -> String {
match n.abs() {
1 => "ce'u".to_string(),
n => format!("ce'u xi {}", jbonum(n)),
}
}
fn jboshow_bound_var(n: i32) -> String {
match n {
1 => "da".to_string(),
2 => "de".to_string(),
3 => "di".to_string(),
_ => format!("da xi {}", jbonum(n)),
}
}
fn jboshow_bound_rvar(n: i32) -> String {
match n {
1 => "broda".to_string(),
2 => "brode".to_string(),
3 => "brodi".to_string(),
4 => "brodo".to_string(),
5 => "brodu".to_string(),
_ => format!("broda xi {}", jbonum(n)),
}
}
fn jboshow_unassigned_rvar(n: i32) -> String {
match n {
1 => "bu'a".to_string(),
2 => "bu'e".to_string(),
3 => "bu'i".to_string(),
_ => format!("bu'a xi {}", jbonum(n)),
}
}
fn logshow_texticule(texticule: &Texticule) -> String {
match texticule {
Texticule::TexticuleFrag(fragment) => match fragment {
JboFragment::JboFragTerms(terms) => format!("[Fragment: {}]", terms.iter().map(logshow_term).collect::<Vec<_>>().join(",")),
JboFragment::JboFragUnparsed(_) => "[Fragment]".to_string(),
},
Texticule::TexticuleProp(prop) => logshow_prop(prop),
Texticule::TexticuleSide(_, inner) => logshow_texticule(inner),
}
}
fn logshow_text(text: &[Texticule]) -> String {
text.iter().map(logshow_texticule).collect::<Vec<_>>().join("\n")
}
pub fn jboshow_texticule(texticule: &Texticule) -> String {
match texticule {
Texticule::TexticuleFrag(fragment) => match fragment {
JboFragment::JboFragTerms(terms) => terms.iter().map(jboshow_term).collect::<Vec<_>>().join(" "),
JboFragment::JboFragUnparsed(_) => "li'o".to_string(),
},
Texticule::TexticuleProp(prop) => jboshow_prop(prop),
Texticule::TexticuleSide(_, inner) => jboshow_texticule(inner),
}
}
fn collect_term_side_texticules_from_texticule(texticule: &Texticule, out: &mut Vec<Texticule>) {
match texticule {
Texticule::TexticuleFrag(JboFragment::JboFragTerms(terms)) => {
for term in terms {
collect_term_side_texticules_from_term(term, out);
}
}
Texticule::TexticuleProp(prop) => collect_term_side_texticules(prop, out),
Texticule::TexticuleSide(_, inner) => collect_term_side_texticules_from_texticule(inner, out),
Texticule::TexticuleFrag(JboFragment::JboFragUnparsed(_)) => {}
}
}
fn collect_term_side_texticules_from_term(term: &JboTerm, out: &mut Vec<Texticule>) {
match term {
JboTerm::TermWithSides(inner, sides) => {
out.extend(sides.iter().cloned());
collect_term_side_texticules_from_term(inner, out);
}
JboTerm::Constant(_, args) => {
for arg in args {
collect_term_side_texticules_from_term(arg, out);
}
}
JboTerm::QualifiedTerm(_, inner) => collect_term_side_texticules_from_term(inner, out),
JboTerm::JoikedTerms(_, left, right) => {
collect_term_side_texticules_from_term(left, out);
collect_term_side_texticules_from_term(right, out);
}
JboTerm::JboQuote(_) => {}
_ => {}
}
}
fn collect_term_side_texticules(prop: &JboProp, out: &mut Vec<Texticule>) {
match prop {
Prop::Rel(_, terms) => {
for term in terms {
collect_term_side_texticules_from_term(term, out);
}
}
Prop::Not(inner) | Prop::Modal(_, inner) => collect_term_side_texticules(inner, out),
Prop::Connected(_, left, right) | Prop::NonLogConnected(_, left, right) => {
collect_term_side_texticules(left, out);
collect_term_side_texticules(right, out);
}
Prop::Quantified(_, _, body) => collect_term_side_texticules(&body(1), out),
Prop::Eet => {}
}
}
pub fn jboshow_text(text: &[Texticule]) -> String {
let mut lines = text.iter().map(jboshow_texticule).collect::<Vec<_>>();
let mut side_lines = Vec::new();
for texticule in text {
collect_term_side_texticules_from_texticule(texticule, &mut side_lines);
}
let side_lines = side_lines
.into_iter()
.flat_map(|texticule| {
jboshow_texticule(&texticule)
.lines()
.filter(|line| !line.is_empty())
.map(str::to_string)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
if !side_lines.is_empty() {
let insert_at = lines
.iter()
.take_while(|line| line.strip_prefix(".i ").unwrap_or(line).starts_with("ju'a nai "))
.count();
for (i, line) in side_lines.into_iter().enumerate() {
lines.insert(insert_at + i, line);
}
}
lines.join("\n.i ")
}
fn jboshow_term_with_lambdas(term: &JboTerm, lambda_vars: &[i32]) -> String {
jboshow_term_with_context(term, lambda_vars, None, &[])
}
fn jboshow_term_with_context(term: &JboTerm, lambda_vars: &[i32], rel_var: Option<ShowBinding>, bindings: &[(i32, ShowBinding)]) -> String {
match term {
JboTerm::NonAnaph(s) => s.clone(),
JboTerm::Named(s) => format!("la {}.", s),
JboTerm::PredNamed(pred) => {
let prop = pred(&JboTerm::BoundVar(0));
format!("la {} ku", jboshow_pred_prop(&prop, 0))
}
JboTerm::Constant(n, args) if args.is_empty() => format!("cy {}", jbonum(*n)),
JboTerm::Constant(n, args) => {
let arg_strs: Vec<String> = args.iter().map(|t| format!("mo'e {}", jboshow_term_with_context(t, lambda_vars, rel_var, bindings))).collect();
format!("li ma'o fy{} {} lo'o", jbonum(*n), arg_strs.join(" "))
}
JboTerm::Var(_) => "lo NALSELTRO zei da ku".to_string(),
JboTerm::BoundVar(n) if rel_var == Some(ShowBinding::SRel(*n)) => "ke'a".to_string(),
JboTerm::BoundVar(n) if lambda_vars.contains(n) => jboshow_lambda_var(*n),
JboTerm::BoundVar(n) => match term_binding(bindings, *n) {
Some(ShowBinding::SVar(i)) => jboshow_bound_var(i),
Some(ShowBinding::SAss(i)) => jboshow_sumti_atom(&SumtiAtom::Assignable(i)),
Some(ShowBinding::SRel(1)) => "ke'a".to_string(),
Some(ShowBinding::SRel(i)) => format!("ke'a xi {}", jbonum(i)),
_ => jboshow_bound_var(*n),
},
JboTerm::Unfilled => "BUG".to_string(),
JboTerm::UnboundSumbasti(atom) => jboshow_sumti_atom(atom),
JboTerm::Value(m) => format!("li {} lo'o", jboshow_mex_with_context(m, rel_var, bindings)),
JboTerm::QualifiedTerm(qual, t) => {
let qualifier = match qual {
crate::jbo_syntax::SumtiQualifier::LAhE(l) => l.clone(),
crate::jbo_syntax::SumtiQualifier::NAhE_BO(n) => format!("{} bo", n),
};
format!("{} {} lu'u", qualifier, jboshow_term_with_context(t, lambda_vars, rel_var, bindings))
}
JboTerm::TheMex(m) => format!("me'o {} lo'o", logshow_syntax_mex(m)),
JboTerm::JoikedTerms(joik, t1, t2) => {
let joik = if joik == "??" { "ji" } else { joik };
format!("{} {} ke {} ke'e", jboshow_term_with_context(t1, lambda_vars, rel_var, bindings), joik, jboshow_term_with_context(t2, lambda_vars, rel_var, bindings))
}
JboTerm::JboQuote(text) => format!("lu {} li'u", jboshow_text(text)),
JboTerm::JboErrorQuote(words) => format!("lo'u {} le'u", words.join(" ")),
JboTerm::JboNonJboQuote(s) => {
let delimiter = non_jbo_quote_delimiter(s);
format!("zoi {} {} {}", delimiter, s, delimiter)
}
JboTerm::Valsi(s) => format!("zo {}", s),
JboTerm::TermWithSides(t, _) => jboshow_term_with_context(t, lambda_vars, rel_var, bindings),
}
}
pub fn jboshow_term(term: &JboTerm) -> String {
jboshow_term_with_lambdas(term, &[])
}
pub fn logshow_term(term: &JboTerm) -> String {
match term {
JboTerm::NonAnaph(s) => s.clone(),
JboTerm::Named(s) => bracket('"', s),
JboTerm::PredNamed(pred) => {
let prop = pred(&JboTerm::Named("_".to_string()));
format!("[Name: {} ]", logshow_prop(&prop).replace("\"_\"", "_"))
}
JboTerm::Constant(n, args) if args.is_empty() => format!("c{}", n),
JboTerm::Constant(n, args) => {
let arg_strs: Vec<String> = args.iter().map(logshow_term).collect();
format!("f{}({})", n, arg_strs.join(","))
}
JboTerm::Var(_) => "[UNBOUND Var]".to_string(),
JboTerm::BoundVar(n) if *n < 0 => format!("\\{}", n.abs()),
JboTerm::BoundVar(n) => format!("x{}", n),
JboTerm::Unfilled => " ".to_string(),
JboTerm::UnboundSumbasti(atom) => logshow_sumti_atom(atom),
JboTerm::Value(m) => logshow_mex(m),
JboTerm::QualifiedTerm(qual, t) => {
let qualifier = match qual {
crate::jbo_syntax::SumtiQualifier::LAhE(l) => l.clone(),
crate::jbo_syntax::SumtiQualifier::NAhE_BO(n) => format!("{} bo", n),
};
format!("{{{}}}({})", qualifier, logshow_term(t))
}
JboTerm::TheMex(m) => format!("[MEX: {}]", logshow_syntax_mex(m)),
JboTerm::JoikedTerms(joik, t1, t2) => format!("({} {{{}}} {})", logshow_term(t1), joik, logshow_term(t2)),
JboTerm::JboQuote(text) => format!("«{}»", logshow_text(text)),
JboTerm::JboErrorQuote(words) => format!("<{{< {} >}}>", words.join(" ")),
JboTerm::JboNonJboQuote(s) => format!("<[< {} >]>", s),
JboTerm::Valsi(s) => format!("{{{}}}", s),
JboTerm::TermWithSides(t, _) => logshow_term(t),
}
}
fn jboshow_log_connective(prefix: &str, con: &crate::jbo_syntax::LogJboConnective) -> String {
let mut parts = Vec::new();
if !con.b1 {
parts.push("na".to_string());
}
if con.c == 'U' {
parts.push(format!("se {}u", prefix));
} else {
parts.push(format!("{}{}", prefix, con.c));
}
if !con.b2 {
parts.push("nai".to_string());
}
parts.join(" ")
}
fn jboshow_connective(prefix: &str, con: &crate::jbo_syntax::Connective) -> String {
let (base, mtag) = match con {
crate::jbo_syntax::Connective::JboConnLog(mtag, lcon) => (jboshow_log_connective(prefix, lcon), mtag.as_deref()),
crate::jbo_syntax::Connective::JboConnJoik(mtag, joik) => {
let base = if joik == "??" {
if prefix == "." { "ji".to_string() } else { "je'i".to_string() }
} else {
joik.clone()
};
(base, mtag.as_deref())
}
};
if let Some(tag) = mtag {
format!("{} {} bo", base, jboshow_tag_syntax(tag))
} else {
base
}
}
fn jboshow_tag_syntax(tag: &crate::jbo_syntax::Tag) -> String {
match tag {
crate::jbo_syntax::Tag::DecoratedTagUnits(dtus) => dtus
.iter()
.map(|dtu| match &dtu.tag_unit {
crate::jbo_syntax::TagUnit::BAI(s)
| crate::jbo_syntax::TagUnit::TenseCmavo(s)
| crate::jbo_syntax::TagUnit::CAhA(s)
| crate::jbo_syntax::TagUnit::CUhE(s) => s.clone(),
crate::jbo_syntax::TagUnit::FAhA { faha_cmavo, .. } => faha_cmavo.clone(),
crate::jbo_syntax::TagUnit::TAhE_ZAhO { tahe_zaho_cmavo, .. } => tahe_zaho_cmavo.clone(),
crate::jbo_syntax::TagUnit::ROI { roiroi, roi_quantifier, .. } => format!("{} {}", jboshow_syntax_mex(roi_quantifier), roiroi),
crate::jbo_syntax::TagUnit::FIhO(_) => "fi'o".to_string(),
crate::jbo_syntax::TagUnit::KI => "ki".to_string(),
})
.collect::<Vec<_>>()
.join(" "),
crate::jbo_syntax::Tag::ConnectedTag(con, tag1, tag2) => format!(
"{} {} {}",
jboshow_tag_syntax(tag1),
jboshow_connective("j", con),
jboshow_tag_syntax(tag2)
),
}
}
fn logshow_connective(prefix: &str, con: &crate::jbo_syntax::Connective) -> String {
format!("{{{}}}", jboshow_connective(prefix, con))
}
pub fn jboshow_tag(tag: &crate::jbo_prop::JboTag) -> String {
match tag {
crate::jbo_prop::JboTag::DecoratedTagUnits(dtus) => dtus
.iter()
.map(|dtu| {
let unit = match &dtu.tag_unit {
crate::jbo_prop::JboTagUnit::BAI(s)
| crate::jbo_prop::JboTagUnit::TenseCmavo(s)
| crate::jbo_prop::JboTagUnit::CAhA(s)
| crate::jbo_prop::JboTagUnit::CUhE(s) => s.clone(),
crate::jbo_prop::JboTagUnit::FAhA(Some(true), s) => format!("mo'i {}", s),
crate::jbo_prop::JboTagUnit::FAhA(_, s) => s.clone(),
crate::jbo_prop::JboTagUnit::TAhE_ZAhO(true, s) => format!("fe'e {}", s),
crate::jbo_prop::JboTagUnit::TAhE_ZAhO(_, s) => s.clone(),
crate::jbo_prop::JboTagUnit::ROI(s, true, q) => format!("fe'e {} {}", jboshow_mex_number(q), s),
crate::jbo_prop::JboTagUnit::ROI(s, _, q) => format!("{} {}", jboshow_mex_number(q), s),
crate::jbo_prop::JboTagUnit::FIhO(p) => format!("fi'o {} fe'u", jboshow_pred_prop(&p(&[crate::jbo_prop::JboTerm::BoundVar(0)]), 0)),
crate::jbo_prop::JboTagUnit::KI => "ki".to_string(),
};
let decorated = format!(
"{}{}{}{}",
dtu.nahe.as_ref().map(|s| format!("{} ", s)).unwrap_or_default(),
dtu.se.map(|se| format!("{} ", se_to_str(se))).unwrap_or_default(),
unit,
if dtu.nai { " nai" } else { "" }
);
decorated
})
.collect::<Vec<_>>()
.join(" "),
crate::jbo_prop::JboTag::ConnectedTag(con, tag1, tag2) => {
let con = match con.as_ref() {
crate::jbo_prop::JboConnective::JboConnLog(_, lcon) => jboshow_log_connective("j", lcon),
crate::jbo_prop::JboConnective::JboConnJoik(_, joik) => {
if joik == "??" { "je'i".to_string() } else { joik.clone() }
}
};
format!("{} {} {}", jboshow_tag(tag1), con, jboshow_tag(tag2))
}
}
}
fn logshow_modal_rel_prefix(modal: &JboModalOp, placeholder_var: Option<i32>) -> String {
match modal {
JboModalOp::Tagged(tag, term) => {
let term = term.as_ref().map(|term| {
if matches!(placeholder_var, Some(var) if matches!(term, JboTerm::BoundVar(n) if *n == var)) {
"_".to_string()
} else {
logshow_term(term)
}
}).unwrap_or_default();
format!("({})({})", jboshow_tag(tag), term)
}
_ => format!("Modal({:?})", modal),
}
}
fn logshow_modal_rel(modal: &JboModalOp, rel: &JboRel) -> String {
format!("{}. {}(_)", logshow_modal_rel_prefix(modal, None), logshow_rel(rel))
}
fn jboshow_modal_rel(modal: &JboModalOp, rel: &JboRel, lambda_vars: &[i32], rel_var: Option<ShowBinding>, bindings: &[(i32, ShowBinding)]) -> String {
match modal {
JboModalOp::Tagged(tag, term) => {
let tag = jboshow_tag(tag);
let term = term
.as_ref()
.map(|t| format!(" {}", jboshow_term_with_context(t, lambda_vars, rel_var, bindings)))
.unwrap_or_default();
format!("poi'i {}{} zo'u ke'a {} kei", tag, term, jboshow_rel_with_context(rel, lambda_vars, rel_var, bindings))
}
_ => format!("poi'i {} kei", jboshow_rel_with_context(rel, lambda_vars, rel_var, bindings)),
}
}
fn jboshow_rel_with_lambdas(rel: &JboRel, lambda_vars: &[i32]) -> String {
jboshow_rel_with_context(rel, lambda_vars, None, &[])
}
fn jboshow_scalar_negated_rel_inner(rel: &JboRel, lambda_vars: &[i32], rel_var: Option<ShowBinding>, bindings: &[(i32, ShowBinding)]) -> String {
match rel {
JboRel::AppliedRel(r, terms) if terms.len() > 2 && matches!(terms.get(1), Some(JboTerm::Unfilled)) => {
let terms = std::iter::once(terms[0].clone())
.chain(terms.iter().skip(2).cloned())
.collect::<Vec<_>>();
jboshow_rel_with_context(&JboRel::AppliedRel(r.clone(), terms), lambda_vars, rel_var, bindings)
}
_ => jboshow_rel_with_context(rel, lambda_vars, rel_var, bindings),
}
}
fn jboshow_rel_with_context(rel: &JboRel, lambda_vars: &[i32], rel_var: Option<ShowBinding>, bindings: &[(i32, ShowBinding)]) -> String {
match rel {
JboRel::Brivla(s) => s.clone(),
JboRel::Equal => "du".to_string(),
JboRel::Tanru(r1, r2) => {
format!("ke {} {} ke'e", jboshow_rel_with_context(r1, lambda_vars, rel_var, bindings), jboshow_rel_with_context(r2, lambda_vars, rel_var, bindings))
}
JboRel::AppliedRel(r, terms) => {
let placeholder = terms.iter().position(|term| matches!(term, JboTerm::BoundVar(0)));
let mut out = if let Some(idx) = placeholder {
if idx == 0 {
jboshow_rel_with_context(r, lambda_vars, rel_var, bindings)
} else {
format!("{} {}", se_to_str((idx + 1) as i32), jboshow_rel_with_context(r, lambda_vars, rel_var, bindings))
}
} else {
jboshow_rel_with_context(r, lambda_vars, rel_var, bindings)
};
let mut rest: Vec<(usize, &JboTerm)> = match placeholder {
Some(0) => terms.iter().enumerate().skip(1).collect(),
Some(idx) => terms.iter().enumerate().filter(|(i, _)| *i != idx).collect(),
None => terms.iter().enumerate().skip(1).collect(),
};
if let Some(idx) = placeholder {
if idx > 0 {
rest.sort_by_key(|(i, _)| if *i == 0 { idx } else { *i });
}
}
let mut first_link = true;
let mut previous_unfilled = false;
for (_idx, term) in rest {
if matches!(term, JboTerm::Unfilled) {
previous_unfilled = true;
continue;
}
let marker = if first_link { "be" } else { "bei" };
if previous_unfilled {
let place = (_idx + 1) as i32;
out.push_str(&format!(" {} {} {}", marker, fa_to_str(place), jboshow_term_with_context(term, lambda_vars, rel_var, bindings)));
} else {
out.push_str(&format!(" {} {}", marker, jboshow_term_with_context(term, lambda_vars, rel_var, bindings)));
}
first_link = false;
previous_unfilled = false;
}
out
}
JboRel::TanruConnective(con, r1, r2) => {
format!("ke {} ke'e {} ke {} ke'e", jboshow_rel_with_context(r1, lambda_vars, rel_var, bindings), jboshow_connective("j", con), jboshow_rel_with_context(r2, lambda_vars, rel_var, bindings))
}
JboRel::PermutedRel(n, r) => format!("{} {}", se_to_str(*n), jboshow_rel_with_context(r, lambda_vars, rel_var, bindings)),
JboRel::Among(t) => format!("me {} me'u", jboshow_term_with_context(t, lambda_vars, rel_var, bindings)),
JboRel::RVar(_) => "NALSELTRO zei bu'a".to_string(),
JboRel::BoundRVar(n) => match rel_var {
Some(ShowBinding::SRVar(rv)) if rv == *n => jboshow_unassigned_rvar(*n),
Some(ShowBinding::SRAss(rv)) if rv == *n => jboshow_bound_rvar(*n),
_ => format!("bu'a xi {}", jbonum(*n)),
},
JboRel::RAss(n) if rel_var == Some(ShowBinding::SRAss(*n)) => jboshow_bound_rvar(*n),
JboRel::RAss(n) => format!("broda xi {}", jbonum(*n)),
JboRel::UnboundBribasti(tu) => match tu {
crate::jbo_syntax::TanruUnit::TUGOhA(g, n) => {
if *n == 1 {
g.clone()
} else {
format!("{} xi {}", g, jbonum(*n))
}
}
crate::jbo_syntax::TanruUnit::TUBrivla(s) => s.clone(),
_ => format!("UNBOUND({:?})", tu),
},
JboRel::Moi(t, moi) => match t.as_ref() {
JboTerm::Value(m @ (JboMex::MexInt(_) | JboMex::MexNumeralString(_) | JboMex::MexLerfuString(_))) => format!("{} {}", jboshow_mex_number(m), moi),
_ => format!("me {} me'u {}", jboshow_term_with_context(t, lambda_vars, rel_var, bindings), moi),
},
JboRel::OperatorRel(op) => jbobracket("nu'a", "te'u", &jboshow_operator_with_context(op, rel_var, bindings)),
JboRel::ScalarNegatedRel(nahe, r) => format!("{} {}", nahe, jboshow_scalar_negated_rel_inner(r, lambda_vars, rel_var, bindings)),
JboRel::VPredRel(vpred) => jboshow_pred_prop_with_rel_context(&vpred(&[JboTerm::BoundVar(0)]), 0, rel_var),
JboRel::AbsPred(abs, npred) => {
let args: Vec<JboTerm> = (1..=npred.arity as i32).map(|n| JboTerm::BoundVar(-n)).collect();
let nested_lambda_vars = args.iter().filter_map(|term| match term { JboTerm::BoundVar(n) => Some(*n), _ => None }).collect::<Vec<_>>();
format!("{} {} kei", abs, jboshow_prop_with_context_at(&(npred.pred)(&args), &nested_lambda_vars, rel_var, next_binding_key(bindings), bindings))
}
JboRel::AbsProp(abs, prop) => format!("{} {} kei", abs, jboshow_prop_with_context_at(prop, lambda_vars, rel_var, next_binding_key(bindings), bindings)),
JboRel::TagRel(tag) => format!("xo'i {}", jboshow_tag(tag)),
JboRel::ModalRel(modal, r) => jboshow_modal_rel(modal, r, lambda_vars, rel_var, bindings),
}
}
pub fn jboshow_rel(rel: &JboRel) -> String {
jboshow_rel_with_lambdas(rel, &[])
}
fn logshow_lambda_term(term: &JboTerm) -> String {
match term {
JboTerm::BoundVar(n) if *n < 0 => format!("\\{}", n.abs()),
_ => logshow_term(term),
}
}
fn logshow_lambda_rel(rel: &JboRel) -> String {
match rel {
JboRel::AppliedRel(r, terms) => {
format!("{}({})", logshow_rel(r), terms.iter().map(logshow_lambda_term).collect::<Vec<_>>().join(","))
}
_ => logshow_rel(rel),
}
}
fn logshow_lambda_restriction_term(term: &JboTerm, var: i32) -> String {
match term {
JboTerm::BoundVar(n) if *n == var => "_".to_string(),
_ => logshow_lambda_term(term),
}
}
fn logshow_lambda_restriction_prop(prop: &JboProp, var: i32) -> String {
match prop {
Prop::Rel(JboRel::Among(t), terms) if matches!(terms.as_slice(), [JboTerm::BoundVar(n)] if *n == var) => {
format!("(_ ≤ {})", logshow_lambda_restriction_term(t, var))
}
Prop::Rel(JboRel::AppliedRel(r, applied_terms), terms)
if terms.is_empty()
&& matches!(r.as_ref(), JboRel::Among(_))
&& matches!(applied_terms.as_slice(), [JboTerm::BoundVar(n)] if *n == var) =>
{
if let JboRel::Among(t) = r.as_ref() {
format!("(_ ≤ {})", logshow_lambda_restriction_term(t, var))
} else {
unreachable!()
}
}
Prop::Rel(rel, terms) => {
format!("{}({})", logshow_lambda_rel(rel), terms.iter().map(|t| logshow_lambda_restriction_term(t, var)).collect::<Vec<_>>().join(","))
}
_ => logshow_lambda_prop(prop),
}
}
fn logshow_lambda_prop_with_placeholder(prop: &JboProp, placeholder_var: Option<i32>) -> String {
match prop {
Prop::Rel(rel, terms) => {
let term_strs: Vec<String> = terms.iter().map(|term| {
if matches!(placeholder_var, Some(var) if matches!(term, JboTerm::BoundVar(n) if *n == var)) {
"_".to_string()
} else {
logshow_lambda_term(term)
}
}).collect();
format!("{}({})", logshow_lambda_rel(rel), term_strs.join(","))
}
Prop::Modal(JboModalOp::Tagged(tag, mt), inner) => {
if let Some(expanded) = expand_connected_tag_modal(tag, mt, inner) {
return logshow_lambda_prop_with_placeholder(&expanded, placeholder_var);
}
let term = mt.as_ref().map(|term| {
if matches!(placeholder_var, Some(var) if matches!(term, JboTerm::BoundVar(n) if *n == var)) {
"_".to_string()
} else {
logshow_lambda_term(term)
}
}).unwrap_or_default();
format!("({})({}). {}", jboshow_tag(tag), term, logshow_lambda_prop_with_placeholder(inner, placeholder_var))
}
Prop::Modal(JboModalOp::WithEventAs(term), inner) => {
let term = if matches!(placeholder_var, Some(var) if matches!(term, JboTerm::BoundVar(n) if *n == var)) {
"_".to_string()
} else {
logshow_lambda_term(term)
};
format!("{}=. {}", term, logshow_lambda_prop_with_placeholder(inner, placeholder_var))
}
Prop::Modal(JboModalOp::QTruthModal, inner) => format!("?. {}", logshow_lambda_prop_with_placeholder(inner, placeholder_var)),
Prop::Modal(JboModalOp::NonVeridical, inner) => format!("non-veridical: {}", logshow_lambda_prop_with_placeholder(inner, placeholder_var)),
Prop::Not(inner) => format!("¬{}", logshow_lambda_prop_with_placeholder(inner, placeholder_var)),
Prop::Connected(con, p1, p2) => {
format!("({} {} {})", logshow_lambda_prop_with_placeholder(p1, placeholder_var), con, logshow_lambda_prop_with_placeholder(p2, placeholder_var))
}
Prop::NonLogConnected(joik, p1, p2) => {
format!("({} {{{}}} {})", logshow_lambda_prop_with_placeholder(p1, placeholder_var), joik, logshow_lambda_prop_with_placeholder(p2, placeholder_var))
}
Prop::Quantified(q, restriction, body) => {
let n = 1;
let mut prefix = match q {
JboQuantifier::RelQuantifier(inner_q) => format_quantified_prefix(inner_q, &format!("R{}", n)),
_ => format_quantified_prefix(q, &format!("x{}", n)),
};
if let Some(r) = restriction {
prefix.push_str(":(");
prefix.push_str(&logshow_lambda_restriction_prop(&r(n), n));
prefix.push_str("). ");
} else {
prefix.push_str(". ");
}
format!("{}{}", prefix, logshow_lambda_prop_with_placeholder(&body(n), placeholder_var))
}
Prop::Eet => "_|_ (BUG)".to_string(),
}
}
fn logshow_lambda_prop(prop: &JboProp) -> String {
logshow_lambda_prop_with_placeholder(prop, None)
}
pub fn logshow_rel(rel: &JboRel) -> String {
match rel {
JboRel::Brivla(s) => s.clone(),
JboRel::Equal => "=".to_string(),
JboRel::Tanru(r1, r2) => {
let seltau = logshow_seltau(r1);
format!("<{}><{}>", seltau, logshow_rel(r2))
}
JboRel::AppliedRel(r, terms) => {
format!("{}({})", logshow_rel(r), terms.iter().map(logshow_term).collect::<Vec<_>>().join(","))
}
JboRel::TanruConnective(con, r1, r2) => {
format!("<{}>{}<{}>", logshow_seltau(r1), logshow_connective("j", con), logshow_seltau(r2))
}
JboRel::PermutedRel(n, r) => format!("{} {}", se_to_str(*n), logshow_rel(r)),
JboRel::Among(t) => format!("({} ≥ ", logshow_term(t)),
JboRel::RVar(_) => "[UNBOUND RVar]".to_string(),
JboRel::BoundRVar(n) => format!("R{}", n),
JboRel::RAss(n) => format!("R{}", n),
JboRel::UnboundBribasti(tu) => format!("[UNBOUND {:?}]", tu),
JboRel::Moi(t, moi) => match t.as_ref() {
JboTerm::Value(JboMex::MexInt(_) | JboMex::MexNumeralString(_) | JboMex::MexLerfuString(_)) => format!("{} {}", logshow_term(t), moi),
_ => format!("[{}] {}", logshow_term(t), moi),
},
JboRel::OperatorRel(op) => format!("[{}]", logshow_operator(op)),
JboRel::ScalarNegatedRel(nahe, r) => format!("{{{}}}({})", nahe, logshow_rel(r)),
JboRel::VPredRel(vpred) => logshow_prop(&vpred(&[JboTerm::BoundVar(0)])),
JboRel::AbsPred(abs, npred) => {
let args: Vec<JboTerm> = (1..=npred.arity as i32).map(|n| JboTerm::BoundVar(-n)).collect();
format!("{}[{}]", abs, logshow_lambda_prop(&(npred.pred)(&args)))
}
JboRel::AbsProp(abs, prop) => format!("{}[{}]", abs, logshow_prop(prop)),
JboRel::TagRel(tag) => format!("{{{}}}", jboshow_tag(tag)),
JboRel::ModalRel(modal, r) => logshow_modal_rel(modal, r),
}
}
pub fn logshow_seltau(rel: &JboRel) -> String {
match rel {
JboRel::VPredRel(vpred) => match vpred(&[JboTerm::BoundVar(0)]) {
Prop::Rel(JboRel::Among(t), terms) if terms.is_empty() => format!("(_ <= {})", logshow_term(&t)),
Prop::Rel(JboRel::AppliedRel(r, applied_terms), terms)
if terms.is_empty()
&& matches!(r.as_ref(), JboRel::Among(_))
&& matches!(applied_terms.as_slice(), [JboTerm::BoundVar(0)]) =>
{
if let JboRel::Among(t) = r.as_ref() {
format!("(_ <= {})", logshow_term(t))
} else {
unreachable!()
}
}
Prop::Rel(rel, terms) if terms.is_empty() => format!("{}(_)", logshow_rel(&rel)),
prop => logshow_lambda_prop_with_placeholder(&prop, Some(0)),
},
JboRel::AppliedRel(r, terms) if matches!(r.as_ref(), JboRel::Among(_)) => {
if let JboRel::Among(t) = r.as_ref() {
format!("(_ <= {})", logshow_term(t))
} else {
unreachable!()
}
}
JboRel::AppliedRel(r, terms) => {
let term_strs = terms.iter().map(|term| {
if matches!(term, JboTerm::BoundVar(0)) {
"_".to_string()
} else {
logshow_term(term)
}
}).collect::<Vec<_>>().join(",");
match r.as_ref() {
JboRel::ModalRel(modal, inner) => format!("{}. {}({})", logshow_modal_rel_prefix(modal, Some(0)), logshow_rel(inner), term_strs),
_ => format!("{}({})", logshow_rel(r), term_strs),
}
}
JboRel::Tanru(_, _) => {
format!("{}(_)", logshow_rel(rel))
}
_ => {
format!("{}(_)", logshow_rel(rel))
}
}
}
fn jboshow_prop_with_lambdas(prop: &JboProp, lambda_vars: &[i32]) -> String {
jboshow_prop_with_context(prop, lambda_vars, None)
}
fn jboshow_pred_prop(prop: &JboProp, rel_var: i32) -> String {
jboshow_pred_prop_with_rel_context(prop, rel_var, Some(ShowBinding::SRel(rel_var)))
}
fn jboshow_pred_prop_with_rel_context(prop: &JboProp, placeholder_var: i32, active_rel_var: Option<ShowBinding>) -> String {
match prop {
Prop::Rel(rel, terms) if terms.is_empty() => {
jboshow_rel_with_context(rel, &[], active_rel_var, &[])
}
Prop::Rel(rel, terms) => {
if let Some(idx) = terms.iter().position(|term| matches!(term, JboTerm::BoundVar(n) if *n == placeholder_var)) {
let rel_str = if idx == 0 {
jboshow_rel_with_context(rel, &[], active_rel_var, &[])
} else {
format!("{} {}", se_to_str((idx + 1) as i32), jboshow_rel_with_context(rel, &[], active_rel_var, &[]))
};
let mut rest_terms: Vec<(usize, &JboTerm)> = if idx == 0 {
terms.iter().enumerate().skip(1).collect()
} else {
terms.iter().enumerate().filter(|(i, _)| *i != idx).collect()
};
if idx > 0 {
rest_terms.sort_by_key(|(i, _)| if *i == 0 { idx } else { *i });
}
let mut rest = Vec::new();
let mut previous_unfilled = false;
for (term_idx, term) in rest_terms {
if matches!(term, JboTerm::Unfilled) {
previous_unfilled = true;
continue;
}
let term_str = jboshow_term_with_context(term, &[], active_rel_var, &[]);
if previous_unfilled {
rest.push(format!("{} {}", fa_to_str((term_idx + 1) as i32), term_str));
} else {
rest.push(term_str);
}
previous_unfilled = false;
}
if rest.is_empty() {
rel_str
} else {
format!("{} be {}", rel_str, rest.join(" bei "))
}
} else {
format!("poi'i {} kei", jboshow_prop_with_context(prop, &[], Some(ShowBinding::SRel(placeholder_var))))
}
}
_ => format!("poi'i {} kei", jboshow_prop_with_context(prop, &[], Some(ShowBinding::SRel(placeholder_var)))),
}
}
fn jboshow_prop_with_context(prop: &JboProp, lambda_vars: &[i32], rel_var: Option<ShowBinding>) -> String {
jboshow_prop_with_context_at(prop, lambda_vars, rel_var, 1, &[])
}
fn next_binding_index(bindings: &[(i32, ShowBinding)], binding: impl Fn(i32) -> ShowBinding) -> i32 {
(1..)
.find(|n| !bindings.iter().any(|(_, existing)| *existing == binding(*n)))
.unwrap()
}
fn with_binding(bindings: &[(i32, ShowBinding)], key: i32, binding: ShowBinding) -> Vec<(i32, ShowBinding)> {
let mut next = bindings.to_vec();
next.push((key, binding));
next
}
fn term_binding(bindings: &[(i32, ShowBinding)], key: i32) -> Option<ShowBinding> {
bindings.iter().rev().find_map(|(binding_key, binding)| (*binding_key == key).then_some(*binding))
}
fn next_binding_key(bindings: &[(i32, ShowBinding)]) -> i32 {
(1..).find(|n| !bindings.iter().any(|(key, _)| key == n)).unwrap()
}
fn jboshow_modal_prefix(modal_op: &JboModalOp, lambda_vars: &[i32], rel_var: Option<ShowBinding>, bindings: &[(i32, ShowBinding)]) -> Vec<String> {
match modal_op {
JboModalOp::Tagged(tag, mt) => {
let mut parts = vec![jboshow_tag(tag)];
parts.push(mt.as_ref()
.map(|t| jboshow_term_with_context(t, lambda_vars, rel_var, bindings))
.unwrap_or_else(|| "ku".to_string()));
parts
}
JboModalOp::WithEventAs(t) => vec!["fi'o".to_string(), "du".to_string(), jboshow_term_with_context(t, lambda_vars, rel_var, bindings)],
JboModalOp::QTruthModal => vec!["xu".to_string(), "kau".to_string()],
JboModalOp::NonVeridical => Vec::new(),
}
}
fn jboshow_prop_with_prefixes(prop: &JboProp, lambda_vars: &[i32], rel_var: Option<ShowBinding>, next_var: i32, bindings: &[(i32, ShowBinding)], mut prefixes: Vec<String>) -> String {
match prop {
Prop::Modal(JboModalOp::NonVeridical, inner) => {
format!("ju'a nai {}", jboshow_prop_with_prefixes(inner, lambda_vars, rel_var, next_var, bindings, prefixes))
}
Prop::Modal(modal_op, inner) => {
if let JboModalOp::Tagged(tag, mt) = modal_op {
if let Some(expanded) = expand_connected_tag_modal(tag, mt, inner) {
return jboshow_prop_with_prefixes(&expanded, lambda_vars, rel_var, next_var, bindings, prefixes);
}
}
prefixes.extend(jboshow_modal_prefix(modal_op, lambda_vars, rel_var, bindings));
jboshow_prop_with_prefixes(inner, lambda_vars, rel_var, next_var, bindings, prefixes)
}
Prop::Quantified(JboQuantifier::QuestionQuantifier, restriction, body) => {
let key = next_var;
let n = next_binding_index(bindings, ShowBinding::SAss);
let question_bindings = with_binding(bindings, key, ShowBinding::SAss(n));
prefixes.push("ma".to_string());
if let Some(r) = restriction {
prefixes.push("poi".to_string());
prefixes.push(jboshow_prop_with_context_at(&r(key), lambda_vars, rel_var, key + 1, &question_bindings));
prefixes.push("ku'o".to_string());
prefixes.push("zi'e".to_string());
}
prefixes.push("goi".to_string());
prefixes.push(jboshow_sumti_atom(&SumtiAtom::Assignable(n)));
jboshow_prop_with_prefixes(&body(key), lambda_vars, rel_var, key + 1, &question_bindings, prefixes)
}
Prop::Quantified(JboQuantifier::RelQuantifier(inner_q), _, body)
if matches!(inner_q.as_ref(), JboQuantifier::QuestionQuantifier) =>
{
let key = 1;
let n = next_binding_index(bindings, ShowBinding::SRAss);
let rel_bindings = with_binding(bindings, key, ShowBinding::SRAss(n));
prefixes.extend(["mo".to_string(), "cei".to_string(), jboshow_bound_rvar(n)]);
jboshow_prop_with_prefixes(&body(key), lambda_vars, Some(ShowBinding::SRAss(n)), next_var, &rel_bindings, prefixes)
}
Prop::Quantified(JboQuantifier::RelQuantifier(inner_q), _, body) => {
let key = 1;
let n = next_binding_index(bindings, ShowBinding::SRVar);
let rel_bindings = with_binding(bindings, key, ShowBinding::SRVar(n));
prefixes.push(jboshow_quantifier(inner_q));
prefixes.push(jboshow_unassigned_rvar(n));
jboshow_prop_with_prefixes(&body(key), lambda_vars, Some(ShowBinding::SRVar(n)), next_var, &rel_bindings, prefixes)
}
Prop::Quantified(q, restriction, body) => {
let key = next_var;
let n = next_binding_index(bindings, ShowBinding::SVar);
let body_bindings = with_binding(bindings, key, ShowBinding::SVar(n));
let mut parts = prefixes;
parts.push(jboshow_quantifier(q));
parts.push(jboshow_bound_var(n));
if let Some(r) = restriction {
let rel_bindings = with_binding(&body_bindings, key, ShowBinding::SRel(1));
parts.push("poi".to_string());
parts.push(jboshow_prop_with_context_at(&r(key), lambda_vars, Some(ShowBinding::SRel(key)), key + 1, &rel_bindings));
parts.push("ku'o".to_string());
}
let body_prop = body(key);
if matches!(body_prop, Prop::Quantified(_, _, _)) {
parts.push(jboshow_prop_with_context_at(&body_prop, lambda_vars, rel_var, key + 1, &body_bindings));
parts.join(" ")
} else {
jboshow_prop_with_prefixes(&body_prop, lambda_vars, rel_var, key + 1, &body_bindings, parts)
}
}
_ if prefixes.is_empty() => jboshow_prop_with_context_at(prop, lambda_vars, rel_var, next_var, bindings),
_ => format!("{} zo'u {}", prefixes.join(" "), jboshow_prop_with_context_at(prop, lambda_vars, rel_var, next_var, bindings)),
}
}
fn scalar_negated_terms(terms: &[JboTerm]) -> Vec<JboTerm> {
if terms.len() > 3 && matches!(terms.get(1), Some(JboTerm::Unfilled)) {
std::iter::once(terms[0].clone())
.chain(terms.iter().skip(3).cloned())
.chain(std::iter::once(terms[2].clone()))
.collect()
} else {
terms.to_vec()
}
}
fn jboshow_prop_with_context_at(prop: &JboProp, lambda_vars: &[i32], rel_var: Option<ShowBinding>, next_var: i32, bindings: &[(i32, ShowBinding)]) -> String {
match prop {
Prop::Rel(rel, terms) => {
let terms = if matches!(rel, JboRel::ScalarNegatedRel(_, _)) { scalar_negated_terms(terms) } else { terms.clone() };
let rel_str = jboshow_rel_with_context(rel, lambda_vars, rel_var, bindings);
if terms.is_empty() {
rel_str
} else if terms.len() == 1 {
if matches!(terms[0], JboTerm::Unfilled) {
rel_str
} else {
format!("{} {}", jboshow_term_with_context(&terms[0], lambda_vars, rel_var, bindings), rel_str)
}
} else if matches!(terms[0], JboTerm::Unfilled) {
let mut rest = Vec::new();
let mut previous_unfilled = false;
for (idx, term) in terms.iter().enumerate().skip(1) {
if matches!(term, JboTerm::Unfilled) {
previous_unfilled = true;
continue;
}
let term_str = jboshow_term_with_context(term, lambda_vars, rel_var, bindings);
if previous_unfilled {
rest.push(format!("{} {}", fa_to_str((idx + 1) as i32), term_str));
} else {
rest.push(term_str);
}
previous_unfilled = false;
}
if rest.is_empty() {
rel_str
} else {
format!("{} {}", rel_str, rest.join(" "))
}
} else {
let x1 = jboshow_term_with_context(&terms[0], lambda_vars, rel_var, bindings);
let mut rest = Vec::new();
let mut previous_unfilled = false;
for (idx, term) in terms.iter().enumerate().skip(1) {
if matches!(term, JboTerm::Unfilled) {
previous_unfilled = true;
continue;
}
let term_str = jboshow_term_with_context(term, lambda_vars, rel_var, bindings);
if previous_unfilled {
rest.push(format!("{} {}", fa_to_str((idx + 1) as i32), term_str));
} else {
rest.push(term_str);
}
previous_unfilled = false;
}
if rest.is_empty() {
format!("{} {}", x1, rel_str)
} else {
format!("{} {} {}", x1, rel_str, rest.join(" "))
}
}
}
Prop::Connected(conn, p1, p2) => {
let ss1 = jboshow_prop_with_context_at(p1, lambda_vars, rel_var, next_var, bindings);
let ss2 = jboshow_prop_with_context_at(p2, lambda_vars, rel_var, next_var, bindings);
let prefix = match conn {
crate::logic::Connective::And => "ge".to_string(),
crate::logic::Connective::Or => "ga".to_string(),
crate::logic::Connective::Impl => "ga nai".to_string(),
crate::logic::Connective::Equiv => "go".to_string(),
};
format!("{} {} gi {}", prefix, ss1, ss2)
}
Prop::NonLogConnected(joik, p1, p2) => {
let ss1 = jboshow_prop_with_context_at(p1, lambda_vars, rel_var, next_var, bindings);
let ss2 = jboshow_prop_with_context_at(p2, lambda_vars, rel_var, next_var, bindings);
if joik == "??" {
format!("ge'i {} gi {}", ss1, ss2)
} else {
format!("{} gi {} gi {}", joik, ss1, ss2)
}
}
Prop::Modal(_, _) => jboshow_prop_with_prefixes(prop, lambda_vars, rel_var, next_var, bindings, Vec::new()),
Prop::Not(inner) => format!("na ku {}", jboshow_prop_with_context_at(inner, lambda_vars, rel_var, next_var, bindings)),
Prop::Quantified(JboQuantifier::RelQuantifier(_), _, _) => {
jboshow_prop_with_prefixes(prop, lambda_vars, rel_var, next_var, bindings, Vec::new())
}
Prop::Quantified(JboQuantifier::QuestionQuantifier, _, _) => {
jboshow_prop_with_prefixes(prop, lambda_vars, rel_var, next_var, bindings, Vec::new())
}
Prop::Quantified(q, restriction, body) => {
let key = next_var;
let n = next_binding_index(bindings, ShowBinding::SVar);
let body_bindings = with_binding(bindings, key, ShowBinding::SVar(n));
let mut parts = vec![jboshow_quantifier(q), jboshow_bound_var(n)];
if let Some(r) = restriction {
let rel_bindings = with_binding(&body_bindings, key, ShowBinding::SRel(1));
parts.push("poi".to_string());
parts.push(jboshow_prop_with_context_at(&r(key), lambda_vars, Some(ShowBinding::SRel(key)), key + 1, &rel_bindings));
parts.push("ku'o".to_string());
}
let body_prop = body(key);
if matches!(body_prop, Prop::Quantified(_, _, _)) {
parts.push(jboshow_prop_with_context_at(&body_prop, lambda_vars, rel_var, key + 1, &body_bindings));
} else {
return jboshow_prop_with_prefixes(&body_prop, lambda_vars, rel_var, key + 1, &body_bindings, parts);
}
parts.join(" ")
}
_ => "broda".to_string(),
}
}
pub fn jboshow_prop(prop: &JboProp) -> String {
jboshow_prop_with_lambdas(prop, &[])
}
fn logshow_modal_prefix(modal_op: &JboModalOp) -> Vec<String> {
match modal_op {
JboModalOp::Tagged(tag, mt) => {
let mut parts = vec![format!("({})", jboshow_tag(tag)), "(".to_string()];
if let Some(t) = mt {
parts.push(logshow_term(t));
}
parts.push("). ".to_string());
parts
}
JboModalOp::WithEventAs(t) => vec![logshow_term(t), "=. ".to_string()],
JboModalOp::QTruthModal => vec!["?. ".to_string()],
JboModalOp::NonVeridical => vec!["non-veridical: ".to_string()],
}
}
fn expand_connected_tag_modal(
tag: &crate::jbo_prop::JboTag,
mt: &Option<JboTerm>,
inner: &JboProp,
) -> Option<JboProp> {
if let crate::jbo_prop::JboTag::ConnectedTag(con, tag1, tag2) = tag {
if let crate::jbo_prop::JboConnective::JboConnLog(_, lcon) = con.as_ref() {
let left = Prop::Modal(
JboModalOp::Tagged((**tag1).clone(), mt.clone()),
Box::new(inner.clone()),
);
let right = Prop::Modal(
JboModalOp::Tagged((**tag2).clone(), mt.clone()),
Box::new(inner.clone()),
);
Some(crate::jbo_prop::conn_to_fol(lcon, left, right))
} else {
None
}
} else {
None
}
}
fn logshow_prop_with_prefixes(prop: &JboProp, mut prefixes: Vec<String>) -> String {
match prop {
Prop::Modal(modal_op, inner) => {
if let JboModalOp::Tagged(tag, mt) = modal_op {
if let Some(expanded) = expand_connected_tag_modal(tag, mt, inner) {
return logshow_prop_with_prefixes(&expanded, prefixes);
}
}
if matches!(modal_op, JboModalOp::NonVeridical) {
let mut parts = logshow_modal_prefix(modal_op);
parts.push(logshow_prop_with_prefixes(inner, prefixes));
parts.concat()
} else {
prefixes.extend(logshow_modal_prefix(modal_op));
logshow_prop_with_prefixes(inner, prefixes)
}
}
_ if prefixes.is_empty() => logshow_prop_without_prefixes(prop),
_ => {
let mut parts = prefixes;
parts.push(logshow_prop_without_prefixes(prop));
parts.concat()
}
}
}
fn logshow_prop_without_prefixes(prop: &JboProp) -> String {
match prop {
Prop::Rel(rel, terms) => {
let rel_str = logshow_rel(rel);
let term_strs: Vec<String> = terms.iter().map(logshow_term).collect();
if term_strs.is_empty() {
format!("{}()", rel_str)
} else {
format!("{}({})", rel_str, term_strs.join(","))
}
}
Prop::Connected(con, p1, p2) => {
let left = logshow_prop(p1);
let right = logshow_prop(p2);
match con {
crate::logic::Connective::And => format!("({} /\\ {})", left, right),
crate::logic::Connective::Or => format!("({} \\/ {})", left, right),
crate::logic::Connective::Impl => format!("({} => {})", left, right),
crate::logic::Connective::Equiv => format!("({} <=> {})", left, right),
}
}
Prop::Not(p) => format!("~{}", logshow_prop(p)),
Prop::Modal(_, _) => logshow_prop_with_prefixes(prop, Vec::new()),
_ => "Prop(...)".to_string(),
}
}
pub fn logshow_prop(prop: &JboProp) -> String {
logshow_prop_with_prefixes(prop, Vec::new())
}
pub fn prop_to_lojban(prop: &JboProp) -> String {
jboshow_prop(prop)
}
pub fn build_lojban_output_from_fragment(
terms: &[JboTerm],
side_props: &[JboProp],
) -> String {
let mut output_parts = vec![];
for prop in side_props {
let loj = jboshow_prop(prop);
for line in loj.lines().filter(|line| !line.is_empty()) {
if output_parts.is_empty() || line.starts_with(".i ") {
output_parts.push(line.to_string());
} else {
output_parts.push(format!(".i {}", line));
}
}
}
if !terms.is_empty() {
let term_strs: Vec<String> = terms.iter().map(jboshow_term).collect();
let frag = term_strs.join(" ");
if output_parts.is_empty() || frag.starts_with(".i ") {
output_parts.push(frag);
} else {
output_parts.push(format!(".i {}", frag));
}
} else if !output_parts.is_empty() {
output_parts.push(".i ".to_string());
}
output_parts.join("\n")
}