Решение на Логически изрази от Божидар Кьоров
Резултати
- 7 точки от тестове
- 0 бонус точки
- 7 точки общо
- 7 успешни тест(а)
- 13 неуспешни тест(а)
Код
use std::collections::VecDeque;
#[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 {
stack: Vec<Expr>,
last_op: Option<char>,
}
impl SimpleExprParser {
pub fn new() -> SimpleExprParser {
SimpleExprParser {
stack: Vec::new(),
last_op: None,
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if self.last_op == Some('!') {
self.stack.push(Expr::Not(Box::new(Expr::Atom(c))));
self.last_op = None;
} else if self.last_op.is_none() || matches!(self.last_op, Some('&') | Some('|')) {
self.stack.push(Expr::Atom(c));
} else {
return Err(ParseError::UnexpectedExpr);
}
Ok(())
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
match op {
'!' => {
if self.last_op.is_some() && self.last_op != Some('&') && self.last_op != Some('|') {
return Err(ParseError::UnexpectedUnaryOp);
}
self.last_op = Some('!');
}
'&' | '|' => {
if self.last_op.is_none() || self.last_op == Some('!') {
return Err(ParseError::UnexpectedBinOp);
}
self.last_op = Some(op);
}
_ => panic!("Invalid operator"),
}
Ok(())
}
pub fn finish(mut self) -> Result<Expr, ParseError> {
if self.stack.is_empty() {
return Err(ParseError::UnexpectedEnd);
}
let mut combined_stack = Vec::new();
let mut current_op = None;
while let Some(expr) = self.stack.pop() {
if let Some(op) = current_op {
match op {
'&' => {
if let Some(Expr::And(mut terms)) = combined_stack.pop() {
terms.push(expr);
combined_stack.push(Expr::And(terms));
} else {
combined_stack.push(Expr::And(vec![expr]));
}
}
'|' => {
if let Some(Expr::Or(mut terms)) = combined_stack.pop() {
terms.push(expr);
combined_stack.push(Expr::Or(terms));
} else {
combined_stack.push(Expr::Or(vec![expr]));
}
}
_ => return Err(ParseError::UnexpectedEnd),
}
current_op = None;
} else {
combined_stack.push(expr);
current_op = self.last_op.take();
}
}
if combined_stack.len() != 1 {
return Err(ParseError::UnexpectedEnd);
}
Ok(combined_stack.pop().unwrap())
}
}
pub struct ExprParser {
stack: Vec<Expr>,
operators: Vec<char>,
parens: Vec<usize>,
}
impl ExprParser {
pub fn new() -> ExprParser {
ExprParser {
stack: Vec::new(),
operators: Vec::new(),
parens: Vec::new(),
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if let Some('!') = self.operators.last() {
self.operators.pop();
self.stack.push(Expr::Not(Box::new(Expr::Atom(c))));
} else {
self.stack.push(Expr::Atom(c));
}
Ok(())
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
match op {
'!' => self.operators.push(op),
'&' | '|' => {
while let Some(&top_op) = self.operators.last() {
if top_op == '(' {
break;
}
self.combine_top();
}
self.operators.push(op);
}
_ => panic!("Invalid operator"),
}
Ok(())
}
pub fn open_paren(&mut self) -> Result<(), ParseError> {
self.operators.push('(');
self.parens.push(self.stack.len());
Ok(())
}
pub fn close_paren(&mut self) -> Result<(), ParseError> {
while let Some(op) = self.operators.pop() {
if op == '(' {
return Ok(());
}
self.combine_top();
}
Err(ParseError::UnexpectedParen)
}
pub fn finish(mut self) -> Result<Expr, ParseError> {
while let Some(_) = self.operators.pop() {
self.combine_top();
}
if self.stack.len() != 1 {
return Err(ParseError::UnexpectedEnd);
}
Ok(self.stack.pop().unwrap())
}
fn combine_top(&mut self) {
if let Some(op) = self.operators.pop() {
if self.stack.len() < 2 {
return;
}
let right = self.stack.pop().unwrap();
let left = self.stack.pop().unwrap();
let new_expr = match op {
'&' => Expr::And(vec![left, right]),
'|' => Expr::Or(vec![left, right]),
_ => panic!("Unexpected operator"),
};
self.stack.push(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::Atom(*c))
}
}
Expr::Not(e) => match eval(e, truthy, falsy) {
Value::True => Value::False,
Value::False => Value::True,
Value::Expr(inner) => Value::Expr(Expr::Not(Box::new(inner))),
},
Expr::And(terms) => {
let mut simplified = Vec::new();
for term in terms {
match eval(term, truthy, falsy) {
Value::False => return Value::False,
Value::True => continue,
Value::Expr(e) => simplified.push(e),
}
}
if simplified.is_empty() {
Value::True
} else if simplified.len() == 1 {
Value::Expr(simplified.pop().unwrap())
} else {
Value::Expr(Expr::And(simplified))
}
}
Expr::Or(terms) => {
let mut simplified = Vec::new();
for term in terms {
match eval(term, truthy, falsy) {
Value::True => return Value::True,
Value::False => continue,
Value::Expr(e) => simplified.push(e),
}
}
if simplified.is_empty() {
Value::False
} else if simplified.len() == 1 {
Value::Expr(simplified.pop().unwrap())
} else {
Value::Expr(Expr::Or(simplified))
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20241224-258381-1ke66ov/solution) warning: unused import: `std::collections::VecDeque` --> src/lib.rs:1:5 | 1 | use std::collections::VecDeque; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` 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 2.06s 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 ... FAILED test solution_test::test_parser_atom ... ok test solution_test::test_parser_and_or ... FAILED test solution_test::test_parser_error_unexpected_end ... FAILED test solution_test::test_parser_errors_basic ... FAILED test solution_test::test_parser_expr_and_not ... FAILED test solution_test::test_parser_multiple_atoms_same_op ... 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 'assertion failed: matches!(parser.close_paren(), Err(_))', tests/solution_test.rs:331:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'solution_test::test_error_paren_mismatched' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:325:5 ---- solution_test::test_paren_around_expr stdout ---- thread '<unnamed>' panicked at 'Unexpected operator', src/lib.rs:185:22 thread 'solution_test::test_paren_around_expr' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:176:5 ---- solution_test::test_paren_expr_priority stdout ---- thread '<unnamed>' panicked at 'Unexpected operator', src/lib.rs:185:22 thread 'solution_test::test_paren_expr_priority' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:197:5 ---- solution_test::test_paren_nested stdout ---- thread '<unnamed>' panicked at 'Unexpected operator', src/lib.rs:185:22 thread 'solution_test::test_paren_nested' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:252:5 ---- solution_test::test_paren_not stdout ---- thread '<unnamed>' panicked at 'Unexpected operator', src/lib.rs:185:22 thread 'solution_test::test_paren_not' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:216:5 ---- solution_test::test_paren_surrounded stdout ---- thread '<unnamed>' panicked at 'Unexpected operator', src/lib.rs:185:22 thread 'solution_test::test_paren_surrounded' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:235:5 ---- solution_test::test_parser_alternating_ops stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:159:51 thread 'solution_test::test_parser_alternating_ops' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:157:5 ---- solution_test::test_parser_and_or stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:87:43 thread 'solution_test::test_parser_and_or' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:84:5 ---- solution_test::test_parser_error_unexpected_end stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:310:41 thread 'solution_test::test_parser_error_unexpected_end' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:305:5 ---- solution_test::test_parser_errors_basic stdout ---- thread '<unnamed>' panicked at 'assertion failed: matches!(parser.push_atom(\'B\'), Err(_))', tests/solution_test.rs:274: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_atoms_same_op stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:126:47 thread 'solution_test::test_parser_multiple_atoms_same_op' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedBinOp', tests/solution_test.rs:124:5 ---- solution_test::test_parser_multiple_not stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', tests/solution_test.rs:142:42 thread 'solution_test::test_parser_multiple_not' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedUnaryOp', 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_alternating_ops solution_test::test_parser_and_or 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_atoms_same_op solution_test::test_parser_multiple_not test result: FAILED. 7 passed; 13 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--test solution_test`