|
|
@@ -93,21 +93,36 @@ type formulaArg struct {
|
|
|
// formulaFuncs is the type of the formula functions.
|
|
|
type formulaFuncs struct{}
|
|
|
|
|
|
+// tokenPriority defined basic arithmetic operator priority.
|
|
|
+var tokenPriority = map[string]int{
|
|
|
+ "^": 5,
|
|
|
+ "*": 4,
|
|
|
+ "/": 4,
|
|
|
+ "+": 3,
|
|
|
+ "-": 3,
|
|
|
+ "=": 2,
|
|
|
+ "<": 2,
|
|
|
+ "<=": 2,
|
|
|
+ ">": 2,
|
|
|
+ ">=": 2,
|
|
|
+ "&": 1,
|
|
|
+}
|
|
|
+
|
|
|
// CalcCellValue provides a function to get calculated cell value. This
|
|
|
// feature is currently in working processing. Array formula, table formula
|
|
|
// and some other formulas are not supported currently.
|
|
|
//
|
|
|
// Supported formulas:
|
|
|
//
|
|
|
-// ABS, ACOS, ACOSH, ACOT, ACOTH, ARABIC, ASIN, ASINH, ATAN2, ATANH, BASE,
|
|
|
-// CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS, COSH, COT,
|
|
|
-// COTH, COUNTA, CSC, CSCH, 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, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA, ODD, PI,
|
|
|
-// POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN, ROUND, ROUNDDOWN,
|
|
|
-// ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI, SUM, SUMIF, SUMSQ,
|
|
|
-// TAN, TANH, TRUNC
|
|
|
+// ABS, ACOS, ACOSH, ACOT, ACOTH, AND, ARABIC, ASIN, ASINH, ATAN2, ATANH,
|
|
|
+// BASE, CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS,
|
|
|
+// COSH, COT, COTH, COUNTA, CSC, CSCH, 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, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA,
|
|
|
+// ODD, OR, PI, POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN,
|
|
|
+// ROUND, ROUNDDOWN, ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI,
|
|
|
+// SUM, SUMIF, SUMSQ, TAN, TANH, TRUNC
|
|
|
//
|
|
|
func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
|
|
|
var (
|
|
|
@@ -131,15 +146,9 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
|
|
|
|
|
|
// getPriority calculate arithmetic operator priority.
|
|
|
func getPriority(token efp.Token) (pri int) {
|
|
|
- var priority = map[string]int{
|
|
|
- "*": 2,
|
|
|
- "/": 2,
|
|
|
- "+": 1,
|
|
|
- "-": 1,
|
|
|
- }
|
|
|
- pri, _ = priority[token.TValue]
|
|
|
+ pri, _ = tokenPriority[token.TValue]
|
|
|
if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix {
|
|
|
- pri = 3
|
|
|
+ pri = 6
|
|
|
}
|
|
|
if token.TSubType == efp.TokenSubTypeStart && token.TType == efp.TokenTypeSubexpression { // (
|
|
|
pri = 0
|
|
|
@@ -306,18 +315,96 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
|
|
|
return opdStack.Peek().(efp.Token), err
|
|
|
}
|
|
|
|
|
|
-// calcAdd evaluate addition arithmetic operations.
|
|
|
-func calcAdd(opdStack *Stack) error {
|
|
|
- if opdStack.Len() < 2 {
|
|
|
- return errors.New("formula not valid")
|
|
|
+// calcPow evaluate exponentiation arithmetic operations.
|
|
|
+func calcPow(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ result := math.Pow(lOpdVal, rOpdVal)
|
|
|
+ opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// calcEq evaluate equal arithmetic operations.
|
|
|
+func calcEq(rOpd, lOpd string, 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 {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal > lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// calcLe evaluate less than or equal arithmetic operations.
|
|
|
+func calcLe(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
- rOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
|
|
|
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal >= lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// calcG evaluate greater than or equal arithmetic operations.
|
|
|
+func calcG(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal < lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// calcGe evaluate greater than or equal arithmetic operations.
|
|
|
+func calcGe(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal <= lOpdVal)), 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})
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// calcAdd evaluate addition arithmetic operations.
|
|
|
+func calcAdd(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -327,17 +414,12 @@ func calcAdd(opdStack *Stack) error {
|
|
|
}
|
|
|
|
|
|
// calcSubtract evaluate subtraction arithmetic operations.
|
|
|
-func calcSubtract(opdStack *Stack) error {
|
|
|
- if opdStack.Len() < 2 {
|
|
|
- return errors.New("formula not valid")
|
|
|
- }
|
|
|
- rOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
|
|
|
+func calcSubtract(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -347,17 +429,12 @@ func calcSubtract(opdStack *Stack) error {
|
|
|
}
|
|
|
|
|
|
// calcMultiply evaluate multiplication arithmetic operations.
|
|
|
-func calcMultiply(opdStack *Stack) error {
|
|
|
- if opdStack.Len() < 2 {
|
|
|
- return errors.New("formula not valid")
|
|
|
- }
|
|
|
- rOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
|
|
|
+func calcMultiply(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -366,18 +443,13 @@ func calcMultiply(opdStack *Stack) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-// calcDivide evaluate division arithmetic operations.
|
|
|
-func calcDivide(opdStack *Stack) error {
|
|
|
- if opdStack.Len() < 2 {
|
|
|
- return errors.New("formula not valid")
|
|
|
- }
|
|
|
- rOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpd := opdStack.Pop().(efp.Token)
|
|
|
- lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
|
|
|
+// calcDiv evaluate division arithmetic operations.
|
|
|
+func calcDiv(rOpd, lOpd string, opdStack *Stack) error {
|
|
|
+ lOpdVal, err := strconv.ParseFloat(lOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
|
|
|
+ rOpdVal, err := strconv.ParseFloat(rOpd, 64)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -403,24 +475,36 @@ 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})
|
|
|
}
|
|
|
-
|
|
|
- if opt.TValue == "+" {
|
|
|
- if err := calcAdd(opdStack); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ tokenCalcFunc := map[string]func(rOpd, lOpd string, opdStack *Stack) error{
|
|
|
+ "^": calcPow,
|
|
|
+ "*": calcMultiply,
|
|
|
+ "/": calcDiv,
|
|
|
+ "+": calcAdd,
|
|
|
+ "=": calcEq,
|
|
|
+ "<": calcL,
|
|
|
+ "<=": calcLe,
|
|
|
+ ">": calcG,
|
|
|
+ ">=": calcGe,
|
|
|
+ "&": calcSplice,
|
|
|
}
|
|
|
if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorInfix {
|
|
|
- if err := calcSubtract(opdStack); err != nil {
|
|
|
- return err
|
|
|
+ if opdStack.Len() < 2 {
|
|
|
+ return errors.New("formula not valid")
|
|
|
}
|
|
|
- }
|
|
|
- if opt.TValue == "*" {
|
|
|
- if err := calcMultiply(opdStack); err != nil {
|
|
|
+ rOpd := opdStack.Pop().(efp.Token)
|
|
|
+ lOpd := opdStack.Pop().(efp.Token)
|
|
|
+ if err := calcSubtract(rOpd.TValue, lOpd.TValue, opdStack); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
- if opt.TValue == "/" {
|
|
|
- if err := calcDivide(opdStack); err != nil {
|
|
|
+ fn, ok := tokenCalcFunc[opt.TValue]
|
|
|
+ if ok {
|
|
|
+ if opdStack.Len() < 2 {
|
|
|
+ return errors.New("formula not valid")
|
|
|
+ }
|
|
|
+ rOpd := opdStack.Pop().(efp.Token)
|
|
|
+ lOpd := opdStack.Pop().(efp.Token)
|
|
|
+ if err := fn(rOpd.TValue, lOpd.TValue, opdStack); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
@@ -459,8 +543,8 @@ func (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Tok
|
|
|
// isOperatorPrefixToken determine if the token is parse operator prefix
|
|
|
// token.
|
|
|
func isOperatorPrefixToken(token efp.Token) bool {
|
|
|
- if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) ||
|
|
|
- token.TValue == "+" || token.TValue == "-" || token.TValue == "*" || token.TValue == "/" {
|
|
|
+ _, ok := tokenPriority[token.TValue]
|
|
|
+ if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) || ok {
|
|
|
return true
|
|
|
}
|
|
|
return false
|
|
|
@@ -3140,3 +3224,87 @@ func (fn *formulaFuncs) NA(argsList *list.List) (result string, err error) {
|
|
|
result = formulaErrorNA
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
+// Logical Functions
|
|
|
+
|
|
|
+// AND function tests a number of supplied conditions and returns TRUE or
|
|
|
+// FALSE.
|
|
|
+func (fn *formulaFuncs) AND(argsList *list.List) (result string, err error) {
|
|
|
+ if argsList.Len() == 0 {
|
|
|
+ err = errors.New("AND requires at least 1 argument")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if argsList.Len() > 30 {
|
|
|
+ err = errors.New("AND accepts at most 30 arguments")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var and = true
|
|
|
+ var val float64
|
|
|
+ for arg := argsList.Front(); arg != nil; arg = arg.Next() {
|
|
|
+ token := arg.Value.(formulaArg)
|
|
|
+ switch token.Type {
|
|
|
+ case ArgUnknown:
|
|
|
+ continue
|
|
|
+ case ArgString:
|
|
|
+ if token.String == "TRUE" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if token.String == "FALSE" {
|
|
|
+ result = token.String
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if val, err = strconv.ParseFloat(token.String, 64); err != nil {
|
|
|
+ err = errors.New(formulaErrorVALUE)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ and = and && (val != 0)
|
|
|
+ case ArgMatrix:
|
|
|
+ // TODO
|
|
|
+ err = errors.New(formulaErrorVALUE)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result = strings.ToUpper(strconv.FormatBool(and))
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// OR function tests a number of supplied conditions and returns either TRUE
|
|
|
+// or FALSE.
|
|
|
+func (fn *formulaFuncs) OR(argsList *list.List) (result string, err error) {
|
|
|
+ if argsList.Len() == 0 {
|
|
|
+ err = errors.New("OR requires at least 1 argument")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if argsList.Len() > 30 {
|
|
|
+ err = errors.New("OR accepts at most 30 arguments")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var or bool
|
|
|
+ var val float64
|
|
|
+ for arg := argsList.Front(); arg != nil; arg = arg.Next() {
|
|
|
+ token := arg.Value.(formulaArg)
|
|
|
+ switch token.Type {
|
|
|
+ case ArgUnknown:
|
|
|
+ continue
|
|
|
+ case ArgString:
|
|
|
+ if token.String == "FALSE" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if token.String == "TRUE" {
|
|
|
+ or = true
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if val, err = strconv.ParseFloat(token.String, 64); err != nil {
|
|
|
+ err = errors.New(formulaErrorVALUE)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ or = val != 0
|
|
|
+ case ArgMatrix:
|
|
|
+ // TODO
|
|
|
+ err = errors.New(formulaErrorVALUE)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result = strings.ToUpper(strconv.FormatBool(or))
|
|
|
+ return
|
|
|
+}
|