use anyhow::{bail, Result};
use tensorlogic_ir::{TLExpr, Term};
pub fn parse_expression(input: &str) -> Result<TLExpr> {
let input = input.trim();
if input.is_empty() {
bail!("Empty expression");
}
if input.starts_with("IF ") || input.starts_with("if ") {
return parse_conditional(input);
}
parse_implication(input)
}
fn parse_implication(input: &str) -> Result<TLExpr> {
if let Some(pos) = find_operator(input, &["->", "IMPLIES", "=>", "→"]) {
let left = parse_or(input[..pos].trim())?;
let right = parse_implication(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::imply(left, right));
}
parse_or(input)
}
fn parse_or(input: &str) -> Result<TLExpr> {
if let Some(pos) = find_operator(input, &[" OR ", " | ", "||"]) {
let left = parse_and(input[..pos].trim())?;
let right = parse_or(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Or(Box::new(left), Box::new(right)));
}
parse_and(input)
}
fn parse_and(input: &str) -> Result<TLExpr> {
if let Some(pos) = find_operator(input, &[" AND ", " & ", "&&", "∧"]) {
let left = parse_comparison(input[..pos].trim())?;
let right = parse_and(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::And(Box::new(left), Box::new(right)));
}
parse_comparison(input)
}
fn parse_comparison(input: &str) -> Result<TLExpr> {
if let Some(pos) = find_operator(input, &[" = ", " == "]) {
let left = parse_additive(input[..pos].trim())?;
let right = parse_additive(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Eq(Box::new(left), Box::new(right)));
}
if let Some(pos) = find_operator(input, &[" <= ", " ≤ "]) {
let left = parse_additive(input[..pos].trim())?;
let right = parse_additive(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Lte(Box::new(left), Box::new(right)));
}
if let Some(pos) = find_operator(input, &[" >= ", " ≥ "]) {
let left = parse_additive(input[..pos].trim())?;
let right = parse_additive(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Gte(Box::new(left), Box::new(right)));
}
if let Some(pos) = find_operator(input, &[" < "]) {
let left = parse_additive(input[..pos].trim())?;
let right = parse_additive(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Lt(Box::new(left), Box::new(right)));
}
if let Some(pos) = find_operator(input, &[" > "]) {
let left = parse_additive(input[..pos].trim())?;
let right = parse_additive(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Gt(Box::new(left), Box::new(right)));
}
if let Some(pos) = find_operator(input, &[" != ", " ≠ "]) {
let left = parse_additive(input[..pos].trim())?;
let right = parse_additive(input[pos + operator_len(&input[pos..])..].trim())?;
let eq = TLExpr::Eq(Box::new(left), Box::new(right));
return Ok(TLExpr::Not(Box::new(eq)));
}
parse_additive(input)
}
fn parse_additive(input: &str) -> Result<TLExpr> {
if let Some(pos) = find_operator(input, &[" + "]) {
let left = parse_multiplicative(input[..pos].trim())?;
let right = parse_additive(input[pos + 3..].trim())?;
return Ok(TLExpr::Add(Box::new(left), Box::new(right)));
}
if let Some(pos) = find_operator(input, &[" - "]) {
let left = parse_multiplicative(input[..pos].trim())?;
let right = parse_additive(input[pos + 3..].trim())?;
return Ok(TLExpr::Sub(Box::new(left), Box::new(right)));
}
parse_multiplicative(input)
}
fn parse_multiplicative(input: &str) -> Result<TLExpr> {
if let Some(pos) = find_operator(input, &[" * ", " × "]) {
let left = parse_unary(input[..pos].trim())?;
let right = parse_multiplicative(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Mul(Box::new(left), Box::new(right)));
}
if let Some(pos) = find_operator(input, &[" / ", " ÷ "]) {
let left = parse_unary(input[..pos].trim())?;
let right = parse_multiplicative(input[pos + operator_len(&input[pos..])..].trim())?;
return Ok(TLExpr::Div(Box::new(left), Box::new(right)));
}
parse_unary(input)
}
fn parse_unary(input: &str) -> Result<TLExpr> {
let input = input.trim();
for prefix in &["NOT ", "not ", "~", "!", "¬"] {
if let Some(rest) = input.strip_prefix(prefix) {
let body = parse_unary(rest.trim())?;
return Ok(TLExpr::Not(Box::new(body)));
}
}
parse_primary(input)
}
fn parse_primary(input: &str) -> Result<TLExpr> {
let input = input.trim();
if let Some(rest) = input
.strip_prefix("EXISTS ")
.or_else(|| input.strip_prefix("exists "))
.or_else(|| input.strip_prefix("∃ "))
{
return parse_quantifier(rest, true);
}
if let Some(rest) = input
.strip_prefix("FORALL ")
.or_else(|| input.strip_prefix("forall "))
.or_else(|| input.strip_prefix("∀ "))
{
return parse_quantifier(rest, false);
}
if input.starts_with('(') && input.ends_with(')') {
let inner = &input[1..input.len() - 1];
if is_balanced(inner) {
return parse_expression(inner);
}
}
if let Ok(value) = input.parse::<f64>() {
return Ok(TLExpr::Constant(value));
}
if let Some(paren_pos) = input.find('(') {
if input.ends_with(')') {
let name = input[..paren_pos].trim();
let args_str = &input[paren_pos + 1..input.len() - 1];
let args: Vec<Term> = if args_str.trim().is_empty() {
vec![]
} else {
args_str
.split(',')
.map(|a| parse_term(a.trim()))
.collect::<Result<Vec<_>>>()?
};
return Ok(TLExpr::pred(name, args));
}
}
Ok(TLExpr::pred(input, vec![]))
}
fn parse_quantifier(input: &str, is_exists: bool) -> Result<TLExpr> {
let parts: Vec<&str> = input.splitn(2, '.').collect();
if parts.len() != 2 {
bail!(
"Invalid quantifier syntax: expected '{} VAR [IN DOMAIN]. BODY'",
if is_exists { "EXISTS" } else { "FORALL" }
);
}
let var_part = parts[0].trim();
let body = parse_expression(parts[1].trim())?;
let (var, domain) = if let Some(in_pos) = var_part.find(" IN ") {
let var = var_part[..in_pos].trim();
let domain = var_part[in_pos + 4..].trim();
(var, domain)
} else if let Some(in_pos) = var_part.find(" in ") {
let var = var_part[..in_pos].trim();
let domain = var_part[in_pos + 4..].trim();
(var, domain)
} else {
(var_part, "D")
};
if is_exists {
Ok(TLExpr::exists(var, domain, body))
} else {
Ok(TLExpr::forall(var, domain, body))
}
}
fn parse_conditional(input: &str) -> Result<TLExpr> {
let input = input
.strip_prefix("IF ")
.or_else(|| input.strip_prefix("if "))
.unwrap();
let then_pos = input
.find(" THEN ")
.or_else(|| input.find(" then "))
.ok_or_else(|| anyhow::anyhow!("Missing THEN in IF-THEN-ELSE"))?;
let else_pos = input
.find(" ELSE ")
.or_else(|| input.find(" else "))
.ok_or_else(|| anyhow::anyhow!("Missing ELSE in IF-THEN-ELSE"))?;
let cond = parse_expression(input[..then_pos].trim())?;
let then_expr = parse_expression(input[then_pos + 6..else_pos].trim())?;
let else_expr = parse_expression(input[else_pos + 6..].trim())?;
Ok(TLExpr::IfThenElse {
condition: Box::new(cond),
then_branch: Box::new(then_expr),
else_branch: Box::new(else_expr),
})
}
fn parse_term(input: &str) -> Result<Term> {
let input = input.trim();
if input.starts_with('"') && input.ends_with('"') {
Ok(Term::Const(input[1..input.len() - 1].to_string()))
} else {
Ok(Term::var(input))
}
}
fn find_operator(input: &str, operators: &[&str]) -> Option<usize> {
let mut depth = 0;
let chars: Vec<char> = input.chars().collect();
for i in 0..chars.len() {
match chars[i] {
'(' => depth += 1,
')' => depth -= 1,
_ => {}
}
if depth == 0 {
for op in operators {
if input[i..].starts_with(op) {
return Some(i);
}
}
}
}
None
}
fn operator_len(input: &str) -> usize {
let operators = vec![
"->", "IMPLIES", "=>", "→", " OR ", " | ", "||", " AND ", " & ", "&&", "∧", " = ", " == ",
" <= ", " ≥ ", " >= ", " ≥ ", " < ", " > ", " != ", " ≠ ", " + ", " - ", " * ", " × ",
" / ", " ÷ ",
];
for op in operators {
if input.starts_with(op) {
return op.len();
}
}
1
}
fn is_balanced(input: &str) -> bool {
let mut depth = 0;
for ch in input.chars() {
match ch {
'(' => depth += 1,
')' => {
depth -= 1;
if depth < 0 {
return false;
}
}
_ => {}
}
}
depth == 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_predicate() {
let expr = parse_expression("knows(x, y)").unwrap();
assert!(matches!(expr, TLExpr::Pred { .. }));
}
#[test]
fn test_and_operation() {
let expr = parse_expression("p(x) AND q(y)").unwrap();
assert!(matches!(expr, TLExpr::And(_, _)));
}
#[test]
fn test_arithmetic() {
let expr = parse_expression("x + y").unwrap();
assert!(matches!(expr, TLExpr::Add(_, _)));
}
#[test]
fn test_comparison() {
let expr = parse_expression("x < y").unwrap();
assert!(matches!(expr, TLExpr::Lt(_, _)));
}
#[test]
fn test_quantifier() {
let expr = parse_expression("EXISTS x IN Person. knows(x, y)").unwrap();
assert!(matches!(expr, TLExpr::Exists { .. }));
}
#[test]
fn test_conditional() {
let expr = parse_expression("IF x < 0 THEN 0 ELSE x").unwrap();
assert!(matches!(expr, TLExpr::IfThenElse { .. }));
}
#[test]
fn test_complex_expression() {
let expr = parse_expression("(p(x) OR q(y)) AND r(z)").unwrap();
assert!(matches!(expr, TLExpr::And(_, _)));
}
}