This commit is contained in:
2025-09-15 02:03:01 +10:00
parent 2470f402e9
commit 482f0120df
3 changed files with 119 additions and 43 deletions

View File

@@ -119,7 +119,6 @@ fn evaluate_expr(
} }
Expr::Group(g) => evaluate_expr(g, precs, grid)?, Expr::Group(g) => evaluate_expr(g, precs, grid)?,
Expr::Function { name, args } => match name.as_str() { Expr::Function { name, args } => match name.as_str() {
// "AVG" => eval_avg(args, precs, grid)?,
"AVG" => eval_numeric_func( "AVG" => eval_numeric_func(
args, args,
precs, precs,
@@ -128,10 +127,16 @@ fn evaluate_expr(
let mut res = 0.0; let mut res = 0.0;
let mut count = 0; let mut count = 0;
for num in nums { for eval in nums {
match eval {
Eval::Literal(Literal::Number(num)) => {
res += num; res += num;
count += 1; count += 1;
} }
Eval::Unset => {}
_ => unreachable!(),
}
}
if count == 0 { if count == 0 {
Err(LeadErr { Err(LeadErr {
@@ -144,7 +149,86 @@ fn evaluate_expr(
} }
}, },
"AVG".into(), "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())?, "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())?, "SIN" => eval_single_arg_numeric(args, precs, grid, |x| x.sin(), "SIN".into())?,

View File

@@ -75,24 +75,30 @@ pub fn eval_n_arg_numeric(
Ok(Eval::Literal(Literal::Number(func(numbers)))) 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( pub fn eval_numeric_func(
args: &Vec<Expr>, args: &Vec<Expr>,
precs: &mut HashSet<CellRef>, precs: &mut HashSet<CellRef>,
grid: Option<&Grid>, grid: Option<&Grid>,
func: fn(Vec<f64>) -> Result<f64, LeadErr>, func: fn(Vec<Eval>) -> Result<f64, LeadErr>,
func_name: String, func_name: String,
unset_val: Option<f64>,
) -> Result<Eval, LeadErr> { ) -> Result<Eval, LeadErr> {
let mut numeric_args = Vec::new(); let mut numeric_args = Vec::new();
for arg in args { for arg in args {
match evaluate_expr(arg, precs, grid)? { let eval = evaluate_expr(arg, precs, grid)?;
Eval::Literal(Literal::Number(num)) => {
numeric_args.push(num); if matches!(eval, Eval::Literal(Literal::Number(_)) | Eval::Unset) {
} numeric_args.push(eval);
Eval::Range(range) => { } else if matches!(eval, Eval::Range(_)) {
if let Eval::Range(range) = eval {
for cell in range { for cell in range {
let Eval::CellRef { eval, reference: _ } = cell else { let Eval::CellRef {
eval: eval2,
reference: _,
} = cell
else {
return Err(LeadErr { return Err(LeadErr {
title: "Evaluation error.".into(), title: "Evaluation error.".into(),
desc: format!( desc: format!(
@@ -102,18 +108,8 @@ pub fn eval_numeric_func(
}); });
}; };
if let Eval::Literal(Literal::Number(num)) = *eval { if matches!(*eval2, Eval::Literal(Literal::Number(_)) | Eval::Unset) {
numeric_args.push(num); numeric_args.push(*eval2);
} 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,
});
}
} else { } else {
return Err(LeadErr { return Err(LeadErr {
title: "Evaluation error.".into(), title: "Evaluation error.".into(),
@@ -123,7 +119,7 @@ pub fn eval_numeric_func(
} }
} }
} }
_ => { } else {
return Err(LeadErr { return Err(LeadErr {
title: "Evaluation error.".into(), title: "Evaluation error.".into(),
desc: format!("Expected numeric types for {func_name} function."), desc: format!("Expected numeric types for {func_name} function."),
@@ -131,10 +127,7 @@ pub fn eval_numeric_func(
}); });
} }
} }
}
match func(numeric_args) { let res = func(numeric_args)?;
Ok(res) => Ok(Eval::Literal(Literal::Number(res))), Ok(Eval::Literal(Literal::Number(res)))
Err(e) => Err(e),
}
} }

View File

@@ -61,21 +61,21 @@ class Grid {
public setCell(pos: Position | null | undefined) { public setCell(pos: Position | null | undefined) {
if (pos === null || pos === undefined) return; if (pos === null || pos === undefined) return;
let data = this.data[pos.key()]; let cell = this.data[pos.key()];
if (data === undefined) return; if (cell === undefined) return;
if (data.temp_raw === '') { if (cell.temp_raw === '') {
delete this.data[pos.key()]; delete this.data[pos.key()];
return; return;
} }
data.raw = data.temp_raw; cell.raw = cell.temp_raw;
data.eval = data.temp_eval; cell.eval = cell.temp_eval;
let msg: LeadMsg = { let msg: LeadMsg = {
msg_type: 'set', msg_type: 'set',
cell: pos.ref(), cell: pos.ref(),
raw: data.temp_raw raw: cell.temp_raw
}; };
this.socket.send(JSON.stringify(msg)); this.socket.send(JSON.stringify(msg));
@@ -178,7 +178,6 @@ class Grid {
let cell = this.getCell(pos); let cell = this.getCell(pos);
if (!cell) return; if (!cell) return;
cell.temp_eval = undefined;
} }
public stopEditing(pos: Position | null | undefined) { public stopEditing(pos: Position | null | undefined) {