use crate::ast::Expr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Limits {
pub max_depth: usize,
pub max_nodes: usize,
pub max_vars: usize,
}
impl Default for Limits {
fn default() -> Self {
Self {
max_depth: 256,
max_nodes: 100_000,
max_vars: 12,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum LimitError {
#[error("表达式深度超过 {limit} 层")]
Depth {
limit: usize,
},
#[error("表达式节点数超过 {limit}")]
Nodes {
limit: usize,
},
#[error("表达式变量数超过 {limit}(3^n 枚举不可行)")]
Vars {
limit: usize,
},
}
pub fn check_expr(expr: &Expr, limits: Limits) -> Result<(), LimitError> {
let mut stack: Vec<(&Expr, usize)> = vec![(expr, 1)];
let mut nodes = 0usize;
while let Some((node, depth)) = stack.pop() {
if depth > limits.max_depth {
return Err(LimitError::Depth {
limit: limits.max_depth,
});
}
nodes += 1;
if nodes > limits.max_nodes {
return Err(LimitError::Nodes {
limit: limits.max_nodes,
});
}
match node {
Expr::Const(_) | Expr::Var(_) => {}
Expr::Not(inner) => stack.push((inner, depth + 1)),
Expr::And(a, b)
| Expr::Or(a, b)
| Expr::Xor(a, b)
| Expr::Implies(a, b)
| Expr::Equiv(a, b) => {
stack.push((a, depth + 1));
stack.push((b, depth + 1));
}
}
}
Ok(())
}
pub fn check_vars(expr: &Expr, limits: Limits) -> Result<(), LimitError> {
let count = expr.variables().len();
if count > limits.max_vars {
return Err(LimitError::Vars {
limit: limits.max_vars,
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::Parser;
#[test]
fn default_limits_accept_ordinary_expressions() {
let e = Parser::parse("(a & b) | (!c => d)").unwrap();
assert!(check_expr(&e, Limits::default()).is_ok());
}
#[test]
fn depth_limit_is_enforced() {
let e = Parser::parse("!!!!a").unwrap();
let tight = Limits {
max_depth: 3,
..Limits::default()
};
assert!(matches!(
check_expr(&e, tight),
Err(LimitError::Depth { .. })
));
}
#[test]
fn node_limit_is_enforced() {
let e = Parser::parse("a & b & c & d").unwrap();
let tight = Limits {
max_nodes: 4,
..Limits::default()
};
assert!(matches!(
check_expr(&e, tight),
Err(LimitError::Nodes { .. })
));
}
#[test]
fn var_limit_is_enforced() {
let e = Parser::parse("a & b & c").unwrap();
let tight = Limits {
max_vars: 2,
..Limits::default()
};
assert!(matches!(
check_vars(&e, tight),
Err(LimitError::Vars { .. })
));
}
}