diff --git a/backend/src/evaluator/mod.rs b/backend/src/evaluator/mod.rs index 851d3de..02f2551 100644 --- a/backend/src/evaluator/mod.rs +++ b/backend/src/evaluator/mod.rs @@ -119,7 +119,6 @@ fn evaluate_expr( } Expr::Group(g) => evaluate_expr(g, precs, grid)?, Expr::Function { name, args } => match name.as_str() { - // "AVG" => eval_avg(args, precs, grid)?, "AVG" => eval_numeric_func( args, precs, @@ -128,9 +127,15 @@ fn evaluate_expr( let mut res = 0.0; let mut count = 0; - for num in nums { - res += num; - count += 1; + for eval in nums { + match eval { + Eval::Literal(Literal::Number(num)) => { + res += num; + count += 1; + } + Eval::Unset => {} + _ => unreachable!(), + } } if count == 0 { @@ -144,7 +149,86 @@ fn evaluate_expr( } }, "AVG".into(), - Some(0f64), + )?, + "SUM" => eval_numeric_func( + args, + precs, + grid, + |nums| { + Ok(nums + .iter() + .filter_map(|e| { + if let Eval::Literal(Literal::Number(n)) = e { + Some(n) + } else { + None + } + }) + .sum()) + }, + "SUM".into(), + )?, + "PROD" => eval_numeric_func( + args, + precs, + grid, + |nums| { + Ok(nums + .iter() + .filter_map(|e| { + if let Eval::Literal(Literal::Number(n)) = e { + Some(n) + } else { + None + } + }) + .product()) + }, + "PROD".into(), + )?, + "MAX" => eval_numeric_func( + args, + precs, + grid, + |nums| { + nums.iter() + .filter_map(|e| { + if let Eval::Literal(Literal::Number(n)) = e { + Some(*n) // deref to f64 + } else { + None + } + }) + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .ok_or(LeadErr { + title: "Evaluation error.".into(), + desc: "MAX on empty set.".into(), + code: LeadErrCode::Unsupported, + }) + }, + "MAX".into(), + )?, + "MIN" => eval_numeric_func( + args, + precs, + grid, + |nums| { + nums.iter() + .filter_map(|e| { + if let Eval::Literal(Literal::Number(n)) = e { + Some(*n) // deref to f64 + } else { + None + } + }) + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .ok_or(LeadErr { + title: "Evaluation error.".into(), + desc: "MIN on empty set.".into(), + code: LeadErrCode::Unsupported, + }) + }, + "MIN".into(), )?, "EXP" => eval_single_arg_numeric(args, precs, grid, |x| x.exp(), "EXP".into())?, "SIN" => eval_single_arg_numeric(args, precs, grid, |x| x.sin(), "SIN".into())?, diff --git a/backend/src/evaluator/utils.rs b/backend/src/evaluator/utils.rs index b18da67..21b0983 100644 --- a/backend/src/evaluator/utils.rs +++ b/backend/src/evaluator/utils.rs @@ -75,24 +75,30 @@ pub fn eval_n_arg_numeric( Ok(Eval::Literal(Literal::Number(func(numbers)))) } +// This is a utility function that filters out and error handles all non literal numbers or unset +// eval types and handles ranges pub fn eval_numeric_func( args: &Vec, precs: &mut HashSet, grid: Option<&Grid>, - func: fn(Vec) -> Result, + func: fn(Vec) -> Result, func_name: String, - unset_val: Option, ) -> Result { let mut numeric_args = Vec::new(); for arg in args { - match evaluate_expr(arg, precs, grid)? { - Eval::Literal(Literal::Number(num)) => { - numeric_args.push(num); - } - Eval::Range(range) => { + let eval = evaluate_expr(arg, precs, grid)?; + + if matches!(eval, Eval::Literal(Literal::Number(_)) | Eval::Unset) { + numeric_args.push(eval); + } else if matches!(eval, Eval::Range(_)) { + if let Eval::Range(range) = eval { for cell in range { - let Eval::CellRef { eval, reference: _ } = cell else { + let Eval::CellRef { + eval: eval2, + reference: _, + } = cell + else { return Err(LeadErr { title: "Evaluation error.".into(), desc: format!( @@ -102,18 +108,8 @@ pub fn eval_numeric_func( }); }; - if let Eval::Literal(Literal::Number(num)) = *eval { - numeric_args.push(num); - } else if matches!(*eval, Eval::Unset) { - if let Some(default) = unset_val { - numeric_args.push(default); - } else { - return Err(LeadErr { - title: "Evaluation error.".into(), - desc: format!("{func_name} does not support unset cells."), - code: LeadErrCode::Unsupported, - }); - } + if matches!(*eval2, Eval::Literal(Literal::Number(_)) | Eval::Unset) { + numeric_args.push(*eval2); } else { return Err(LeadErr { title: "Evaluation error.".into(), @@ -123,18 +119,15 @@ pub fn eval_numeric_func( } } } - _ => { - return Err(LeadErr { - title: "Evaluation error.".into(), - desc: format!("Expected numeric types for {func_name} function."), - code: LeadErrCode::Unsupported, - }); - } + } else { + return Err(LeadErr { + title: "Evaluation error.".into(), + desc: format!("Expected numeric types for {func_name} function."), + code: LeadErrCode::Unsupported, + }); } } - match func(numeric_args) { - Ok(res) => Ok(Eval::Literal(Literal::Number(res))), - Err(e) => Err(e), - } + let res = func(numeric_args)?; + Ok(Eval::Literal(Literal::Number(res))) } diff --git a/frontend/src/lib/components/grid/grid.svelte.ts b/frontend/src/lib/components/grid/grid.svelte.ts index 0d9a668..24be023 100644 --- a/frontend/src/lib/components/grid/grid.svelte.ts +++ b/frontend/src/lib/components/grid/grid.svelte.ts @@ -61,21 +61,21 @@ class Grid { public setCell(pos: Position | null | undefined) { if (pos === null || pos === undefined) return; - let data = this.data[pos.key()]; - if (data === undefined) return; + let cell = this.data[pos.key()]; + if (cell === undefined) return; - if (data.temp_raw === '') { + if (cell.temp_raw === '') { delete this.data[pos.key()]; return; } - data.raw = data.temp_raw; - data.eval = data.temp_eval; + cell.raw = cell.temp_raw; + cell.eval = cell.temp_eval; let msg: LeadMsg = { msg_type: 'set', cell: pos.ref(), - raw: data.temp_raw + raw: cell.temp_raw }; this.socket.send(JSON.stringify(msg)); @@ -178,7 +178,6 @@ class Grid { let cell = this.getCell(pos); if (!cell) return; - cell.temp_eval = undefined; } public stopEditing(pos: Position | null | undefined) {