use crate::eval::{evaluate_expr, functions::{check_arity_len, EvalCtx}};
use crate::parser::ast::Expr;
use crate::types::{ErrorKind, Value};
use crate::eval::functions::lookup::cell_ref::{col_index_to_label, parse_cell_ref};
pub fn sheets_fn(args: &[Expr], _ctx: &mut EvalCtx<'_>) -> Value {
match args.len() {
0 => Value::Number(1.0),
1 => {
if matches!(args[0], Expr::Variable(_, _) | Expr::Reference(_, _)) {
Value::Number(1.0)
} else {
Value::Error(ErrorKind::NA)
}
}
_ => Value::Error(ErrorKind::NA),
}
}
pub fn error_type_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
match val {
Value::Error(ErrorKind::Null) => Value::Number(1.0),
Value::Error(ErrorKind::DivByZero) => Value::Number(2.0),
Value::Error(ErrorKind::Value) => Value::Number(3.0),
Value::Error(ErrorKind::Ref) => Value::Number(4.0),
Value::Error(ErrorKind::Name) => Value::Number(5.0),
Value::Error(ErrorKind::Num) => Value::Number(6.0),
Value::Error(ErrorKind::NA) => Value::Number(7.0),
_ => Value::Error(ErrorKind::NA),
}
}
pub fn n_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
match val {
Value::Number(n) | Value::Date(n) => Value::Number(n),
Value::Bool(b) => Value::Number(if b { 1.0 } else { 0.0 }),
Value::Empty | Value::Text(_) | Value::Array(_) => Value::Number(0.0),
Value::Error(_) => val,
}
}
pub fn type_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if let Some(err) = check_arity_len(args.len(), 1, 1) {
return err;
}
let val = evaluate_expr(&args[0], ctx);
let code = match val {
Value::Number(_) | Value::Date(_) => 1.0,
Value::Text(_) => 2.0,
Value::Bool(_) => 4.0,
Value::Error(_) => 16.0,
Value::Array(_) => 64.0,
Value::Empty => 1.0, };
Value::Number(code)
}
fn extract_ref_address(expr: &Expr) -> Option<&str> {
match expr {
Expr::Variable(name, _) => Some(name.as_str()),
Expr::FunctionCall { name, args, .. } if name.eq_ignore_ascii_case("INDIRECT") => {
if !args.is_empty() {
if let Expr::Text(s, _) = &args[0] {
return Some(s.as_str());
}
}
None
}
_ => None,
}
}
pub fn cell_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if args.is_empty() || args.len() > 2 {
return Value::Error(ErrorKind::NA);
}
let info_type_val = evaluate_expr(&args[0], ctx);
let info_type = match &info_type_val {
Value::Text(s) => s.to_ascii_lowercase(),
Value::Error(e) => return Value::Error(e.clone()),
_ => return Value::Error(ErrorKind::NA),
};
if args.len() < 2 {
return Value::Error(ErrorKind::NA);
}
let ref_expr = &args[1];
match info_type.as_str() {
"type" => {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Empty => Value::Text("l".to_string()),
Value::Text(_) => Value::Text("l".to_string()),
Value::Number(_) | Value::Date(_) => Value::Text("n".to_string()),
Value::Bool(_) => Value::Text("l".to_string()),
Value::Error(e) => Value::Error(e),
Value::Array(_) => Value::Error(ErrorKind::NA),
}
}
"col" => {
match extract_ref_address(ref_expr) {
Some(addr) => {
if let Some((col, _row)) = parse_cell_ref(addr) {
Value::Number(col as f64)
} else {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
_ => Value::Error(ErrorKind::NA),
}
}
}
None => {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
_ => Value::Error(ErrorKind::NA),
}
}
}
}
"row" => {
match extract_ref_address(ref_expr) {
Some(addr) => {
if let Some((_col, row)) = parse_cell_ref(addr) {
Value::Number(row as f64)
} else {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
_ => Value::Error(ErrorKind::NA),
}
}
}
None => {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
_ => Value::Error(ErrorKind::NA),
}
}
}
}
"address" => {
match extract_ref_address(ref_expr) {
Some(addr) => {
if let Some((col, row)) = parse_cell_ref(addr) {
let col_label = col_index_to_label(col);
Value::Text(format!("${col_label}${row}"))
} else {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
_ => Value::Error(ErrorKind::NA),
}
}
}
None => {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
_ => Value::Error(ErrorKind::NA),
}
}
}
}
"contents" => {
if extract_ref_address(ref_expr).is_some() {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
other => other,
}
} else {
let val = evaluate_expr(ref_expr, ctx);
match val {
Value::Error(e) => Value::Error(e),
_ => Value::Error(ErrorKind::NA),
}
}
}
_ => Value::Error(ErrorKind::NA),
}
}
#[cfg(test)]
mod tests;