ref #65, new formula functions FORMULATEXT and TYPE

This commit is contained in:
xuri 2022-03-01 00:10:59 +08:00
parent 42a9665aa9
commit 1efa283887
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
2 changed files with 87 additions and 9 deletions

67
calc.go
View File

@ -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) {

View File

@ -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"},