Решение на Логически изрази от Александър Глушков
Към профила на Александър Глушков
Резултати
- 13 точки от тестове
- 0 бонус точки
- 13 точки общо
- 13 успешни тест(а)
- 7 неуспешни тест(а)
Код
use std::vec;
#[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 {
expr: Option<Expr>,
current_op: Option<char>,
expecting_not: bool,
}
impl SimpleExprParser {
pub fn new() -> SimpleExprParser {
SimpleExprParser {
expr: None,
current_op: None,
expecting_not: false,
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
let atom = Expr::Atom(c);
let atom = if self.expecting_not {Expr::Not(Box::new(atom))} else {atom};
match (&mut self.expr, self.current_op) {
(None, None) => self.expr = Some(atom),
(Some(Expr::And(vec)), Some('&')) => vec.push(atom),
(Some(Expr::Or(vec)), Some('|')) => vec.push(atom),
_ => return Err(ParseError::UnexpectedExpr),
}
self.expecting_not = false;
self.current_op = None;
Ok(())
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
match op {
'!' => {
if self.expecting_not {
return Err(ParseError::UnexpectedUnaryOp);
}
self.expecting_not = true;
}
'&' | '|' => {
if self.expr.is_none() || self.current_op.is_some() || self.expecting_not {
return Err(ParseError::UnexpectedBinOp);
}
if matches!(&mut self.expr, Some(Expr::And(_vec))) && op == '&' {
// do nothing, the current operation is Expr::And and we'll add the next atom to it
}
else if matches!(&mut self.expr, Some(Expr::Or(_vec))) && op == '|' {
// again, do nothing
}
else {
let new_expr = if op == '&' { Expr::And(vec![]) } else { Expr::Or(vec![]) };
let old_expr = std::mem::replace(&mut self.expr, Some(new_expr));
if let Some(Expr::And(vec)) | Some(Expr::Or(vec)) = &mut self.expr {
// add the old expression to the new result as part of the and operation
vec.push(old_expr.unwrap());
}
}
self.current_op = Some(op);
}
_ => panic!("Invalid operator"),
}
Ok(())
}
pub fn finish(self) -> Result<Expr, ParseError> {
if self.expecting_not {
Err(ParseError::UnexpectedUnaryOp)
}
else if self.current_op.is_some() {
Err(ParseError::UnexpectedEnd)
} else {
self.expr.ok_or(ParseError::UnexpectedEnd)
}
}
}
pub struct ExprParser {
stack: Vec<SimpleExprParser>,
}
impl ExprParser {
pub fn new() -> ExprParser {
ExprParser {
stack: vec![SimpleExprParser::new()],
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
self.stack.last_mut().unwrap().push_atom(c)
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
self.stack.last_mut().unwrap().push_op(op)
}
pub fn open_paren(&mut self) -> Result<(), ParseError> {
self.stack.push(SimpleExprParser::new());
Ok(())
}
pub fn close_paren(&mut self) -> Result<(), ParseError> {
if self.stack.len() == 1 {
return Err(ParseError::UnexpectedParen);
}
let inner_expr = self.stack.pop().unwrap().finish()?;
self.stack.last_mut().unwrap().push_atom(' ')?;
if let Some(Expr::Not(inner)) = &mut self.stack.last_mut().unwrap().expr {
*inner = Box::new(inner_expr);
} else if let Some(Expr::And(vec)) | Some(Expr::Or(vec)) = &mut self.stack.last_mut().unwrap().expr {
vec.pop();
vec.push(inner_expr);
}
Ok(())
}
pub fn finish(self) -> Result<Expr, ParseError> {
if self.stack.len() != 1 {
return Err(ParseError::UnexpectedEnd);
}
self.stack.into_iter().next().unwrap().finish()
}
}
#[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(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(exprs) => {
let mut result = Vec::new();
for e in exprs {
match eval(e, truthy, falsy) {
Value::False => return Value::False,
Value::True => continue,
Value::Expr(e) => result.push(e),
}
}
match result.len() {
0 => Value::True,
1 => Value::Expr(result.pop().unwrap()),
_ => Value::Expr(Expr::And(result)),
}
}
Expr::Or(exprs) => {
let mut result = Vec::new();
for e in exprs {
match eval(e, truthy, falsy) {
Value::True => return Value::True,
Value::False => continue,
Value::Expr(e) => result.push(e),
}
}
match result.len() {
0 => Value::False,
1 => Value::Expr(result.pop().unwrap()),
_ => Value::Expr(Expr::Or(result)),
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20241224-258381-1b8avpi/solution) 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.74s 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 ... ok test solution_test::test_parser_alternating_ops ... ok test solution_test::test_parser_and_or ... ok test solution_test::test_parser_atom ... ok test solution_test::test_parser_error_unexpected_end ... ok test solution_test::test_parser_errors_basic ... FAILED test solution_test::test_parser_expr_and_not ... ok test solution_test::test_parser_multiple_atoms_same_op ... ok 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.open_paren(), Err(_))', tests/solution_test.rs:355: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 'assertion failed: `(left == right)` left: `Atom(' ')`, right: `Atom('A')`', tests/solution_test.rs:179:9 thread 'solution_test::test_paren_around_expr' panicked at 'assertion failed: `(left == right)` left: `Atom(' ')`, right: `Atom('A')`', tests/solution_test.rs:176:5 ---- solution_test::test_paren_expr_priority stdout ---- thread '<unnamed>' panicked at 'assertion failed: `(left == right)` left: `And([Atom(' '), Atom('X')])`, right: `And([Or([Atom('A'), Atom('B')]), Atom('X')])`', tests/solution_test.rs:207:9 thread 'solution_test::test_paren_expr_priority' panicked at 'assertion failed: `(left == right)` left: `And([Atom(' '), Atom('X')])`, right: `And([Or([Atom('A'), Atom('B')]), Atom('X')])`', tests/solution_test.rs:197:5 ---- solution_test::test_paren_nested stdout ---- thread '<unnamed>' panicked at 'assertion failed: `(left == right)` left: `Not(And([Atom('A'), And([Atom('B'), And([Atom('C'), Atom('D')])])]))`, right: `Not(And([Atom('A'), Not(And([Atom('B'), Not(And([Atom('C'), Atom('D')]))]))]))`', tests/solution_test.rs:255:9 thread 'solution_test::test_paren_nested' panicked at 'assertion failed: `(left == right)` left: `Not(And([Atom('A'), And([Atom('B'), And([Atom('C'), Atom('D')])])]))`, right: `Not(And([Atom('A'), Not(And([Atom('B'), Not(And([Atom('C'), Atom('D')]))]))]))`', tests/solution_test.rs:252:5 ---- solution_test::test_paren_not stdout ---- thread '<unnamed>' panicked at 'assertion failed: `(left == right)` left: `And([Atom('X'), Or([Atom('A'), Atom('B')])])`, right: `And([Atom('X'), Not(Or([Atom('A'), Atom('B')]))])`', tests/solution_test.rs:219:9 thread 'solution_test::test_paren_not' panicked at 'assertion failed: `(left == right)` left: `And([Atom('X'), Or([Atom('A'), Atom('B')])])`, right: `And([Atom('X'), Not(Or([Atom('A'), Atom('B')]))])`', tests/solution_test.rs:216: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_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_parser_errors_basic solution_test::test_parser_multiple_not test result: FAILED. 13 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--test solution_test`