diff --git a/src/cell.rs b/src/cell.rs new file mode 100644 index 0000000..5a196dd --- /dev/null +++ b/src/cell.rs @@ -0,0 +1,77 @@ +use std::collections::HashSet; + +use crate::evaluator::*; + +#[derive(Clone)] +pub struct Cell { + eval: Option, + raw: String, + deps: HashSet, +} + +impl Cell { + pub fn new(eval: Option, raw: String) -> Self { + Self { + eval, + raw, + deps: HashSet::new(), + } + } + + pub fn raw(&self) -> String { + self.raw.clone() + } + + pub fn eval(&self) -> Option { + self.eval.clone() + } +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct CellRef { + pub row: i64, + pub col: i64, +} + +impl CellRef { + pub fn new(s: String) -> Result { + let s = s.trim(); + let mut col: i64 = 0; + let mut i = 0; + + // consume leading letters for the column + for (idx, ch) in s.char_indices() { + if ch.is_ascii_alphabetic() { + let u = ch.to_ascii_uppercase() as u8; + let val = (u - b'A' + 1) as i64; // A->1 ... Z->26 + col = col * 26 + val; + i = idx + ch.len_utf8(); + } else { + break; + } + } + + if col <= 0 { + return Err(format!( + "Parse error: missing column letters in cell ref: {s}" + )); + } + + let row_part = &s[i..]; + if row_part.is_empty() { + return Err(format!( + "Parse error: missing column letters in cell ref: {s}" + )); + } else if !row_part.chars().all(|c| c.is_ascii_digit()) { + return Err(format!( + "Parse error: row part must be numeric in cell ref: {s}" + )); + } + + if let Ok(row) = row_part.parse::() { + Ok(CellRef { row, col }) + } else { + Err(format!("Parse error: invalid row number.")) + } + } +} diff --git a/src/evaluator.rs b/src/evaluator.rs index ee5f412..4b9958e 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,8 +1,10 @@ +use crate::cell::{Cell, CellRef}; use crate::parser::*; use crate::tokenizer::Literal; +use std::collections::HashMap; use std::fmt; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Eval { Literal(Literal), Expr(Expr), @@ -17,25 +19,70 @@ impl fmt::Display for Eval { } } -pub fn _evaluate(expr: &mut Expr) -> Result { - let res = match expr { - Expr::Literal(lit) => Eval::Literal(lit.clone()), - Expr::Infix { op, lhs, rhs } => { - let lval = _evaluate(lhs)?; - let rval = _evaluate(rhs)?; +pub struct Evaluator { + cells: HashMap, +} - match op { - InfixOp::ADD => eval_add(&lval, &rval)?, - InfixOp::SUB => eval_sub(&lval, &rval)?, - InfixOp::MUL => eval_mul(&lval, &rval)?, - InfixOp::DIV => eval_div(&lval, &rval)?, - _ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)), - } +impl Evaluator { + pub fn new() -> Evaluator { + return Evaluator { + cells: HashMap::new(), + }; + } + + pub fn set_cell(&mut self, cell_ref: CellRef, raw_val: String) -> Result<(), String> { + if self.cells.contains_key(&cell_ref) && self.cells[&cell_ref].raw() == raw_val { + return Ok(()); } - it => return Err(format!("Evaluation error: Unsupported expression {:?}", it)), - }; - Ok(res) + let mut eval: Option = None; + if let Some(c) = raw_val.chars().nth(0) + && c == '=' + { + if let Ok(e) = self.evaluate(raw_val[1..].to_owned()) { + eval = Some(e); + }; + } + + self.cells.insert(cell_ref, Cell::new(eval, raw_val)); + Ok(()) + } + + pub fn get_cell(&mut self, cell_ref: CellRef) -> Result<(String, Option), String> { + if !self.cells.contains_key(&cell_ref) { + return Err(format!("Cell at {:?} not found.", cell_ref)); + } + + let cell = &self.cells[&cell_ref]; + + Ok((cell.raw(), cell.eval())) + } + + pub fn evaluate(&self, str: String) -> Result { + let mut expr = parse(&str)?; + self.evaluate_expr(&mut expr) + } + + fn evaluate_expr(&self, expr: &mut Expr) -> Result { + let res = match expr { + Expr::Literal(lit) => Eval::Literal(lit.clone()), + Expr::Infix { op, lhs, rhs } => { + let lval = self.evaluate_expr(lhs)?; + let rval = self.evaluate_expr(rhs)?; + + match op { + InfixOp::ADD => eval_add(&lval, &rval)?, + InfixOp::SUB => eval_sub(&lval, &rval)?, + InfixOp::MUL => eval_mul(&lval, &rval)?, + InfixOp::DIV => eval_div(&lval, &rval)?, + _ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)), + } + } + it => return Err(format!("Evaluation error: Unsupported expression {:?}", it)), + }; + + Ok(res) + } } fn eval_add(lval: &Eval, rval: &Eval) -> Result { @@ -59,16 +106,43 @@ fn eval_add(lval: &Eval, rval: &Eval) -> Result { } fn eval_sub(lval: &Eval, rval: &Eval) -> Result { - Err("Todo.".to_string()) + match (lval, rval) { + (Eval::Literal(a), Eval::Literal(b)) => { + if let Some(res) = eval_numeric_infix(a, b, |x, y| x - y, |x, y| x - y) { + return Ok(Eval::Literal(res)); + } + + Err("Evaluation error: expected string or numeric types for SUB function.".to_string()) + } + _ => return Err("Evalutation error: expected literals for SUB function.".to_string()), + } } fn eval_mul(lval: &Eval, rval: &Eval) -> Result { - Err("Todo.".to_string()) + match (lval, rval) { + (Eval::Literal(a), Eval::Literal(b)) => { + if let Some(res) = eval_numeric_infix(a, b, |x, y| x * y, |x, y| x * y) { + return Ok(Eval::Literal(res)); + } + + Err("Evaluation error: expected string or numeric types for MUL function.".to_string()) + } + _ => return Err("Evalutation error: expected literals for MUL function.".to_string()), + } } fn eval_div(lval: &Eval, rval: &Eval) -> Result { - Err("Todo.".to_string()) + match (lval, rval) { + (Eval::Literal(a), Eval::Literal(b)) => { + if let Some(res) = eval_numeric_infix(a, b, |x, y| x / y, |x, y| x / y) { + return Ok(Eval::Literal(res)); + } + + Err("Evaluation error: expected string or numeric types for DIV function.".to_string()) + } + _ => return Err("Evalutation error: expected literals for DIV function.".to_string()), + } } -pub fn eval_numeric_infix( +fn eval_numeric_infix( lhs: &Literal, rhs: &Literal, int_op: FInt, diff --git a/src/main.rs b/src/main.rs index 21ee077..b9e3f44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,61 @@ +mod cell; mod evaluator; mod parser; mod tokenizer; use std::io; -fn main() { - let mut input = String::new(); - io::stdin().read_line(&mut input).expect("Expected input."); +use crate::{cell::CellRef, evaluator::Evaluator}; - let mut ast = parser::parse(&input).unwrap(); - println!("{}", ast.pretty()); - println!("{}", evaluator::_evaluate(&mut ast).unwrap()); +fn main() { + // let mut input = String::new(); + // io::stdin().read_line(&mut input).expect("Expected input."); + + // let mut ast = parser::parse(&input).unwrap(); + // println!("{}", ast.pretty()); + let mut evaluator = Evaluator::new(); + // // println!("{}", evaluator.evaluate(input).unwrap()); + // let a1 = CellRef { row: 1, col: 2 }; + // evaluator.set_cell(a1, input).unwrap(); + // println!("{:?}", evaluator.get_cell(a1).unwrap()); + + println!("CMDS : set , get "); + loop { + let mut input = String::new(); + io::stdin().read_line(&mut input).expect("Expected input."); + + let cmds = ["set", "get"]; + let cmd = &input[0..3]; + if !cmds.iter().any(|c| c == &cmd) { + println!("{} is an invalid command!", cmd); + println!("CMDS : set , get "); + continue; + } + + let rest = &input[4..]; + let mut parts = rest.splitn(2, char::is_whitespace); + + let raw_ref = parts.next().unwrap_or("").trim(); // cell reference + let raw_str = parts.next().unwrap_or("").trim(); // rest of the string (value) + // println!("{} {}", raw_ref, raw_str); + + if let Ok(cell_ref) = CellRef::new(raw_ref.to_owned()) { + match cmd { + "set" => match evaluator.set_cell(cell_ref, raw_str.to_owned()) { + Ok(_) => println!("Successfully set cell {} to {}.", raw_ref, raw_str), + Err(e) => println!("{}", e), + }, + "get" => match evaluator.get_cell(cell_ref) { + Ok(res) => println!("{:?}", res), + Err(e) => println!("{}", e), + }, + _ => { + continue; // Impossible + } + } + } else { + println!("{} is an invalid cell reference!", raw_ref); + continue; + } + } } diff --git a/src/parser.rs b/src/parser.rs index 4286d4d..c546baf 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,19 +1,19 @@ -use crate::tokenizer::*; +use crate::{cell::CellRef, tokenizer::*}; use std::fmt; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PrefixOp { POS, NEG, NOT, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PostfixOp { PERCENT, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum InfixOp { MUL, DIV, @@ -23,10 +23,10 @@ pub enum InfixOp { OR, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Expr { Literal(Literal), - CellRef(String), + CellRef(CellRef), Function { name: String, args: Vec, @@ -86,7 +86,7 @@ impl fmt::Display for Expr { Expr::Postfix { op, expr } => write!(f, "({op:?} {expr})"), Expr::Infix { op, lhs, rhs } => write!(f, "({lhs} {op:?} {rhs})"), Expr::Function { name, args } => write!(f, "{name}({args:?})"), - Expr::CellRef(it) => write!(f, "CellRef({it})"), + Expr::CellRef(it) => write!(f, "{it:?}"), } } } @@ -149,7 +149,7 @@ impl Expr { Expr::Postfix { op, .. } => format!("Postfix({:?})", op), Expr::Infix { op, .. } => format!("Infix({:?})", op), Expr::Function { name, .. } => format!("Function({:?})", name), - Expr::CellRef(it) => format!("CellRef({:?})", it), + Expr::CellRef(it) => format!("{:?}", it), } } } @@ -218,7 +218,7 @@ pub fn _parse(input: &mut Tokenizer, min_prec: u8) -> Result { args: args, } } - _ => Expr::CellRef(id), + _ => Expr::CellRef(CellRef::new(id)?), }, it => return Err(format!("Parse error: did not expect token {:?}.", it)),