From 1b3040659d3155732961c45b0c2e13e39e0b2576 Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 9 Dec 2021 20:16:12 +0800 Subject: [PATCH] ref #65: new formula functions: ISFORMULA, ISLOGICAL and ISREF --- calc.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ calc_test.go | 33 ++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/calc.go b/calc.go index e9ac0c8..ebfa815 100644 --- a/calc.go +++ b/calc.go @@ -449,10 +449,13 @@ type formulaFuncs struct { // ISERR // ISERROR // ISEVEN +// ISFORMULA +// ISLOGICAL // ISNA // ISNONTEXT // ISNUMBER // ISODD +// ISREF // ISTEXT // ISO.CEILING // ISOWEEKNUM @@ -6622,6 +6625,44 @@ func (fn *formulaFuncs) ISEVEN(argsList *list.List) formulaArg { return newStringFormulaArg(result) } +// ISFORMULA function tests if a specified cell contains a formula, and if so, +// returns TRUE; Otherwise, the function returns FALSE. The syntax of the +// function is: +// +// ISFORMULA(reference) +// +func (fn *formulaFuncs) ISFORMULA(argsList *list.List) formulaArg { + if argsList.Len() != 1 { + return newErrorFormulaArg(formulaErrorVALUE, "ISFORMULA requires 1 argument") + } + arg := argsList.Front().Value.(formulaArg) + if arg.cellRefs != nil && arg.cellRefs.Len() == 1 { + ref := arg.cellRefs.Front().Value.(cellRef) + cell, _ := CoordinatesToCellName(ref.Col, ref.Row) + if formula, _ := fn.f.GetCellFormula(ref.Sheet, cell); len(formula) > 0 { + return newBoolFormulaArg(true) + } + } + return newBoolFormulaArg(false) +} + +// ISLOGICAL function tests if a supplied value (or expression) returns a +// logical value (i.e. evaluates to True or False). If so, the function +// returns TRUE; Otherwise, it returns FALSE. The syntax of the function is: +// +// ISLOGICAL(value) +// +func (fn *formulaFuncs) ISLOGICAL(argsList *list.List) formulaArg { + if argsList.Len() != 1 { + return newErrorFormulaArg(formulaErrorVALUE, "ISLOGICAL requires 1 argument") + } + val := argsList.Front().Value.(formulaArg).Value() + if strings.EqualFold("TRUE", val) || strings.EqualFold("FALSE", val) { + return newBoolFormulaArg(true) + } + return newBoolFormulaArg(false) +} + // ISNA function tests if an initial supplied expression (or value) returns // the Excel #N/A Error, and if so, returns TRUE; Otherwise the function // returns FALSE. The syntax of the function is: @@ -6704,6 +6745,23 @@ func (fn *formulaFuncs) ISODD(argsList *list.List) formulaArg { return newStringFormulaArg(result) } +// ISREF function tests if a supplied value is a reference. If so, the +// function returns TRUE; Otherwise it returns FALSE. The syntax of the +// function is: +// +// ISREF(value) +// +func (fn *formulaFuncs) ISREF(argsList *list.List) formulaArg { + if argsList.Len() != 1 { + return newErrorFormulaArg(formulaErrorVALUE, "ISREF requires 1 argument") + } + arg := argsList.Front().Value.(formulaArg) + if arg.cellRanges != nil && arg.cellRanges.Len() > 0 || arg.cellRefs != nil && arg.cellRefs.Len() > 0 { + return newBoolFormulaArg(true) + } + return newBoolFormulaArg(false) +} + // ISTEXT function tests if a supplied value is text, and if so, returns TRUE; // Otherwise, the function returns FALSE. The syntax of the function is: // diff --git a/calc_test.go b/calc_test.go index 1de8fb8..50f8023 100644 --- a/calc_test.go +++ b/calc_test.go @@ -988,6 +988,17 @@ func TestCalcCellValue(t *testing.T) { // ISEVEN "=ISEVEN(A1)": "FALSE", "=ISEVEN(A2)": "TRUE", + // ISFORMULA + "=ISFORMULA(A1)": "FALSE", + "=ISFORMULA(\"A\")": "FALSE", + // ISLOGICAL + "=ISLOGICAL(TRUE)": "TRUE", + "=ISLOGICAL(FALSE)": "TRUE", + "=ISLOGICAL(A1=A2)": "TRUE", + "=ISLOGICAL(\"true\")": "TRUE", + "=ISLOGICAL(\"false\")": "TRUE", + "=ISLOGICAL(A1)": "FALSE", + "=ISLOGICAL(20/5)": "FALSE", // ISNA "=ISNA(A1)": "FALSE", "=ISNA(NA())": "TRUE", @@ -1002,6 +1013,11 @@ func TestCalcCellValue(t *testing.T) { // ISODD "=ISODD(A1)": "TRUE", "=ISODD(A2)": "FALSE", + // ISREF + "=ISREF(B1)": "TRUE", + "=ISREF(B1:B2)": "TRUE", + "=ISREF(\"text\")": "FALSE", + "=ISREF(B1*B2)": "FALSE", // ISTEXT "=ISTEXT(D1)": "TRUE", "=ISTEXT(A1)": "FALSE", @@ -2402,6 +2418,10 @@ func TestCalcCellValue(t *testing.T) { // ISEVEN "=ISEVEN()": "ISEVEN requires 1 argument", `=ISEVEN("text")`: "strconv.Atoi: parsing \"text\": invalid syntax", + // ISFORMULA + "=ISFORMULA()": "ISFORMULA requires 1 argument", + // ISLOGICAL + "=ISLOGICAL()": "ISLOGICAL requires 1 argument", // ISNA "=ISNA()": "ISNA requires 1 argument", // ISNONTEXT @@ -2411,6 +2431,8 @@ func TestCalcCellValue(t *testing.T) { // ISODD "=ISODD()": "ISODD requires 1 argument", `=ISODD("text")`: "strconv.Atoi: parsing \"text\": invalid syntax", + // ISREF + "=ISREF()": "ISREF requires 1 argument", // ISTEXT "=ISTEXT()": "ISTEXT requires 1 argument", // N @@ -3725,6 +3747,17 @@ func TestCalcMATCH(t *testing.T) { assert.Equal(t, newErrorFormulaArg(formulaErrorNA, formulaErrorNA), calcMatch(2, nil, []formulaArg{})) } +func TestCalcISFORMULA(t *testing.T) { + f := NewFile() + assert.NoError(t, f.SetCellFormula("Sheet1", "B1", "=ISFORMULA(A1)")) + for _, formula := range []string{"=NA()", "=SUM(A1:A3)"} { + assert.NoError(t, f.SetCellFormula("Sheet1", "A1", formula)) + result, err := f.CalcCellValue("Sheet1", "B1") + assert.NoError(t, err, formula) + assert.Equal(t, "TRUE", result, formula) + } +} + func TestCalcZTEST(t *testing.T) { f := NewFile() assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]int{4, 5, 2, 5, 8, 9, 3, 2, 3, 8, 9, 5}))