Skip to content

Commit 4188dc7

Browse files
committed
- fn: SUMSQ
- resolve ineffectual assignment - handle exception with invalid formula - update range resolver
1 parent 08185c3 commit 4188dc7

File tree

2 files changed

+116
-29
lines changed

2 files changed

+116
-29
lines changed

calc.go

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,18 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
259259
}
260260
optStack.Pop()
261261
}
262+
if opdStack.Len() == 0 {
263+
return efp.Token{}, errors.New("formula not valid")
264+
}
262265
return opdStack.Peek().(efp.Token), err
263266
}
264267

265268
// calculate evaluate basic arithmetic operations.
266269
func calculate(opdStack *Stack, opt efp.Token) error {
267270
if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorPrefix {
271+
if opdStack.Len() < 1 {
272+
return errors.New("formula not valid")
273+
}
268274
opd := opdStack.Pop().(efp.Token)
269275
opdVal, err := strconv.ParseFloat(opd.TValue, 64)
270276
if err != nil {
@@ -274,6 +280,9 @@ func calculate(opdStack *Stack, opt efp.Token) error {
274280
opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
275281
}
276282
if opt.TValue == "+" {
283+
if opdStack.Len() < 2 {
284+
return errors.New("formula not valid")
285+
}
277286
rOpd := opdStack.Pop().(efp.Token)
278287
lOpd := opdStack.Pop().(efp.Token)
279288
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
@@ -288,6 +297,9 @@ func calculate(opdStack *Stack, opt efp.Token) error {
288297
opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
289298
}
290299
if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorInfix {
300+
if opdStack.Len() < 2 {
301+
return errors.New("formula not valid")
302+
}
291303
rOpd := opdStack.Pop().(efp.Token)
292304
lOpd := opdStack.Pop().(efp.Token)
293305
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
@@ -302,6 +314,9 @@ func calculate(opdStack *Stack, opt efp.Token) error {
302314
opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
303315
}
304316
if opt.TValue == "*" {
317+
if opdStack.Len() < 2 {
318+
return errors.New("formula not valid")
319+
}
305320
rOpd := opdStack.Pop().(efp.Token)
306321
lOpd := opdStack.Pop().(efp.Token)
307322
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
@@ -316,6 +331,9 @@ func calculate(opdStack *Stack, opt efp.Token) error {
316331
opdStack.Push(efp.Token{TValue: fmt.Sprintf("%g", result), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
317332
}
318333
if opt.TValue == "/" {
334+
if opdStack.Len() < 2 {
335+
return errors.New("formula not valid")
336+
}
319337
rOpd := opdStack.Pop().(efp.Token)
320338
lOpd := opdStack.Pop().(efp.Token)
321339
lOpdVal, err := strconv.ParseFloat(lOpd.TValue, 64)
@@ -444,35 +462,73 @@ func (f *File) parseReference(sheet, reference string) (result []string, matrix
444462
}
445463

446464
// rangeResolver extract value as string from given reference and range list.
447-
// This function will not ignore the empty cell. Note that the result of 3D
448-
// range references may be different from Excel in some cases, for example,
449-
// A1:A2:A2:B3 in Excel will include B1, but we wont.
465+
// This function will not ignore the empty cell. For example,
466+
// A1:A2:A2:B3 will be reference A1:B3.
450467
func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (result []string, matrix [][]string, err error) {
468+
var fromRow, toRow, fromCol, toCol int = 1, 1, 1, 1
469+
var sheet string
451470
filter := map[string]string{}
452-
// extract value from ranges
471+
// prepare value range
453472
for temp := cellRanges.Front(); temp != nil; temp = temp.Next() {
454473
cr := temp.Value.(cellRange)
455474
if cr.From.Sheet != cr.To.Sheet {
456475
err = errors.New(formulaErrorVALUE)
457476
}
458477
rng := []int{cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row}
459478
sortCoordinates(rng)
460-
matrix = [][]string{}
461-
for row := rng[1]; row <= rng[3]; row++ {
479+
if cr.From.Row < fromRow {
480+
fromRow = cr.From.Row
481+
}
482+
if cr.From.Col < fromCol {
483+
fromCol = cr.From.Col
484+
}
485+
if cr.To.Row > fromRow {
486+
toRow = cr.To.Row
487+
}
488+
if cr.To.Col > toCol {
489+
toCol = cr.To.Col
490+
}
491+
if cr.From.Sheet != "" {
492+
sheet = cr.From.Sheet
493+
}
494+
}
495+
for temp := cellRefs.Front(); temp != nil; temp = temp.Next() {
496+
cr := temp.Value.(cellRef)
497+
if cr.Sheet != "" {
498+
sheet = cr.Sheet
499+
}
500+
if cr.Row < fromRow {
501+
fromRow = cr.Row
502+
}
503+
if cr.Col < fromCol {
504+
fromCol = cr.Col
505+
}
506+
if cr.Row > fromRow {
507+
toRow = cr.Row
508+
}
509+
if cr.Col > toCol {
510+
toCol = cr.Col
511+
}
512+
}
513+
// extract value from ranges
514+
if cellRanges.Len() > 0 {
515+
for row := fromRow; row <= toRow; row++ {
462516
var matrixRow = []string{}
463-
for col := rng[0]; col <= rng[2]; col++ {
517+
for col := fromCol; col <= toCol; col++ {
464518
var cell, value string
465519
if cell, err = CoordinatesToCellName(col, row); err != nil {
466520
return
467521
}
468-
if value, err = f.GetCellValue(cr.From.Sheet, cell); err != nil {
522+
if value, err = f.GetCellValue(sheet, cell); err != nil {
469523
return
470524
}
471525
filter[cell] = value
472526
matrixRow = append(matrixRow, value)
527+
result = append(result, value)
473528
}
474529
matrix = append(matrix, matrixRow)
475530
}
531+
return
476532
}
477533
// extract value from references
478534
for temp := cellRefs.Front(); temp != nil; temp = temp.Next() {
@@ -824,7 +880,7 @@ func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error)
824880
err = errors.New("CEILING allows at most 2 arguments")
825881
return
826882
}
827-
var number, significance float64 = 0, 1
883+
number, significance, res := 0.0, 1.0, 0.0
828884
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
829885
return
830886
}
@@ -844,7 +900,7 @@ func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error)
844900
result = fmt.Sprintf("%g", math.Ceil(number))
845901
return
846902
}
847-
number, res := math.Modf(number / significance)
903+
number, res = math.Modf(number / significance)
848904
if res > 0 {
849905
number++
850906
}
@@ -866,7 +922,7 @@ func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err err
866922
err = errors.New("CEILING.MATH allows at most 3 arguments")
867923
return
868924
}
869-
var number, significance, mode float64 = 0, 1, 1
925+
number, significance, mode := 0.0, 1.0, 1.0
870926
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
871927
return
872928
}
@@ -914,7 +970,7 @@ func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) (result string, err
914970
err = errors.New("CEILING.PRECISE allows at most 2 arguments")
915971
return
916972
}
917-
var number, significance float64 = 0, 1
973+
number, significance := 0.0, 1.0
918974
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
919975
return
920976
}
@@ -955,7 +1011,7 @@ func (fn *formulaFuncs) COMBIN(argsList *list.List) (result string, err error) {
9551011
err = errors.New("COMBIN requires 2 argument")
9561012
return
9571013
}
958-
var number, chosen, val float64 = 0, 0, 1
1014+
number, chosen, val := 0.0, 0.0, 1.0
9591015
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
9601016
return
9611017
}
@@ -1274,7 +1330,7 @@ func (fn *formulaFuncs) FACTDOUBLE(argsList *list.List) (result string, err erro
12741330
err = errors.New("FACTDOUBLE requires 1 numeric argument")
12751331
return
12761332
}
1277-
var number, val float64 = 0, 1
1333+
number, val := 0.0, 1.0
12781334
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
12791335
return
12801336
}
@@ -1298,7 +1354,7 @@ func (fn *formulaFuncs) FLOOR(argsList *list.List) (result string, err error) {
12981354
err = errors.New("FLOOR requires 2 numeric arguments")
12991355
return
13001356
}
1301-
var number, significance float64 = 0, 1
1357+
var number, significance float64
13021358
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
13031359
return
13041360
}
@@ -1333,7 +1389,7 @@ func (fn *formulaFuncs) FLOORMATH(argsList *list.List) (result string, err error
13331389
err = errors.New("FLOOR.MATH allows at most 3 arguments")
13341390
return
13351391
}
1336-
var number, significance, mode float64 = 0, 1, 1
1392+
number, significance, mode := 0.0, 1.0, 1.0
13371393
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
13381394
return
13391395
}
@@ -1376,7 +1432,7 @@ func (fn *formulaFuncs) FLOORPRECISE(argsList *list.List) (result string, err er
13761432
err = errors.New("FLOOR.PRECISE allows at most 2 arguments")
13771433
return
13781434
}
1379-
var number, significance float64 = 0, 1
1435+
var number, significance float64
13801436
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
13811437
return
13821438
}
@@ -1488,7 +1544,7 @@ func (fn *formulaFuncs) ISOCEILING(argsList *list.List) (result string, err erro
14881544
err = errors.New("ISO.CEILING allows at most 2 arguments")
14891545
return
14901546
}
1491-
var number, significance float64 = 0, 1
1547+
var number, significance float64
14921548
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
14931549
return
14941550
}
@@ -1605,7 +1661,7 @@ func (fn *formulaFuncs) LOG(argsList *list.List) (result string, err error) {
16051661
err = errors.New("LOG allows at most 2 arguments")
16061662
return
16071663
}
1608-
var number, base float64 = 0, 10
1664+
number, base := 0.0, 10.0
16091665
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
16101666
return
16111667
}
@@ -1757,7 +1813,7 @@ func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) {
17571813
err = errors.New("MROUND requires 2 numeric arguments")
17581814
return
17591815
}
1760-
var number, multiple float64 = 0, 1
1816+
var number, multiple float64
17611817
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
17621818
return
17631819
}
@@ -1788,7 +1844,7 @@ func (fn *formulaFuncs) MROUND(argsList *list.List) (result string, err error) {
17881844
// MULTINOMIAL(number1,[number2],...)
17891845
//
17901846
func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) (result string, err error) {
1791-
var val, num, denom float64 = 0, 0, 1
1847+
val, num, denom := 0.0, 0.0, 1.0
17921848
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
17931849
token := arg.Value.(formulaArg)
17941850
if token.Value == "" {
@@ -1915,7 +1971,7 @@ func (fn *formulaFuncs) POWER(argsList *list.List) (result string, err error) {
19151971
// PRODUCT(number1,[number2],...)
19161972
//
19171973
func (fn *formulaFuncs) PRODUCT(argsList *list.List) (result string, err error) {
1918-
var val, product float64 = 0, 1
1974+
val, product := 0.0, 1.0
19191975
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
19201976
token := arg.Value.(formulaArg)
19211977
if token.Value == "" {
@@ -2088,7 +2144,7 @@ const (
20882144

20892145
// round rounds a supplied number up or down.
20902146
func (fn *formulaFuncs) round(number, digits float64, mode roundMode) float64 {
2091-
significance := 1.0
2147+
var significance float64
20922148
if digits > 0 {
20932149
significance = math.Pow(1/10.0, digits)
20942150
} else {
@@ -2343,6 +2399,27 @@ func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) {
23432399
return
23442400
}
23452401

2402+
// SUMSQ function returns the sum of squares of a supplied set of values. The
2403+
// syntax of the function is:
2404+
//
2405+
// SUMSQ(number1,[number2],...)
2406+
//
2407+
func (fn *formulaFuncs) SUMSQ(argsList *list.List) (result string, err error) {
2408+
var val, sq float64
2409+
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
2410+
token := arg.Value.(formulaArg)
2411+
if token.Value == "" {
2412+
continue
2413+
}
2414+
if val, err = strconv.ParseFloat(token.Value, 64); err != nil {
2415+
return
2416+
}
2417+
sq += val * val
2418+
}
2419+
result = fmt.Sprintf("%g", sq)
2420+
return
2421+
}
2422+
23462423
// TAN function calculates the tangent of a given angle. The syntax of the
23472424
// function is:
23482425
//

calc_test.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ func TestCalcCellValue(t *testing.T) {
329329
"=((3+5*2)+3)/5+(-6)/4*2+3": "3.2",
330330
"=1+SUM(SUM(1,2*3),4)*-4/2+5+(4+2)*3": "2",
331331
"=1+SUM(SUM(1,2*3),4)*4/3+5+(4+2)*3": "38.666666666666664",
332+
// SUMSQ
333+
"=SUMSQ(A1:A4)": "14",
334+
"=SUMSQ(A1,B1,A2,B2,6)": "82",
332335
// TAN
333336
"=TAN(1.047197551)": "1.732050806782486",
334337
"=TAN(0)": "0",
@@ -349,7 +352,7 @@ func TestCalcCellValue(t *testing.T) {
349352
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
350353
result, err := f.CalcCellValue("Sheet1", "C1")
351354
assert.NoError(t, err)
352-
assert.Equal(t, expected, result)
355+
assert.Equal(t, expected, result, formula)
353356
}
354357
mathCalcError := map[string]string{
355358
// ABS
@@ -507,6 +510,13 @@ func TestCalcCellValue(t *testing.T) {
507510
"=SQRT(-1)": "#NUM!",
508511
// SQRTPI
509512
"=SQRTPI()": "SQRTPI requires 1 numeric argument",
513+
// SUM
514+
"=SUM((": "formula not valid",
515+
"=SUM(-)": "formula not valid",
516+
"=SUM(1+)": "formula not valid",
517+
"=SUM(1-)": "formula not valid",
518+
"=SUM(1*)": "formula not valid",
519+
"=SUM(1/)": "formula not valid",
510520
// TAN
511521
"=TAN()": "TAN requires 1 numeric argument",
512522
// TANH
@@ -519,7 +529,7 @@ func TestCalcCellValue(t *testing.T) {
519529
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
520530
result, err := f.CalcCellValue("Sheet1", "C1")
521531
assert.EqualError(t, err, expected)
522-
assert.Equal(t, "", result)
532+
assert.Equal(t, "", result, formula)
523533
}
524534

525535
referenceCalc := map[string]string{
@@ -535,15 +545,15 @@ func TestCalcCellValue(t *testing.T) {
535545
"=SUM(Sheet1!A1:Sheet1!A1:A2,A2)": "5",
536546
"=SUM(A1,A2,A3)*SUM(2,3)": "30",
537547
"=1+SUM(SUM(A1+A2/A3)*(2-3),2)": "1.3333333333333335",
538-
"=A1/A2/SUM(A1:A2:B1)": "0.07142857142857142",
539-
"=A1/A2/SUM(A1:A2:B1)*A3": "0.21428571428571427",
548+
"=A1/A2/SUM(A1:A2:B1)": "0.041666666666666664",
549+
"=A1/A2/SUM(A1:A2:B1)*A3": "0.125",
540550
}
541551
for formula, expected := range referenceCalc {
542552
f := prepareData()
543553
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
544554
result, err := f.CalcCellValue("Sheet1", "C1")
545555
assert.NoError(t, err)
546-
assert.Equal(t, expected, result)
556+
assert.Equal(t, expected, result, formula)
547557
}
548558

549559
referenceCalcError := map[string]string{
@@ -557,7 +567,7 @@ func TestCalcCellValue(t *testing.T) {
557567
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
558568
result, err := f.CalcCellValue("Sheet1", "C1")
559569
assert.EqualError(t, err, expected)
560-
assert.Equal(t, "", result)
570+
assert.Equal(t, "", result, formula)
561571
}
562572

563573
// Test get calculated cell value on not formula cell.

0 commit comments

Comments
 (0)