Решение на Логически изрази от Ясен Узунов
Резултати
- 18 точки от тестове
- 0 бонус точки
- 18 точки общо
- 18 успешни тест(а)
- 2 неуспешни тест(а)
Код
#[cfg(test)]
mod tests;
#[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 {
chars: Vec<Expr>, // treated as stack
ops: Vec<char>,
should_next_input_be_char: bool,
}
impl SimpleExprParser {
pub fn new() -> SimpleExprParser {
SimpleExprParser {
chars: Vec::new(),
ops: Vec::new(),
should_next_input_be_char: true,
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if !self.should_next_input_be_char {
return Err(ParseError::UnexpectedExpr);
}
self.chars.push(Expr::Atom(c));
self.should_next_input_be_char = false;
Ok(())
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
match op {
'!' => {
if !self.should_next_input_be_char {
return Err(ParseError::UnexpectedUnaryOp);
}
self.ops.push(op);
self.should_next_input_be_char = true;
}
'&' | '|' => {
if self.should_next_input_be_char {
return Err(ParseError::UnexpectedBinOp);
}
self.apply_ops(op)?;
self.ops.push(op);
self.should_next_input_be_char = true;
}
_ => panic!("invalid operator"),
}
Ok(())
}
fn apply_not(&mut self) {
self.ops.pop();
let expr = self.chars.pop().unwrap();
self.chars.push(Expr::Not(Box::new(expr)));
}
fn combine_and(&mut self, left: Expr, right: Expr) -> Expr {
let mut exprs = Vec::new();
match left {
Expr::And(left_exprs) => exprs.extend(left_exprs),
other => exprs.push(other),
}
match right {
Expr::And(right_exprs) => exprs.extend(right_exprs),
other => exprs.push(other),
}
Expr::And(exprs)
}
fn combine_or(&mut self, left: Expr, right: Expr) -> Expr {
let mut exprs = Vec::new();
match left {
Expr::Or(left_exprs) => exprs.extend(left_exprs),
other => exprs.push(other),
}
match right {
Expr::Or(right_exprs) => exprs.extend(right_exprs),
other => exprs.push(other),
}
Expr::Or(exprs)
}
fn apply_ops(&mut self, new_op: char) -> Result<(), ParseError> {
while let Some(&last_op) = self.ops.last() {
if last_op == '!' {
self.apply_not();
} else if last_op == new_op {
self.ops.pop();
let right = self.chars.pop().unwrap();
let left = self.chars.pop().unwrap();
let combined = match new_op {
'&' => self.combine_and(left, right),
'|' => self.combine_or(left, right),
_ => unreachable!(),
};
self.chars.push(combined);
}
}
Ok(())
}
pub fn finish(mut self) -> Result<Expr, ParseError> {
if self.should_next_input_be_char {
return Err(ParseError::UnexpectedEnd);
}
if !self.ops.is_empty() {
self.apply_ops(self.ops[0])?;
}
Ok(self.chars.pop().unwrap())
}
}
pub struct ExprParser {
// making use of the logic behind SimpplExpressionParser
parsers: Vec<SimpleExprParser>,
current: SimpleExprParser,
}
impl ExprParser {
pub fn new() -> ExprParser {
ExprParser {
parsers: Vec::new(),
current: SimpleExprParser::new(),
}
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
self.current.push_atom(c)
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
self.current.push_op(op)
}
pub fn open_paren(&mut self) -> Result<(), ParseError> {
let new_parser = SimpleExprParser::new();
let old_parser = std::mem::replace(&mut self.current, new_parser);
self.parsers.push(old_parser);
Ok(())
}
pub fn close_paren(&mut self) -> Result<(), ParseError> {
if self.parsers.is_empty() {
return Err(ParseError::UnexpectedParen);
}
let temp_parser = SimpleExprParser::new();
let current_parser = std::mem::replace(&mut self.current, temp_parser);
let inner_expr = current_parser.finish()?;
self.current = self.parsers.pop().unwrap();
self.current.chars.push(inner_expr);
self.current.should_next_input_be_char = false;
Ok(())
}
pub fn finish(self) -> Result<Expr, ParseError> {
if !self.parsers.is_empty() {
return Err(ParseError::UnexpectedEnd);
}
self.current.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_exprs = Vec::new();
for e in exprs {
match eval(e, truthy, falsy) {
Value::False => return Value::False,
Value::True => continue,
Value::Expr(e) => result_exprs.push(e),
}
}
match result_exprs.len() {
0 => Value::True,
1 => Value::Expr(result_exprs.pop().unwrap()),
_ => Value::Expr(Expr::And(result_exprs)),
}
},
Expr::Or(exprs) => {
let mut result_exprs = Vec::new();
for e in exprs {
match eval(e, truthy, falsy) {
Value::True => return Value::True,
Value::False => continue,
Value::Expr(e) => result_exprs.push(e),
}
}
match result_exprs.len() {
0 => Value::False,
1 => Value::Expr(result_exprs.pop().unwrap()),
_ => Value::Expr(Expr::Or(result_exprs)),
}
},
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20241224-258381-1hntxsb/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.71s 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_expr_priority ... ok test solution_test::test_paren_nested ... ok test solution_test::test_paren_not ... ok test solution_test::test_paren_surrounded ... 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 ... ok 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 ... ok test solution_test::test_parser_not ... ok test solution_test::test_parser_alternating_ops ... FAILED 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_parser_alternating_ops stdout ---- thread 'solution_test::test_parser_alternating_ops' panicked at 'called `Result::unwrap()` on an `Err` value: Timeout', tests/solution_test.rs:157:5 failures: solution_test::test_error_paren_mismatched solution_test::test_parser_alternating_ops test result: FAILED. 18 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.00s error: test failed, to rerun pass `--test solution_test`