🙃
This commit is contained in:
20
backend/Cargo.lock
generated
20
backend/Cargo.lock
generated
@@ -331,6 +331,8 @@ dependencies = [
|
||||
"env_logger",
|
||||
"futures-util",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
]
|
||||
@@ -512,6 +514,12 @@ version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
@@ -532,6 +540,18 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
|
||||
@@ -7,5 +7,7 @@ edition = "2024"
|
||||
env_logger = "0.11.8"
|
||||
futures-util = "0.3.31"
|
||||
log = "0.4.27"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.143"
|
||||
tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "net", "time"] }
|
||||
tokio-tungstenite = "0.27.0"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::evaluator::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -49,23 +51,24 @@ impl Cell {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct CellRef {
|
||||
pub row: i64,
|
||||
pub col: i64,
|
||||
pub row: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
impl CellRef {
|
||||
// Zero indexed
|
||||
pub fn new(s: String) -> Result<CellRef, String> {
|
||||
let s = s.trim();
|
||||
let mut col: i64 = 0;
|
||||
let mut col: usize = 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
|
||||
let val = (u - b'A' + 1) as usize; // A->1 ... Z->26
|
||||
col = col * 26 + val;
|
||||
i = idx + ch.len_utf8();
|
||||
} else {
|
||||
@@ -90,8 +93,11 @@ impl CellRef {
|
||||
));
|
||||
}
|
||||
|
||||
if let Ok(row) = row_part.parse::<i64>() {
|
||||
Ok(CellRef { row, col })
|
||||
if let Ok(row) = row_part.parse::<usize>() {
|
||||
Ok(CellRef {
|
||||
row: row - 1,
|
||||
col: col - 1,
|
||||
})
|
||||
} else {
|
||||
Err(format!("Parse error: invalid row number."))
|
||||
}
|
||||
|
||||
0
backend/src/common.rs
Normal file
0
backend/src/common.rs
Normal file
@@ -1,7 +1,8 @@
|
||||
use crate::cell::{Cell, CellRef};
|
||||
use crate::cell::CellRef;
|
||||
use crate::grid::Grid;
|
||||
use crate::parser::*;
|
||||
use crate::tokenizer::Literal;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -17,115 +18,58 @@ impl fmt::Display for Eval {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Evaluator {
|
||||
cells: HashMap<CellRef, Cell>,
|
||||
pub fn evaluate(str: String, grid: Option<&Grid>) -> Result<(Eval, HashSet<CellRef>), String> {
|
||||
let (expr, deps) = parse(&str)?;
|
||||
|
||||
match evaluate_expr(&expr, grid) {
|
||||
Ok(it) => Ok((it, deps)),
|
||||
Err(it) => Err(it),
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluator {
|
||||
pub fn new() -> Evaluator {
|
||||
return Evaluator {
|
||||
cells: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_cell(&mut self, cell_ref: CellRef, raw_val: String) -> Result<Eval, String> {
|
||||
if self.cells.contains_key(&cell_ref) && self.cells[&cell_ref].raw() == raw_val {
|
||||
return self.get_cell(cell_ref);
|
||||
}
|
||||
|
||||
let eval: Eval;
|
||||
let deps: HashSet<CellRef>;
|
||||
|
||||
if let Some(c) = raw_val.chars().nth(0)
|
||||
&& c == '='
|
||||
{
|
||||
(eval, deps) = self.evaluate(raw_val[1..].to_owned())?;
|
||||
// for dep in deps {}
|
||||
} else {
|
||||
match self.evaluate(raw_val.to_owned()) {
|
||||
Ok(e) => {
|
||||
(eval, deps) = e;
|
||||
}
|
||||
Err(_) => eval = Eval::Literal(Literal::String(raw_val.to_owned())),
|
||||
fn evaluate_expr(expr: &Expr, grid: Option<&Grid>) -> Result<Eval, String> {
|
||||
let res = match expr {
|
||||
Expr::Literal(lit) => Eval::Literal(lit.clone()),
|
||||
Expr::CellRef(re) => {
|
||||
if let Some(g) = grid {
|
||||
g.get_cell(re.to_owned())?
|
||||
} else {
|
||||
return Err("Evaluation error: Found cell reference but no grid.".into());
|
||||
}
|
||||
}
|
||||
Expr::Infix { op, lhs, rhs } => {
|
||||
let lval = evaluate_expr(lhs, grid)?;
|
||||
let rval = evaluate_expr(rhs, grid)?;
|
||||
|
||||
self.cells
|
||||
.insert(cell_ref, Cell::new(eval.clone(), raw_val));
|
||||
Ok(eval)
|
||||
}
|
||||
|
||||
// pub fn get_cell(&mut self, cell_ref: CellRef) -> Result<(String, Eval), String> {
|
||||
pub fn get_cell(&mut self, cell_ref: CellRef) -> Result<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()))
|
||||
Ok(cell.eval())
|
||||
}
|
||||
|
||||
pub fn add_cell_dep(&mut self, cell_ref: CellRef, dep_ref: CellRef) -> Result<(), String> {
|
||||
if !self.cells.contains_key(&cell_ref) {
|
||||
return Err(format!("Cell at {:?} not found.", cell_ref));
|
||||
}
|
||||
|
||||
if let Some(cell) = self.cells.get_mut(&cell_ref) {
|
||||
cell.add_i_dep(dep_ref);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn evaluate(&mut self, str: String) -> Result<(Eval, HashSet<CellRef>), String> {
|
||||
let (expr, deps) = parse(&str)?;
|
||||
|
||||
match self.evaluate_expr(&expr) {
|
||||
Ok(it) => Ok((it, deps)),
|
||||
Err(it) => Err(it),
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_expr(&mut self, expr: &Expr) -> Result<Eval, String> {
|
||||
let res = match expr {
|
||||
Expr::Literal(lit) => Eval::Literal(lit.clone()),
|
||||
Expr::CellRef(re) => self.get_cell(re.to_owned())?,
|
||||
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)),
|
||||
}
|
||||
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)),
|
||||
}
|
||||
Expr::Prefix { op, expr } => {
|
||||
let val = self.evaluate_expr(expr)?;
|
||||
}
|
||||
Expr::Prefix { op, expr } => {
|
||||
let val = evaluate_expr(expr, grid)?;
|
||||
|
||||
match op {
|
||||
PrefixOp::POS => eval_pos(&val)?,
|
||||
PrefixOp::NEG => eval_neg(&val)?,
|
||||
PrefixOp::NOT => eval_not(&val)?,
|
||||
_ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)),
|
||||
}
|
||||
match op {
|
||||
PrefixOp::POS => eval_pos(&val)?,
|
||||
PrefixOp::NEG => eval_neg(&val)?,
|
||||
PrefixOp::NOT => eval_not(&val)?,
|
||||
// _ => return Err(format!("Evaluation error: Unsupported operator {:?}", op)),
|
||||
}
|
||||
Expr::Group(g) => self.evaluate_expr(g)?,
|
||||
it => return Err(format!("Evaluation error: Unsupported expression {:?}", it)),
|
||||
};
|
||||
}
|
||||
Expr::Group(g) => evaluate_expr(g, grid)?,
|
||||
it => return Err(format!("Evaluation error: Unsupported expression {:?}", it)),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn eval_add(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) {
|
||||
if let Some(res) = eval_numeric_infix(a, b, |x, y| x + y) {
|
||||
return Ok(Eval::Literal(res));
|
||||
}
|
||||
|
||||
@@ -144,7 +88,7 @@ fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
fn eval_sub(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) {
|
||||
if let Some(res) = eval_numeric_infix(a, b, |x, y| x - y) {
|
||||
return Ok(Eval::Literal(res));
|
||||
}
|
||||
|
||||
@@ -155,7 +99,7 @@ fn eval_sub(lval: &Eval, rval: &Eval) -> Result<Eval, 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) {
|
||||
if let Some(res) = eval_numeric_infix(a, b, |x, y| x * y) {
|
||||
return Ok(Eval::Literal(res));
|
||||
}
|
||||
|
||||
@@ -166,15 +110,15 @@ fn eval_mul(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
fn eval_div(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
match (lval, rval) {
|
||||
(Eval::Literal(a), Eval::Literal(b)) => {
|
||||
if let (Literal::Integer(_), Literal::Integer(y)) = (a, b) {
|
||||
if *y == 0 {
|
||||
if let (Literal::Number(_), Literal::Number(y)) = (a, b) {
|
||||
if *y == 0f64 {
|
||||
return Err(
|
||||
"Evaluation error: integers attempted to divide by zero.".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(res) = eval_numeric_infix(a, b, |x, y| x / y, |x, y| x / y) {
|
||||
if let Some(res) = eval_numeric_infix(a, b, |x, y| x / y) {
|
||||
return Ok(Eval::Literal(res));
|
||||
}
|
||||
|
||||
@@ -183,41 +127,23 @@ fn eval_div(lval: &Eval, rval: &Eval) -> Result<Eval, String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_numeric_infix<FInt, FDouble>(
|
||||
lhs: &Literal,
|
||||
rhs: &Literal,
|
||||
int_op: FInt,
|
||||
double_op: FDouble,
|
||||
) -> Option<Literal>
|
||||
where
|
||||
FInt: Fn(i64, i64) -> i64,
|
||||
FDouble: Fn(f64, f64) -> f64,
|
||||
{
|
||||
fn eval_numeric_infix(lhs: &Literal, rhs: &Literal, op: fn(f64, f64) -> f64) -> Option<Literal> {
|
||||
match (lhs, rhs) {
|
||||
(Literal::Integer(a), Literal::Integer(b)) => Some(Literal::Integer(int_op(*a, *b))),
|
||||
(Literal::Double(a), Literal::Double(b)) => Some(Literal::Double(double_op(*a, *b))),
|
||||
(Literal::Integer(a), Literal::Double(b)) => {
|
||||
Some(Literal::Double(double_op(*a as f64, *b)))
|
||||
}
|
||||
(Literal::Double(a), Literal::Integer(b)) => {
|
||||
Some(Literal::Double(double_op(*a, *b as f64)))
|
||||
}
|
||||
(Literal::Number(a), Literal::Number(b)) => Some(Literal::Number(op(*a, *b))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_pos(val: &Eval) -> Result<Eval, String> {
|
||||
match val {
|
||||
Eval::Literal(Literal::Integer(it)) => Ok(Eval::Literal(Literal::Integer(*it))),
|
||||
Eval::Literal(Literal::Double(it)) => Ok(Eval::Literal(Literal::Double(*it))),
|
||||
Eval::Literal(Literal::Number(it)) => Ok(Eval::Literal(Literal::Number(*it))),
|
||||
_ => Err("Evaluation error: expected numeric type for POS function.".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_neg(val: &Eval) -> Result<Eval, String> {
|
||||
match val {
|
||||
Eval::Literal(Literal::Integer(it)) => Ok(Eval::Literal(Literal::Integer(-it))),
|
||||
Eval::Literal(Literal::Double(it)) => Ok(Eval::Literal(Literal::Double(-it))),
|
||||
Eval::Literal(Literal::Number(it)) => Ok(Eval::Literal(Literal::Number(-it))),
|
||||
_ => Err("Evaluation error: expected numeric type for NEG function.".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
72
backend/src/grid.rs
Normal file
72
backend/src/grid.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::{
|
||||
cell::{Cell, CellRef},
|
||||
evaluator::{Eval, evaluate},
|
||||
tokenizer::Literal,
|
||||
};
|
||||
|
||||
pub struct Grid {
|
||||
cells: HashMap<CellRef, Cell>,
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
pub fn new() -> Grid {
|
||||
return Grid {
|
||||
cells: HashMap::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
pub fn set_cell(&mut self, cell_ref: CellRef, raw_val: String) -> Result<Eval, String> {
|
||||
if self.cells.contains_key(&cell_ref) && self.cells[&cell_ref].raw() == raw_val {
|
||||
return self.get_cell(cell_ref);
|
||||
}
|
||||
|
||||
let eval: Eval;
|
||||
let deps: HashSet<CellRef>;
|
||||
|
||||
if let Some(c) = raw_val.chars().nth(0)
|
||||
&& c == '='
|
||||
{
|
||||
(eval, deps) = evaluate(raw_val[1..].to_owned(), Some(&self))?;
|
||||
// for dep in deps {}
|
||||
} else {
|
||||
match evaluate(raw_val.to_owned(), Some(&self)) {
|
||||
Ok(e) => {
|
||||
(eval, deps) = e;
|
||||
}
|
||||
Err(_) => eval = Eval::Literal(Literal::String(raw_val.to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
self.cells
|
||||
.insert(cell_ref, Cell::new(eval.clone(), raw_val));
|
||||
Ok(eval)
|
||||
}
|
||||
|
||||
// pub fn get_cell(&mut self, cell_ref: CellRef) -> Result<(String, Eval), String> {
|
||||
pub fn get_cell(&self, cell_ref: CellRef) -> Result<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()))
|
||||
Ok(cell.eval())
|
||||
}
|
||||
|
||||
pub fn add_cell_dep(&mut self, cell_ref: CellRef, dep_ref: CellRef) -> Result<(), String> {
|
||||
if !self.cells.contains_key(&cell_ref) {
|
||||
return Err(format!("Cell at {:?} not found.", cell_ref));
|
||||
}
|
||||
|
||||
if let Some(cell) = self.cells.get_mut(&cell_ref) {
|
||||
cell.add_i_dep(dep_ref);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,24 @@
|
||||
mod cell;
|
||||
mod evaluator;
|
||||
mod grid;
|
||||
mod messages;
|
||||
mod parser;
|
||||
mod tokenizer;
|
||||
|
||||
use futures_util::{SinkExt, StreamExt, TryStreamExt, future};
|
||||
use futures_util::{SinkExt, StreamExt, TryStreamExt};
|
||||
use log::info;
|
||||
use std::{env, io::Error};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use crate::{cell::CellRef, evaluator::Evaluator};
|
||||
use crate::{
|
||||
evaluator::Eval,
|
||||
grid::Grid,
|
||||
messages::{LeadMsg, MsgType},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Error> {
|
||||
env_logger::init();
|
||||
// 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());
|
||||
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
@@ -38,78 +34,8 @@ async fn main() -> Result<(), Error> {
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
// println!("CMDS : set <cell_ref>, get <cell_ref>");
|
||||
// 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 <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),
|
||||
// },
|
||||
// _ => {
|
||||
// panic!("Impossible.");
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// println!("{} is an invalid cell reference!", raw_ref);
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// async fn accept_connection(stream: TcpStream) {
|
||||
// let addr = stream
|
||||
// .peer_addr()
|
||||
// .expect("connected streams should have a peer address");
|
||||
// info!("Peer address: {}", addr);
|
||||
//
|
||||
// let ws_stream = tokio_tungstenite::accept_async(stream)
|
||||
// .await
|
||||
// .expect("Error during the websocket handshake occurred");
|
||||
//
|
||||
// info!("New WebSocket connection: {}", addr);
|
||||
//
|
||||
// let (mut write, mut read) = ws_stream.split();
|
||||
//
|
||||
// // We should not forward messages other than text or binary.
|
||||
// while let Some(msg) = read.try_next().await.unwrap_or(None) {
|
||||
// if msg.is_text() || msg.is_binary() {
|
||||
// if let Err(e) = write
|
||||
// .send(format!("This is a message {}!", msg.to_text().unwrap_or("")).into())
|
||||
// .await
|
||||
// {
|
||||
// eprintln!("send error: {}", e);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// info!("Disconnected from {}", addr);
|
||||
// }
|
||||
|
||||
async fn accept_connection(stream: TcpStream) {
|
||||
let addr = stream
|
||||
.peer_addr()
|
||||
@@ -125,56 +51,51 @@ async fn accept_connection(stream: TcpStream) {
|
||||
let (mut write, mut read) = ws_stream.split();
|
||||
|
||||
// Each connection gets its own evaluator
|
||||
let mut evaluator = Evaluator::new();
|
||||
let mut grid = Grid::new();
|
||||
|
||||
while let Some(msg) = read.try_next().await.unwrap_or(None) {
|
||||
if msg.is_text() {
|
||||
let input = msg.to_text().unwrap_or("").trim().to_string();
|
||||
let input = msg.to_text().unwrap();
|
||||
|
||||
let cmds = ["set", "get"];
|
||||
let cmd = &input[0..3.min(input.len())]; // avoid panic on short strings
|
||||
if let Ok(req) = serde_json::from_str::<LeadMsg>(&input) {
|
||||
match req.msg_type {
|
||||
MsgType::Set => {
|
||||
let Some(cell_ref) = req.cell else { continue };
|
||||
let Some(raw) = req.raw else { continue };
|
||||
|
||||
if !cmds.iter().any(|c| c == &cmd) {
|
||||
let _ = write
|
||||
.send(format!("ERR invalid command: {}", input).into())
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
|
||||
let rest = input[4..].trim();
|
||||
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 (value)
|
||||
|
||||
if let Ok(cell_ref) = CellRef::new(raw_ref.to_owned()) {
|
||||
match cmd {
|
||||
"set" => match evaluator.set_cell(cell_ref.clone(), raw_str.to_owned()) {
|
||||
Ok(eval) => {
|
||||
let _ = write.send(format!("{} {}", raw_ref, eval).into()).await;
|
||||
match grid.set_cell(cell_ref.clone(), raw.to_owned()) {
|
||||
Ok(eval) => match eval {
|
||||
Eval::Literal(lit) => {
|
||||
let res = LeadMsg {
|
||||
msg_type: MsgType::Set,
|
||||
cell: Some(cell_ref),
|
||||
raw: Some(raw.to_string()),
|
||||
eval: Some(lit),
|
||||
};
|
||||
let _ = write
|
||||
.send(serde_json::to_string(&res).unwrap().into())
|
||||
.await;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let res = LeadMsg {
|
||||
msg_type: MsgType::Error,
|
||||
cell: Some(cell_ref),
|
||||
raw: Some(e.to_string()),
|
||||
eval: None,
|
||||
};
|
||||
let _ = write
|
||||
.send(serde_json::to_string(&res).unwrap().into())
|
||||
.await;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = write.send(format!("ERR {}", e).into()).await;
|
||||
}
|
||||
},
|
||||
"get" => match evaluator.get_cell(cell_ref.clone()) {
|
||||
Ok(res) => {
|
||||
let _ = write
|
||||
.send(format!("{} {}", raw_ref, res.to_string()).into())
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = write.send(format!("ERR {}", e).into()).await;
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
let _ = write.send("ERR impossible".into()).await;
|
||||
continue; // handle other cases
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = write
|
||||
.send(format!("ERR invalid cell reference: {}", raw_ref).into())
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
backend/src/messages.rs
Normal file
19
backend/src/messages.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{cell::CellRef, tokenizer::Literal};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum MsgType {
|
||||
Set,
|
||||
Get,
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LeadMsg {
|
||||
pub msg_type: MsgType,
|
||||
pub cell: Option<CellRef>,
|
||||
pub raw: Option<String>,
|
||||
pub eval: Option<Literal>,
|
||||
}
|
||||
@@ -169,11 +169,9 @@ pub fn _parse(
|
||||
) -> Result<Expr, String> {
|
||||
let mut lhs = match input.next() {
|
||||
Token::Literal(it) => Expr::Literal(it),
|
||||
Token::Identifier(id) if id == "true" => Expr::Literal(Literal::Boolean(true)),
|
||||
Token::Identifier(id) if id == "false" => Expr::Literal(Literal::Boolean(false)),
|
||||
Token::Paren('(') => {
|
||||
Token::OpenParen => {
|
||||
let lhs = _parse(input, 0, deps)?;
|
||||
if input.next() != Token::Paren(')') {
|
||||
if input.next() != Token::CloseParen {
|
||||
return Err(format!("Parse error: expected closing paren."));
|
||||
}
|
||||
Expr::Group(Box::new(lhs))
|
||||
@@ -194,14 +192,14 @@ pub fn _parse(
|
||||
}
|
||||
}
|
||||
Token::Identifier(id) => match input.peek() {
|
||||
Token::Paren('(') => {
|
||||
Token::OpenParen => {
|
||||
input.next();
|
||||
|
||||
let mut args: Vec<Expr> = Vec::new();
|
||||
loop {
|
||||
let nxt = input.peek();
|
||||
|
||||
if nxt == Token::Paren(')') {
|
||||
if nxt == Token::CloseParen {
|
||||
input.next();
|
||||
break;
|
||||
} else if nxt != Token::Comma && args.len() != 0 {
|
||||
@@ -286,6 +284,5 @@ pub fn _parse(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(lhs)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", content = "value")]
|
||||
pub enum Literal {
|
||||
Integer(i64),
|
||||
Double(f64),
|
||||
Number(f64),
|
||||
Boolean(bool),
|
||||
String(String),
|
||||
}
|
||||
@@ -11,7 +13,8 @@ pub enum Token {
|
||||
Identifier(String), // Could be a function
|
||||
Literal(Literal),
|
||||
Operator(char),
|
||||
Paren(char),
|
||||
OpenParen,
|
||||
CloseParen,
|
||||
Comma,
|
||||
Eof,
|
||||
}
|
||||
@@ -39,29 +42,38 @@ impl Tokenizer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tokens.push(Token::Identifier(ident));
|
||||
let res = match ident.as_str() {
|
||||
"true" => Token::Literal(Literal::Boolean(true)),
|
||||
"false" => Token::Literal(Literal::Boolean(false)),
|
||||
it => Token::Identifier(it.into()),
|
||||
};
|
||||
|
||||
tokens.push(res);
|
||||
} else if c.is_ascii_digit() {
|
||||
// parse number
|
||||
let mut number = String::new();
|
||||
let mut is_decimal = false;
|
||||
let mut is_exp = false;
|
||||
|
||||
while let Some(&ch) = chars.peek() {
|
||||
if ch.is_ascii_digit() {
|
||||
number.push(ch);
|
||||
chars.next();
|
||||
} else if ch == '.' && !is_decimal {
|
||||
} else if ch == '.' && !is_decimal && !is_exp {
|
||||
is_decimal = true;
|
||||
number.push(ch);
|
||||
chars.next();
|
||||
} else if ch == 'e' && !is_decimal && !is_exp {
|
||||
is_exp = true;
|
||||
number.push(ch);
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if is_decimal {
|
||||
tokens.push(Token::Literal(Literal::Double(number.parse().unwrap())))
|
||||
} else {
|
||||
tokens.push(Token::Literal(Literal::Integer(number.parse().unwrap())))
|
||||
};
|
||||
|
||||
// TODO: REMOVE UNWRAP
|
||||
tokens.push(Token::Literal(Literal::Number(number.parse().unwrap())));
|
||||
} else if c == '"' || c == '\'' {
|
||||
// parse string literal
|
||||
let mut string = String::new();
|
||||
@@ -89,7 +101,11 @@ impl Tokenizer {
|
||||
tokens.push(Token::Operator(c));
|
||||
chars.next();
|
||||
} else if "()".contains(c) {
|
||||
tokens.push(Token::Paren(c));
|
||||
if c == '(' {
|
||||
tokens.push(Token::OpenParen);
|
||||
} else {
|
||||
tokens.push(Token::CloseParen);
|
||||
}
|
||||
chars.next();
|
||||
} else if c == ',' {
|
||||
tokens.push(Token::Comma);
|
||||
@@ -116,20 +132,140 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_tokenizer() {
|
||||
let raw = "hello hello 1.23 this 5 (1+2)";
|
||||
let expected: Vec<Token> = vec![
|
||||
Token::Identifier("hello".to_string()),
|
||||
Token::Identifier("hello".to_string()),
|
||||
Token::Literal(Literal::Double(1.23)),
|
||||
Token::Identifier("this".to_string()),
|
||||
Token::Literal(Literal::Integer(5)),
|
||||
Token::Paren('('),
|
||||
Token::Literal(Literal::Integer(1)),
|
||||
fn test_single_token() {
|
||||
assert_eq!(
|
||||
Tokenizer::new("1").unwrap().tokens,
|
||||
Vec::from([Token::Literal(Literal::Number(1.0))])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new("2.0").unwrap().tokens,
|
||||
Vec::from([Token::Literal(Literal::Number(2.0))])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new("\"hello\"").unwrap().tokens,
|
||||
Vec::from([Token::Literal(Literal::String("hello".into()))])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new("\'hello\'").unwrap().tokens,
|
||||
Vec::from([Token::Literal(Literal::String("hello".into()))])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new("hello").unwrap().tokens,
|
||||
Vec::from([Token::Identifier("hello".into())])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new("+").unwrap().tokens,
|
||||
Vec::from([Token::Operator('+')])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new(",").unwrap().tokens,
|
||||
Vec::from([Token::Comma])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new(")").unwrap().tokens,
|
||||
Vec::from([Token::CloseParen])
|
||||
);
|
||||
assert_eq!(
|
||||
Tokenizer::new("(").unwrap().tokens,
|
||||
Vec::from([Token::OpenParen])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_punctuation() {
|
||||
let mut exp = Vec::from([
|
||||
Token::Comma,
|
||||
Token::CloseParen,
|
||||
Token::Comma,
|
||||
Token::OpenParen,
|
||||
]);
|
||||
exp.reverse();
|
||||
|
||||
assert_eq!(Tokenizer::new(", ) , (").unwrap().tokens, exp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_operators() {
|
||||
let mut exp = Vec::from([
|
||||
Token::Operator('+'),
|
||||
Token::Literal(Literal::Integer(2)),
|
||||
Token::Paren(')'),
|
||||
Token::Operator('-'),
|
||||
Token::Operator('*'),
|
||||
Token::Operator('/'),
|
||||
Token::Operator('^'),
|
||||
Token::Operator('!'),
|
||||
Token::Operator('%'),
|
||||
Token::Operator('&'),
|
||||
Token::Operator('|'),
|
||||
]);
|
||||
exp.reverse();
|
||||
|
||||
assert_eq!(Tokenizer::new("+-*/^!%&|").unwrap().tokens, exp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_string() {
|
||||
let raw = "\"hello\" \'world\'";
|
||||
let mut expected: Vec<Token> = vec![
|
||||
Token::Literal(Literal::String("hello".into())),
|
||||
Token::Literal(Literal::String("world".into())),
|
||||
];
|
||||
expected.reverse();
|
||||
let t = Tokenizer::new(&raw).unwrap();
|
||||
assert_eq!(t.tokens, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_number() {
|
||||
let raw = "123 4.56";
|
||||
let mut expected: Vec<Token> = vec![
|
||||
Token::Literal(Literal::Number(123.0)),
|
||||
Token::Literal(Literal::Number(4.56)),
|
||||
];
|
||||
expected.reverse();
|
||||
let t = Tokenizer::new(&raw).unwrap();
|
||||
assert_eq!(t.tokens, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_boolean() {
|
||||
let raw = "false true";
|
||||
let mut expected: Vec<Token> = vec![
|
||||
Token::Literal(Literal::Boolean(false)),
|
||||
Token::Literal(Literal::Boolean(true)),
|
||||
];
|
||||
expected.reverse();
|
||||
let t = Tokenizer::new(&raw).unwrap();
|
||||
assert_eq!(t.tokens, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_identifier() {
|
||||
let raw = "hello test";
|
||||
let mut expected: Vec<Token> = vec![
|
||||
Token::Identifier("hello".to_string()),
|
||||
Token::Identifier("test".to_string()),
|
||||
];
|
||||
expected.reverse();
|
||||
let t = Tokenizer::new(&raw).unwrap();
|
||||
assert_eq!(t.tokens, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_mix() {
|
||||
let raw = "hello test 1.23 this 5 (1+2)";
|
||||
let mut expected: Vec<Token> = vec![
|
||||
Token::Identifier("hello".to_string()),
|
||||
Token::Identifier("test".to_string()),
|
||||
Token::Literal(Literal::Number(1.23)),
|
||||
Token::Identifier("this".to_string()),
|
||||
Token::Literal(Literal::Number(5.0)),
|
||||
Token::OpenParen,
|
||||
Token::Literal(Literal::Number(1.0)),
|
||||
Token::Operator('+'),
|
||||
Token::Literal(Literal::Number(2.0)),
|
||||
Token::CloseParen,
|
||||
];
|
||||
expected.reverse();
|
||||
let t = Tokenizer::new(&raw).unwrap();
|
||||
assert_eq!(t.tokens, expected);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user