Skip to content

Commit 02530e8

Browse files
committed
Fix qax-os#701, init new formula function AND and OR, prevent formula lexer panic on retrieving the top token type
1 parent ac3dce0 commit 02530e8

File tree

4 files changed

+337
-89
lines changed

4 files changed

+337
-89
lines changed

calc.go

Lines changed: 232 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,36 @@ type formulaArg struct {
9393
// formulaFuncs is the type of the formula functions.
9494
type formulaFuncs struct{}
9595

96+
// tokenPriority defined basic arithmetic operator priority.
97+
var tokenPriority = map[string]int{
98+
"^": 5,
99+
"*": 4,
100+
"/": 4,
101+
"+": 3,
102+
"-": 3,
103+
"=": 2,
104+
"<": 2,
105+
"<=": 2,
106+
">": 2,
107+
">=": 2,
108+
"&": 1,
109+
}
110+
96111
// CalcCellValue provides a function to get calculated cell value. This
97112
// feature is currently in working processing. Array formula, table formula
98113
// and some other formulas are not supported currently.
99114
//
100115
// Supported formulas:
101116
//
102-
// ABS, ACOS, ACOSH, ACOT, ACOTH, ARABIC, ASIN, ASINH, ATAN2, ATANH, BASE,
103-
// CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS, COSH, COT,
104-
// COTH, COUNTA, CSC, CSCH, DECIMAL, DEGREES, EVEN, EXP, FACT, FACTDOUBLE,
105-
// FLOOR, FLOOR.MATH, FLOOR.PRECISE, GCD, INT, ISBLANK, ISERR, ISERROR,
106-
// ISEVEN, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISODD, LCM, LN, LOG,
107-
// LOG10, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA, ODD, PI,
108-
// POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN, ROUND, ROUNDDOWN,
109-
// ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI, SUM, SUMIF, SUMSQ,
110-
// TAN, TANH, TRUNC
117+
// ABS, ACOS, ACOSH, ACOT, ACOTH, AND, ARABIC, ASIN, ASINH, ATAN2, ATANH,
118+
// BASE, CEILING, CEILING.MATH, CEILING.PRECISE, COMBIN, COMBINA, COS,
119+
// COSH, COT, COTH, COUNTA, CSC, CSCH, DECIMAL, DEGREES, EVEN, EXP, FACT,
120+
// FACTDOUBLE, FLOOR, FLOOR.MATH, FLOOR.PRECISE, GCD, INT, ISBLANK, ISERR,
121+
// ISERROR, ISEVEN, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISODD, LCM,
122+
// LN, LOG, LOG10, MDETERM, MEDIAN, MOD, MROUND, MULTINOMIAL, MUNIT, NA,
123+
// ODD, OR, PI, POWER, PRODUCT, QUOTIENT, RADIANS, RAND, RANDBETWEEN,
124+
// ROUND, ROUNDDOWN, ROUNDUP, SEC, SECH, SIGN, SIN, SINH, SQRT, SQRTPI,
125+
// SUM, SUMIF, SUMSQ, TAN, TANH, TRUNC
111126
//
112127
func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
113128
var (
@@ -131,15 +146,9 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
131146

132147
// getPriority calculate arithmetic operator priority.
133148
func getPriority(token efp.Token) (pri int) {
134-
var priority = map[string]int{
135-
"*": 2,
136-
"/": 2,
137-
"+": 1,
138-
"-": 1,
139-
}
140-
pri, _ = priority[token.TValue]
149+
pri, _ = tokenPriority[token.TValue]
141150
if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix {
142-
pri = 3
151+
pri = 6
143152
}
144153
if token.TSubType == efp.TokenSubTypeStart && token.TType == efp.TokenTypeSubexpression { // (
145154
pri = 0
@@ -306,18 +315,96 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
306315
return opdStack.Peek().(efp.Token), err
307316
}
308317

309-
// calcAdd evaluate addition arithmetic operations.
310-
func calcAdd(opdStack *Stack) error {
311-
if opdStack.Len() < 2 {
312-
return errors.New("formula not valid")
318+
// calcPow evaluate exponentiation arithmetic operations.
319+
func calcPow(rOpd, lOpd string, opdStack *Stack) error {
320+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
321+
if err != nil {
322+
return err
323+
}
324+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
325+
if err != nil {
326+
return err
327+
}
328+
result := math.Pow(lOpdVal, rOpdVal)
329+
opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
330+
return nil
331+
}
332+
333+
// calcEq evaluate equal arithmetic operations.
334+
func calcEq(rOpd, lOpd string, opdStack *Stack) error {
335+
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpd == lOpd)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
336+
return nil
337+
}
338+
339+
// calcL evaluate less than arithmetic operations.
340+
func calcL(rOpd, lOpd string, opdStack *Stack) error {
341+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
342+
if err != nil {
343+
return err
344+
}
345+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
346+
if err != nil {
347+
return err
348+
}
349+
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal > lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
350+
return nil
351+
}
352+
353+
// calcLe evaluate less than or equal arithmetic operations.
354+
func calcLe(rOpd, lOpd string, opdStack *Stack) error {
355+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
356+
if err != nil {
357+
return err
358+
}
359+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
360+
if err != nil {
361+
return err
313362
}
314-
rOpd := opdStack.Pop().(efp.Token)
315-
lOpd := opdStack.Pop().(efp.Token)
316-
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
363+
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal >= lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
364+
return nil
365+
}
366+
367+
// calcG evaluate greater than or equal arithmetic operations.
368+
func calcG(rOpd, lOpd string, opdStack *Stack) error {
369+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
370+
if err != nil {
371+
return err
372+
}
373+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
374+
if err != nil {
375+
return err
376+
}
377+
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal < lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
378+
return nil
379+
}
380+
381+
// calcGe evaluate greater than or equal arithmetic operations.
382+
func calcGe(rOpd, lOpd string, opdStack *Stack) error {
383+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
317384
if err != nil {
318385
return err
319386
}
320-
rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
387+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
388+
if err != nil {
389+
return err
390+
}
391+
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal <= lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
392+
return nil
393+
}
394+
395+
// calcSplice evaluate splice '&' operations.
396+
func calcSplice(rOpd, lOpd string, opdStack *Stack) error {
397+
opdStack.Push(efp.Token{TValue: lOpd + rOpd, TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
398+
return nil
399+
}
400+
401+
// calcAdd evaluate addition arithmetic operations.
402+
func calcAdd(rOpd, lOpd string, opdStack *Stack) error {
403+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
404+
if err != nil {
405+
return err
406+
}
407+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
321408
if err != nil {
322409
return err
323410
}
@@ -327,17 +414,12 @@ func calcAdd(opdStack *Stack) error {
327414
}
328415

329416
// calcSubtract evaluate subtraction arithmetic operations.
330-
func calcSubtract(opdStack *Stack) error {
331-
if opdStack.Len() < 2 {
332-
return errors.New("formula not valid")
333-
}
334-
rOpd := opdStack.Pop().(efp.Token)
335-
lOpd := opdStack.Pop().(efp.Token)
336-
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
417+
func calcSubtract(rOpd, lOpd string, opdStack *Stack) error {
418+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
337419
if err != nil {
338420
return err
339421
}
340-
rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
422+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
341423
if err != nil {
342424
return err
343425
}
@@ -347,17 +429,12 @@ func calcSubtract(opdStack *Stack) error {
347429
}
348430

349431
// calcMultiply evaluate multiplication arithmetic operations.
350-
func calcMultiply(opdStack *Stack) error {
351-
if opdStack.Len() < 2 {
352-
return errors.New("formula not valid")
353-
}
354-
rOpd := opdStack.Pop().(efp.Token)
355-
lOpd := opdStack.Pop().(efp.Token)
356-
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
432+
func calcMultiply(rOpd, lOpd string, opdStack *Stack) error {
433+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
357434
if err != nil {
358435
return err
359436
}
360-
rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
437+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
361438
if err != nil {
362439
return err
363440
}
@@ -366,18 +443,13 @@ func calcMultiply(opdStack *Stack) error {
366443
return nil
367444
}
368445

369-
// calcDivide evaluate division arithmetic operations.
370-
func calcDivide(opdStack *Stack) error {
371-
if opdStack.Len() < 2 {
372-
return errors.New("formula not valid")
373-
}
374-
rOpd := opdStack.Pop().(efp.Token)
375-
lOpd := opdStack.Pop().(efp.Token)
376-
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
446+
// calcDiv evaluate division arithmetic operations.
447+
func calcDiv(rOpd, lOpd string, opdStack *Stack) error {
448+
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
377449
if err != nil {
378450
return err
379451
}
380-
rOpdVal, err := strconv.ParseFloat(rOpd.TValue, 64)
452+
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
381453
if err != nil {
382454
return err
383455
}
@@ -403,24 +475,36 @@ func calculate(opdStack *Stack, opt efp.Token) error {
403475
result := 0 - opdVal
404476
opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
405477
}
406-
407-
if opt.TValue == "+" {
408-
if err := calcAdd(opdStack); err != nil {
409-
return err
410-
}
478+
tokenCalcFunc := map[string]func(rOpd, lOpd string, opdStack *Stack) error{
479+
"^": calcPow,
480+
"*": calcMultiply,
481+
"/": calcDiv,
482+
"+": calcAdd,
483+
"=": calcEq,
484+
"<": calcL,
485+
"<=": calcLe,
486+
">": calcG,
487+
">=": calcGe,
488+
"&": calcSplice,
411489
}
412490
if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorInfix {
413-
if err := calcSubtract(opdStack); err != nil {
414-
return err
491+
if opdStack.Len() < 2 {
492+
return errors.New("formula not valid")
415493
}
416-
}
417-
if opt.TValue == "*" {
418-
if err := calcMultiply(opdStack); err != nil {
494+
rOpd := opdStack.Pop().(efp.Token)
495+
lOpd := opdStack.Pop().(efp.Token)
496+
if err := calcSubtract(rOpd.TValue, lOpd.TValue, opdStack); err != nil {
419497
return err
420498
}
421499
}
422-
if opt.TValue == "/" {
423-
if err := calcDivide(opdStack); err != nil {
500+
fn, ok := tokenCalcFunc[opt.TValue]
501+
if ok {
502+
if opdStack.Len() < 2 {
503+
return errors.New("formula not valid")
504+
}
505+
rOpd := opdStack.Pop().(efp.Token)
506+
lOpd := opdStack.Pop().(efp.Token)
507+
if err := fn(rOpd.TValue, lOpd.TValue, opdStack); err != nil {
424508
return err
425509
}
426510
}
@@ -459,8 +543,8 @@ func (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Tok
459543
// isOperatorPrefixToken determine if the token is parse operator prefix
460544
// token.
461545
func isOperatorPrefixToken(token efp.Token) bool {
462-
if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) ||
463-
token.TValue == "+" || token.TValue == "-" || token.TValue == "*" || token.TValue == "/" {
546+
_, ok := tokenPriority[token.TValue]
547+
if (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) || ok {
464548
return true
465549
}
466550
return false
@@ -3140,3 +3224,87 @@ func (fn *formulaFuncs) NA(argsList *list.List) (result string, err error) {
31403224
result = formulaErrorNA
31413225
return
31423226
}
3227+
3228+
// Logical Functions
3229+
3230+
// AND function tests a number of supplied conditions and returns TRUE or
3231+
// FALSE.
3232+
func (fn *formulaFuncs) AND(argsList *list.List) (result string, err error) {
3233+
if argsList.Len() == 0 {
3234+
err = errors.New("AND requires at least 1 argument")
3235+
return
3236+
}
3237+
if argsList.Len() > 30 {
3238+
err = errors.New("AND accepts at most 30 arguments")
3239+
return
3240+
}
3241+
var and = true
3242+
var val float64
3243+
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
3244+
token := arg.Value.(formulaArg)
3245+
switch token.Type {
3246+
case ArgUnknown:
3247+
continue
3248+
case ArgString:
3249+
if token.String == "TRUE" {
3250+
continue
3251+
}
3252+
if token.String == "FALSE" {
3253+
result = token.String
3254+
return
3255+
}
3256+
if val, err = strconv.ParseFloat(token.String, 64); err != nil {
3257+
err = errors.New(formulaErrorVALUE)
3258+
return
3259+
}
3260+
and = and && (val != 0)
3261+
case ArgMatrix:
3262+
// TODO
3263+
err = errors.New(formulaErrorVALUE)
3264+
return
3265+
}
3266+
}
3267+
result = strings.ToUpper(strconv.FormatBool(and))
3268+
return
3269+
}
3270+
3271+
// OR function tests a number of supplied conditions and returns either TRUE
3272+
// or FALSE.
3273+
func (fn *formulaFuncs) OR(argsList *list.List) (result string, err error) {
3274+
if argsList.Len() == 0 {
3275+
err = errors.New("OR requires at least 1 argument")
3276+
return
3277+
}
3278+
if argsList.Len() > 30 {
3279+
err = errors.New("OR accepts at most 30 arguments")
3280+
return
3281+
}
3282+
var or bool
3283+
var val float64
3284+
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
3285+
token := arg.Value.(formulaArg)
3286+
switch token.Type {
3287+
case ArgUnknown:
3288+
continue
3289+
case ArgString:
3290+
if token.String == "FALSE" {
3291+
continue
3292+
}
3293+
if token.String == "TRUE" {
3294+
or = true
3295+
continue
3296+
}
3297+
if val, err = strconv.ParseFloat(token.String, 64); err != nil {
3298+
err = errors.New(formulaErrorVALUE)
3299+
return
3300+
}
3301+
or = val != 0
3302+
case ArgMatrix:
3303+
// TODO
3304+
err = errors.New(formulaErrorVALUE)
3305+
return
3306+
}
3307+
}
3308+
result = strings.ToUpper(strconv.FormatBool(or))
3309+
return
3310+
}

0 commit comments

Comments
 (0)