summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2022-03-01 00:10:59 +0800
committerxuri <xuri.me@gmail.com>2022-03-01 00:10:59 +0800
commit1efa2838875efe06a9a4b7b1d582f70205de3aa5 (patch)
tree9d60fb542a424b65c3f0eaf76fd3a23317446300
parent42a9665aa91912f570bb83052b20cc50529d7b6a (diff)
ref #65, new formula functions FORMULATEXT and TYPE
-rw-r--r--calc.go67
-rw-r--r--calc_test.go29
2 files changed, 87 insertions, 9 deletions
diff --git a/calc.go b/calc.go
index 8358d33..0f44912 100644
--- a/calc.go
+++ b/calc.go
@@ -416,6 +416,7 @@ type formulaFuncs struct {
// FLOOR
// FLOOR.MATH
// FLOOR.PRECISE
+// FORMULATEXT
// FV
// FVSCHEDULE
// GAMMA
@@ -615,6 +616,7 @@ type formulaFuncs struct {
// TRIMMEAN
// TRUE
// TRUNC
+// TYPE
// UNICHAR
// UNICODE
// UPPER
@@ -6874,7 +6876,7 @@ func (fn *formulaFuncs) ISERR(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "ISERR requires 1 argument")
}
token := argsList.Front().Value.(formulaArg)
- result := "FALSE"
+ result := false
if token.Type == ArgError {
for _, errType := range []string{
formulaErrorDIV, formulaErrorNAME, formulaErrorNUM,
@@ -6882,11 +6884,11 @@ func (fn *formulaFuncs) ISERR(argsList *list.List) formulaArg {
formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA,
} {
if errType == token.String {
- result = "TRUE"
+ result = true
}
}
}
- return newStringFormulaArg(result)
+ return newBoolFormulaArg(result)
}
// ISERROR function tests if an initial supplied expression (or value) returns
@@ -6900,7 +6902,7 @@ func (fn *formulaFuncs) ISERROR(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "ISERROR requires 1 argument")
}
token := argsList.Front().Value.(formulaArg)
- result := "FALSE"
+ result := false
if token.Type == ArgError {
for _, errType := range []string{
formulaErrorDIV, formulaErrorNAME, formulaErrorNA, formulaErrorNUM,
@@ -6908,11 +6910,11 @@ func (fn *formulaFuncs) ISERROR(argsList *list.List) formulaArg {
formulaErrorCALC, formulaErrorGETTINGDATA,
} {
if errType == token.String {
- result = "TRUE"
+ result = true
}
}
}
- return newStringFormulaArg(result)
+ return newBoolFormulaArg(result)
}
// ISEVEN function tests if a supplied number (or numeric expression)
@@ -7190,6 +7192,32 @@ func (fn *formulaFuncs) SHEETS(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
}
+// TYPE function returns an integer that represents the value's data type. The
+// syntax of the function is:
+//
+// TYPE(value)
+//
+func (fn *formulaFuncs) TYPE(argsList *list.List) formulaArg {
+ if argsList.Len() != 1 {
+ return newErrorFormulaArg(formulaErrorVALUE, "TYPE requires 1 argument")
+ }
+ token := argsList.Front().Value.(formulaArg)
+ switch token.Type {
+ case ArgError:
+ return newNumberFormulaArg(16)
+ case ArgMatrix:
+ return newNumberFormulaArg(64)
+ default:
+ if arg := token.ToNumber(); arg.Type != ArgError || len(token.Value()) == 0 {
+ return newNumberFormulaArg(1)
+ }
+ if arg := token.ToBool(); arg.Type != ArgError {
+ return newNumberFormulaArg(4)
+ }
+ return newNumberFormulaArg(2)
+ }
+}
+
// T function tests if a supplied value is text and if so, returns the
// supplied text; Otherwise, the function returns an empty text string. The
// syntax of the function is:
@@ -7343,7 +7371,6 @@ func (fn *formulaFuncs) NOT(argsList *list.List) formulaArg {
case ArgNumber:
return newBoolFormulaArg(!(token.Number != 0))
case ArgError:
-
return token
}
return newErrorFormulaArg(formulaErrorVALUE, "NOT expects 1 boolean or numeric argument")
@@ -9415,6 +9442,32 @@ func (fn *formulaFuncs) COLUMNS(argsList *list.List) formulaArg {
return newNumberFormulaArg(float64(result))
}
+// FORMULATEXT function returns a formula as a text string. The syntax of the
+// function is:
+//
+// FORMULATEXT(reference)
+//
+func (fn *formulaFuncs) FORMULATEXT(argsList *list.List) formulaArg {
+ if argsList.Len() != 1 {
+ return newErrorFormulaArg(formulaErrorVALUE, "FORMULATEXT requires 1 argument")
+ }
+ refs := argsList.Front().Value.(formulaArg).cellRefs
+ col, row := 0, 0
+ if refs != nil && refs.Len() > 0 {
+ col, row = refs.Front().Value.(cellRef).Col, refs.Front().Value.(cellRef).Row
+ }
+ ranges := argsList.Front().Value.(formulaArg).cellRanges
+ if ranges != nil && ranges.Len() > 0 {
+ col, row = ranges.Front().Value.(cellRange).From.Col, ranges.Front().Value.(cellRange).From.Row
+ }
+ cell, err := CoordinatesToCellName(col, row)
+ if err != nil {
+ return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+ }
+ formula, _ := fn.f.GetCellFormula(fn.sheet, cell)
+ return newStringFormulaArg(formula)
+}
+
// checkHVLookupArgs checking arguments, prepare extract mode, lookup value,
// and data for the formula functions HLOOKUP and VLOOKUP.
func checkHVLookupArgs(name string, argsList *list.List) (idx int, lookupValue, tableArray, matchMode, errArg formulaArg) {
diff --git a/calc_test.go b/calc_test.go
index e620eb3..d01f31b 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -1005,8 +1005,9 @@ func TestCalcCellValue(t *testing.T) {
"=ISERR(NA())": "FALSE",
"=ISERR(POWER(0,-1)))": "TRUE",
// ISERROR
- "=ISERROR(A1)": "FALSE",
- "=ISERROR(NA())": "TRUE",
+ "=ISERROR(A1)": "FALSE",
+ "=ISERROR(NA())": "TRUE",
+ "=ISERROR(\"#VALUE!\")": "FALSE",
// ISEVEN
"=ISEVEN(A1)": "FALSE",
"=ISEVEN(A2)": "TRUE",
@@ -1055,6 +1056,14 @@ func TestCalcCellValue(t *testing.T) {
// SHEETS
"=SHEETS()": "1",
"=SHEETS(A1)": "1",
+ // TYPE
+ "=TYPE(2)": "1",
+ "=TYPE(10/2)": "1",
+ "=TYPE(C1)": "1",
+ "=TYPE(\"text\")": "2",
+ "=TYPE(TRUE)": "4",
+ "=TYPE(NA())": "16",
+ "=TYPE(MUNIT(2))": "64",
// T
"=T(\"text\")": "text",
"=T(N(10))": "",
@@ -2536,6 +2545,8 @@ func TestCalcCellValue(t *testing.T) {
// SHEETS
"=SHEETS(\"\",\"\")": "SHEETS accepts at most 1 argument",
"=SHEETS(\"Sheet1\")": "#N/A",
+ // TYPE
+ "=TYPE()": "TYPE requires 1 argument",
// T
"=T()": "T requires 1 argument",
"=T(NA())": "#N/A",
@@ -2839,6 +2850,9 @@ func TestCalcCellValue(t *testing.T) {
"=COLUMNS(Sheet1)": newInvalidColumnNameError("Sheet1").Error(),
"=COLUMNS(Sheet1!A1!B1)": newInvalidColumnNameError("Sheet1").Error(),
"=COLUMNS(Sheet1!Sheet1)": newInvalidColumnNameError("Sheet1").Error(),
+ // FORMULATEXT
+ "=FORMULATEXT()": "FORMULATEXT requires 1 argument",
+ "=FORMULATEXT(1)": "#VALUE!",
// HLOOKUP
"=HLOOKUP()": "HLOOKUP requires at least 3 arguments",
"=HLOOKUP(D2,D1,1,FALSE)": "HLOOKUP requires second argument of table array",
@@ -3693,6 +3707,17 @@ func TestCalcAVERAGEIF(t *testing.T) {
}
}
+func TestCalcFORMULATEXT(t *testing.T) {
+ f, formulaText := NewFile(), "=SUM(B1:C1)"
+ assert.NoError(t, f.SetCellFormula("Sheet1", "A1", formulaText))
+ for _, formula := range []string{"=FORMULATEXT(A1)", "=FORMULATEXT(A:A)", "=FORMULATEXT(A1:B1)"} {
+ assert.NoError(t, f.SetCellFormula("Sheet1", "D1", formula), formula)
+ result, err := f.CalcCellValue("Sheet1", "D1")
+ assert.NoError(t, err, formula)
+ assert.Equal(t, formulaText, result, formula)
+ }
+}
+
func TestCalcHLOOKUP(t *testing.T) {
cellData := [][]interface{}{
{"Example Result Table"},