This commit is contained in:
2025-09-08 02:32:01 +10:00
parent 8d03316b1b
commit 60e215dd3d
10 changed files with 518 additions and 121 deletions

View File

@@ -4,59 +4,93 @@ use serde::{Deserialize, Serialize};
use crate::evaluator::*; use crate::evaluator::*;
#[derive(Clone)]
pub struct Cell {
eval: Eval,
raw: String,
i_dep: HashSet<CellRef>,
they_dep: HashSet<CellRef>,
}
impl Cell {
pub fn new(eval: Eval, raw: String) -> Self {
Self {
eval,
raw,
i_dep: HashSet::new(),
they_dep: HashSet::new(),
}
}
pub fn raw(&self) -> String {
self.raw.clone()
}
pub fn eval(&self) -> Eval {
self.eval.clone()
}
pub fn add_i_dep(&mut self, dep: CellRef) {
self.i_dep.insert(dep);
}
pub fn add_they_dep(&mut self, dep: CellRef) {
self.they_dep.insert(dep);
}
pub fn clear_i_dep(&mut self) {
self.i_dep.clear();
}
pub fn clear_they_dep(&mut self) {
self.they_dep.clear();
}
pub fn set_eval(&mut self, eval: Eval) {
self.eval = eval;
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct CellRef { pub struct CellRef {
pub row: usize, pub row: usize,
pub col: usize, pub col: usize,
} }
#[derive(Clone, Debug)]
pub struct Cell {
reference: CellRef,
eval: Eval,
raw: String,
precedents: HashSet<CellRef>, // Cells that this cell reads
dependents: HashSet<CellRef>, // Cells that read this cell
}
impl Cell {
pub fn new(reference: CellRef, eval: Eval, raw: String) -> Self {
Self {
reference,
eval,
raw,
precedents: HashSet::new(),
dependents: HashSet::new(),
}
}
pub fn new_all(
reference: CellRef,
eval: Eval,
raw: String,
precedents: HashSet<CellRef>,
dependents: HashSet<CellRef>,
) -> Self {
Self {
reference,
eval,
raw,
precedents,
dependents,
}
}
pub fn raw(&self) -> String {
self.raw.to_owned()
}
pub fn eval(&self) -> Eval {
self.eval.to_owned()
}
pub fn reference(&self) -> CellRef {
self.reference.to_owned()
}
pub fn set_raw(&mut self, raw: String) {
self.raw = raw;
}
pub fn set_eval(&mut self, eval: Eval) {
self.eval = eval;
}
pub fn set_ref(&mut self, reference: CellRef) {
self.reference = reference;
}
pub fn add_dep(&mut self, it: CellRef) {
self.dependents.insert(it);
}
pub fn remove_dep(&mut self, it: &CellRef) {
self.dependents.remove(&it);
}
pub fn add_prec(&mut self, it: CellRef) {
self.precedents.insert(it);
}
pub fn set_precs(&mut self, it: HashSet<CellRef>) {
self.precedents = it;
}
pub fn deps(&self) -> HashSet<CellRef> {
self.dependents.to_owned()
}
pub fn precs(&self) -> HashSet<CellRef> {
self.precedents.to_owned()
}
}
impl CellRef { impl CellRef {
// Zero indexed // Zero indexed
pub fn new(s: String) -> Result<CellRef, String> { pub fn new(s: String) -> Result<CellRef, String> {

View File

@@ -8,49 +8,72 @@ use std::fmt;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Eval { pub enum Eval {
Literal(Literal), Literal(Literal),
CellRef { eval: Box<Eval>, reference: CellRef },
Range(Vec<Eval>),
Unset,
} }
impl fmt::Display for Eval { impl fmt::Display for Eval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Eval::Literal(lit) => write!(f, "{lit:?}"), Eval::Literal(lit) => write!(f, "{lit:?}"),
Eval::Range(it) => write!(f, "Range({it:?})"),
Eval::CellRef { eval, reference } => write!(f, "EvalRef({eval:?}, {reference:?})"),
Eval::Unset => write!(f, "Unset"),
} }
} }
} }
pub fn evaluate(str: String, grid: Option<&Grid>) -> Result<(Eval, HashSet<CellRef>), String> { pub fn evaluate(str: String, grid: Option<&Grid>) -> Result<(Eval, HashSet<CellRef>), String> {
let (expr, deps) = parse(&str)?; let (expr, _) = parse(&str)?;
match evaluate_expr(&expr, grid) { let mut precs = HashSet::new();
Ok(it) => Ok((it, deps)),
// Make evaulator adds precs for ranges
match evaluate_expr(&expr, &mut precs, grid) {
Ok(it) => Ok((it, precs)),
Err(it) => Err(it), Err(it) => Err(it),
} }
} }
fn evaluate_expr(expr: &Expr, grid: Option<&Grid>) -> Result<Eval, String> { fn evaluate_expr(
expr: &Expr,
precs: &mut HashSet<CellRef>,
grid: Option<&Grid>,
) -> Result<Eval, String> {
let res = match expr { let res = match expr {
Expr::Literal(lit) => Eval::Literal(lit.clone()), Expr::Literal(lit) => Eval::Literal(lit.clone()),
Expr::CellRef(re) => { Expr::CellRef(re) => {
if let Some(g) = grid { if let Some(g) = grid {
g.get_cell(re.to_owned())? Eval::CellRef {
eval: Box::new(
g.get_cell(re.to_owned())
.map_or(Eval::Unset, |cell| cell.eval()),
),
reference: {
precs.insert(*re);
*re
},
}
} else { } else {
return Err("Evaluation error: Found cell reference but no grid.".into()); return Err("Evaluation error: Found cell reference but no grid.".into());
} }
} }
Expr::Infix { op, lhs, rhs } => { Expr::Infix { op, lhs, rhs } => {
let lval = evaluate_expr(lhs, grid)?; let lval = evaluate_expr(lhs, precs, grid)?;
let rval = evaluate_expr(rhs, grid)?; let rval = evaluate_expr(rhs, precs, grid)?;
match op { match op {
InfixOp::ADD => eval_add(&lval, &rval)?, InfixOp::ADD => eval_add(&lval, &rval)?,
InfixOp::SUB => eval_sub(&lval, &rval)?, InfixOp::SUB => eval_sub(&lval, &rval)?,
InfixOp::MUL => eval_mul(&lval, &rval)?, InfixOp::MUL => eval_mul(&lval, &rval)?,
InfixOp::DIV => eval_div(&lval, &rval)?, InfixOp::DIV => eval_div(&lval, &rval)?,
InfixOp::RANGE => eval_range(&lval, &rval, precs, grid)?,
_ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)), _ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)),
} }
} }
Expr::Prefix { op, expr } => { Expr::Prefix { op, expr } => {
let val = evaluate_expr(expr, grid)?; let val = evaluate_expr(expr, precs, grid)?;
match op { match op {
PrefixOp::POS => eval_pos(&val)?, PrefixOp::POS => eval_pos(&val)?,
@@ -59,13 +82,115 @@ fn evaluate_expr(expr: &Expr, grid: Option<&Grid>) -> Result<Eval, String> {
// _ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)), // _ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)),
} }
} }
Expr::Group(g) => evaluate_expr(g, grid)?, Expr::Group(g) => evaluate_expr(g, precs, grid)?,
Expr::Function { name, args } => match name.as_str() {
"AVG" => eval_avg(args, precs, grid)?,
it => return Err(format!("Evaluation error: Unsupported function {}.", it)),
},
it => return Err(format!("Evaluation error: Unsupported expression {:?}", it)), it => return Err(format!("Evaluation error: Unsupported expression {:?}", it)),
}; };
Ok(res) Ok(res)
} }
fn eval_range(
lval: &Eval,
rval: &Eval,
precs: &mut HashSet<CellRef>,
grid: Option<&Grid>,
) -> Result<Eval, String> {
match (lval, rval) {
(
Eval::CellRef {
eval: _,
reference: a_ref,
},
Eval::CellRef {
eval: _,
reference: b_ref,
},
) => {
let mut cells = Vec::new();
// assume row-major expansion
let row_start = a_ref.row.min(b_ref.row);
let row_end = a_ref.row.max(b_ref.row);
let col_start = a_ref.col.min(b_ref.col);
let col_end = a_ref.col.max(b_ref.col);
for row in row_start..=row_end {
for col in col_start..=col_end {
let reference = CellRef { row, col };
let Some(g) = grid else {
return Err("Evaluation error: Found cell range but no grid.".into());
};
cells.push(Eval::CellRef {
eval: Box::new(
g.get_cell(reference.to_owned())
.map_or(Eval::Unset, |cell| cell.eval()),
),
reference: {
precs.insert(reference);
reference
},
});
}
}
Ok(Eval::Range(cells))
}
_ => Err("Evaluation error: expected cellref types for RANGE function.".to_string()),
}
}
fn eval_avg(
args: &Vec<Expr>,
precs: &mut HashSet<CellRef>,
grid: Option<&Grid>,
) -> Result<Eval, String> {
let mut res = 0.0;
let mut count = 0;
for arg in args {
match evaluate_expr(arg, precs, grid)? {
Eval::Literal(Literal::Number(num)) => {
res += num;
count += 1;
}
Eval::Range(range) => {
for cell in range {
let Eval::CellRef { eval, reference: _ } = cell else {
panic!("Found non cellref in evaluation time RANGE!.");
};
if let Eval::Literal(Literal::Number(num)) = *eval {
res += num;
count += 1;
} else if matches!(*eval, Eval::Unset) {
continue;
} else {
return Err("Evaluation error: expected numeric types for AVG function."
.to_string());
}
}
}
_ => {
return Err(
"Evaluation error: expected numeric types for AVG function.".to_string()
);
}
}
}
if count == 0 {
Err("Evaluation error: attempted to divide by zero.".to_string())
} else {
Ok(Eval::Literal(Literal::Number(res / count as f64)))
}
}
fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, String> { fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
match (lval, rval) { match (lval, rval) {
(Eval::Literal(a), Eval::Literal(b)) => { (Eval::Literal(a), Eval::Literal(b)) => {
@@ -82,6 +207,9 @@ fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
Err("Evaluation error: expected string or numeric types for ADD function.".to_string()) Err("Evaluation error: expected string or numeric types for ADD function.".to_string())
} }
_ => {
Err("Evaluation error: expected string or numeric types for ADD function.".to_string())
}
} }
} }
@@ -92,8 +220,9 @@ fn eval_sub(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
return Ok(Eval::Literal(res)); return Ok(Eval::Literal(res));
} }
Err("Evaluation error: expected string or numeric types for SUB function.".to_string()) Err("Evaluation error: expected numeric types for SUB function.".to_string())
} }
_ => Err("Evaluation error: expected numeric types for SUB function.".to_string()),
} }
} }
fn eval_mul(lval: &Eval, rval: &Eval) -> Result<Eval, String> { fn eval_mul(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
@@ -103,8 +232,9 @@ fn eval_mul(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
return Ok(Eval::Literal(res)); return Ok(Eval::Literal(res));
} }
Err("Evaluation error: expected string or numeric types for MUL function.".to_string()) Err("Evaluation error: expected numeric types for MUL function.".to_string())
} }
_ => Err("Evaluation error: expected numeric types for MUL function.".to_string()),
} }
} }
fn eval_div(lval: &Eval, rval: &Eval) -> Result<Eval, String> { fn eval_div(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
@@ -122,8 +252,9 @@ fn eval_div(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
return Ok(Eval::Literal(res)); return Ok(Eval::Literal(res));
} }
Err("Evaluation error: expected string or numeric types for DIV function.".to_string()) Err("Evaluation error: expected numeric types for DIV function.".to_string())
} }
_ => Err("Evaluation error: expected numeric types for DIV function.".to_string()),
} }
} }

View File

@@ -1,5 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use log::info;
use crate::{ use crate::{
cell::{Cell, CellRef}, cell::{Cell, CellRef},
evaluator::{Eval, evaluate}, evaluator::{Eval, evaluate},
@@ -16,38 +18,43 @@ impl Grid {
cells: HashMap::new(), cells: HashMap::new(),
}; };
} }
}
impl Grid { pub fn update_cell(
pub fn set_cell(&mut self, cell_ref: CellRef, raw_val: String) -> Result<Eval, String> { &mut self,
cell_ref: CellRef,
raw_val: String,
) -> Result<Vec<CellRef>, String> {
if self.cells.contains_key(&cell_ref) && self.cells[&cell_ref].raw() == raw_val { if self.cells.contains_key(&cell_ref) && self.cells[&cell_ref].raw() == raw_val {
return self.get_cell(cell_ref); return Ok(Vec::new());
} }
let eval: Eval; let eval: Eval;
let deps: HashSet<CellRef>; let mut precs: HashSet<CellRef> = HashSet::new();
let mut updated_cells = vec![cell_ref];
if let Some(c) = raw_val.chars().nth(0) if raw_val.chars().nth(0) != Some('=') {
&& c == '=' eval = Eval::Literal(Literal::String(raw_val.to_owned()));
{
(eval, deps) = evaluate(raw_val[1..].to_owned(), Some(&self))?;
// for dep in deps {}
} else { } else {
match evaluate(raw_val.to_owned(), Some(&self)) { // Evaluate raw expr and get precedents
Ok(e) => { let (res_eval, res_precs) = evaluate(raw_val[1..].to_owned(), Some(&self))?;
(eval, deps) = e; eval = res_eval;
} precs = res_precs;
Err(_) => eval = Eval::Literal(Literal::String(raw_val.to_owned())),
}
} }
self.cells if self.cells.contains_key(&cell_ref) {
.insert(cell_ref, Cell::new(eval.clone(), raw_val)); updated_cells = self
Ok(eval) .update_exisiting_cell(raw_val, eval, precs, cell_ref)?
.into_iter()
.chain(updated_cells)
.collect();
} else {
self.create_cell(raw_val, eval, precs, cell_ref);
} }
// pub fn get_cell(&mut self, cell_ref: CellRef) -> Result<(String, Eval), String> { Ok(updated_cells)
pub fn get_cell(&self, cell_ref: CellRef) -> Result<Eval, String> { }
pub fn get_cell(&self, cell_ref: CellRef) -> Result<Cell, String> {
if !self.cells.contains_key(&cell_ref) { if !self.cells.contains_key(&cell_ref) {
return Err(format!("Cell at {:?} not found.", cell_ref)); return Err(format!("Cell at {:?} not found.", cell_ref));
} }
@@ -55,18 +62,200 @@ impl Grid {
let cell = &self.cells[&cell_ref]; let cell = &self.cells[&cell_ref];
// Ok((cell.raw(), cell.eval())) // Ok((cell.raw(), cell.eval()))
Ok(cell.eval()) Ok(cell.to_owned())
} }
pub fn add_cell_dep(&mut self, cell_ref: CellRef, dep_ref: CellRef) -> Result<(), String> { pub fn get_cell_mut(&mut self, cell_ref: CellRef) -> Result<&mut Cell, String> {
if !self.cells.contains_key(&cell_ref) { if let Some(res) = self.cells.get_mut(&cell_ref) {
return Err(format!("Cell at {:?} not found.", cell_ref)); Ok(res)
} else {
Err(format!("Cell at {:?} not found.", cell_ref))
}
} }
if let Some(cell) = self.cells.get_mut(&cell_ref) { // This is a topological order on the precedents graph
cell.add_i_dep(dep_ref); // i.e. if a requires b (e.g. a = 1 + b) then a -> b
// so a comes before b in the topo order
fn topo_order(&self, from: CellRef) -> Result<Vec<CellRef>, String> {
let mut res: Vec<CellRef> = Vec::new();
let mut search_set = Vec::new();
let mut temp = HashSet::new();
let mut perm = HashSet::new();
search_set.push(from);
let cell_data = &self.cells[&from];
search_set.extend(cell_data.deps().iter());
temp.insert(from);
perm.insert(from);
let mut searched = 1;
while searched != search_set.len() {
if perm.contains(&search_set[searched]) {
searched += 1;
continue;
} }
self.topo_visit(
search_set[searched],
&mut temp,
&mut perm,
&mut search_set,
&mut res,
)?;
searched += 1;
}
Ok(res)
}
fn topo_visit(
&self,
cell: CellRef,
temp: &mut HashSet<CellRef>,
perm: &mut HashSet<CellRef>,
search_set: &mut Vec<CellRef>,
res: &mut Vec<CellRef>,
) -> Result<(), String> {
if perm.contains(&cell) {
return Ok(());
}
if temp.contains(&cell) {
return Err("Evalutation error: Cycle detected in cell refs.".into());
}
temp.insert(cell);
if !self.cells.contains_key(&cell) {
perm.insert(cell);
res.push(cell);
return Ok(());
}
let cell_data = &self.cells[&cell];
search_set.extend(cell_data.deps().iter());
// search_set.extend(cell_data.precedents.iter().cloned());
for prec in cell_data.precs().iter() {
self.topo_visit(*prec, temp, perm, search_set, res)?;
}
perm.insert(cell);
res.push(cell);
Ok(()) Ok(())
} }
fn update_exisiting_cell(
&mut self,
raw: String,
new_eval: Eval,
new_precs: HashSet<CellRef>,
cell_ref: CellRef,
) -> Result<Vec<CellRef>, String> {
let (old_precs, old_eval) = match self.cells.get_mut(&cell_ref) {
Some(cell) => {
cell.set_raw(raw);
(cell.precs().clone(), cell.eval().clone())
}
None => return Ok(Vec::new()),
};
// diffs (outside any borrow)
let removed: Vec<_> = old_precs.difference(&new_precs).cloned().collect(); // old \ new
let added: Vec<_> = new_precs.difference(&old_precs).cloned().collect(); // new \ old
let eval_changed = old_eval != new_eval;
// ---- phase 2: apply (fresh borrows) ----
for p in &removed {
if let Some(c) = self.cells.get_mut(p) {
c.remove_dep(&cell_ref);
}
}
for p in &added {
if let Some(c) = self.cells.get_mut(p) {
c.add_dep(cell_ref);
} else {
self.cells.insert(
*p,
Cell::new_all(
*p,
Eval::Unset,
"".into(),
HashSet::new(),
HashSet::from([cell_ref]),
),
);
}
}
let cell = self.cells.get_mut(&cell_ref).unwrap(); // Should be impossible to crash
cell.set_precs(new_precs);
cell.set_eval(new_eval);
if eval_changed {
self.propagate(cell_ref)
} else {
Ok(Vec::new())
}
}
fn create_cell(&mut self, raw: String, eval: Eval, precs: HashSet<CellRef>, cell_ref: CellRef) {
for prec in &precs {
if let Some(it) = self.cells.get_mut(&prec) {
it.add_dep(cell_ref);
} else {
self.cells.insert(
*prec,
Cell::new_all(
*prec,
Eval::Unset,
"".into(),
HashSet::new(),
HashSet::from([cell_ref]),
),
);
info!("{:?}", self.cells.get(&prec));
}
}
self.cells.insert(
cell_ref,
Cell::new_all(cell_ref, eval, raw, precs, HashSet::new()),
);
}
fn propagate(&mut self, from: CellRef) -> Result<Vec<CellRef>, String> {
let mut res = Vec::new();
let topo = self.topo_order(from)?;
for cell_ref in topo {
res.push(cell_ref);
let raw = if let Some(cell) = self.cells.get(&cell_ref) {
let s = cell.raw();
if let Some(rest) = s.strip_prefix('=') {
rest.to_owned()
} else {
continue;
}
} else {
continue;
};
// Now we dropped the borrow of self.cells before this point
let (e, _) = evaluate(raw, Some(self))?;
if let Some(cell) = self.cells.get_mut(&cell_ref) {
cell.set_eval(e);
}
}
Ok(res)
}
} }

View File

@@ -14,6 +14,7 @@ use crate::{
evaluator::Eval, evaluator::Eval,
grid::Grid, grid::Grid,
messages::{LeadMsg, MsgType}, messages::{LeadMsg, MsgType},
tokenizer::Literal,
}; };
#[tokio::main] #[tokio::main]
@@ -63,26 +64,45 @@ async fn accept_connection(stream: TcpStream) {
let Some(cell_ref) = req.cell else { continue }; let Some(cell_ref) = req.cell else { continue };
let Some(raw) = req.raw else { continue }; let Some(raw) = req.raw else { continue };
match grid.set_cell(cell_ref.clone(), raw.to_owned()) { match grid.update_cell(cell_ref.clone(), raw.to_owned()) {
Ok(eval) => match eval { Ok(updates) => {
Eval::Literal(lit) => { let mut msgs = Vec::new();
let res = LeadMsg {
msg_type: MsgType::Set, for update in &updates {
cell: Some(cell_ref), if let Ok(cell) = grid.get_cell(*update) {
raw: Some(raw.to_string()), let Eval::Literal(lit) = cell.eval() else {
eval: Some(lit), continue;
}; };
msgs.push(LeadMsg {
msg_type: MsgType::Set,
cell: Some(*update),
raw: Some(cell.raw()),
eval: Some(lit),
bulk_msgs: None,
});
}
}
let msg = LeadMsg {
cell: None,
raw: None,
eval: None,
bulk_msgs: Some(msgs),
msg_type: MsgType::Bulk,
};
let _ = write let _ = write
.send(serde_json::to_string(&res).unwrap().into()) .send(serde_json::to_string(&msg).unwrap().into())
.await; .await;
} }
},
Err(e) => { Err(e) => {
let res = LeadMsg { let res = LeadMsg {
msg_type: MsgType::Error, msg_type: MsgType::Error,
cell: Some(cell_ref), cell: Some(cell_ref),
raw: Some(e.to_string()), raw: Some(e.to_string()),
eval: None, eval: None,
bulk_msgs: None,
}; };
let _ = write let _ = write
.send(serde_json::to_string(&res).unwrap().into()) .send(serde_json::to_string(&res).unwrap().into())

View File

@@ -8,6 +8,7 @@ pub enum MsgType {
Set, Set,
Get, Get,
Error, Error,
Bulk,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@@ -16,4 +17,5 @@ pub struct LeadMsg {
pub cell: Option<CellRef>, pub cell: Option<CellRef>,
pub raw: Option<String>, pub raw: Option<String>,
pub eval: Option<Literal>, pub eval: Option<Literal>,
pub bulk_msgs: Option<Vec<LeadMsg>>,
} }

View File

@@ -1,3 +1,5 @@
use log::info;
use crate::{cell::CellRef, tokenizer::*}; use crate::{cell::CellRef, tokenizer::*};
use std::{collections::HashSet, fmt}; use std::{collections::HashSet, fmt};
@@ -21,6 +23,7 @@ pub enum InfixOp {
SUB, SUB,
AND, AND,
OR, OR,
RANGE,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@@ -57,6 +60,7 @@ pub trait Precedence {
impl Precedence for InfixOp { impl Precedence for InfixOp {
fn prec(&self) -> (u8, u8) { fn prec(&self) -> (u8, u8) {
match self { match self {
InfixOp::RANGE => (7, 8),
InfixOp::MUL | InfixOp::DIV | InfixOp::AND => (3, 4), InfixOp::MUL | InfixOp::DIV | InfixOp::AND => (3, 4),
InfixOp::ADD | InfixOp::SUB | InfixOp::OR => (1, 2), InfixOp::ADD | InfixOp::SUB | InfixOp::OR => (1, 2),
} }
@@ -157,20 +161,21 @@ impl Expr {
pub fn parse(input: &str) -> Result<(Expr, HashSet<CellRef>), String> { pub fn parse(input: &str) -> Result<(Expr, HashSet<CellRef>), String> {
let mut tokenizer = Tokenizer::new(input)?; let mut tokenizer = Tokenizer::new(input)?;
// println!("{:?}", tokenizer.tokens); let mut precs = HashSet::new();
let mut deps = HashSet::new(); let expr = _parse(&mut tokenizer, 0, &mut precs)?;
Ok((_parse(&mut tokenizer, 0, &mut deps)?, deps)) info!("{}", expr.pretty());
Ok((expr, precs))
} }
pub fn _parse( pub fn _parse(
input: &mut Tokenizer, input: &mut Tokenizer,
min_prec: u8, min_prec: u8,
deps: &mut HashSet<CellRef>, precedents: &mut HashSet<CellRef>,
) -> Result<Expr, String> { ) -> Result<Expr, String> {
let mut lhs = match input.next() { let mut lhs = match input.next() {
Token::Literal(it) => Expr::Literal(it), Token::Literal(it) => Expr::Literal(it),
Token::OpenParen => { Token::OpenParen => {
let lhs = _parse(input, 0, deps)?; let lhs = _parse(input, 0, precedents)?;
if input.next() != Token::CloseParen { if input.next() != Token::CloseParen {
return Err(format!("Parse error: expected closing paren.")); return Err(format!("Parse error: expected closing paren."));
} }
@@ -184,7 +189,7 @@ pub fn _parse(
it => return Err(format!("Parse error: unknown prefix operator {:?}.", it)), it => return Err(format!("Parse error: unknown prefix operator {:?}.", it)),
}; };
let rhs = _parse(input, prefix_op.prec().1, deps)?; let rhs = _parse(input, prefix_op.prec().1, precedents)?;
Expr::Prefix { Expr::Prefix {
op: prefix_op, op: prefix_op,
@@ -213,7 +218,7 @@ pub fn _parse(
input.next(); // Skip comma input.next(); // Skip comma
} }
let arg = _parse(input, 0, deps)?; let arg = _parse(input, 0, precedents)?;
args.push(arg); args.push(arg);
} }
@@ -224,7 +229,7 @@ pub fn _parse(
} }
_ => { _ => {
let cell_ref = CellRef::new(id)?; let cell_ref = CellRef::new(id)?;
deps.insert(cell_ref); precedents.insert(cell_ref);
Expr::CellRef(cell_ref) Expr::CellRef(cell_ref)
} }
}, },
@@ -235,7 +240,7 @@ pub fn _parse(
// In the reference article this is a loop with match // In the reference article this is a loop with match
// statement that breaks on Eof and closing paren but this is simpler and works as expected // statement that breaks on Eof and closing paren but this is simpler and works as expected
while let Token::Operator(op) = input.peek() { while let Token::Operator(op) = input.peek() {
if "+-*/&|".contains(op) { if OPERATORS_STR.contains(op) {
let infix_op = match op { let infix_op = match op {
'+' => InfixOp::ADD, '+' => InfixOp::ADD,
'-' => InfixOp::SUB, '-' => InfixOp::SUB,
@@ -243,6 +248,7 @@ pub fn _parse(
'/' => InfixOp::DIV, '/' => InfixOp::DIV,
'&' => InfixOp::AND, '&' => InfixOp::AND,
'|' => InfixOp::OR, '|' => InfixOp::OR,
':' => InfixOp::RANGE,
it => { it => {
return Err(format!("Parse error: do not know infix operator {:?}.", it)); return Err(format!("Parse error: do not know infix operator {:?}.", it));
} }
@@ -254,7 +260,7 @@ pub fn _parse(
} }
input.next(); input.next();
let rhs = _parse(input, r_prec, deps)?; let rhs = _parse(input, r_prec, precedents)?;
lhs = Expr::Infix { lhs = Expr::Infix {
op: infix_op, op: infix_op,
lhs: Box::new(lhs), lhs: Box::new(lhs),

View File

@@ -19,6 +19,8 @@ pub enum Token {
Eof, Eof,
} }
pub const OPERATORS_STR: &str = "+-*/^!%&|:";
pub struct Tokenizer { pub struct Tokenizer {
pub tokens: Vec<Token>, pub tokens: Vec<Token>,
} }
@@ -97,7 +99,7 @@ impl Tokenizer {
string.push(ch); string.push(ch);
} }
tokens.push(Token::Literal(Literal::String(string))); tokens.push(Token::Literal(Literal::String(string)));
} else if "+-*/^!%&|".contains(c) { } else if OPERATORS_STR.contains(c) {
tokens.push(Token::Operator(c)); tokens.push(Token::Operator(c));
chars.next(); chars.next();
} else if "()".contains(c) { } else if "()".contains(c) {

View File

@@ -25,26 +25,38 @@
return; return;
} }
switch (res.msg_type) { handle_msg(res);
};
function handle_msg(msg: LeadMsg) {
switch (msg.msg_type) {
case 'error': { case 'error': {
toast.error('Error', { toast.error('Error', {
description: res.raw description: msg.raw
}); });
break; break;
} }
case 'set': { case 'set': {
if (res.cell === undefined) { if (msg.cell === undefined) {
console.error('Expected cell ref for SET response from server.'); console.error('Expected cell ref for SET msgponse from server.');
return; return;
} else if (res.eval === undefined) { } else if (msg.eval === undefined) {
console.error('Expected cell value for SET response from server.'); console.error('Expected cell value for SET msgponse from server.');
return; return;
} }
setCellVal(res.cell.row, res.cell.col, res.eval.value); setCellVal(msg.cell.row, msg.cell.col, msg.eval.value);
break; break;
} }
case 'bulk': {
if (msg.bulk_msgs === undefined) {
console.error('Expected bulk_msgs field to be defined for BULK message.');
return;
}
for (const m of msg.bulk_msgs) handle_msg(m);
}
}
} }
};
let rows = 50; let rows = 50;
let cols = 40; let cols = 40;

View File

@@ -1,8 +1,9 @@
interface LeadMsg { interface LeadMsg {
msg_type: 'set' | 'get' | 'error'; msg_type: 'set' | 'get' | 'error' | 'bulk';
cell?: CellRef; cell?: CellRef;
raw?: string; raw?: string;
eval?: Literal; eval?: Literal;
bulk_msgs?: Array<LeadMsg>;
} }
interface CellRef { interface CellRef {

View File

@@ -66,7 +66,7 @@
<div <div
data-slot="sidebar-gap" data-slot="sidebar-gap"
class={cn( class={cn(
'relative w-(--sidebar-width) bg-transparent transition-[width] duration-100 ease-linear', 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-150 ease-linear',
'group-data-[collapsible=offcanvas]:w-0', 'group-data-[collapsible=offcanvas]:w-0',
'group-data-[side=right]:rotate-180', 'group-data-[side=right]:rotate-180',
variant === 'floating' || variant === 'inset' variant === 'floating' || variant === 'inset'
@@ -77,7 +77,7 @@
<div <div
data-slot="sidebar-container" data-slot="sidebar-container"
class={cn( class={cn(
'fixed inset-y-0 z-[1000] hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-100 ease-linear md:flex', 'fixed inset-y-0 z-[1000] hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-150 ease-linear md:flex',
side === 'left' side === 'left'
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]' ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]', : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',