Решение на Логически изрази от Йоанна Ненкова
Резултати
- 10 точки от тестове
- 0 бонус точки
- 10 точки общо
- 10 успешни тест(а)
- 10 неуспешни тест(а)
Код
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr {
Atom(char),
Not(Box<Expr>),
And(Vec<Expr>),
Or(Vec<Expr>),
}
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
UnexpectedExpr,
UnexpectedUnaryOp,
UnexpectedBinOp,
UnexpectedParen,
UnexpectedEnd,
}
pub struct SimpleExprParser {
current_expr: Option<Expr>,
current_op: Option<char>,
}
impl SimpleExprParser {
pub fn new() -> SimpleExprParser {
SimpleExprParser {
current_expr: None,
current_op: None,
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if self.current_op == Some('!') {
self.current_expr = Some(Expr::Not(Box::new(Expr::Atom(c))));
self.current_op = None;
} else if let Some(op) = self.current_op {
let new_expr = Expr::Atom(c);
self.current_expr = match op {
'&' => self.combine(&Expr::And, new_expr),
'|' => self.combine(&Expr::Or, new_expr),
_ => return Err(ParseError::UnexpectedBinOp),
};
self.current_op = None;
} else if self.current_expr.is_some() {
return Err(ParseError::UnexpectedExpr);
} else {
self.current_expr = Some(Expr::Atom(c));
}
Ok(())
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
if self.current_op.is_some() {
return Err(ParseError::UnexpectedBinOp);
}
if self.current_expr.is_none() && op != '!' {
return Err(ParseError::UnexpectedExpr);
}
self.current_op = Some(op);
Ok(())
}
pub fn finish(self) -> Result<Expr, ParseError> {
self.current_expr.ok_or(ParseError::UnexpectedEnd)
}
fn combine<F>(&mut self, constructor: &F, new_expr: Expr) -> Option<Expr>
where
F: FnOnce(Vec<Expr>) -> Expr + Clone,
{
match self.current_expr.take() {
Some(Expr::And(mut vec)) if std::mem::discriminant(&constructor.clone()(vec![])) == std::mem::discriminant(&Expr::And(vec![]))
=> {
vec.push(new_expr);
Some(Expr::And(vec))
}
Some(Expr::Or(mut vec)) if std::mem::discriminant(&constructor.clone()(vec![])) == std::mem::discriminant(&Expr::Or(vec![]))
=> {
vec.push(new_expr);
Some(Expr::Or(vec))
}
Some(expr) => Some(constructor.clone()(vec![expr, new_expr])),
None => None,
}
}
}
pub struct ExprParser {
stack: Vec<(Option<char>, Option<Expr>)>,
}
impl ExprParser {
pub fn new() -> ExprParser {
ExprParser { stack: vec![] }
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
self.push_expr(Expr::Atom(c))
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
if let Some((None, None)) = self.stack.last() {
self.stack.push((Some(op), None));
} else {
return Err(ParseError::UnexpectedUnaryOp);
}
Ok(())
}
pub fn open_paren(&mut self) -> Result<(), ParseError> {
self.stack.push((None, None));
Ok(())
}
pub fn close_paren(&mut self) -> Result<(), ParseError> {
if let Some((_op, expr)) = self.stack.pop() {
if let Some(e) = expr {
self.push_expr(e)?;
} else {
return Err(ParseError::UnexpectedParen);
}
} else {
return Err(ParseError::UnexpectedParen);
}
Ok(())
}
pub fn finish(mut self) -> Result<Expr, ParseError> {
if let Some((_, Some(expr))) = self.stack.pop() {
Ok(expr)
} else {
Err(ParseError::UnexpectedEnd)
}
}
fn push_expr(&mut self, expr: Expr) -> Result<(), ParseError> {
if let Some((op, current_expr)) = self.stack.last_mut() {
match op {
Some('&') => *current_expr = combine(&Expr::And, current_expr.take(), expr),
Some('|') => *current_expr = combine(&Expr::Or, current_expr.take(), expr),
Some('!') => *current_expr = Some(Expr::Not(Box::new(expr))),
_ => *current_expr = Some(expr),
}
} else {
self.stack.push((None, Some(expr)));
}
Ok(())
}
}
fn combine<F>(constructor: &F, current_expr: Option<Expr>, new_expr: Expr) -> Option<Expr>
where
F: FnOnce(Vec<Expr>) -> Expr + Clone,
{
match current_expr {
Some(Expr::And(mut vec)) if std::mem::discriminant(&constructor.clone()(vec![])) == std::mem::discriminant(&Expr::And(vec![]))
=> {
vec.push(new_expr);
Some(Expr::And(vec))
}
Some(Expr::Or(mut vec)) if std::mem::discriminant(&constructor.clone()(vec![])) == std::mem::discriminant(&Expr::Or(vec![]))
=> {
vec.push(new_expr);
Some(Expr::Or(vec))
}
Some(expr) => Some(constructor.clone()(vec![expr, new_expr])),
None => Some(new_expr),
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
True,
False,
Expr(Expr),
}
pub fn eval(expr: &Expr, truthy: &[char], falsy: &[char]) -> Value {
match expr {
Expr::Atom(c) => {
if truthy.contains(&c) {
Value::True
} else if falsy.contains(&c) {
Value::False
} else {
Value::Expr(expr.clone())
}
}
Expr::Not(inner) => match eval(inner, truthy, falsy) {
Value::True => Value::False,
Value::False => Value::True,
Value::Expr(e) => Value::Expr(Expr::Not(Box::new(e))),
},
Expr::And(operands) => {
let mut simplified = vec![];
for operand in operands {
match eval(operand, truthy, falsy) {
Value::True => continue,
Value::False => return Value::False,
Value::Expr(e) => simplified.push(e),
}
}
match simplified.len() {
0 => Value::True,
1 => Value::Expr(simplified.pop().unwrap()),
_ => Value::Expr(Expr::And(simplified)),
}
}
Expr::Or(operands) => {
let mut simplified = vec![];
for operand in operands {
match eval(operand, truthy, falsy) {
Value::True => return Value::True,
Value::False => continue,
Value::Expr(e) => simplified.push(e),
}
}
match simplified.len() {
0 => Value::False,
1 => Value::Expr(simplified.pop().unwrap()),
_ => Value::Expr(Expr::Or(simplified)),
}
}
}
}
macro_rules! expr {
(atom($c:expr)) => {
Expr::Atom($c)
};
(not($e:tt)) => {
Expr::Not(Box::new(expr!($e)))
};
(and($($e:tt),*)) => {
Expr::And(vec![$(expr!($e)),*])
};
(or($($e:tt),*)) => {
Expr::Or(vec![$(expr!($e)),*])
};
(($($e:tt)*)) => {
expr!($($e)*)
};
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20241224-258381-av1cvu/solution) warning: unused macro definition: `expr` --> src/lib.rs:225:14 | 225 | macro_rules! expr { | ^^^^ | = note: `#[warn(unused_macros)]` on by default warning: `solution` (lib) generated 1 warning warning: variable does not need to be mutable --> tests/solution_test.rs:306:13 | 306 | let mut parser = SimpleExprParser::new(); | ----^^^^^^ | | | help: remove this `mut` | = note: `#[warn(unused_mut)]` on by default warning: `solution` (test "solution_test") generated 1 warning Finished test [unoptimized + debuginfo] target(s) in 1.72s Running tests/solution_test.rs (target/debug/deps/solution_test-1428e1090729d165) running 20 tests test solution_test::test_eval_full ... ok test solution_test::test_error_paren_mismatched ... FAILED test solution_test::test_eval_not_and_or ... ok test solution_test::test_eval_partial ... ok test solution_test::test_eval_unwrap_and_or ... ok test solution_test::test_eval_unwrap_nested ... ok test solution_test::test_paren_around_expr ... FAILED test solution_test::test_paren_expr_priority ... FAILED test solution_test::test_paren_nested ... FAILED test solution_test::test_paren_not ... FAILED test solution_test::test_paren_surrounded ... FAILED test solution_test::test_parser_alternating_ops ... ok test solution_test::test_parser_atom ... ok test solution_test::test_parser_and_or ... ok test solution_test::test_parser_error_unexpected_end ... FAILED test solution_test::test_parser_errors_basic ... FAILED test solution_test::test_parser_multiple_atoms_same_op ... ok test solution_test::test_parser_expr_and_not ... FAILED test solution_test::test_parser_multiple_not ... FAILED test solution_test::test_parser_not ... ok failures: ---- solution_test::test_error_paren_mismatched stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:336:29 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'solution_test::test_error_paren_mismatched' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:325:5 ---- solution_test::test_paren_around_expr stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:182:43 thread 'solution_test::test_paren_around_expr' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:176:5 ---- solution_test::test_paren_expr_priority stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:199:47 thread 'solution_test::test_paren_expr_priority' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:197:5 ---- solution_test::test_paren_nested stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:254:58 thread 'solution_test::test_paren_nested' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:252:5 ---- solution_test::test_paren_not stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:218:48 thread 'solution_test::test_paren_not' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:216:5 ---- solution_test::test_paren_surrounded stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:237:61 thread 'solution_test::test_paren_surrounded' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:235:5 ---- solution_test::test_parser_error_unexpected_end stdout ---- thread '<unnamed>' panicked at 'assertion failed: matches!(parser.finish(), Err(_))', tests/solution_test.rs:311:9 thread 'solution_test::test_parser_error_unexpected_end' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:305:5 ---- solution_test::test_parser_errors_basic stdout ---- thread '<unnamed>' panicked at 'assertion failed: matches!(parser.push_op(\'!\'), Err(_))', tests/solution_test.rs:278:9 thread 'solution_test::test_parser_errors_basic' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:265:5 ---- solution_test::test_parser_expr_and_not stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:109:44 thread 'solution_test::test_parser_expr_and_not' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:107:5 ---- solution_test::test_parser_multiple_not stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:142:42 thread 'solution_test::test_parser_multiple_not' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:140:5 failures: solution_test::test_error_paren_mismatched solution_test::test_paren_around_expr solution_test::test_paren_expr_priority solution_test::test_paren_nested solution_test::test_paren_not solution_test::test_paren_surrounded solution_test::test_parser_error_unexpected_end solution_test::test_parser_errors_basic solution_test::test_parser_expr_and_not solution_test::test_parser_multiple_not test result: FAILED. 10 passed; 10 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--test solution_test`