#![allow(clippy::result_large_err)]
use super::*;
impl LatexParser {
pub(super) fn parse_expression(&mut self) -> ParseResult<Expression> {
self.parse_function_signature()
}
pub(super) fn parse_function_signature(&mut self) -> ParseResult<Expression> {
let left = self.parse_logical_iff()?;
if let Some((LatexToken::Colon, _)) = self.peek() {
let name = match &left {
Expression::Variable(n) => n.clone(),
_ => {
return Ok(left);
}
};
self.next(); let domain = self.parse_logical_iff()?;
if let Some((LatexToken::To, _)) = self.peek() {
self.next(); let codomain = self.parse_logical_iff()?;
return Ok(Expression::FunctionSignature {
name,
domain: Box::new(domain),
codomain: Box::new(codomain),
});
} else {
return Err(ParseError::custom(
"expected \\to after domain in function signature".to_string(),
Some(self.current_span()),
));
}
}
Ok(left)
}
pub(super) fn parse_logical_iff(&mut self) -> ParseResult<Expression> {
let mut left = self.parse_logical_implies()?;
while let Some((LatexToken::Iff, _)) = self.peek() {
self.next(); let right = self.parse_logical_implies()?;
left = Expression::Logical {
op: LogicalOp::Iff,
operands: vec![left, right],
};
}
Ok(left)
}
pub(super) fn parse_logical_implies(&mut self) -> ParseResult<Expression> {
let mut left = self.parse_logical_or()?;
while let Some((LatexToken::Implies, _)) = self.peek() {
self.next(); let right = self.parse_logical_or()?;
left = Expression::Logical {
op: LogicalOp::Implies,
operands: vec![left, right],
};
}
Ok(left)
}
pub(super) fn parse_logical_or(&mut self) -> ParseResult<Expression> {
let mut left = self.parse_logical_and()?;
while let Some((LatexToken::Lor, _)) = self.peek() {
self.next(); let right = self.parse_logical_and()?;
left = Expression::Logical {
op: LogicalOp::Or,
operands: vec![left, right],
};
}
Ok(left)
}
pub(super) fn parse_logical_and(&mut self) -> ParseResult<Expression> {
let mut left = self.parse_set_membership()?;
while let Some((LatexToken::Land, _)) = self.peek() {
self.next(); let right = self.parse_set_membership()?;
left = Expression::Logical {
op: LogicalOp::And,
operands: vec![left, right],
};
}
Ok(left)
}
pub(super) fn parse_set_membership(&mut self) -> ParseResult<Expression> {
let left = self.parse_relation()?;
if let Some((token, _)) = self.peek() {
let relation = match token {
LatexToken::In => Some(SetRelation::In),
LatexToken::NotIn => Some(SetRelation::NotIn),
LatexToken::Subset => Some(SetRelation::Subset),
LatexToken::SubsetEq => Some(SetRelation::SubsetEq),
LatexToken::Superset => Some(SetRelation::Superset),
LatexToken::SupersetEq => Some(SetRelation::SupersetEq),
_ => None,
};
if let Some(rel) = relation {
self.next(); let right = self.parse_relation()?;
return Ok(Expression::SetRelationExpr {
relation: rel,
element: Box::new(left),
set: Box::new(right),
});
}
}
Ok(left)
}
fn match_math_relation(token: &LatexToken) -> Option<RelationOp> {
match token {
LatexToken::Sim => Some(RelationOp::Similar),
LatexToken::Equiv => Some(RelationOp::Equivalent),
LatexToken::Cong => Some(RelationOp::Congruent),
LatexToken::Approx => Some(RelationOp::Approx),
_ => None,
}
}
fn match_ineq_relation(token: &LatexToken) -> Option<Option<InequalityOp>> {
match token {
LatexToken::Equals => Some(None),
LatexToken::Less => Some(Some(InequalityOp::Lt)),
LatexToken::Greater => Some(Some(InequalityOp::Gt)),
LatexToken::Command(cmd) => match cmd.as_str() {
"lt" => Some(Some(InequalityOp::Lt)),
"gt" => Some(Some(InequalityOp::Gt)),
"leq" | "le" => Some(Some(InequalityOp::Le)),
"geq" | "ge" => Some(Some(InequalityOp::Ge)),
"neq" | "ne" => Some(Some(InequalityOp::Ne)),
_ => None,
},
_ => None,
}
}
fn is_relation_token(token: &LatexToken) -> bool {
matches!(
token,
LatexToken::Equals
| LatexToken::Less
| LatexToken::Greater
| LatexToken::Sim
| LatexToken::Equiv
| LatexToken::Cong
| LatexToken::Approx
) || matches!(
token,
LatexToken::Command(cmd) if matches!(
cmd.as_str(),
"lt" | "gt" | "leq" | "le" | "geq" | "ge" | "neq" | "ne"
)
)
}
fn build_equation_or_ineq(
rel_op: Option<InequalityOp>,
left: Expression,
right: Expression,
) -> Expression {
match rel_op {
None => Expression::Equation {
left: Box::new(left),
right: Box::new(right),
},
Some(op) => Expression::Inequality {
op,
left: Box::new(left),
right: Box::new(right),
},
}
}
pub(super) fn parse_relation(&mut self) -> ParseResult<Expression> {
let left = self.parse_additive()?;
if let Some((token, _span)) = self.peek() {
if let Some(rel_op) = Self::match_math_relation(token) {
self.next();
let right = self.parse_additive()?;
return Ok(Expression::Relation {
op: rel_op,
left: Box::new(left),
right: Box::new(right),
});
}
if let Some(rel_op) = Self::match_ineq_relation(token) {
self.next();
let right = self.parse_additive()?;
if let Some((next_token, next_span)) = self.peek() {
if Self::is_relation_token(next_token) {
return Err(ParseError::custom(
"chained relations are not supported; use explicit grouping"
.to_string(),
Some(*next_span),
));
}
}
return Ok(Self::build_equation_or_ineq(rel_op, left, right));
}
}
Ok(left)
}
}