diff --git a/calc.go b/calc.go index 5b975f5..0a65328 100644 --- a/calc.go +++ b/calc.go @@ -88,15 +88,36 @@ type ArgType byte // Formula argument types enumeration. const ( ArgUnknown ArgType = iota + ArgNumber ArgString + ArgList ArgMatrix + ArgError + ArgEmpty ) // formulaArg is the argument of a formula or function. type formulaArg struct { - String string - Matrix [][]formulaArg - Type ArgType + Number float64 + String string + List []formulaArg + Matrix [][]formulaArg + Boolean bool + Error string + Type ArgType +} + +// Value returns a string data type of the formula argument. +func (fa formulaArg) Value() (value string) { + switch fa.Type { + case ArgNumber: + return fmt.Sprintf("%g", fa.Number) + case ArgString: + return fa.String + case ArgError: + return fa.Error + } + return } // formulaFuncs is the type of the formula functions. @@ -124,15 +145,94 @@ var tokenPriority = map[string]int{ // // Supported formulas: // -// ABS, ACOS, ACOSH, ACOT, ACOTH, AND, ARABIC, ASIN, ASINH, ATAN2, ATANH, -// BASE, CEILING, CEILING.MATH, CEILING.PRECISE, CLEAN, COMBIN, COMBINA, -// COS, COSH, COT, COTH, COUNTA, CSC, CSCH, DATE, DECIMAL, DEGREES, EVEN, -// EXP, FACT, FACTDOUBLE, FLOOR, FLOOR.MATH, FLOOR.PRECISE, GCD, INT, -// ISBLANK, ISERR, ISERROR, ISEVEN, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, -// ISODD, LCM, LN, LOG, LOG10, LOWER, MDETERM, MEDIAN, MOD, MROUND, -// MULTINOMIAL, MUNIT, NA, ODD, OR, PI, POWER, PRODUCT, PROPER, QUOTIENT, -// RADIANS, RAND, RANDBETWEEN, ROUND, ROUNDDOWN, ROUNDUP, SEC, SECH, SIGN, -// SIN, SINH, SQRT, SQRTPI, SUM, SUMIF, SUMSQ, TAN, TANH, TRIM, TRUNC, +// ABS +// ACOS +// ACOSH +// ACOT +// ACOTH +// AND +// ARABIC +// ASIN +// ASINH +// ATAN2 +// ATANH +// BASE +// CEILING +// CEILING.MATH +// CEILING.PRECISE +// CHOOSE +// CLEAN +// COMBIN +// COMBINA +// COS +// COSH +// COT +// COTH +// COUNTA +// CSC +// CSCH +// DATE +// DECIMAL +// DEGREES +// EVEN +// EXP +// FACT +// FACTDOUBLE +// FLOOR +// FLOOR.MATH +// FLOOR.PRECISE +// GCD +// IF +// INT +// ISBLANK +// ISERR +// ISERROR +// ISEVEN +// ISNA +// ISNONTEXT +// ISNUMBER +// ISODD +// ISO.CEILING +// LCM +// LEN +// LN +// LOG +// LOG10 +// LOWER +// MDETERM +// MEDIAN +// MOD +// MROUND +// MULTINOMIAL +// MUNIT +// NA +// ODD +// OR +// PI +// POWER +// PRODUCT +// PROPER +// QUOTIENT +// RADIANS +// RAND +// RANDBETWEEN +// ROUND +// ROUNDDOWN +// ROUNDUP +// SEC +// SECH +// SIGN +// SIN +// SINH +// SQRT +// SQRTPI +// SUM +// SUMIF +// SUMSQ +// TAN +// TANH +// TRIM +// TRUNC // UPPER // func (f *File) CalcCellValue(sheet, cell string) (result string, err error) { @@ -172,6 +272,21 @@ func getPriority(token efp.Token) (pri int) { return } +// newNumberFormulaArg constructs a number formula argument. +func newNumberFormulaArg(n float64) formulaArg { + return formulaArg{Type: ArgNumber, Number: n} +} + +// newStringFormulaArg constructs a string formula argument. +func newStringFormulaArg(s string) formulaArg { + return formulaArg{Type: ArgString, String: s} +} + +// newErrorFormulaArg create an error formula argument of a given type with a specified error message. +func newErrorFormulaArg(formulaError, msg string) formulaArg { + return formulaArg{Type: ArgError, String: formulaError, Error: msg} +} + // evalInfixExp evaluate syntax analysis by given infix expression after // lexical analysis. Evaluate an infix expression containing formulas by // stacks: @@ -302,18 +417,18 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) }) } // call formula function to evaluate - result, err := callFuncByName(&formulaFuncs{}, strings.NewReplacer( + arg := callFuncByName(&formulaFuncs{}, strings.NewReplacer( "_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue), []reflect.Value{reflect.ValueOf(argsList)}) - if err != nil { - return efp.Token{}, err + if arg.Type == ArgError { + return efp.Token{}, errors.New(arg.Value()) } argsList.Init() opfStack.Pop() if opfStack.Len() > 0 { // still in function stack - opfdStack.Push(efp.Token{TValue: result, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + opfdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } else { - opdStack.Push(efp.Token{TValue: result, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) + opdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) } } } @@ -573,6 +688,7 @@ func isOperatorPrefixToken(token efp.Token) bool { return false } +// getDefinedNameRefTo convert defined name to reference range. func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) { for _, definedName := range f.GetDefinedName() { if definedName.Name == definedNameName { @@ -775,22 +891,17 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (arg formulaArg, e // callFuncByName calls the no error or only error return function with // reflect by given receiver, name and parameters. -func callFuncByName(receiver interface{}, name string, params []reflect.Value) (result string, err error) { +func callFuncByName(receiver interface{}, name string, params []reflect.Value) (arg formulaArg) { function := reflect.ValueOf(receiver).MethodByName(name) if function.IsValid() { rt := function.Call(params) if len(rt) == 0 { return } - if !rt[1].IsNil() { - err = rt[1].Interface().(error) - return - } - result = rt[0].Interface().(string) + arg = rt[0].Interface().(formulaArg) return } - err = fmt.Errorf("not support %s function", name) - return + return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("not support %s function", name)) } // formulaCriteriaParser parse formula criteria. @@ -879,18 +990,15 @@ func formulaCriteriaEval(val string, criteria *formulaCriteria) (result bool, er // // ABS(number) // -func (fn *formulaFuncs) ABS(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ABS(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ABS requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ABS requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Abs(val)) - return + return newNumberFormulaArg(math.Abs(val)) } // ACOS function calculates the arccosine (i.e. the inverse cosine) of a given @@ -899,18 +1007,15 @@ func (fn *formulaFuncs) ABS(argsList *list.List) (result string, err error) { // // ACOS(number) // -func (fn *formulaFuncs) ACOS(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ACOS(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ACOS requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ACOS requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Acos(val)) - return + return newNumberFormulaArg(math.Acos(val)) } // ACOSH function calculates the inverse hyperbolic cosine of a supplied number. @@ -918,18 +1023,15 @@ func (fn *formulaFuncs) ACOS(argsList *list.List) (result string, err error) { // // ACOSH(number) // -func (fn *formulaFuncs) ACOSH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ACOSH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ACOSH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ACOSH requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Acosh(val)) - return + return newNumberFormulaArg(math.Acosh(val)) } // ACOT function calculates the arccotangent (i.e. the inverse cotangent) of a @@ -938,18 +1040,15 @@ func (fn *formulaFuncs) ACOSH(argsList *list.List) (result string, err error) { // // ACOT(number) // -func (fn *formulaFuncs) ACOT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ACOT(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ACOT requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ACOT requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Pi/2-math.Atan(val)) - return + return newNumberFormulaArg(math.Pi/2 - math.Atan(val)) } // ACOTH function calculates the hyperbolic arccotangent (coth) of a supplied @@ -957,18 +1056,15 @@ func (fn *formulaFuncs) ACOT(argsList *list.List) (result string, err error) { // // ACOTH(number) // -func (fn *formulaFuncs) ACOTH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ACOTH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ACOTH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ACOTH requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Atanh(1/val)) - return + return newNumberFormulaArg(math.Atanh(1 / val)) } // ARABIC function converts a Roman numeral into an Arabic numeral. The syntax @@ -976,10 +1072,9 @@ func (fn *formulaFuncs) ACOTH(argsList *list.List) (result string, err error) { // // ARABIC(text) // -func (fn *formulaFuncs) ARABIC(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ARABIC(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ARABIC requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ARABIC requires 1 numeric argument") } charMap := map[rune]float64{'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} val, last, prefix := 0.0, 0.0, 1.0 @@ -993,19 +1088,16 @@ func (fn *formulaFuncs) ARABIC(argsList *list.List) (result string, err error) { val += digit switch { case last == digit && (last == 5 || last == 50 || last == 500): - result = formulaErrorVALUE - return + return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) case 2*last == digit: - result = formulaErrorVALUE - return + return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } if last < digit { val -= 2 * last } last = digit } - result = fmt.Sprintf("%g", prefix*val) - return + return newNumberFormulaArg(prefix * val) } // ASIN function calculates the arcsine (i.e. the inverse sine) of a given @@ -1014,18 +1106,15 @@ func (fn *formulaFuncs) ARABIC(argsList *list.List) (result string, err error) { // // ASIN(number) // -func (fn *formulaFuncs) ASIN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ASIN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ASIN requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ASIN requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Asin(val)) - return + return newNumberFormulaArg(math.Asin(val)) } // ASINH function calculates the inverse hyperbolic sine of a supplied number. @@ -1033,18 +1122,15 @@ func (fn *formulaFuncs) ASIN(argsList *list.List) (result string, err error) { // // ASINH(number) // -func (fn *formulaFuncs) ASINH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ASINH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ASINH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ASINH requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Asinh(val)) - return + return newNumberFormulaArg(math.Asinh(val)) } // ATAN function calculates the arctangent (i.e. the inverse tangent) of a @@ -1053,18 +1139,15 @@ func (fn *formulaFuncs) ASINH(argsList *list.List) (result string, err error) { // // ATAN(number) // -func (fn *formulaFuncs) ATAN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ATAN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ATAN requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ATAN requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Atan(val)) - return + return newNumberFormulaArg(math.Atan(val)) } // ATANH function calculates the inverse hyperbolic tangent of a supplied @@ -1072,18 +1155,15 @@ func (fn *formulaFuncs) ATAN(argsList *list.List) (result string, err error) { // // ATANH(number) // -func (fn *formulaFuncs) ATANH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ATANH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ATANH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ATANH requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Atanh(val)) - return + return newNumberFormulaArg(math.Atanh(val)) } // ATAN2 function calculates the arctangent (i.e. the inverse tangent) of a @@ -1092,22 +1172,19 @@ func (fn *formulaFuncs) ATANH(argsList *list.List) (result string, err error) { // // ATAN2(x_num,y_num) // -func (fn *formulaFuncs) ATAN2(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ATAN2(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("ATAN2 requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "ATAN2 requires 2 numeric arguments") } - var x, y float64 - if x, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + x, err := strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if y, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + y, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Atan2(x, y)) - return + return newNumberFormulaArg(math.Atan2(x, y)) } // BASE function converts a number into a supplied base (radix), and returns a @@ -1115,41 +1192,35 @@ func (fn *formulaFuncs) ATAN2(argsList *list.List) (result string, err error) { // // BASE(number,radix,[min_length]) // -func (fn *formulaFuncs) BASE(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) BASE(argsList *list.List) formulaArg { if argsList.Len() < 2 { - err = errors.New("BASE requires at least 2 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "BASE requires at least 2 arguments") } if argsList.Len() > 3 { - err = errors.New("BASE allows at most 3 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "BASE allows at most 3 arguments") } var number float64 var radix, minLength int + var err error if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if radix, err = strconv.Atoi(argsList.Front().Next().Value.(formulaArg).String); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if radix < 2 || radix > 36 { - err = errors.New("radix must be an integer >= 2 and <= 36") - return + return newErrorFormulaArg(formulaErrorVALUE, "radix must be an integer >= 2 and <= 36") } if argsList.Len() > 2 { if minLength, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } - result = strconv.FormatInt(int64(number), radix) + result := strconv.FormatInt(int64(number), radix) if len(result) < minLength { result = strings.Repeat("0", minLength-len(result)) + result } - result = strings.ToUpper(result) - return + return newStringFormulaArg(strings.ToUpper(result)) } // CEILING function rounds a supplied number away from zero, to the nearest @@ -1157,43 +1228,38 @@ func (fn *formulaFuncs) BASE(argsList *list.List) (result string, err error) { // // CEILING(number,significance) // -func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) CEILING(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("CEILING requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "CEILING requires at least 1 argument") } if argsList.Len() > 2 { - err = errors.New("CEILING allows at most 2 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "CEILING allows at most 2 arguments") } number, significance, res := 0.0, 1.0, 0.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { significance = -1 } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } if significance < 0 && number > 0 { - err = errors.New("negative sig to CEILING invalid") - return + return newErrorFormulaArg(formulaErrorVALUE, "negative sig to CEILING invalid") } if argsList.Len() == 1 { - result = fmt.Sprintf("%g", math.Ceil(number)) - return + return newNumberFormulaArg(math.Ceil(number)) } number, res = math.Modf(number / significance) if res > 0 { number++ } - result = fmt.Sprintf("%g", number*significance) - return + return newNumberFormulaArg(number * significance) } // CEILINGMATH function rounds a supplied number up to a supplied multiple of @@ -1201,37 +1267,32 @@ func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error) // // CEILING.MATH(number,[significance],[mode]) // -func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("CEILING.MATH requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "CEILING.MATH requires at least 1 argument") } if argsList.Len() > 3 { - err = errors.New("CEILING.MATH allows at most 3 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "CEILING.MATH allows at most 3 arguments") } number, significance, mode := 0.0, 1.0, 1.0 + var err error if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { significance = -1 } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } if argsList.Len() == 1 { - result = fmt.Sprintf("%g", math.Ceil(number)) - return + return newNumberFormulaArg(math.Ceil(number)) } if argsList.Len() > 2 { if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } val, res := math.Modf(number / significance) @@ -1242,8 +1303,7 @@ func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err err val-- } } - result = fmt.Sprintf("%g", val*significance) - return + return newNumberFormulaArg(val * significance) } // CEILINGPRECISE function rounds a supplied number up (regardless of the @@ -1252,36 +1312,33 @@ func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err err // // CEILING.PRECISE(number,[significance]) // -func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("CEILING.PRECISE requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "CEILING.PRECISE requires at least 1 argument") } if argsList.Len() > 2 { - err = errors.New("CEILING.PRECISE allows at most 2 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "CEILING.PRECISE allows at most 2 arguments") } number, significance := 0.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { significance = -1 } if argsList.Len() == 1 { - result = fmt.Sprintf("%g", math.Ceil(number)) - return + return newNumberFormulaArg(math.Ceil(number)) } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } significance = math.Abs(significance) if significance == 0 { - result = "0" - return + return newStringFormulaArg("0") } } val, res := math.Modf(number / significance) @@ -1290,8 +1347,7 @@ func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err val++ } } - result = fmt.Sprintf("%g", val*significance) - return + return newNumberFormulaArg(val * significance) } // COMBIN function calculates the number of combinations (in any order) of a @@ -1299,34 +1355,29 @@ func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err // // COMBIN(number,number_chosen) // -func (fn *formulaFuncs) COMBIN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) COMBIN(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("COMBIN requires 2 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires 2 argument") } number, chosen, val := 0.0, 0.0, 1.0 + var err error if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } number, chosen = math.Trunc(number), math.Trunc(chosen) if chosen > number { - err = errors.New("COMBIN requires number >= number_chosen") - return + return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires number >= number_chosen") } if chosen == number || chosen == 0 { - result = "1" - return + return newStringFormulaArg("1") } for c := float64(1); c <= chosen; c++ { val *= (number + 1 - c) / c } - result = fmt.Sprintf("%g", math.Ceil(val)) - return + return newNumberFormulaArg(math.Ceil(val)) } // COMBINA function calculates the number of combinations, with repetitions, @@ -1334,28 +1385,26 @@ func (fn *formulaFuncs) COMBIN(argsList *list.List) (result string, err error) { // // COMBINA(number,number_chosen) // -func (fn *formulaFuncs) COMBINA(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) COMBINA(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("COMBINA requires 2 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires 2 argument") } var number, chosen float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } number, chosen = math.Trunc(number), math.Trunc(chosen) if number < chosen { - err = errors.New("COMBINA requires number > number_chosen") - return + return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires number > number_chosen") } if number == 0 { - result = "0" - return + return newStringFormulaArg("0") } args := list.New() args.PushBack(formulaArg{ @@ -1374,18 +1423,15 @@ func (fn *formulaFuncs) COMBINA(argsList *list.List) (result string, err error) // // COS(number) // -func (fn *formulaFuncs) COS(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) COS(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("COS requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "COS requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Cos(val)) - return + return newNumberFormulaArg(math.Cos(val)) } // COSH function calculates the hyperbolic cosine (cosh) of a supplied number. @@ -1393,18 +1439,15 @@ func (fn *formulaFuncs) COS(argsList *list.List) (result string, err error) { // // COSH(number) // -func (fn *formulaFuncs) COSH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) COSH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("COSH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "COSH requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Cosh(val)) - return + return newNumberFormulaArg(math.Cosh(val)) } // COT function calculates the cotangent of a given angle. The syntax of the @@ -1412,22 +1455,18 @@ func (fn *formulaFuncs) COSH(argsList *list.List) (result string, err error) { // // COT(number) // -func (fn *formulaFuncs) COT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) COT(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("COT requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "COT requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if val == 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", math.Tan(val)) - return + return newNumberFormulaArg(math.Tan(val)) } // COTH function calculates the hyperbolic cotangent (coth) of a supplied @@ -1435,22 +1474,18 @@ func (fn *formulaFuncs) COT(argsList *list.List) (result string, err error) { // // COTH(number) // -func (fn *formulaFuncs) COTH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) COTH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("COTH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "COTH requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if val == 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", math.Tanh(val)) - return + return newNumberFormulaArg(math.Tanh(val)) } // CSC function calculates the cosecant of a given angle. The syntax of the @@ -1458,22 +1493,18 @@ func (fn *formulaFuncs) COTH(argsList *list.List) (result string, err error) { // // CSC(number) // -func (fn *formulaFuncs) CSC(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) CSC(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("CSC requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "CSC requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if val == 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", 1/math.Sin(val)) - return + return newNumberFormulaArg(1 / math.Sin(val)) } // CSCH function calculates the hyperbolic cosecant (csch) of a supplied @@ -1481,22 +1512,18 @@ func (fn *formulaFuncs) CSC(argsList *list.List) (result string, err error) { // // CSCH(number) // -func (fn *formulaFuncs) CSCH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) CSCH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("CSCH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "CSCH requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if val == 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", 1/math.Sinh(val)) - return + return newNumberFormulaArg(1 / math.Sinh(val)) } // DECIMAL function converts a text representation of a number in a specified @@ -1504,27 +1531,25 @@ func (fn *formulaFuncs) CSCH(argsList *list.List) (result string, err error) { // // DECIMAL(text,radix) // -func (fn *formulaFuncs) DECIMAL(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) DECIMAL(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("DECIMAL requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "DECIMAL requires 2 numeric arguments") } var text = argsList.Front().Value.(formulaArg).String var radix int - if radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if len(text) > 2 && (strings.HasPrefix(text, "0x") || strings.HasPrefix(text, "0X")) { text = text[2:] } val, err := strconv.ParseInt(text, radix, 64) if err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", float64(val)) - return + return newNumberFormulaArg(float64(val)) } // DEGREES function converts radians into degrees. The syntax of the function @@ -1532,22 +1557,18 @@ func (fn *formulaFuncs) DECIMAL(argsList *list.List) (result string, err error) // // DEGREES(angle) // -func (fn *formulaFuncs) DEGREES(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) DEGREES(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("DEGREES requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "DEGREES requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if val == 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", 180.0/math.Pi*val) - return + return newNumberFormulaArg(180.0 / math.Pi * val) } // EVEN function rounds a supplied number away from zero (i.e. rounds a @@ -1556,15 +1577,13 @@ func (fn *formulaFuncs) DEGREES(argsList *list.List) (result string, err error) // // EVEN(number) // -func (fn *formulaFuncs) EVEN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) EVEN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("EVEN requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "EVEN requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sign := math.Signbit(number) m, frac := math.Modf(number / 2) @@ -1576,8 +1595,7 @@ func (fn *formulaFuncs) EVEN(argsList *list.List) (result string, err error) { val -= 2 } } - result = fmt.Sprintf("%g", val) - return + return newNumberFormulaArg(val) } // EXP function calculates the value of the mathematical constant e, raised to @@ -1585,18 +1603,15 @@ func (fn *formulaFuncs) EVEN(argsList *list.List) (result string, err error) { // // EXP(number) // -func (fn *formulaFuncs) EXP(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) EXP(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("EXP requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "EXP requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = strings.ToUpper(fmt.Sprintf("%g", math.Exp(number))) - return + return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%g", math.Exp(number)))) } // fact returns the factorial of a supplied number. @@ -1613,21 +1628,18 @@ func fact(number float64) float64 { // // FACT(number) // -func (fn *formulaFuncs) FACT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) FACT(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("FACT requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "FACT requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { - err = errors.New(formulaErrorNUM) + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } - result = strings.ToUpper(fmt.Sprintf("%g", fact(number))) - return + return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%g", fact(number)))) } // FACTDOUBLE function returns the double factorial of a supplied number. The @@ -1635,25 +1647,22 @@ func (fn *formulaFuncs) FACT(argsList *list.List) (result string, err error) { // // FACTDOUBLE(number) // -func (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("FACTDOUBLE requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "FACTDOUBLE requires 1 numeric argument") } - number, val := 0.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val := 1.0 + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } for i := math.Trunc(number); i > 1; i -= 2 { val *= i } - result = strings.ToUpper(fmt.Sprintf("%g", val)) - return + return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%g", val))) } // FLOOR function rounds a supplied number towards zero to the nearest @@ -1661,23 +1670,22 @@ func (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) (result string, err erro // // FLOOR(number,significance) // -func (fn *formulaFuncs) FLOOR(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) FLOOR(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("FLOOR requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "FLOOR requires 2 numeric arguments") } var number, significance float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if significance < 0 && number >= 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, "invalid arguments to FLOOR") } val := number val, res := math.Modf(val / significance) @@ -1686,8 +1694,7 @@ func (fn *formulaFuncs) FLOOR(argsList *list.List) (result string, err error) { val-- } } - result = strings.ToUpper(fmt.Sprintf("%g", val*significance)) - return + return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%g", val*significance))) } // FLOORMATH function rounds a supplied number down to a supplied multiple of @@ -1695,45 +1702,40 @@ func (fn *formulaFuncs) FLOOR(argsList *list.List) (result string, err error) { // // FLOOR.MATH(number,[significance],[mode]) // -func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) FLOORMATH(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("FLOOR.MATH requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.MATH requires at least 1 argument") } if argsList.Len() > 3 { - err = errors.New("FLOOR.MATH allows at most 3 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.MATH allows at most 3 arguments") } number, significance, mode := 0.0, 1.0, 1.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { significance = -1 } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } if argsList.Len() == 1 { - result = fmt.Sprintf("%g", math.Floor(number)) - return + return newNumberFormulaArg(math.Floor(number)) } if argsList.Len() > 2 { if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } val, res := math.Modf(number / significance) if res != 0 && number < 0 && mode > 0 { val-- } - result = fmt.Sprintf("%g", val*significance) - return + return newNumberFormulaArg(val * significance) } // FLOORPRECISE function rounds a supplied number down to a supplied multiple @@ -1741,36 +1743,32 @@ func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error // // FLOOR.PRECISE(number,[significance]) // -func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("FLOOR.PRECISE requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.PRECISE requires at least 1 argument") } if argsList.Len() > 2 { - err = errors.New("FLOOR.PRECISE allows at most 2 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "FLOOR.PRECISE allows at most 2 arguments") } var number, significance float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { significance = -1 } if argsList.Len() == 1 { - result = fmt.Sprintf("%g", math.Floor(number)) - return + return newNumberFormulaArg(math.Floor(number)) } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } significance = math.Abs(significance) if significance == 0 { - result = "0" - return + return newStringFormulaArg("0") } } val, res := math.Modf(number / significance) @@ -1779,8 +1777,7 @@ func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) (result string, err er val-- } } - result = fmt.Sprintf("%g", val*significance) - return + return newNumberFormulaArg(val * significance) } // gcd returns the greatest common divisor of two supplied integers. @@ -1807,14 +1804,14 @@ func gcd(x, y float64) float64 { // // GCD(number1,[number2],...) // -func (fn *formulaFuncs) GCD(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) GCD(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("GCD requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "GCD requires at least 1 argument") } var ( val float64 nums = []float64{} + err error ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg).String @@ -1822,29 +1819,24 @@ func (fn *formulaFuncs) GCD(argsList *list.List) (result string, err error) { continue } if val, err = strconv.ParseFloat(token, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } nums = append(nums, val) } if nums[0] < 0 { - err = errors.New("GCD only accepts positive arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "GCD only accepts positive arguments") } if len(nums) == 1 { - result = fmt.Sprintf("%g", nums[0]) - return + return newNumberFormulaArg(nums[0]) } cd := nums[0] for i := 1; i < len(nums); i++ { if nums[i] < 0 { - err = errors.New("GCD only accepts positive arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "GCD only accepts positive arguments") } cd = gcd(cd, nums[i]) } - result = fmt.Sprintf("%g", cd) - return + return newNumberFormulaArg(cd) } // INT function truncates a supplied number down to the closest integer. The @@ -1852,22 +1844,19 @@ func (fn *formulaFuncs) GCD(argsList *list.List) (result string, err error) { // // INT(number) // -func (fn *formulaFuncs) INT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) INT(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("INT requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "INT requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } val, frac := math.Modf(number) if frac < 0 { val-- } - result = fmt.Sprintf("%g", val) - return + return newNumberFormulaArg(val) } // ISOCEILING function rounds a supplied number up (regardless of the number's @@ -1876,36 +1865,31 @@ func (fn *formulaFuncs) INT(argsList *list.List) (result string, err error) { // // ISO.CEILING(number,[significance]) // -func (fn *formulaFuncs) ISOCEILING(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISOCEILING(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("ISO.CEILING requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISO.CEILING requires at least 1 argument") } if argsList.Len() > 2 { - err = errors.New("ISO.CEILING allows at most 2 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISO.CEILING allows at most 2 arguments") } var number, significance float64 + var err error if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number < 0 { significance = -1 } if argsList.Len() == 1 { - result = fmt.Sprintf("%g", math.Ceil(number)) - return + return newNumberFormulaArg(math.Ceil(number)) } if argsList.Len() > 1 { if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } significance = math.Abs(significance) if significance == 0 { - result = "0" - return + return newStringFormulaArg("0") } } val, res := math.Modf(number / significance) @@ -1914,8 +1898,7 @@ func (fn *formulaFuncs) ISOCEILING(argsList *list.List) (result string, err erro val++ } } - result = fmt.Sprintf("%g", val*significance) - return + return newNumberFormulaArg(val * significance) } // lcm returns the least common multiple of two supplied integers. @@ -1933,14 +1916,14 @@ func lcm(a, b float64) float64 { // // LCM(number1,[number2],...) // -func (fn *formulaFuncs) LCM(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) LCM(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("LCM requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "LCM requires at least 1 argument") } var ( val float64 nums = []float64{} + err error ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg).String @@ -1948,29 +1931,24 @@ func (fn *formulaFuncs) LCM(argsList *list.List) (result string, err error) { continue } if val, err = strconv.ParseFloat(token, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } nums = append(nums, val) } if nums[0] < 0 { - err = errors.New("LCM only accepts positive arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "LCM only accepts positive arguments") } if len(nums) == 1 { - result = fmt.Sprintf("%g", nums[0]) - return + return newNumberFormulaArg(nums[0]) } cm := nums[0] for i := 1; i < len(nums); i++ { if nums[i] < 0 { - err = errors.New("LCM only accepts positive arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "LCM only accepts positive arguments") } cm = lcm(cm, nums[i]) } - result = fmt.Sprintf("%g", cm) - return + return newNumberFormulaArg(cm) } // LN function calculates the natural logarithm of a given number. The syntax @@ -1978,18 +1956,15 @@ func (fn *formulaFuncs) LCM(argsList *list.List) (result string, err error) { // // LN(number) // -func (fn *formulaFuncs) LN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) LN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("LN requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "LN requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Log(number)) - return + return newNumberFormulaArg(math.Log(number)) } // LOG function calculates the logarithm of a given number, to a supplied @@ -1997,40 +1972,34 @@ func (fn *formulaFuncs) LN(argsList *list.List) (result string, err error) { // // LOG(number,[base]) // -func (fn *formulaFuncs) LOG(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) LOG(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("LOG requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "LOG requires at least 1 argument") } if argsList.Len() > 2 { - err = errors.New("LOG allows at most 2 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "LOG allows at most 2 arguments") } number, base := 0.0, 10.0 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if argsList.Len() > 1 { if base, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } if number == 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorDIV) } if base == 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorDIV) } if base == 1 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", math.Log(number)/math.Log(base)) - return + return newNumberFormulaArg(math.Log(number) / math.Log(base)) } // LOG10 function calculates the base 10 logarithm of a given number. The @@ -2038,20 +2007,19 @@ func (fn *formulaFuncs) LOG(argsList *list.List) (result string, err error) { // // LOG10(number) // -func (fn *formulaFuncs) LOG10(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) LOG10(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("LOG10 requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "LOG10 requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Log10(number)) - return + return newNumberFormulaArg(math.Log10(number)) } +// minor function implement a minor of a matrix A is the determinant of some +// smaller square matrix. func minor(sqMtx [][]float64, idx int) [][]float64 { ret := [][]float64{} for i := range sqMtx { @@ -2092,30 +2060,31 @@ func det(sqMtx [][]float64) float64 { // // MDETERM(array) // -func (fn *formulaFuncs) MDETERM(argsList *list.List) (result string, err error) { - var num float64 - var numMtx = [][]float64{} - var strMtx = argsList.Front().Value.(formulaArg).Matrix +func (fn *formulaFuncs) MDETERM(argsList *list.List) (result formulaArg) { + var ( + num float64 + numMtx = [][]float64{} + err error + strMtx = argsList.Front().Value.(formulaArg).Matrix + ) if argsList.Len() < 1 { return } var rows = len(strMtx) for _, row := range argsList.Front().Value.(formulaArg).Matrix { if len(row) != rows { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } numRow := []float64{} for _, ele := range row { if num, err = strconv.ParseFloat(ele.String, 64); err != nil { - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } numRow = append(numRow, num) } numMtx = append(numMtx, numRow) } - result = fmt.Sprintf("%g", det(numMtx)) - return + return newNumberFormulaArg(det(numMtx)) } // MOD function returns the remainder of a division between two supplied @@ -2123,30 +2092,28 @@ func (fn *formulaFuncs) MDETERM(argsList *list.List) (result string, err error) // // MOD(number,divisor) // -func (fn *formulaFuncs) MOD(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) MOD(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("MOD requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "MOD requires 2 numeric arguments") } var number, divisor float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if divisor, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + divisor, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if divisor == 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, "MOD divide by zero") } trunc, rem := math.Modf(number / divisor) if rem < 0 { trunc-- } - result = fmt.Sprintf("%g", number-divisor*trunc) - return + return newNumberFormulaArg(number - divisor*trunc) } // MROUND function rounds a supplied number up or down to the nearest multiple @@ -2154,35 +2121,32 @@ func (fn *formulaFuncs) MOD(argsList *list.List) (result string, err error) { // // MROUND(number,multiple) // -func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) MROUND(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("MROUND requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "MROUND requires 2 numeric arguments") } var number, multiple float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if multiple, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + multiple, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if multiple == 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if multiple < 0 && number > 0 || multiple > 0 && number < 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } number, res := math.Modf(number / multiple) if math.Trunc(res+0.5) > 0 { number++ } - result = fmt.Sprintf("%g", number*multiple) - return + return newNumberFormulaArg(number * multiple) } // MULTINOMIAL function calculates the ratio of the factorial of a sum of @@ -2191,22 +2155,21 @@ func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) { // // MULTINOMIAL(number1,[number2],...) // -func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) formulaArg { val, num, denom := 0.0, 0.0, 1.0 + var err error for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) if token.String == "" { continue } if val, err = strconv.ParseFloat(token.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } num += val denom *= fact(val) } - result = fmt.Sprintf("%g", fact(num)/denom) - return + return newNumberFormulaArg(fact(num) / denom) } // MUNIT function returns the unit matrix for a specified dimension. The @@ -2214,15 +2177,13 @@ func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) (result string, err err // // MUNIT(dimension) // -func (fn *formulaFuncs) MUNIT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) MUNIT(argsList *list.List) (result formulaArg) { if argsList.Len() != 1 { - err = errors.New("MUNIT requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "MUNIT requires 1 numeric argument") } - var dimension int - if dimension, err = strconv.Atoi(argsList.Front().Value.(formulaArg).String); err != nil { - err = errors.New(formulaErrorVALUE) - return + dimension, err := strconv.Atoi(argsList.Front().Value.(formulaArg).String) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } matrix := make([][]float64, 0, dimension) for i := 0; i < dimension; i++ { @@ -2245,19 +2206,16 @@ func (fn *formulaFuncs) MUNIT(argsList *list.List) (result string, err error) { // // ODD(number) // -func (fn *formulaFuncs) ODD(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ODD(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ODD requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ODD requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if number == 0 { - result = "1" - return + return newStringFormulaArg("1") } sign := math.Signbit(number) m, frac := math.Modf((number - 1) / 2) @@ -2269,8 +2227,7 @@ func (fn *formulaFuncs) ODD(argsList *list.List) (result string, err error) { val -= 2 } } - result = fmt.Sprintf("%g", val) - return + return newNumberFormulaArg(val) } // PI function returns the value of the mathematical constant π (pi), accurate @@ -2278,13 +2235,11 @@ func (fn *formulaFuncs) ODD(argsList *list.List) (result string, err error) { // // PI() // -func (fn *formulaFuncs) PI(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) PI(argsList *list.List) formulaArg { if argsList.Len() != 0 { - err = errors.New("PI accepts no arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "PI accepts no arguments") } - result = fmt.Sprintf("%g", math.Pi) - return + return newNumberFormulaArg(math.Pi) } // POWER function calculates a given number, raised to a supplied power. @@ -2292,30 +2247,27 @@ func (fn *formulaFuncs) PI(argsList *list.List) (result string, err error) { // // POWER(number,power) // -func (fn *formulaFuncs) POWER(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) POWER(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("POWER requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "POWER requires 2 numeric arguments") } var x, y float64 - if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if x == 0 && y == 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } if x == 0 && y < 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", math.Pow(x, y)) - return + return newNumberFormulaArg(math.Pow(x, y)) } // PRODUCT function returns the product (multiplication) of a supplied set of @@ -2323,8 +2275,9 @@ func (fn *formulaFuncs) POWER(argsList *list.List) (result string, err error) { // // PRODUCT(number1,[number2],...) // -func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) PRODUCT(argsList *list.List) formulaArg { val, product := 0.0, 1.0 + var err error for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { @@ -2335,8 +2288,7 @@ func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) continue } if val, err = strconv.ParseFloat(token.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } product = product * val case ArgMatrix: @@ -2346,16 +2298,14 @@ func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) continue } if val, err = strconv.ParseFloat(value.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } product = product * val } } } } - result = fmt.Sprintf("%g", product) - return + return newNumberFormulaArg(product) } // QUOTIENT function returns the integer portion of a division between two @@ -2363,44 +2313,39 @@ func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) // // QUOTIENT(numerator,denominator) // -func (fn *formulaFuncs) QUOTIENT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) QUOTIENT(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("QUOTIENT requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "QUOTIENT requires 2 numeric arguments") } var x, y float64 - if x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + x, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + y, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if y == 0 { - err = errors.New(formulaErrorDIV) - return + return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) } - result = fmt.Sprintf("%g", math.Trunc(x/y)) - return + return newNumberFormulaArg(math.Trunc(x / y)) } // RADIANS function converts radians into degrees. The syntax of the function is: // // RADIANS(angle) // -func (fn *formulaFuncs) RADIANS(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) RADIANS(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("RADIANS requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "RADIANS requires 1 numeric argument") } - var angle float64 - if angle, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + angle, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Pi/180.0*angle) - return + return newNumberFormulaArg(math.Pi / 180.0 * angle) } // RAND function generates a random real number between 0 and 1. The syntax of @@ -2408,13 +2353,11 @@ func (fn *formulaFuncs) RADIANS(argsList *list.List) (result string, err error) // // RAND() // -func (fn *formulaFuncs) RAND(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) RAND(argsList *list.List) formulaArg { if argsList.Len() != 0 { - err = errors.New("RAND accepts no arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "RAND accepts no arguments") } - result = fmt.Sprintf("%g", rand.New(rand.NewSource(time.Now().UnixNano())).Float64()) - return + return newNumberFormulaArg(rand.New(rand.NewSource(time.Now().UnixNano())).Float64()) } // RANDBETWEEN function generates a random integer between two supplied @@ -2422,26 +2365,24 @@ func (fn *formulaFuncs) RAND(argsList *list.List) (result string, err error) { // // RANDBETWEEN(bottom,top) // -func (fn *formulaFuncs) RANDBETWEEN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) RANDBETWEEN(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("RANDBETWEEN requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "RANDBETWEEN requires 2 numeric arguments") } var bottom, top int64 - if bottom, err = strconv.ParseInt(argsList.Front().Value.(formulaArg).String, 10, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + bottom, err = strconv.ParseInt(argsList.Front().Value.(formulaArg).String, 10, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if top, err = strconv.ParseInt(argsList.Back().Value.(formulaArg).String, 10, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + top, err = strconv.ParseInt(argsList.Back().Value.(formulaArg).String, 10, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if top < bottom { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } - result = fmt.Sprintf("%g", float64(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(top-bottom+1)+bottom)) - return + return newNumberFormulaArg(float64(rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(top-bottom+1) + bottom)) } // romanNumerals defined a numeral system that originated in ancient Rome and @@ -2464,25 +2405,23 @@ var romanTable = [][]romanNumerals{{{1000, "M"}, {900, "CM"}, {500, "D"}, {400, // // ROMAN(number,[form]) // -func (fn *formulaFuncs) ROMAN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ROMAN(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("ROMAN requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ROMAN requires at least 1 argument") } if argsList.Len() > 2 { - err = errors.New("ROMAN allows at most 2 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "ROMAN allows at most 2 arguments") } var number float64 var form int - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if argsList.Len() > 1 { if form, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if form < 0 { form = 0 @@ -2509,8 +2448,7 @@ func (fn *formulaFuncs) ROMAN(argsList *list.List) (result string, err error) { val -= r.n } } - result = buf.String() - return + return newStringFormulaArg(buf.String()) } type roundMode byte @@ -2554,22 +2492,21 @@ func (fn *formulaFuncs) round(number, digits float64, mode roundMode) float64 { // // ROUND(number,num_digits) // -func (fn *formulaFuncs) ROUND(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ROUND(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("ROUND requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "ROUND requires 2 numeric arguments") } var number, digits float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", fn.round(number, digits, closest)) - return + return newNumberFormulaArg(fn.round(number, digits, closest)) } // ROUNDDOWN function rounds a supplied number down towards zero, to a @@ -2577,22 +2514,21 @@ func (fn *formulaFuncs) ROUND(argsList *list.List) (result string, err error) { // // ROUNDDOWN(number,num_digits) // -func (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("ROUNDDOWN requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "ROUNDDOWN requires 2 numeric arguments") } var number, digits float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", fn.round(number, digits, down)) - return + return newNumberFormulaArg(fn.round(number, digits, down)) } // ROUNDUP function rounds a supplied number up, away from zero, to a @@ -2600,22 +2536,21 @@ func (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) (result string, err error // // ROUNDUP(number,num_digits) // -func (fn *formulaFuncs) ROUNDUP(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ROUNDUP(argsList *list.List) formulaArg { if argsList.Len() != 2 { - err = errors.New("ROUNDUP requires 2 numeric arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "ROUNDUP requires 2 numeric arguments") } var number, digits float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", fn.round(number, digits, up)) - return + return newNumberFormulaArg(fn.round(number, digits, up)) } // SEC function calculates the secant of a given angle. The syntax of the @@ -2623,18 +2558,15 @@ func (fn *formulaFuncs) ROUNDUP(argsList *list.List) (result string, err error) // // SEC(number) // -func (fn *formulaFuncs) SEC(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SEC(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("SEC requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SEC requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Cos(number)) - return + return newNumberFormulaArg(math.Cos(number)) } // SECH function calculates the hyperbolic secant (sech) of a supplied angle. @@ -2642,18 +2574,15 @@ func (fn *formulaFuncs) SEC(argsList *list.List) (result string, err error) { // // SECH(number) // -func (fn *formulaFuncs) SECH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SECH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("SECH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SECH requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", 1/math.Cosh(number)) - return + return newNumberFormulaArg(1 / math.Cosh(number)) } // SIGN function returns the arithmetic sign (+1, -1 or 0) of a supplied @@ -2663,26 +2592,21 @@ func (fn *formulaFuncs) SECH(argsList *list.List) (result string, err error) { // // SIGN(number) // -func (fn *formulaFuncs) SIGN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SIGN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("SIGN requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SIGN requires 1 numeric argument") } - var val float64 - if val, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if val < 0 { - result = "-1" - return + return newStringFormulaArg("-1") } if val > 0 { - result = "1" - return + return newStringFormulaArg("1") } - result = "0" - return + return newStringFormulaArg("0") } // SIN function calculates the sine of a given angle. The syntax of the @@ -2690,18 +2614,15 @@ func (fn *formulaFuncs) SIGN(argsList *list.List) (result string, err error) { // // SIN(number) // -func (fn *formulaFuncs) SIN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SIN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("SIN requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SIN requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Sin(number)) - return + return newNumberFormulaArg(math.Sin(number)) } // SINH function calculates the hyperbolic sine (sinh) of a supplied number. @@ -2709,18 +2630,15 @@ func (fn *formulaFuncs) SIN(argsList *list.List) (result string, err error) { // // SINH(number) // -func (fn *formulaFuncs) SINH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SINH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("SINH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SINH requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Sinh(number)) - return + return newNumberFormulaArg(math.Sinh(number)) } // SQRT function calculates the positive square root of a supplied number. The @@ -2728,27 +2646,23 @@ func (fn *formulaFuncs) SINH(argsList *list.List) (result string, err error) { // // SQRT(number) // -func (fn *formulaFuncs) SQRT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SQRT(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("SQRT requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SQRT requires 1 numeric argument") } var res float64 var value = argsList.Front().Value.(formulaArg).String if value == "" { - result = "0" - return + return newStringFormulaArg("0") } - if res, err = strconv.ParseFloat(value, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + res, err := strconv.ParseFloat(value, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if res < 0 { - err = errors.New(formulaErrorNUM) - return + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) } - result = fmt.Sprintf("%g", math.Sqrt(res)) - return + return newNumberFormulaArg(math.Sqrt(res)) } // SQRTPI function returns the square root of a supplied number multiplied by @@ -2756,18 +2670,15 @@ func (fn *formulaFuncs) SQRT(argsList *list.List) (result string, err error) { // // SQRTPI(number) // -func (fn *formulaFuncs) SQRTPI(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SQRTPI(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("SQRTPI requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SQRTPI requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Sqrt(number*math.Pi)) - return + return newNumberFormulaArg(math.Sqrt(number * math.Pi)) } // SUM function adds together a supplied set of numbers and returns the sum of @@ -2775,8 +2686,11 @@ func (fn *formulaFuncs) SQRTPI(argsList *list.List) (result string, err error) { // // SUM(number1,[number2],...) // -func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) { - var val, sum float64 +func (fn *formulaFuncs) SUM(argsList *list.List) formulaArg { + var ( + val, sum float64 + err error + ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { @@ -2787,8 +2701,7 @@ func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) { continue } if val, err = strconv.ParseFloat(token.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sum += val case ArgMatrix: @@ -2798,16 +2711,14 @@ func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) { continue } if val, err = strconv.ParseFloat(value.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sum += val } } } } - result = fmt.Sprintf("%g", sum) - return + return newNumberFormulaArg(sum) } // SUMIF function finds the values in a supplied array, that satisfy a given @@ -2816,10 +2727,9 @@ func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) { // // SUMIF(range,criteria,[sum_range]) // -func (fn *formulaFuncs) SUMIF(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SUMIF(argsList *list.List) formulaArg { if argsList.Len() < 2 { - err = errors.New("SUMIF requires at least 2 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "SUMIF requires at least 2 argument") } var criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) var rangeMtx = argsList.Front().Value.(formulaArg).Matrix @@ -2828,6 +2738,7 @@ func (fn *formulaFuncs) SUMIF(argsList *list.List) (result string, err error) { sumRange = argsList.Back().Value.(formulaArg).Matrix } var sum, val float64 + var err error for rowIdx, row := range rangeMtx { for colIdx, col := range row { var ok bool @@ -2836,7 +2747,7 @@ func (fn *formulaFuncs) SUMIF(argsList *list.List) (result string, err error) { continue } if ok, err = formulaCriteriaEval(fromVal, criteria); err != nil { - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if ok { if argsList.Len() == 3 { @@ -2846,15 +2757,13 @@ func (fn *formulaFuncs) SUMIF(argsList *list.List) (result string, err error) { fromVal = sumRange[rowIdx][colIdx].String } if val, err = strconv.ParseFloat(fromVal, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sum += val } } } - result = fmt.Sprintf("%g", sum) - return + return newNumberFormulaArg(sum) } // SUMSQ function returns the sum of squares of a supplied set of values. The @@ -2862,8 +2771,9 @@ func (fn *formulaFuncs) SUMIF(argsList *list.List) (result string, err error) { // // SUMSQ(number1,[number2],...) // -func (fn *formulaFuncs) SUMSQ(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) SUMSQ(argsList *list.List) formulaArg { var val, sq float64 + var err error for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { @@ -2872,8 +2782,7 @@ func (fn *formulaFuncs) SUMSQ(argsList *list.List) (result string, err error) { continue } if val, err = strconv.ParseFloat(token.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sq += val * val case ArgMatrix: @@ -2883,16 +2792,14 @@ func (fn *formulaFuncs) SUMSQ(argsList *list.List) (result string, err error) { continue } if val, err = strconv.ParseFloat(value.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sq += val * val } } } } - result = fmt.Sprintf("%g", sq) - return + return newNumberFormulaArg(sq) } // TAN function calculates the tangent of a given angle. The syntax of the @@ -2900,18 +2807,15 @@ func (fn *formulaFuncs) SUMSQ(argsList *list.List) (result string, err error) { // // TAN(number) // -func (fn *formulaFuncs) TAN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) TAN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("TAN requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "TAN requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Tan(number)) - return + return newNumberFormulaArg(math.Tan(number)) } // TANH function calculates the hyperbolic tangent (tanh) of a supplied @@ -2919,18 +2823,15 @@ func (fn *formulaFuncs) TAN(argsList *list.List) (result string, err error) { // // TANH(number) // -func (fn *formulaFuncs) TANH(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) TANH(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("TANH requires 1 numeric argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "TANH requires 1 numeric argument") } - var number float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + number, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - result = fmt.Sprintf("%g", math.Tanh(number)) - return + return newNumberFormulaArg(math.Tanh(number)) } // TRUNC function truncates a supplied number to a specified number of decimal @@ -2938,20 +2839,19 @@ func (fn *formulaFuncs) TANH(argsList *list.List) (result string, err error) { // // TRUNC(number,[number_digits]) // -func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) TRUNC(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("TRUNC requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "TRUNC requires at least 1 argument") } var number, digits, adjust, rtrim float64 - if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + var err error + number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if argsList.Len() > 1 { if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } digits = math.Floor(digits) } @@ -2959,15 +2859,13 @@ func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) { x := int((math.Abs(number) - math.Abs(float64(int(number)))) * adjust) if x != 0 { if rtrim, err = strconv.ParseFloat(strings.TrimRight(strconv.Itoa(x), "0"), 64); err != nil { - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } } if (digits > 0) && (rtrim < adjust/10) { - result = fmt.Sprintf("%g", number) - return + return newNumberFormulaArg(number) } - result = fmt.Sprintf("%g", float64(int(number*adjust))/adjust) - return + return newNumberFormulaArg(float64(int(number*adjust)) / adjust) } // Statistical functions @@ -2977,7 +2875,7 @@ func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) { // // COUNTA(value1,[value2],...) // -func (fn *formulaFuncs) COUNTA(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) COUNTA(argsList *list.List) formulaArg { var count int for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) @@ -2996,8 +2894,7 @@ func (fn *formulaFuncs) COUNTA(argsList *list.List) (result string, err error) { } } } - result = fmt.Sprintf("%d", count) - return + return newStringFormulaArg(fmt.Sprintf("%d", count)) } // MEDIAN function returns the statistical median (the middle value) of a list @@ -3005,20 +2902,19 @@ func (fn *formulaFuncs) COUNTA(argsList *list.List) (result string, err error) { // // MEDIAN(number1,[number2],...) // -func (fn *formulaFuncs) MEDIAN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) MEDIAN(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("MEDIAN requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "MEDIAN requires at least 1 argument") } - values := []float64{} + var values = []float64{} var median, digits float64 + var err error for token := argsList.Front(); token != nil; token = token.Next() { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } values = append(values, digits) case ArgMatrix: @@ -3028,8 +2924,7 @@ func (fn *formulaFuncs) MEDIAN(argsList *list.List) (result string, err error) { continue } if digits, err = strconv.ParseFloat(value.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } values = append(values, digits) } @@ -3042,8 +2937,7 @@ func (fn *formulaFuncs) MEDIAN(argsList *list.List) (result string, err error) { } else { median = values[len(values)/2] } - result = fmt.Sprintf("%g", median) - return + return newNumberFormulaArg(median) } // Information functions @@ -3054,13 +2948,12 @@ func (fn *formulaFuncs) MEDIAN(argsList *list.List) (result string, err error) { // // ISBLANK(value) // -func (fn *formulaFuncs) ISBLANK(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISBLANK(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISBLANK requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISBLANK requires 1 argument") } token := argsList.Front().Value.(formulaArg) - result = "FALSE" + result := "FALSE" switch token.Type { case ArgUnknown: result = "TRUE" @@ -3069,7 +2962,7 @@ func (fn *formulaFuncs) ISBLANK(argsList *list.List) (result string, err error) result = "TRUE" } } - return + return newStringFormulaArg(result) } // ISERR function tests if an initial supplied expression (or value) returns @@ -3079,13 +2972,12 @@ func (fn *formulaFuncs) ISBLANK(argsList *list.List) (result string, err error) // // ISERR(value) // -func (fn *formulaFuncs) ISERR(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISERR(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISERR requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISERR requires 1 argument") } token := argsList.Front().Value.(formulaArg) - result = "FALSE" + result := "FALSE" if token.Type == ArgString { for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} { if errType == token.String { @@ -3093,7 +2985,7 @@ func (fn *formulaFuncs) ISERR(argsList *list.List) (result string, err error) { } } } - return + return newStringFormulaArg(result) } // ISERROR function tests if an initial supplied expression (or value) returns @@ -3102,13 +2994,12 @@ func (fn *formulaFuncs) ISERR(argsList *list.List) (result string, err error) { // // ISERROR(value) // -func (fn *formulaFuncs) ISERROR(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISERROR(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISERROR requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISERROR requires 1 argument") } token := argsList.Front().Value.(formulaArg) - result = "FALSE" + result := "FALSE" if token.Type == ArgString { for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNA, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} { if errType == token.String { @@ -3116,7 +3007,7 @@ func (fn *formulaFuncs) ISERROR(argsList *list.List) (result string, err error) } } } - return + return newStringFormulaArg(result) } // ISEVEN function tests if a supplied number (or numeric expression) @@ -3125,25 +3016,25 @@ func (fn *formulaFuncs) ISERROR(argsList *list.List) (result string, err error) // // ISEVEN(value) // -func (fn *formulaFuncs) ISEVEN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISEVEN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISEVEN requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISEVEN requires 1 argument") } - token := argsList.Front().Value.(formulaArg) - result = "FALSE" - var numeric int + var ( + token = argsList.Front().Value.(formulaArg) + result = "FALSE" + numeric int + err error + ) if token.Type == ArgString { if numeric, err = strconv.Atoi(token.String); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if numeric == numeric/2*2 { - result = "TRUE" - return + return newStringFormulaArg("TRUE") } } - return + return newStringFormulaArg(result) } // ISNA function tests if an initial supplied expression (or value) returns @@ -3152,17 +3043,16 @@ func (fn *formulaFuncs) ISEVEN(argsList *list.List) (result string, err error) { // // ISNA(value) // -func (fn *formulaFuncs) ISNA(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISNA(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISNA requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISNA requires 1 argument") } token := argsList.Front().Value.(formulaArg) - result = "FALSE" + result := "FALSE" if token.Type == ArgString && token.String == formulaErrorNA { result = "TRUE" } - return + return newStringFormulaArg(result) } // ISNONTEXT function function tests if a supplied value is text. If not, the @@ -3171,17 +3061,16 @@ func (fn *formulaFuncs) ISNA(argsList *list.List) (result string, err error) { // // ISNONTEXT(value) // -func (fn *formulaFuncs) ISNONTEXT(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISNONTEXT(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISNONTEXT requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISNONTEXT requires 1 argument") } token := argsList.Front().Value.(formulaArg) - result = "TRUE" + result := "TRUE" if token.Type == ArgString && token.String != "" { result = "FALSE" } - return + return newStringFormulaArg(result) } // ISNUMBER function function tests if a supplied value is a number. If so, @@ -3190,20 +3079,18 @@ func (fn *formulaFuncs) ISNONTEXT(argsList *list.List) (result string, err error // // ISNUMBER(value) // -func (fn *formulaFuncs) ISNUMBER(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISNUMBER(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISNUMBER requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISNUMBER requires 1 argument") } token := argsList.Front().Value.(formulaArg) - result = "FALSE" + result := "FALSE" if token.Type == ArgString && token.String != "" { - if _, err = strconv.Atoi(token.String); err == nil { + if _, err := strconv.Atoi(token.String); err == nil { result = "TRUE" } - err = nil } - return + return newStringFormulaArg(result) } // ISODD function tests if a supplied number (or numeric expression) evaluates @@ -3212,25 +3099,25 @@ func (fn *formulaFuncs) ISNUMBER(argsList *list.List) (result string, err error) // // ISODD(value) // -func (fn *formulaFuncs) ISODD(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) ISODD(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("ISODD requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "ISODD requires 1 argument") } - token := argsList.Front().Value.(formulaArg) - result = "FALSE" - var numeric int + var ( + token = argsList.Front().Value.(formulaArg) + result = "FALSE" + numeric int + err error + ) if token.Type == ArgString { if numeric, err = strconv.Atoi(token.String); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if numeric != numeric/2*2 { - result = "TRUE" - return + return newStringFormulaArg("TRUE") } } - return + return newStringFormulaArg(result) } // NA function returns the Excel #N/A error. This error message has the @@ -3239,13 +3126,11 @@ func (fn *formulaFuncs) ISODD(argsList *list.List) (result string, err error) { // // NA() // -func (fn *formulaFuncs) NA(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) NA(argsList *list.List) formulaArg { if argsList.Len() != 0 { - err = errors.New("NA accepts no arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "NA accepts no arguments") } - result = formulaErrorNA - return + return newStringFormulaArg(formulaErrorNA) } // Logical Functions @@ -3255,17 +3140,18 @@ func (fn *formulaFuncs) NA(argsList *list.List) (result string, err error) { // // AND(logical_test1,[logical_test2],...) // -func (fn *formulaFuncs) AND(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) AND(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("AND requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "AND requires at least 1 argument") } if argsList.Len() > 30 { - err = errors.New("AND accepts at most 30 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "AND accepts at most 30 arguments") } - var and = true - var val float64 + var ( + and = true + val float64 + err error + ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { @@ -3276,22 +3162,18 @@ func (fn *formulaFuncs) AND(argsList *list.List) (result string, err error) { continue } if token.String == "FALSE" { - result = token.String - return + return newStringFormulaArg(token.String) } if val, err = strconv.ParseFloat(token.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } and = and && (val != 0) case ArgMatrix: // TODO - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } - result = strings.ToUpper(strconv.FormatBool(and)) - return + return newStringFormulaArg(strings.ToUpper(strconv.FormatBool(and))) } // OR function tests a number of supplied conditions and returns either TRUE @@ -3299,17 +3181,18 @@ func (fn *formulaFuncs) AND(argsList *list.List) (result string, err error) { // // OR(logical_test1,[logical_test2],...) // -func (fn *formulaFuncs) OR(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) OR(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("OR requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "OR requires at least 1 argument") } if argsList.Len() > 30 { - err = errors.New("OR accepts at most 30 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "OR accepts at most 30 arguments") } - var or bool - var val float64 + var ( + or bool + val float64 + err error + ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { @@ -3324,18 +3207,15 @@ func (fn *formulaFuncs) OR(argsList *list.List) (result string, err error) { continue } if val, err = strconv.ParseFloat(token.String, 64); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } or = val != 0 case ArgMatrix: // TODO - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) } } - result = strings.ToUpper(strconv.FormatBool(or)) - return + return newStringFormulaArg(strings.ToUpper(strconv.FormatBool(or))) } // Date and Time Functions @@ -3345,27 +3225,23 @@ func (fn *formulaFuncs) OR(argsList *list.List) (result string, err error) { // // DATE(year,month,day) // -func (fn *formulaFuncs) DATE(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) DATE(argsList *list.List) formulaArg { if argsList.Len() != 3 { - err = errors.New("DATE requires 3 number arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "DATE requires 3 number arguments") } var year, month, day int + var err error if year, err = strconv.Atoi(argsList.Front().Value.(formulaArg).String); err != nil { - err = errors.New("DATE requires 3 number arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "DATE requires 3 number arguments") } if month, err = strconv.Atoi(argsList.Front().Next().Value.(formulaArg).String); err != nil { - err = errors.New("DATE requires 3 number arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "DATE requires 3 number arguments") } if day, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String); err != nil { - err = errors.New("DATE requires 3 number arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "DATE requires 3 number arguments") } d := makeDate(year, time.Month(month), day) - result = timeFromExcelTime(daysBetween(excelMinTime1900.Unix(), d)+1, false).String() - return + return newStringFormulaArg(timeFromExcelTime(daysBetween(excelMinTime1900.Unix(), d)+1, false).String()) } // makeDate return date as a Unix time, the number of seconds elapsed since @@ -3391,10 +3267,9 @@ func daysBetween(startDate, endDate int64) float64 { // // CLEAN(text) // -func (fn *formulaFuncs) CLEAN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) CLEAN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("CLEAN requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "CLEAN requires 1 argument") } b := bytes.Buffer{} for _, c := range argsList.Front().Value.(formulaArg).String { @@ -3402,8 +3277,7 @@ func (fn *formulaFuncs) CLEAN(argsList *list.List) (result string, err error) { b.WriteRune(c) } } - result = b.String() - return + return newStringFormulaArg(b.String()) } // LEN returns the length of a supplied text string. The syntax of the @@ -3411,13 +3285,11 @@ func (fn *formulaFuncs) CLEAN(argsList *list.List) (result string, err error) { // // LEN(text) // -func (fn *formulaFuncs) LEN(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) LEN(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("LEN requires 1 string argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "LEN requires 1 string argument") } - result = strconv.Itoa(len(argsList.Front().Value.(formulaArg).String)) - return + return newStringFormulaArg(strconv.Itoa(len(argsList.Front().Value.(formulaArg).String))) } // TRIM removes extra spaces (i.e. all spaces except for single spaces between @@ -3426,13 +3298,11 @@ func (fn *formulaFuncs) LEN(argsList *list.List) (result string, err error) { // // TRIM(text) // -func (fn *formulaFuncs) TRIM(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) TRIM(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("TRIM requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "TRIM requires 1 argument") } - result = strings.TrimSpace(argsList.Front().Value.(formulaArg).String) - return + return newStringFormulaArg(strings.TrimSpace(argsList.Front().Value.(formulaArg).String)) } // LOWER converts all characters in a supplied text string to lower case. The @@ -3440,13 +3310,11 @@ func (fn *formulaFuncs) TRIM(argsList *list.List) (result string, err error) { // // LOWER(text) // -func (fn *formulaFuncs) LOWER(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) LOWER(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("LOWER requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "LOWER requires 1 argument") } - result = strings.ToLower(argsList.Front().Value.(formulaArg).String) - return + return newStringFormulaArg(strings.ToLower(argsList.Front().Value.(formulaArg).String)) } // PROPER converts all characters in a supplied text string to proper case @@ -3456,10 +3324,9 @@ func (fn *formulaFuncs) LOWER(argsList *list.List) (result string, err error) { // // PROPER(text) // -func (fn *formulaFuncs) PROPER(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) PROPER(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("PROPER requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "PROPER requires 1 argument") } buf := bytes.Buffer{} isLetter := false @@ -3471,9 +3338,7 @@ func (fn *formulaFuncs) PROPER(argsList *list.List) (result string, err error) { } isLetter = unicode.IsLetter(char) } - - result = buf.String() - return + return newStringFormulaArg(buf.String()) } // UPPER converts all characters in a supplied text string to upper case. The @@ -3481,13 +3346,11 @@ func (fn *formulaFuncs) PROPER(argsList *list.List) (result string, err error) { // // UPPER(text) // -func (fn *formulaFuncs) UPPER(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) UPPER(argsList *list.List) formulaArg { if argsList.Len() != 1 { - err = errors.New("UPPER requires 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "UPPER requires 1 argument") } - result = strings.ToUpper(argsList.Front().Value.(formulaArg).String) - return + return newStringFormulaArg(strings.ToUpper(argsList.Front().Value.(formulaArg).String)) } // Conditional Functions @@ -3496,36 +3359,61 @@ func (fn *formulaFuncs) UPPER(argsList *list.List) (result string, err error) { // condition evaluates to TRUE, and another result if the condition evaluates // to FALSE. The syntax of the function is: // -// IF( logical_test, value_if_true, value_if_false ) +// IF(logical_test,value_if_true,value_if_false) // -func (fn *formulaFuncs) IF(argsList *list.List) (result string, err error) { +func (fn *formulaFuncs) IF(argsList *list.List) formulaArg { if argsList.Len() == 0 { - err = errors.New("IF requires at least 1 argument") - return + return newErrorFormulaArg(formulaErrorVALUE, "IF requires at least 1 argument") } if argsList.Len() > 3 { - err = errors.New("IF accepts at most 3 arguments") - return + return newErrorFormulaArg(formulaErrorVALUE, "IF accepts at most 3 arguments") } token := argsList.Front().Value.(formulaArg) - var cond bool + var ( + cond bool + err error + result string + ) switch token.Type { case ArgString: if cond, err = strconv.ParseBool(token.String); err != nil { - err = errors.New(formulaErrorVALUE) - return + return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } if argsList.Len() == 1 { - result = strings.ToUpper(strconv.FormatBool(cond)) - return + return newStringFormulaArg(strings.ToUpper(strconv.FormatBool(cond))) } if cond { - result = argsList.Front().Next().Value.(formulaArg).String - return + return newStringFormulaArg(argsList.Front().Next().Value.(formulaArg).String) } if argsList.Len() == 3 { result = argsList.Back().Value.(formulaArg).String } } - return + return newStringFormulaArg(result) +} + +// Excel Lookup and Reference Functions + +// CHOOSE function returns a value from an array, that corresponds to a +// supplied index number (position). The syntax of the function is: +// +// CHOOSE(index_num,value1,[value2],...) +// +// TODO: resolve range choose. +func (fn *formulaFuncs) CHOOSE(argsList *list.List) formulaArg { + if argsList.Len() < 2 { + return newErrorFormulaArg(formulaErrorVALUE, "CHOOSE requires 2 arguments") + } + idx, err := strconv.Atoi(argsList.Front().Value.(formulaArg).String) + if err != nil { + return newErrorFormulaArg(formulaErrorVALUE, "CHOOSE requires first argument of type number") + } + if argsList.Len() <= idx { + return newErrorFormulaArg(formulaErrorVALUE, "index_num should be <= to the number of values") + } + arg := argsList.Front() + for i := 0; i < idx; i++ { + arg = arg.Next() + } + return newStringFormulaArg(arg.Value.(formulaArg).String) } diff --git a/calc_test.go b/calc_test.go index c999540..d890043 100644 --- a/calc_test.go +++ b/calc_test.go @@ -494,6 +494,10 @@ func TestCalcCellValue(t *testing.T) { "=IF(1<>1)": "FALSE", "=IF(5<0, \"negative\", \"positive\")": "positive", "=IF(-2<0, \"negative\", \"positive\")": "negative", + // Excel Lookup and Reference Functions + // CHOOSE + "=CHOOSE(4,\"red\",\"blue\",\"green\",\"brown\")": "brown", + "=CHOOSE(1,\"red\",\"blue\",\"green\",\"brown\")": "red", } for formula, expected := range mathCalc { f := prepareData() @@ -505,248 +509,248 @@ func TestCalcCellValue(t *testing.T) { mathCalcError := map[string]string{ // ABS "=ABS()": "ABS requires 1 numeric argument", - `=ABS("X")`: "#VALUE!", + `=ABS("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=ABS(~)": `cannot convert cell "~" to coordinates: invalid cell name "~"`, // ACOS "=ACOS()": "ACOS requires 1 numeric argument", - `=ACOS("X")`: "#VALUE!", + `=ACOS("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ACOSH "=ACOSH()": "ACOSH requires 1 numeric argument", - `=ACOSH("X")`: "#VALUE!", + `=ACOSH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.ACOT "=_xlfn.ACOT()": "ACOT requires 1 numeric argument", - `=_xlfn.ACOT("X")`: "#VALUE!", + `=_xlfn.ACOT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.ACOTH "=_xlfn.ACOTH()": "ACOTH requires 1 numeric argument", - `=_xlfn.ACOTH("X")`: "#VALUE!", + `=_xlfn.ACOTH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.ARABIC "=_xlfn.ARABIC()": "ARABIC requires 1 numeric argument", // ASIN "=ASIN()": "ASIN requires 1 numeric argument", - `=ASIN("X")`: "#VALUE!", + `=ASIN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ASINH "=ASINH()": "ASINH requires 1 numeric argument", - `=ASINH("X")`: "#VALUE!", + `=ASINH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ATAN "=ATAN()": "ATAN requires 1 numeric argument", - `=ATAN("X")`: "#VALUE!", + `=ATAN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ATANH "=ATANH()": "ATANH requires 1 numeric argument", - `=ATANH("X")`: "#VALUE!", + `=ATANH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ATAN2 "=ATAN2()": "ATAN2 requires 2 numeric arguments", - `=ATAN2("X",0)`: "#VALUE!", - `=ATAN2(0,"X")`: "#VALUE!", + `=ATAN2("X",0)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=ATAN2(0,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // BASE "=BASE()": "BASE requires at least 2 arguments", "=BASE(1,2,3,4)": "BASE allows at most 3 arguments", "=BASE(1,1)": "radix must be an integer >= 2 and <= 36", - `=BASE("X",2)`: "#VALUE!", - `=BASE(1,"X")`: "#VALUE!", - `=BASE(1,2,"X")`: "#VALUE!", + `=BASE("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=BASE(1,"X")`: "strconv.Atoi: parsing \"X\": invalid syntax", + `=BASE(1,2,"X")`: "strconv.Atoi: parsing \"X\": invalid syntax", // CEILING "=CEILING()": "CEILING requires at least 1 argument", "=CEILING(1,2,3)": "CEILING allows at most 2 arguments", "=CEILING(1,-1)": "negative sig to CEILING invalid", - `=CEILING("X",0)`: "#VALUE!", - `=CEILING(0,"X")`: "#VALUE!", + `=CEILING("X",0)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=CEILING(0,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.CEILING.MATH "=_xlfn.CEILING.MATH()": "CEILING.MATH requires at least 1 argument", "=_xlfn.CEILING.MATH(1,2,3,4)": "CEILING.MATH allows at most 3 arguments", - `=_xlfn.CEILING.MATH("X")`: "#VALUE!", - `=_xlfn.CEILING.MATH(1,"X")`: "#VALUE!", - `=_xlfn.CEILING.MATH(1,2,"X")`: "#VALUE!", + `=_xlfn.CEILING.MATH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=_xlfn.CEILING.MATH(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=_xlfn.CEILING.MATH(1,2,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.CEILING.PRECISE "=_xlfn.CEILING.PRECISE()": "CEILING.PRECISE requires at least 1 argument", "=_xlfn.CEILING.PRECISE(1,2,3)": "CEILING.PRECISE allows at most 2 arguments", - `=_xlfn.CEILING.PRECISE("X",2)`: "#VALUE!", + `=_xlfn.CEILING.PRECISE("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", `=_xlfn.CEILING.PRECISE(1,"X")`: "#VALUE!", // COMBIN "=COMBIN()": "COMBIN requires 2 argument", "=COMBIN(-1,1)": "COMBIN requires number >= number_chosen", - `=COMBIN("X",1)`: "#VALUE!", - `=COMBIN(-1,"X")`: "#VALUE!", + `=COMBIN("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=COMBIN(-1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.COMBINA "=_xlfn.COMBINA()": "COMBINA requires 2 argument", "=_xlfn.COMBINA(-1,1)": "COMBINA requires number > number_chosen", "=_xlfn.COMBINA(-1,-1)": "COMBIN requires number >= number_chosen", - `=_xlfn.COMBINA("X",1)`: "#VALUE!", - `=_xlfn.COMBINA(-1,"X")`: "#VALUE!", + `=_xlfn.COMBINA("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=_xlfn.COMBINA(-1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // COS "=COS()": "COS requires 1 numeric argument", - `=COS("X")`: "#VALUE!", + `=COS("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // COSH "=COSH()": "COSH requires 1 numeric argument", - `=COSH("X")`: "#VALUE!", + `=COSH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.COT "=COT()": "COT requires 1 numeric argument", - `=COT("X")`: "#VALUE!", + `=COT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=COT(0)": "#DIV/0!", // _xlfn.COTH "=COTH()": "COTH requires 1 numeric argument", - `=COTH("X")`: "#VALUE!", + `=COTH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=COTH(0)": "#DIV/0!", // _xlfn.CSC "=_xlfn.CSC()": "CSC requires 1 numeric argument", - `=_xlfn.CSC("X")`: "#VALUE!", + `=_xlfn.CSC("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=_xlfn.CSC(0)": "#DIV/0!", // _xlfn.CSCH "=_xlfn.CSCH()": "CSCH requires 1 numeric argument", - `=_xlfn.CSCH("X")`: "#VALUE!", + `=_xlfn.CSCH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=_xlfn.CSCH(0)": "#DIV/0!", // _xlfn.DECIMAL "=_xlfn.DECIMAL()": "DECIMAL requires 2 numeric arguments", - `=_xlfn.DECIMAL("X", 2)`: "#VALUE!", - `=_xlfn.DECIMAL(2000, "X")`: "#VALUE!", + `=_xlfn.DECIMAL("X", 2)`: "strconv.ParseInt: parsing \"X\": invalid syntax", + `=_xlfn.DECIMAL(2000, "X")`: "strconv.Atoi: parsing \"X\": invalid syntax", // DEGREES "=DEGREES()": "DEGREES requires 1 numeric argument", - `=DEGREES("X")`: "#VALUE!", + `=DEGREES("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=DEGREES(0)": "#DIV/0!", // EVEN "=EVEN()": "EVEN requires 1 numeric argument", - `=EVEN("X")`: "#VALUE!", + `=EVEN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // EXP "=EXP()": "EXP requires 1 numeric argument", - `=EXP("X")`: "#VALUE!", + `=EXP("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // FACT "=FACT()": "FACT requires 1 numeric argument", - `=FACT("X")`: "#VALUE!", + `=FACT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=FACT(-1)": "#NUM!", // FACTDOUBLE "=FACTDOUBLE()": "FACTDOUBLE requires 1 numeric argument", - `=FACTDOUBLE("X")`: "#VALUE!", + `=FACTDOUBLE("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=FACTDOUBLE(-1)": "#NUM!", // FLOOR "=FLOOR()": "FLOOR requires 2 numeric arguments", - `=FLOOR("X",-1)`: "#VALUE!", - `=FLOOR(1,"X")`: "#VALUE!", - "=FLOOR(1,-1)": "#NUM!", + `=FLOOR("X",-1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=FLOOR(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + "=FLOOR(1,-1)": "invalid arguments to FLOOR", // _xlfn.FLOOR.MATH "=_xlfn.FLOOR.MATH()": "FLOOR.MATH requires at least 1 argument", "=_xlfn.FLOOR.MATH(1,2,3,4)": "FLOOR.MATH allows at most 3 arguments", - `=_xlfn.FLOOR.MATH("X",2,3)`: "#VALUE!", - `=_xlfn.FLOOR.MATH(1,"X",3)`: "#VALUE!", - `=_xlfn.FLOOR.MATH(1,2,"X")`: "#VALUE!", + `=_xlfn.FLOOR.MATH("X",2,3)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=_xlfn.FLOOR.MATH(1,"X",3)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=_xlfn.FLOOR.MATH(1,2,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.FLOOR.PRECISE "=_xlfn.FLOOR.PRECISE()": "FLOOR.PRECISE requires at least 1 argument", "=_xlfn.FLOOR.PRECISE(1,2,3)": "FLOOR.PRECISE allows at most 2 arguments", - `=_xlfn.FLOOR.PRECISE("X",2)`: "#VALUE!", - `=_xlfn.FLOOR.PRECISE(1,"X")`: "#VALUE!", + `=_xlfn.FLOOR.PRECISE("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=_xlfn.FLOOR.PRECISE(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // GCD "=GCD()": "GCD requires at least 1 argument", "=GCD(-1)": "GCD only accepts positive arguments", "=GCD(1,-1)": "GCD only accepts positive arguments", - `=GCD("X")`: "#VALUE!", + `=GCD("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // INT "=INT()": "INT requires 1 numeric argument", - `=INT("X")`: "#VALUE!", + `=INT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ISO.CEILING "=ISO.CEILING()": "ISO.CEILING requires at least 1 argument", "=ISO.CEILING(1,2,3)": "ISO.CEILING allows at most 2 arguments", - `=ISO.CEILING("X",2)`: "#VALUE!", - `=ISO.CEILING(1,"X")`: "#VALUE!", + `=ISO.CEILING("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=ISO.CEILING(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // LCM "=LCM()": "LCM requires at least 1 argument", "=LCM(-1)": "LCM only accepts positive arguments", "=LCM(1,-1)": "LCM only accepts positive arguments", - `=LCM("X")`: "#VALUE!", + `=LCM("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // LN "=LN()": "LN requires 1 numeric argument", - `=LN("X")`: "#VALUE!", + `=LN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // LOG "=LOG()": "LOG requires at least 1 argument", "=LOG(1,2,3)": "LOG allows at most 2 arguments", - `=LOG("X",1)`: "#VALUE!", - `=LOG(1,"X")`: "#VALUE!", - "=LOG(0,0)": "#NUM!", - "=LOG(1,0)": "#NUM!", + `=LOG("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=LOG(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + "=LOG(0,0)": "#DIV/0!", + "=LOG(1,0)": "#DIV/0!", "=LOG(1,1)": "#DIV/0!", // LOG10 "=LOG10()": "LOG10 requires 1 numeric argument", - `=LOG10("X")`: "#VALUE!", + `=LOG10("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // MOD "=MOD()": "MOD requires 2 numeric arguments", - "=MOD(6,0)": "#DIV/0!", - `=MOD("X",0)`: "#VALUE!", - `=MOD(6,"X")`: "#VALUE!", + "=MOD(6,0)": "MOD divide by zero", + `=MOD("X",0)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=MOD(6,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // MROUND "=MROUND()": "MROUND requires 2 numeric arguments", "=MROUND(1,0)": "#NUM!", "=MROUND(1,-1)": "#NUM!", - `=MROUND("X",0)`: "#VALUE!", - `=MROUND(1,"X")`: "#VALUE!", + `=MROUND("X",0)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=MROUND(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // MULTINOMIAL - `=MULTINOMIAL("X")`: "#VALUE!", + `=MULTINOMIAL("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.MUNIT - "=_xlfn.MUNIT()": "MUNIT requires 1 numeric argument", // not support currently - `=_xlfn.MUNIT("X")`: "#VALUE!", // not support currently + "=_xlfn.MUNIT()": "MUNIT requires 1 numeric argument", // not support currently + `=_xlfn.MUNIT("X")`: "strconv.Atoi: parsing \"X\": invalid syntax", // not support currently // ODD "=ODD()": "ODD requires 1 numeric argument", - `=ODD("X")`: "#VALUE!", + `=ODD("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // PI "=PI(1)": "PI accepts no arguments", // POWER - `=POWER("X",1)`: "#VALUE!", - `=POWER(1,"X")`: "#VALUE!", + `=POWER("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=POWER(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=POWER(0,0)": "#NUM!", "=POWER(0,-1)": "#DIV/0!", "=POWER(1)": "POWER requires 2 numeric arguments", // PRODUCT - `=PRODUCT("X")`: "#VALUE!", + `=PRODUCT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // QUOTIENT - `=QUOTIENT("X",1)`: "#VALUE!", - `=QUOTIENT(1,"X")`: "#VALUE!", + `=QUOTIENT("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=QUOTIENT(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=QUOTIENT(1,0)": "#DIV/0!", "=QUOTIENT(1)": "QUOTIENT requires 2 numeric arguments", // RADIANS - `=RADIANS("X")`: "#VALUE!", + `=RADIANS("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=RADIANS()": "RADIANS requires 1 numeric argument", // RAND "=RAND(1)": "RAND accepts no arguments", // RANDBETWEEN - `=RANDBETWEEN("X",1)`: "#VALUE!", - `=RANDBETWEEN(1,"X")`: "#VALUE!", + `=RANDBETWEEN("X",1)`: "strconv.ParseInt: parsing \"X\": invalid syntax", + `=RANDBETWEEN(1,"X")`: "strconv.ParseInt: parsing \"X\": invalid syntax", "=RANDBETWEEN()": "RANDBETWEEN requires 2 numeric arguments", "=RANDBETWEEN(2,1)": "#NUM!", // ROMAN "=ROMAN()": "ROMAN requires at least 1 argument", "=ROMAN(1,2,3)": "ROMAN allows at most 2 arguments", - `=ROMAN("X")`: "#VALUE!", - `=ROMAN("X",1)`: "#VALUE!", + `=ROMAN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=ROMAN("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ROUND "=ROUND()": "ROUND requires 2 numeric arguments", - `=ROUND("X",1)`: "#VALUE!", - `=ROUND(1,"X")`: "#VALUE!", + `=ROUND("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=ROUND(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ROUNDDOWN "=ROUNDDOWN()": "ROUNDDOWN requires 2 numeric arguments", - `=ROUNDDOWN("X",1)`: "#VALUE!", - `=ROUNDDOWN(1,"X")`: "#VALUE!", + `=ROUNDDOWN("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=ROUNDDOWN(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // ROUNDUP "=ROUNDUP()": "ROUNDUP requires 2 numeric arguments", - `=ROUNDUP("X",1)`: "#VALUE!", - `=ROUNDUP(1,"X")`: "#VALUE!", + `=ROUNDUP("X",1)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=ROUNDUP(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // SEC "=_xlfn.SEC()": "SEC requires 1 numeric argument", - `=_xlfn.SEC("X")`: "#VALUE!", + `=_xlfn.SEC("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // _xlfn.SECH "=_xlfn.SECH()": "SECH requires 1 numeric argument", - `=_xlfn.SECH("X")`: "#VALUE!", + `=_xlfn.SECH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // SIGN "=SIGN()": "SIGN requires 1 numeric argument", - `=SIGN("X")`: "#VALUE!", + `=SIGN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // SIN "=SIN()": "SIN requires 1 numeric argument", - `=SIN("X")`: "#VALUE!", + `=SIN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // SINH "=SINH()": "SINH requires 1 numeric argument", - `=SINH("X")`: "#VALUE!", + `=SINH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // SQRT "=SQRT()": "SQRT requires 1 numeric argument", - `=SQRT("X")`: "#VALUE!", + `=SQRT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", "=SQRT(-1)": "#NUM!", // SQRTPI "=SQRTPI()": "SQRTPI requires 1 numeric argument", - `=SQRTPI("X")`: "#VALUE!", + `=SQRTPI("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // SUM "=SUM((": "formula not valid", "=SUM(-)": "formula not valid", @@ -754,21 +758,21 @@ func TestCalcCellValue(t *testing.T) { "=SUM(1-)": "formula not valid", "=SUM(1*)": "formula not valid", "=SUM(1/)": "formula not valid", - `=SUM("X")`: "#VALUE!", + `=SUM("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // SUMIF "=SUMIF()": "SUMIF requires at least 2 argument", // SUMSQ - `=SUMSQ("X")`: "#VALUE!", + `=SUMSQ("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // TAN "=TAN()": "TAN requires 1 numeric argument", - `=TAN("X")`: "#VALUE!", + `=TAN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // TANH "=TANH()": "TANH requires 1 numeric argument", - `=TANH("X")`: "#VALUE!", + `=TANH("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // TRUNC "=TRUNC()": "TRUNC requires at least 1 argument", - `=TRUNC("X")`: "#VALUE!", - `=TRUNC(1,"X")`: "#VALUE!", + `=TRUNC("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=TRUNC(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // Statistical Functions // MEDIAN "=MEDIAN()": "MEDIAN requires at least 1 argument", @@ -781,7 +785,7 @@ func TestCalcCellValue(t *testing.T) { "=ISERROR()": "ISERROR requires 1 argument", // ISEVEN "=ISEVEN()": "ISEVEN requires 1 argument", - `=ISEVEN("text")`: "#VALUE!", + `=ISEVEN("text")`: "strconv.Atoi: parsing \"text\": invalid syntax", // ISNA "=ISNA()": "ISNA requires 1 argument", // ISNONTEXT @@ -790,17 +794,17 @@ func TestCalcCellValue(t *testing.T) { "=ISNUMBER()": "ISNUMBER requires 1 argument", // ISODD "=ISODD()": "ISODD requires 1 argument", - `=ISODD("text")`: "#VALUE!", + `=ISODD("text")`: "strconv.Atoi: parsing \"text\": invalid syntax", // NA "=NA(1)": "NA accepts no arguments", // Logical Functions // AND - `=AND("text")`: "#VALUE!", + `=AND("text")`: "strconv.ParseFloat: parsing \"text\": invalid syntax", `=AND(A1:B1)`: "#VALUE!", "=AND()": "AND requires at least 1 argument", "=AND(1" + strings.Repeat(",1", 30) + ")": "AND accepts at most 30 arguments", // OR - `=OR("text")`: "#VALUE!", + `=OR("text")`: "strconv.ParseFloat: parsing \"text\": invalid syntax", `=OR(A1:B1)`: "#VALUE!", "=OR()": "OR requires at least 1 argument", "=OR(1" + strings.Repeat(",1", 30) + ")": "OR accepts at most 30 arguments", @@ -832,13 +836,18 @@ func TestCalcCellValue(t *testing.T) { // IF "=IF()": "IF requires at least 1 argument", "=IF(0,1,2,3)": "IF accepts at most 3 arguments", - "=IF(D1,1,2)": "#VALUE!", + "=IF(D1,1,2)": "strconv.ParseBool: parsing \"Month\": invalid syntax", + // Excel Lookup and Reference Functions + // CHOOSE + "=CHOOSE()": "CHOOSE requires 2 arguments", + "=CHOOSE(\"index_num\",0)": "CHOOSE requires first argument of type number", + "=CHOOSE(2,0)": "index_num should be <= to the number of values", } for formula, expected := range mathCalcError { f := prepareData() assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula)) result, err := f.CalcCellValue("Sheet1", "C1") - assert.EqualError(t, err, expected) + assert.EqualError(t, err, expected, formula) assert.Equal(t, "", result, formula) } @@ -976,9 +985,9 @@ func TestISBLANK(t *testing.T) { Type: ArgUnknown, }) fn := formulaFuncs{} - result, err := fn.ISBLANK(argsList) - assert.Equal(t, result, "TRUE") - assert.NoError(t, err) + result := fn.ISBLANK(argsList) + assert.Equal(t, result.String, "TRUE") + assert.Empty(t, result.Error) } func TestAND(t *testing.T) { @@ -987,9 +996,9 @@ func TestAND(t *testing.T) { Type: ArgUnknown, }) fn := formulaFuncs{} - result, err := fn.AND(argsList) - assert.Equal(t, result, "TRUE") - assert.NoError(t, err) + result := fn.AND(argsList) + assert.Equal(t, result.String, "TRUE") + assert.Empty(t, result.Error) } func TestOR(t *testing.T) { @@ -998,9 +1007,9 @@ func TestOR(t *testing.T) { Type: ArgUnknown, }) fn := formulaFuncs{} - result, err := fn.OR(argsList) - assert.Equal(t, result, "FALSE") - assert.NoError(t, err) + result := fn.OR(argsList) + assert.Equal(t, result.String, "FALSE") + assert.Empty(t, result.Error) } func TestDet(t *testing.T) { diff --git a/go.mod b/go.mod index 9637ba0..41babe1 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/richardlehane/mscfb v1.0.3 github.com/stretchr/testify v1.6.1 - github.com/xuri/efp v0.0.0-20201016154823-031c29024257 - golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee + github.com/xuri/efp v0.0.0-20210128032744-13be4fd5dcb5 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/image v0.0.0-20201208152932-35266b937fa6 - golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 - golang.org/x/text v0.3.3 + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 + golang.org/x/text v0.3.5 ) diff --git a/go.sum b/go.sum index 24aa225..75323d6 100644 --- a/go.sum +++ b/go.sum @@ -8,28 +8,30 @@ github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o= github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/xuri/efp v0.0.0-20201016154823-031c29024257 h1:6ldmGEJXtsRMwdR2KuS3esk9wjVJNvgk05/YY2XmOj0= -github.com/xuri/efp v0.0.0-20201016154823-031c29024257/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk= +github.com/xuri/efp v0.0.0-20210128032744-13be4fd5dcb5 h1:hO7we8DcWAkmZX/Voqa04Tuo84SbeXIJbdgMj92hIpA= +github.com/xuri/efp v0.0.0-20210128032744-13be4fd5dcb5/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI= -golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 h1:5kGOVHlq0euqwzgTC9Vu15p6fV1Wi0ArVi8da2urnVg= -golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=