🙃
This commit is contained in:
77
src/cell.rs
Normal file
77
src/cell.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::evaluator::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cell {
|
||||
eval: Option<Eval>,
|
||||
raw: String,
|
||||
deps: HashSet<CellRef>,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn new(eval: Option<Eval>, raw: String) -> Self {
|
||||
Self {
|
||||
eval,
|
||||
raw,
|
||||
deps: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> String {
|
||||
self.raw.clone()
|
||||
}
|
||||
|
||||
pub fn eval(&self) -> Option<Eval> {
|
||||
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<CellRef, String> {
|
||||
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::<i64>() {
|
||||
Ok(CellRef { row, col })
|
||||
} else {
|
||||
Err(format!("Parse error: invalid row number."))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,12 +19,56 @@ impl fmt::Display for Eval {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _evaluate(expr: &mut Expr) -> Result<Eval, String> {
|
||||
pub struct Evaluator {
|
||||
cells: HashMap<CellRef, Cell>,
|
||||
}
|
||||
|
||||
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(());
|
||||
}
|
||||
|
||||
let mut eval: Option<Eval> = 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<Eval>), 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<Eval, String> {
|
||||
let mut expr = parse(&str)?;
|
||||
self.evaluate_expr(&mut expr)
|
||||
}
|
||||
|
||||
fn evaluate_expr(&self, expr: &mut Expr) -> Result<Eval, String> {
|
||||
let res = match expr {
|
||||
Expr::Literal(lit) => Eval::Literal(lit.clone()),
|
||||
Expr::Infix { op, lhs, rhs } => {
|
||||
let lval = _evaluate(lhs)?;
|
||||
let rval = _evaluate(rhs)?;
|
||||
let lval = self.evaluate_expr(lhs)?;
|
||||
let rval = self.evaluate_expr(rhs)?;
|
||||
|
||||
match op {
|
||||
InfixOp::ADD => eval_add(&lval, &rval)?,
|
||||
@@ -37,6 +83,7 @@ pub fn _evaluate(expr: &mut Expr) -> Result<Eval, String> {
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
match (lval, rval) {
|
||||
@@ -59,16 +106,43 @@ fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
}
|
||||
|
||||
fn eval_sub(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
Err("Todo.".to_string())
|
||||
}
|
||||
fn eval_mul(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
Err("Todo.".to_string())
|
||||
}
|
||||
fn eval_div(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn eval_numeric_infix<FInt, FDouble>(
|
||||
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<Eval, 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<Eval, 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()),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_numeric_infix<FInt, FDouble>(
|
||||
lhs: &Literal,
|
||||
rhs: &Literal,
|
||||
int_op: FInt,
|
||||
|
||||
53
src/main.rs
53
src/main.rs
@@ -1,14 +1,61 @@
|
||||
mod cell;
|
||||
mod evaluator;
|
||||
mod parser;
|
||||
mod tokenizer;
|
||||
|
||||
use std::io;
|
||||
|
||||
use crate::{cell::CellRef, evaluator::Evaluator};
|
||||
|
||||
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 <cell_ref>, get <cell_ref>");
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input).expect("Expected input.");
|
||||
|
||||
let mut ast = parser::parse(&input).unwrap();
|
||||
println!("{}", ast.pretty());
|
||||
println!("{}", evaluator::_evaluate(&mut ast).unwrap());
|
||||
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 <cell_ref>, get <cell_ref>");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Expr>,
|
||||
@@ -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<Expr, String> {
|
||||
args: args,
|
||||
}
|
||||
}
|
||||
_ => Expr::CellRef(id),
|
||||
_ => Expr::CellRef(CellRef::new(id)?),
|
||||
},
|
||||
|
||||
it => return Err(format!("Parse error: did not expect token {:?}.", it)),
|
||||
|
||||
Reference in New Issue
Block a user