From a6f41960adb0ea1e68b1ffa7b50f56b29418fb8a Mon Sep 17 00:00:00 2001 From: static Date: Wed, 9 Oct 2024 13:18:03 +0000 Subject: [PATCH] Assignment 4 Done --- src/assignments/assignment04/context.rs | 31 +++++++++++++- src/assignments/assignment04/parser.rs | 56 ++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/assignments/assignment04/context.rs b/src/assignments/assignment04/context.rs index 62a7507..cafa188 100644 --- a/src/assignments/assignment04/context.rs +++ b/src/assignments/assignment04/context.rs @@ -27,7 +27,26 @@ impl Context { /// Calculates the given expression. (We assume the absence of overflow.) pub fn calc_expression(&self, expression: &Expression) -> Result { - todo!("fill here") + match expression { + Expression::Num(value) => Ok(*value), + Expression::Variable(name) => self + .variables + .get(name) + .copied() + .ok_or(anyhow!("Unknown variable")), + Expression::BinOp { op, lhs, rhs } => { + let lhs = self.calc_expression(lhs)?; + let rhs = self.calc_expression(rhs)?; + match op { + BinOp::Add => Ok(lhs + rhs), + BinOp::Subtract => Ok(lhs - rhs), + BinOp::Multiply => Ok(lhs * rhs), + BinOp::Divide if rhs == 0.0 => Err(anyhow!("Division by zero")), + BinOp::Divide => Ok(lhs / rhs), + BinOp::Power => Ok(lhs.powf(rhs)), + } + } + } } /// Calculates the given command. (We assume the absence of overflow.) @@ -43,6 +62,14 @@ impl Context { /// /// After calculating commad `3 ^ 2` => Context's variables = `{($0,8),(v,1),($1,9)}` pub fn calc_command(&mut self, command: &Command) -> Result<(String, f64)> { - todo!("fill here") + let value = self.calc_expression(&command.expression)?; + let name = command.variable.clone().unwrap_or_else(|| { + let name = format!("${}", self.anonymous_counter); + self.anonymous_counter += 1; + name + }); + + _ = self.variables.insert(name.clone(), value); + Ok((name, value)) } } diff --git a/src/assignments/assignment04/parser.rs b/src/assignments/assignment04/parser.rs index 4173270..8b5fc5b 100644 --- a/src/assignments/assignment04/parser.rs +++ b/src/assignments/assignment04/parser.rs @@ -14,11 +14,19 @@ use super::syntax::*; #[allow(missing_docs)] #[allow(missing_debug_implementations)] mod inner { + use pest::pratt_parser::{Assoc::*, *}; use pest_derive::*; #[derive(Parser)] #[grammar = "assignments/assignment04/syntax.pest"] pub(crate) struct SyntaxParser; + + lazy_static::lazy_static! { + pub static ref PRATT_PARSER: PrattParser = PrattParser::new() + .op(Op::infix(Rule::add, Left) | Op::infix(Rule::subtract, Left)) + .op(Op::infix(Rule::multiply, Left) | Op::infix(Rule::divide, Left)) + .op(Op::infix(Rule::power, Right)); + } } use inner::*; @@ -32,5 +40,51 @@ use inner::*; /// e.g. `1+2+3` should be parsed into `(1+2)+3`, not `1+(2+3)` because the associativity of /// plus("add" in our hw) operator is `Left`. pub fn parse_command(line: &str) -> Result { - todo!("fill here") + let mut variable: Option<&str> = None; + let mut expression: Option = None; + + for pair in SyntaxParser::parse(Rule::command, line)? { + match pair.as_rule() { + Rule::var => variable = Some(pair.as_str()), + Rule::expr => expression = Some(parse_expression(pair.into_inner())?), + Rule::EOI => {} + _ => unreachable!(), + } + } + + Ok(Command { + variable: variable.map(|s| s.to_string()), + expression: expression.unwrap(), + }) +} + +/// Parses expression. +pub fn parse_expression(pairs: Pairs<'_, Rule>) -> Result { + let mut first_term: Option = None; + let mut operation: Option = None; + let mut second_term: Option = None; + + PRATT_PARSER + .map_primary(|primary| match primary.as_rule() { + Rule::num => Ok(Expression::Num(primary.as_str().parse()?)), + Rule::var => Ok(Expression::Variable(primary.as_str().to_string())), + Rule::expr => Ok(parse_expression(primary.into_inner())?), + _ => unreachable!(), + }) + .map_infix(|lhs, op, rhs| { + let op = match op.as_rule() { + Rule::add => BinOp::Add, + Rule::subtract => BinOp::Subtract, + Rule::multiply => BinOp::Multiply, + Rule::divide => BinOp::Divide, + Rule::power => BinOp::Power, + _ => unreachable!(), + }; + Ok(Expression::BinOp { + op, + lhs: Box::new(lhs?), + rhs: Box::new(rhs?), + }) + }) + .parse(pairs) }