Решение на Логически изрази от Елис Шукри
Резултати
- 16 точки от тестове
- 0 бонус точки
- 16 точки общо
- 16 успешни тест(а)
- 4 неуспешни тест(а)
Код
#[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: Option<Expr>,
pending_op: Option<char>,
no_op_precedence: bool,
}
impl Clone for SimpleExprParser {
fn clone(&self) -> Self {
SimpleExprParser {
current: self.current.clone(),
pending_op: self.pending_op.clone(),
no_op_precedence: self.no_op_precedence.clone(),
}
}
}
impl SimpleExprParser {
pub fn new() -> SimpleExprParser {
SimpleExprParser {
current: None,
pending_op: None,
no_op_precedence: false,
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if self.no_op_precedence == true {
let not_expr = Expr::Not(Box::new(Expr::Atom(c)));
self.no_op_precedence = false;
self.combine_expr(not_expr)
}
else {
self.combine_expr(Expr::Atom(c))
}
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
match op {
'!' => {
if self.pending_op.is_none() && self.current.is_some() {
return Err(ParseError::UnexpectedUnaryOp);
}
if self.no_op_precedence == true {
self.no_op_precedence = false;
}
else {
self.no_op_precedence = true;
}
Ok(())
}
'&' | '|' => {
if self.current.is_none() || self.pending_op.is_some() {
return Err(ParseError::UnexpectedBinOp);
}
self.pending_op = Some(op);
Ok(())
}
_ => panic!("Invalid operator")
}
}
pub fn finish(self) -> Result<Expr, ParseError> {
self.current.ok_or(ParseError::UnexpectedEnd)
}
fn combine_expr(&mut self, expr: Expr) -> Result<(), ParseError> {
if let Some(op) = self.pending_op.take() {
match op {
'&' => {
self.current = Some(match self.current.take() {
Some(Expr::And(mut vec)) => {
vec.push(expr);
Expr::And(vec)
}
Some(left) => Expr::And(vec![left, expr]),
None => return Err(ParseError::UnexpectedBinOp),
});
}
'|' => {
self.current = Some(match self.current.take() {
Some(Expr::Or(mut vec)) => {
vec.push(expr);
Expr::Or(vec)
}
Some(left) => Expr::Or(vec![left, expr]),
None => return Err(ParseError::UnexpectedBinOp),
});
}
_ => panic!("Not a logical operator"),
}
}
else if self.current.is_some() {
return Err(ParseError::UnexpectedExpr);
}
else {
self.current = Some(expr);
}
Ok(())
}
}
pub struct ExprParser {
simple_parser_vec: Vec<(Option<char>, Option<Expr>, bool)>,
simple_parser: SimpleExprParser,
paren_open: usize,
paren_close: usize,
}
impl ExprParser {
pub fn new() -> ExprParser {
ExprParser {
simple_parser_vec: vec![],
simple_parser: SimpleExprParser::new(),
paren_open: 0,
paren_close: 0,
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
self.simple_parser.push_atom(c)
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
self.simple_parser.push_op(op)
}
pub fn open_paren(&mut self) -> Result<(), ParseError> {
if self.simple_parser.pending_op.is_none() && self.simple_parser.current.is_some() {
return Err(ParseError::UnexpectedParen);
}
self.paren_open += 1;
let current_state = (
self.simple_parser.pending_op.take(),
self.simple_parser.current.take(),
self.simple_parser.no_op_precedence,
);
self.simple_parser_vec.push(current_state);
self.simple_parser = SimpleExprParser::new();
Ok(())
}
pub fn close_paren(&mut self) -> Result<(), ParseError> {
let inner_expr = self.simple_parser.clone().finish()?;
self.paren_close += 1;
if let Some((pending_op, current, no_op_precedence)) = self.simple_parser_vec.pop() {
self.simple_parser = SimpleExprParser { current, pending_op, no_op_precedence };
if self.simple_parser.no_op_precedence == true {
let new_inner_expr = Expr::Not(Box::new(inner_expr));
self.simple_parser.combine_expr(new_inner_expr)
}
else {
self.simple_parser.combine_expr(inner_expr)
}
}
else {
Err(ParseError::UnexpectedParen)
}
}
pub fn finish(mut self) -> Result<Expr, ParseError> {
if self.paren_close != self.paren_open {
return Err(ParseError::UnexpectedParen);
}
while !self.simple_parser_vec.is_empty() {
self.close_paren()?;
}
if self.simple_parser.pending_op.is_some() {
return Err(ParseError::UnexpectedEnd);
}
self.simple_parser.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.clone())
}
}
Expr::Not(inner) => match eval(inner, truthy, falsy) {
Value::True => Value::False,
Value::False => Value::True,
Value::Expr(inner_expr) => Value::Expr(Expr::Not(Box::new(inner_expr))),
},
Expr::And(vec) => {
let mut new_vec = vec![];
for sub_expr in vec {
match eval(sub_expr, truthy, falsy) {
Value::True => continue,
Value::False => return Value::False,
Value::Expr(inner_expr) => new_vec.push(inner_expr),
}
}
if new_vec.is_empty() {
Value::True
} else if new_vec.len() == 1 {
Value::Expr(new_vec.pop().unwrap())
} else {
Value::Expr(Expr::And(new_vec))
}
}
Expr::Or(vec) => {
let mut new_vec = vec![];
for sub_expr in vec {
match eval(sub_expr, truthy, falsy) {
Value::True => return Value::True,
Value::False => continue,
Value::Expr(inner_expr) => new_vec.push(inner_expr),
}
}
if new_vec.is_empty() {
Value::False
} else if new_vec.len() == 1 {
Value::Expr(new_vec.pop().unwrap())
} else {
Value::Expr(Expr::Or(new_vec))
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20241224-258381-1ogtxr8/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.96s 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_nested ... ok test solution_test::test_eval_unwrap_and_or ... ok test solution_test::test_paren_around_expr ... ok test solution_test::test_paren_nested ... ok test solution_test::test_paren_expr_priority ... ok test solution_test::test_paren_not ... FAILED test solution_test::test_parser_alternating_ops ... ok test solution_test::test_parser_and_or ... ok test solution_test::test_paren_surrounded ... ok test solution_test::test_parser_atom ... ok test solution_test::test_parser_error_unexpected_end ... FAILED test solution_test::test_parser_errors_basic ... ok test solution_test::test_parser_expr_and_not ... ok test solution_test::test_parser_multiple_not ... FAILED test solution_test::test_parser_multiple_atoms_same_op ... ok 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:337: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_not stdout ---- thread '<unnamed>' panicked at 'assertion failed: `(left == right)` left: `Or([Not(Or([Atom('A'), Atom('B')])), Not(Atom('X'))])`, right: `Or([Not(Or([Atom('A'), Atom('B')])), Atom('X')])`', tests/solution_test.rs:226:9 thread 'solution_test::test_paren_not' panicked at 'assertion failed: `(left == right)` left: `Or([Not(Or([Atom('A'), Atom('B')])), Not(Atom('X'))])`, right: `Or([Not(Or([Atom('A'), Atom('B')])), Atom('X')])`', tests/solution_test.rs:216: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_multiple_not stdout ---- thread '<unnamed>' panicked at 'assertion failed: `(left == right)` left: `Not(Atom('B'))`, right: `Not(Not(Not(Atom('B'))))`', tests/solution_test.rs:143:9 thread 'solution_test::test_parser_multiple_not' panicked at 'assertion failed: `(left == right)` left: `Not(Atom('B'))`, right: `Not(Not(Not(Atom('B'))))`', tests/solution_test.rs:140:5 failures: solution_test::test_error_paren_mismatched solution_test::test_paren_not solution_test::test_parser_error_unexpected_end solution_test::test_parser_multiple_not test result: FAILED. 16 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s error: test failed, to rerun pass `--test solution_test`