summaryrefslogtreecommitdiff
path: root/calc.go
diff options
context:
space:
mode:
Diffstat (limited to 'calc.go')
-rw-r--r--calc.go153
1 files changed, 123 insertions, 30 deletions
diff --git a/calc.go b/calc.go
index ba9085e..60ca0cb 100644
--- a/calc.go
+++ b/calc.go
@@ -357,6 +357,8 @@ type formulaFuncs struct {
// HLOOKUP
// IF
// IFERROR
+// IFNA
+// IFS
// IMABS
// IMAGINARY
// IMARGUMENT
@@ -754,12 +756,12 @@ func (f *File) evalInfixExpFunc(sheet, cell string, token, nextToken efp.Token,
}
// calcPow evaluate exponentiation arithmetic operations.
-func calcPow(rOpd, lOpd string, opdStack *Stack) error {
- lOpdVal, err := strconv.ParseFloat(lOpd, 64)
+func calcPow(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
if err != nil {
return err
}
- rOpdVal, err := strconv.ParseFloat(rOpd, 64)
+ rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
if err != nil {
return err
}
@@ -769,54 +771,106 @@ func calcPow(rOpd, lOpd string, opdStack *Stack) error {
}
// calcEq evaluate equal arithmetic operations.
-func calcEq(rOpd, lOpd string, opdStack *Stack) error {
+func calcEq(rOpd, lOpd efp.Token, opdStack *Stack) error {
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpd == lOpd)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
return nil
}
// calcNEq evaluate not equal arithmetic operations.
-func calcNEq(rOpd, lOpd string, opdStack *Stack) error {
+func calcNEq(rOpd, lOpd efp.Token, opdStack *Stack) error {
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpd != lOpd)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
return nil
}
// calcL evaluate less than arithmetic operations.
-func calcL(rOpd, lOpd string, opdStack *Stack) error {
- opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) == -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+func calcL(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber {
+ lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64)
+ rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64)
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal < rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) == -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
return nil
}
// calcLe evaluate less than or equal arithmetic operations.
-func calcLe(rOpd, lOpd string, opdStack *Stack) error {
- opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) != 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+func calcLe(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber {
+ lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64)
+ rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64)
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal <= rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) != 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
return nil
}
// calcG evaluate greater than or equal arithmetic operations.
-func calcG(rOpd, lOpd string, opdStack *Stack) error {
- opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) == 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+func calcG(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber {
+ lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64)
+ rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64)
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal > rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) == 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
return nil
}
// calcGe evaluate greater than or equal arithmetic operations.
-func calcGe(rOpd, lOpd string, opdStack *Stack) error {
- opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) != -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+func calcGe(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeNumber {
+ lOpdVal, _ := strconv.ParseFloat(lOpd.TValue, 64)
+ rOpdVal, _ := strconv.ParseFloat(rOpd.TValue, 64)
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(lOpdVal >= rOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd.TValue, rOpd.TValue) != -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeNumber && lOpd.TSubType == efp.TokenSubTypeText {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(true)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ if rOpd.TSubType == efp.TokenSubTypeText && lOpd.TSubType == efp.TokenSubTypeNumber {
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(false)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
return nil
}
// calcSplice evaluate splice '&' operations.
-func calcSplice(rOpd, lOpd string, opdStack *Stack) error {
- opdStack.Push(efp.Token{TValue: lOpd + rOpd, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+func calcSplice(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ opdStack.Push(efp.Token{TValue: lOpd.TValue + rOpd.TValue, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
return nil
}
// calcAdd evaluate addition arithmetic operations.
-func calcAdd(rOpd, lOpd string, opdStack *Stack) error {
- lOpdVal, err := strconv.ParseFloat(lOpd, 64)
+func calcAdd(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
if err != nil {
return err
}
- rOpdVal, err := strconv.ParseFloat(rOpd, 64)
+ rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
if err != nil {
return err
}
@@ -826,12 +880,12 @@ func calcAdd(rOpd, lOpd string, opdStack *Stack) error {
}
// calcSubtract evaluate subtraction arithmetic operations.
-func calcSubtract(rOpd, lOpd string, opdStack *Stack) error {
- lOpdVal, err := strconv.ParseFloat(lOpd, 64)
+func calcSubtract(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
if err != nil {
return err
}
- rOpdVal, err := strconv.ParseFloat(rOpd, 64)
+ rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
if err != nil {
return err
}
@@ -841,12 +895,12 @@ func calcSubtract(rOpd, lOpd string, opdStack *Stack) error {
}
// calcMultiply evaluate multiplication arithmetic operations.
-func calcMultiply(rOpd, lOpd string, opdStack *Stack) error {
- lOpdVal, err := strconv.ParseFloat(lOpd, 64)
+func calcMultiply(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
if err != nil {
return err
}
- rOpdVal, err := strconv.ParseFloat(rOpd, 64)
+ rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
if err != nil {
return err
}
@@ -856,12 +910,12 @@ func calcMultiply(rOpd, lOpd string, opdStack *Stack) error {
}
// calcDiv evaluate division arithmetic operations.
-func calcDiv(rOpd, lOpd string, opdStack *Stack) error {
- lOpdVal, err := strconv.ParseFloat(lOpd, 64)
+func calcDiv(rOpd, lOpd efp.Token, opdStack *Stack) error {
+ lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
if err != nil {
return err
}
- rOpdVal, err := strconv.ParseFloat(rOpd, 64)
+ rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
if err != nil {
return err
}
@@ -887,7 +941,7 @@ func calculate(opdStack *Stack, opt efp.Token) error {
result := 0 - opdVal
opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
}
- tokenCalcFunc := map[string]func(rOpd, lOpd string, opdStack *Stack) error{
+ tokenCalcFunc := map[string]func(rOpd, lOpd efp.Token, opdStack *Stack) error{
"^": calcPow,
"*": calcMultiply,
"/": calcDiv,
@@ -906,7 +960,7 @@ func calculate(opdStack *Stack, opt efp.Token) error {
}
rOpd := opdStack.Pop().(efp.Token)
lOpd := opdStack.Pop().(efp.Token)
- if err := calcSubtract(rOpd.TValue, lOpd.TValue, opdStack); err != nil {
+ if err := calcSubtract(rOpd, lOpd, opdStack); err != nil {
return err
}
}
@@ -917,7 +971,8 @@ func calculate(opdStack *Stack, opt efp.Token) error {
}
rOpd := opdStack.Pop().(efp.Token)
lOpd := opdStack.Pop().(efp.Token)
- if err := fn(rOpd.TValue, lOpd.TValue, opdStack); err != nil {
+
+ if err := fn(rOpd, lOpd, opdStack); err != nil {
return err
}
}
@@ -6056,6 +6111,44 @@ func (fn *formulaFuncs) IFERROR(argsList *list.List) formulaArg {
return argsList.Back().Value.(formulaArg)
}
+// IFNA function tests if an initial supplied value (or expression) evaluates
+// to the Excel #N/A error. If so, the function returns a second supplied
+// value; Otherwise the function returns the first supplied value. The syntax
+// of the function is:
+//
+// IFNA(value,value_if_na)
+//
+func (fn *formulaFuncs) IFNA(argsList *list.List) formulaArg {
+ if argsList.Len() != 2 {
+ return newErrorFormulaArg(formulaErrorVALUE, "IFNA requires 2 arguments")
+ }
+ arg := argsList.Front().Value.(formulaArg)
+ if arg.Type == ArgError && arg.Value() == formulaErrorNA {
+ return argsList.Back().Value.(formulaArg)
+ }
+ return arg
+}
+
+// IFS function tests a number of supplied conditions and returns the result
+// corresponding to the first condition that evaluates to TRUE. If none of
+// the supplied conditions evaluate to TRUE, the function returns the #N/A
+// error.
+//
+// IFS(logical_test1,value_if_true1,[logical_test2,value_if_true2],...)
+//
+func (fn *formulaFuncs) IFS(argsList *list.List) formulaArg {
+ if argsList.Len() < 2 {
+ return newErrorFormulaArg(formulaErrorVALUE, "IFS requires at least 2 arguments")
+ }
+ for arg := argsList.Front(); arg != nil; arg = arg.Next() {
+ if arg.Value.(formulaArg).ToBool().Number == 1 {
+ return arg.Next().Value.(formulaArg)
+ }
+ arg = arg.Next()
+ }
+ return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
+}
+
// NOT function returns the opposite to a supplied logical value. The syntax
// of the function is:
//