///|
fn eval_transpose(call : EvalCall) -> FormulaValue raise XlsxError {
match call.args {
[a0] => {
let base_range = range_from_expr_or_value(
call.workbook,
call.sheet_name,
a0,
call.values[0],
call.ctx,
)
List(transpose_range_values(base_range).values)
}
_ => Error(formula_error_value)
}
}
///|
fn eval_take(call : EvalCall) -> FormulaValue raise XlsxError {
take_values(call.workbook, call.sheet_name, call.args, call.values, call.ctx)
}
///|
fn eval_drop(call : EvalCall) -> FormulaValue raise XlsxError {
drop_values(call.workbook, call.sheet_name, call.args, call.values, call.ctx)
}
///|
fn eval_choosecols(call : EvalCall) -> FormulaValue raise XlsxError {
choosecols_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
)
}
///|
fn eval_chooserows(call : EvalCall) -> FormulaValue raise XlsxError {
chooserows_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
)
}
///|
fn eval_hstack(call : EvalCall) -> FormulaValue raise XlsxError {
stack_from_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
true,
)
}
///|
fn eval_vstack(call : EvalCall) -> FormulaValue raise XlsxError {
stack_from_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
false,
)
}
///|
fn eval_expand(call : EvalCall) -> FormulaValue raise XlsxError {
expand_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
)
}
///|
fn eval_wraprows(call : EvalCall) -> FormulaValue raise XlsxError {
wrap_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
true,
)
}
///|
fn eval_wrapcols(call : EvalCall) -> FormulaValue raise XlsxError {
wrap_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
false,
)
}
///|
fn eval_tocol(call : EvalCall) -> FormulaValue raise XlsxError {
tocol_torow_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
false,
)
}
///|
fn eval_torow(call : EvalCall) -> FormulaValue raise XlsxError {
tocol_torow_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
false,
)
}
///|
fn eval_sort(call : EvalCall) -> FormulaValue raise XlsxError {
sort_values(call.workbook, call.sheet_name, call.args, call.values, call.ctx)
}
///|
fn eval_sortby(call : EvalCall) -> FormulaValue raise XlsxError {
sortby_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
)
}
///|
fn eval_filter(call : EvalCall) -> FormulaValue raise XlsxError {
filter_values(
call.workbook,
call.sheet_name,
call.args,
call.values,
call.ctx,
)
}
///|
fn eval_unique(call : EvalCall) -> FormulaValue raise XlsxError {
if call.args.length() >= 1 && call.args.length() <= 3 {
let by_col = if call.args.length() >= 2 {
match value_as_bool(call.values[1]) {
Ok(flag) => flag
Err(err) => return err
}
} else {
false
}
let exactly_once = if call.args.length() == 3 {
match value_as_bool(call.values[2]) {
Ok(flag) => flag
Err(err) => return err
}
} else {
false
}
let range = match call.args[0] {
Range(_, _, _) | Cell(_, _) =>
eval_range_expr(call.workbook, call.sheet_name, call.args[0], call.ctx)
_ =>
match call.values[0] {
Error(err) => return Error(err)
List(list) =>
if list.length() == 0 {
return Error(formula_error_value)
} else {
{ rows: 1, cols: list.length(), values: list, }
}
_ => { rows: 1, cols: 1, values: [call.values[0]], }
}
}
match unique_range_values(range, by_col, exactly_once) {
Ok(result) => List(result.values)
Err(err) => err
}
} else {
Error(formula_error_value)
}
}
///|
fn eval_index(call : EvalCall) -> FormulaValue raise XlsxError {
match call.args {
[a0, _] | [a0, _, _] => {
let range = eval_range_expr(call.workbook, call.sheet_name, a0, call.ctx)
let row_num = match value_as_int(call.values[1]) {
Ok(num) => num
Err(err) => return err
}
let col_num = if call.args is [_, _, _] {
match value_as_int(call.values[2]) {
Ok(num) => num
Err(err) => return err
}
} else {
1
}
if row_num <= 0 || col_num <= 0 {
Error(formula_error_value)
} else if row_num > range.rows || col_num > range.cols {
Error(formula_error_ref)
} else {
let index = (row_num - 1) * range.cols + (col_num - 1)
range.values[index]
}
}
_ => Error(formula_error_value)
}
}
///|
fn eval_match(call : EvalCall) -> FormulaValue raise XlsxError {
match call.args {
[_, a1] | [_, a1, _] => {
let lookup = call.values[0]
let range = eval_range_expr(call.workbook, call.sheet_name, a1, call.ctx)
let match_type = if call.args is [_, _, _] {
match value_as_int(call.values[2]) {
Ok(num) => num
Err(err) => return err
}
} else {
1
}
match_range_value(lookup, range, match_type)
}
_ => Error(formula_error_value)
}
}
///|
fn eval_vlookup(call : EvalCall) -> FormulaValue raise XlsxError {
match call.args {
[_, a1, _] | [_, a1, _, _] => {
let lookup = call.values[0]
let table = eval_range_expr(call.workbook, call.sheet_name, a1, call.ctx)
let col_index = match value_as_int(call.values[2]) {
Ok(num) => num
Err(err) => return err
}
let range_lookup = if call.args is [_, _, _, _] {
match lookup_range_flag(call.values[3]) {
Ok(flag) => flag
Err(err) => return err
}
} else {
true
}
vlookup_value(lookup, table, col_index, range_lookup)
}
_ => Error(formula_error_value)
}
}
///|
fn eval_hlookup(call : EvalCall) -> FormulaValue raise XlsxError {
match call.args {
[_, a1, _] | [_, a1, _, _] => {
let lookup = call.values[0]
let table = eval_range_expr(call.workbook, call.sheet_name, a1, call.ctx)
let row_index = match value_as_int(call.values[2]) {
Ok(num) => num
Err(err) => return err
}
let range_lookup = if call.args is [_, _, _, _] {
match lookup_range_flag(call.values[3]) {
Ok(flag) => flag
Err(err) => return err
}
} else {
true
}
hlookup_value(lookup, table, row_index, range_lookup)
}
_ => Error(formula_error_value)
}
}
///|
fn eval_lookup(call : EvalCall) -> FormulaValue raise XlsxError {
if call.args.length() == 2 || call.args.length() == 3 {
let lookup = call.values[0]
if call.args.length() == 2 {
let array_range = range_from_expr_or_value(
call.workbook,
call.sheet_name,
call.args[1],
call.values[1],
call.ctx,
)
if array_range.rows == 0 || array_range.cols == 0 {
return Error(formula_error_value)
}
if array_range.rows == 1 || array_range.cols == 1 {
let lookup_vec = if array_range.rows == 1 {
range_row_required(array_range, 0)
} else {
range_column_required(array_range, 0)
}
let idx = match lookup_best_index(lookup, lookup_vec) {
Ok(value) => value
Err(err) => return err
}
match idx {
Some(i) => lookup_vec[i]
None => Error(formula_error_na)
}
} else {
let use_cols = array_range.rows >= array_range.cols
let lookup_list = if use_cols {
range_column_required(array_range, 0)
} else {
range_row_required(array_range, 0)
}
let result_list = if use_cols {
range_column_required(array_range, array_range.cols - 1)
} else {
range_row_required(array_range, array_range.rows - 1)
}
let idx = match lookup_best_index(lookup, lookup_list) {
Ok(value) => value
Err(err) => return err
}
match idx {
Some(i) => result_list[i]
None => Error(formula_error_na)
}
}
} else {
let lookup_range = range_from_expr_or_value(
call.workbook,
call.sheet_name,
call.args[1],
call.values[1],
call.ctx,
)
let result_range = range_from_expr_or_value(
call.workbook,
call.sheet_name,
call.args[2],
call.values[2],
call.ctx,
)
if lookup_range.rows == 0 ||
lookup_range.cols == 0 ||
result_range.rows == 0 ||
result_range.cols == 0 {
return Error(formula_error_value)
}
let lookup_list = match range_vector(lookup_range) {
Some(list) => list
None => return Error(formula_error_na)
}
let result_list = match range_vector(result_range) {
Some(list) => list
None => return Error(formula_error_na)
}
if lookup_list.length() != result_list.length() {
return Error(formula_error_na)
}
let idx = match lookup_best_index(lookup, lookup_list) {
Ok(value) => value
Err(err) => return err
}
match idx {
Some(i) => result_list[i]
None => Error(formula_error_na)
}
}
} else {
Error(formula_error_value)
}
}
///|
fn eval_xlookup(call : EvalCall) -> FormulaValue raise XlsxError {
if call.args.length() >= 3 && call.args.length() <= 6 {
let lookup = call.values[0]
let lookup_range = range_from_expr_or_value(
call.workbook,
call.sheet_name,
call.args[1],
call.values[1],
call.ctx,
)
let return_range = range_from_expr_or_value(
call.workbook,
call.sheet_name,
call.args[2],
call.values[2],
call.ctx,
)
let if_not_found = if call.args.length() >= 4 {
call.values[3]
} else {
Error(formula_error_na)
}
let match_mode = if call.args.length() >= 5 {
match value_as_int(call.values[4]) {
Ok(num) => num
Err(err) => return err
}
} else {
0
}
let search_mode = if call.args.length() >= 6 {
match value_as_int(call.values[5]) {
Ok(num) => num
Err(err) => return err
}
} else {
1
}
if match_mode != -1 && match_mode != 0 && match_mode != 1 && match_mode != 2 {
return Error(formula_error_value)
}
if search_mode != 1 &&
search_mode != -1 &&
search_mode != 2 &&
search_mode != -2 {
return Error(formula_error_value)
}
let is_row = lookup_range.rows == 1
let is_col = lookup_range.cols == 1
if !is_row && !is_col {
return Error(formula_error_value)
}
let lookup_values = if is_row {
range_row_required(lookup_range, 0)
} else {
range_column_required(lookup_range, 0)
}
let match_idx = match
xlookup_find_index(lookup, lookup_values, match_mode, search_mode) {
Ok(value) => value
Err(err) => return err
}
let idx = match match_idx {
Some(value) => value
None => return if_not_found
}
if is_row {
if return_range.cols != lookup_range.cols {
return Error(formula_error_value)
}
if return_range.rows == 1 {
return_range.values[idx]
} else {
List(range_column_required(return_range, idx))
}
} else {
if return_range.rows != lookup_range.rows {
return Error(formula_error_value)
}
if return_range.cols == 1 {
return_range.values[idx * return_range.cols]
} else {
List(range_row_required(return_range, idx))
}
}
} else {
Error(formula_error_value)
}
}
///|
fn eval_anchorarray(call : EvalCall) -> FormulaValue {
anchorarray_values(call.workbook, call.sheet_name, call.args, call.values)
}
///|
fn eval_address(call : EvalCall) -> FormulaValue raise XlsxError {
if call.values.length() >= 2 && call.values.length() <= 5 {
let row_num = match value_as_int(call.values[0]) {
Ok(num) => num
Err(err) => return err
}
let col_num = match value_as_int(call.values[1]) {
Ok(num) => num
Err(err) => return err
}
if row_num <= 0 ||
row_num > cell_ref_max_rows ||
col_num <= 0 ||
col_num > cell_ref_max_cols {
return Error(formula_error_value)
}
let abs_num = if call.values.length() >= 3 {
match value_as_int(call.values[2]) {
Ok(num) => num
Err(err) => return err
}
} else {
1
}
if abs_num < 1 || abs_num > 4 {
return Error(formula_error_value)
}
let a1 = if call.values.length() >= 4 {
match value_as_bool(call.values[3]) {
Ok(flag) => flag
Err(err) => return err
}
} else {
true
}
let sheet_text = if call.values.length() == 5 {
match value_as_string(call.values[4]) {
Ok(text) => text
Err(err) => return err
}
} else {
""
}
let address = if a1 {
address_a1(row_num, col_num, abs_num)
} else {
address_r1c1(row_num, col_num, abs_num)
}
let text = if sheet_text == "" {
address
} else {
sheet_text + "!" + address
}
String(text)
} else {
Error(formula_error_value)
}
}
///|
fn eval_indirect(call : EvalCall) -> FormulaValue raise XlsxError {
match call.values {
[v0] | [v0, _] => {
let ref_text = match value_as_string(v0) {
Ok(text) => text
Err(err) => return err
}
let a1 = if call.values is [_, v1] {
match value_as_bool(v1) {
Ok(flag) => flag
Err(err) => return err
}
} else {
true
}
let (sheet_text, ref_part) = split_sheet_ref(ref_text)
let target_sheet = if sheet_text == "" {
call.sheet_name
} else {
sheet_text
}
if a1 {
match ref_part.find(":") {
Some(idx) => {
let start_token = ref_part.unsafe_substring(start=0, end=idx)
let end_token = ref_part.unsafe_substring(
start=idx + 1,
end=ref_part.length(),
)
let (start_ref, _start_end) = parse_range_ref_token(start_token) catch {
_ => return Error(formula_error_ref)
}
let (_end_start, end_ref) = parse_range_ref_token(end_token) catch {
_ => return Error(formula_error_ref)
}
List(
collect_range_values(
call.workbook,
target_sheet,
start_ref,
end_ref,
call.ctx,
),
)
}
None =>
match parse_cell_ref_token(ref_part) {
Some(reference) =>
resolve_cell_value(
call.workbook,
target_sheet,
reference,
call.ctx,
)
None => {
let (start_ref, end_ref) = parse_range_ref_token(ref_part) catch {
_ => return Error(formula_error_ref)
}
List(
collect_range_values(
call.workbook,
target_sheet,
start_ref,
end_ref,
call.ctx,
),
)
}
}
}
} else {
match ref_part.find(":") {
Some(idx) => {
let start_token = ref_part.unsafe_substring(start=0, end=idx)
let end_token = ref_part.unsafe_substring(
start=idx + 1,
end=ref_part.length(),
)
let (row1, col1) = match parse_r1c1_token(start_token) {
Some(value) => value
None => return Error(formula_error_ref)
}
let (row2, col2) = match parse_r1c1_token(end_token) {
Some(value) => value
None => return Error(formula_error_ref)
}
let start_ref = cell_ref_from(row1, col1) catch {
_ => return Error(formula_error_ref)
}
let end_ref = cell_ref_from(row2, col2) catch {
_ => return Error(formula_error_ref)
}
List(
collect_range_values(
call.workbook,
target_sheet,
start_ref,
end_ref,
call.ctx,
),
)
}
None =>
match parse_r1c1_token(ref_part) {
Some((row, col)) => {
let reference = cell_ref_from(row, col) catch {
_ => return Error(formula_error_ref)
}
resolve_cell_value(
call.workbook,
target_sheet,
reference,
call.ctx,
)
}
None => Error(formula_error_ref)
}
}
}
}
_ => Error(formula_error_value)
}
}
///|
fn eval_hyperlink(call : EvalCall) -> FormulaValue {
if call.values.length() == 1 || call.values.length() == 2 {
match value_as_string(call.values[call.values.length() - 1]) {
Ok(text) => String(text)
Err(err) => err
}
} else {
Error(formula_error_value)
}
}
///|
fn eval_choose(call : EvalCall) -> FormulaValue {
if call.values.length() >= 2 {
let index_text = match value_as_string(call.values[0]) {
Ok(text) => text
Err(err) => return err
}
let idx = @string.parse_int(index_text, base=10) catch {
_ => return Error(formula_error_value)
}
if call.values.length() <= idx {
Error(formula_error_value)
} else if idx <= 0 {
call.values[0]
} else {
call.values[idx]
}
} else {
Error(formula_error_value)
}
}