Skip to content

Commit ef3e81d

Browse files
committed
This fixed across worksheet reference issue for the formula calculation engine
1 parent 1088302 commit ef3e81d

File tree

2 files changed

+118
-108
lines changed

2 files changed

+118
-108
lines changed

calc.go

+85-93
Original file line numberDiff line numberDiff line change
@@ -951,9 +951,6 @@ func (f *File) evalInfixExp(ctx *calcContext, sheet, cell string, tokens []efp.T
951951
if err != nil {
952952
return result, err
953953
}
954-
if result.Type == ArgError {
955-
return result, errors.New(result.Error)
956-
}
957954
opfdStack.Push(result)
958955
continue
959956
}
@@ -965,10 +962,7 @@ func (f *File) evalInfixExp(ctx *calcContext, sheet, cell string, tokens []efp.T
965962
}
966963
result, err := f.parseReference(ctx, sheet, token.TValue)
967964
if err != nil {
968-
return newEmptyFormulaArg(), err
969-
}
970-
if result.Type == ArgUnknown {
971-
return newEmptyFormulaArg(), errors.New(formulaErrorVALUE)
965+
return result, err
972966
}
973967
// when current token is range, next token is argument and opfdStack not empty,
974968
// should push value to opfdStack and continue
@@ -1442,74 +1436,99 @@ func (f *File) parseToken(ctx *calcContext, sheet string, token efp.Token, opdSt
14421436
return nil
14431437
}
14441438

1439+
// parseRef parse reference for a cell, column name or row number.
1440+
func (f *File) parseRef(ref string) (cellRef, bool, bool, error) {
1441+
var (
1442+
err, colErr, rowErr error
1443+
cr cellRef
1444+
cell = ref
1445+
tokens = strings.Split(ref, "!")
1446+
)
1447+
if len(tokens) == 2 { // have a worksheet
1448+
cr.Sheet, cell = tokens[0], tokens[1]
1449+
}
1450+
if cr.Col, cr.Row, err = CellNameToCoordinates(cell); err != nil {
1451+
if cr.Col, colErr = ColumnNameToNumber(cell); colErr == nil { // cast to column
1452+
return cr, true, false, nil
1453+
}
1454+
if cr.Row, rowErr = strconv.Atoi(cell); rowErr == nil { // cast to row
1455+
return cr, false, true, nil
1456+
}
1457+
return cr, false, false, err
1458+
}
1459+
return cr, false, false, err
1460+
}
1461+
1462+
// prepareCellRange checking and convert cell reference to a cell range.
1463+
func (cr *cellRange) prepareCellRange(col, row bool, cellRef cellRef) error {
1464+
if col {
1465+
cellRef.Row = TotalRows
1466+
}
1467+
if row {
1468+
cellRef.Col = MaxColumns
1469+
}
1470+
if cellRef.Sheet == "" {
1471+
cellRef.Sheet = cr.From.Sheet
1472+
}
1473+
if cr.From.Sheet != cellRef.Sheet || cr.To.Sheet != cellRef.Sheet {
1474+
return errors.New("invalid reference")
1475+
}
1476+
if cr.From.Col > cellRef.Col {
1477+
cr.From.Col = cellRef.Col
1478+
}
1479+
if cr.From.Row > cellRef.Row {
1480+
cr.From.Row = cellRef.Row
1481+
}
1482+
if cr.To.Col < cellRef.Col {
1483+
cr.To.Col = cellRef.Col
1484+
}
1485+
if cr.To.Row < cellRef.Row {
1486+
cr.To.Row = cellRef.Row
1487+
}
1488+
return nil
1489+
}
1490+
14451491
// parseReference parse reference and extract values by given reference
14461492
// characters and default sheet name.
1447-
func (f *File) parseReference(ctx *calcContext, sheet, reference string) (arg formulaArg, err error) {
1493+
func (f *File) parseReference(ctx *calcContext, sheet, reference string) (formulaArg, error) {
14481494
reference = strings.ReplaceAll(reference, "$", "")
1449-
refs, cellRanges, cellRefs := list.New(), list.New(), list.New()
1450-
for _, ref := range strings.Split(reference, ":") {
1451-
tokens := strings.Split(ref, "!")
1452-
cr := cellRef{}
1453-
if len(tokens) == 2 { // have a worksheet name
1454-
cr.Sheet = tokens[0]
1455-
// cast to cell reference
1456-
if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[1]); err != nil {
1457-
// cast to column
1458-
if cr.Col, err = ColumnNameToNumber(tokens[1]); err != nil {
1459-
// cast to row
1460-
if cr.Row, err = strconv.Atoi(tokens[1]); err != nil {
1461-
err = newInvalidColumnNameError(tokens[1])
1462-
return
1463-
}
1464-
cr.Col = MaxColumns
1465-
}
1466-
}
1467-
if refs.Len() > 0 {
1468-
e := refs.Back()
1469-
cellRefs.PushBack(e.Value.(cellRef))
1470-
refs.Remove(e)
1495+
ranges, cellRanges, cellRefs := strings.Split(reference, ":"), list.New(), list.New()
1496+
if len(ranges) > 1 {
1497+
var cr cellRange
1498+
for i, ref := range ranges {
1499+
cellRef, col, row, err := f.parseRef(ref)
1500+
if err != nil {
1501+
return newErrorFormulaArg(formulaErrorNAME, "invalid reference"), errors.New("invalid reference")
14711502
}
1472-
refs.PushBack(cr)
1473-
continue
1474-
}
1475-
// cast to cell reference
1476-
if cr.Col, cr.Row, err = CellNameToCoordinates(tokens[0]); err != nil {
1477-
// cast to column
1478-
if cr.Col, err = ColumnNameToNumber(tokens[0]); err != nil {
1479-
// cast to row
1480-
if cr.Row, err = strconv.Atoi(tokens[0]); err != nil {
1481-
err = newInvalidColumnNameError(tokens[0])
1482-
return
1503+
if i == 0 {
1504+
if col {
1505+
cellRef.Row = 1
1506+
}
1507+
if row {
1508+
cellRef.Col = 1
14831509
}
1484-
cr.Col = MaxColumns
1510+
if cellRef.Sheet == "" {
1511+
cellRef.Sheet = sheet
1512+
}
1513+
cr.From, cr.To = cellRef, cellRef
1514+
continue
1515+
}
1516+
if err := cr.prepareCellRange(col, row, cellRef); err != nil {
1517+
return newErrorFormulaArg(formulaErrorNAME, err.Error()), err
14851518
}
1486-
cellRanges.PushBack(cellRange{
1487-
From: cellRef{Sheet: sheet, Col: cr.Col, Row: 1},
1488-
To: cellRef{Sheet: sheet, Col: cr.Col, Row: TotalRows},
1489-
})
1490-
cellRefs.Init()
1491-
arg, err = f.rangeResolver(ctx, cellRefs, cellRanges)
1492-
return
1493-
}
1494-
e := refs.Back()
1495-
if e == nil {
1496-
cr.Sheet = sheet
1497-
refs.PushBack(cr)
1498-
continue
14991519
}
1500-
cellRanges.PushBack(cellRange{
1501-
From: e.Value.(cellRef),
1502-
To: cr,
1503-
})
1504-
refs.Remove(e)
1520+
cellRanges.PushBack(cr)
1521+
return f.rangeResolver(ctx, cellRefs, cellRanges)
15051522
}
1506-
if refs.Len() > 0 {
1507-
e := refs.Back()
1508-
cellRefs.PushBack(e.Value.(cellRef))
1509-
refs.Remove(e)
1523+
cellRef, _, _, err := f.parseRef(reference)
1524+
if err != nil {
1525+
return newErrorFormulaArg(formulaErrorNAME, "invalid reference"), errors.New("invalid reference")
15101526
}
1511-
arg, err = f.rangeResolver(ctx, cellRefs, cellRanges)
1512-
return
1527+
if cellRef.Sheet == "" {
1528+
cellRef.Sheet = sheet
1529+
}
1530+
cellRefs.PushBack(cellRef)
1531+
return f.rangeResolver(ctx, cellRefs, cellRanges)
15131532
}
15141533

15151534
// prepareValueRange prepare value range.
@@ -1598,9 +1617,6 @@ func (f *File) rangeResolver(ctx *calcContext, cellRefs, cellRanges *list.List)
15981617
// prepare value range
15991618
for temp := cellRanges.Front(); temp != nil; temp = temp.Next() {
16001619
cr := temp.Value.(cellRange)
1601-
if cr.From.Sheet != cr.To.Sheet {
1602-
err = errors.New(formulaErrorVALUE)
1603-
}
16041620
rng := []int{cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row}
16051621
_ = sortCoordinates(rng)
16061622
cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row = rng[0], rng[1], rng[2], rng[3]
@@ -14155,18 +14171,9 @@ func calcColumnsMinMax(argsList *list.List) (min, max int) {
1415514171
if min == 0 {
1415614172
min = cr.Value.(cellRange).From.Col
1415714173
}
14158-
if min > cr.Value.(cellRange).From.Col {
14159-
min = cr.Value.(cellRange).From.Col
14160-
}
14161-
if min > cr.Value.(cellRange).To.Col {
14162-
min = cr.Value.(cellRange).To.Col
14163-
}
1416414174
if max < cr.Value.(cellRange).To.Col {
1416514175
max = cr.Value.(cellRange).To.Col
1416614176
}
14167-
if max < cr.Value.(cellRange).From.Col {
14168-
max = cr.Value.(cellRange).From.Col
14169-
}
1417014177
}
1417114178
}
1417214179
if argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 {
@@ -14175,9 +14182,6 @@ func calcColumnsMinMax(argsList *list.List) (min, max int) {
1417514182
if min == 0 {
1417614183
min = refs.Value.(cellRef).Col
1417714184
}
14178-
if min > refs.Value.(cellRef).Col {
14179-
min = refs.Value.(cellRef).Col
14180-
}
1418114185
if max < refs.Value.(cellRef).Col {
1418214186
max = refs.Value.(cellRef).Col
1418314187
}
@@ -14936,18 +14940,9 @@ func calcRowsMinMax(argsList *list.List) (min, max int) {
1493614940
if min == 0 {
1493714941
min = cr.Value.(cellRange).From.Row
1493814942
}
14939-
if min > cr.Value.(cellRange).From.Row {
14940-
min = cr.Value.(cellRange).From.Row
14941-
}
14942-
if min > cr.Value.(cellRange).To.Row {
14943-
min = cr.Value.(cellRange).To.Row
14944-
}
1494514943
if max < cr.Value.(cellRange).To.Row {
1494614944
max = cr.Value.(cellRange).To.Row
1494714945
}
14948-
if max < cr.Value.(cellRange).From.Row {
14949-
max = cr.Value.(cellRange).From.Row
14950-
}
1495114946
}
1495214947
}
1495314948
if argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 {
@@ -14956,9 +14951,6 @@ func calcRowsMinMax(argsList *list.List) (min, max int) {
1495614951
if min == 0 {
1495714952
min = refs.Value.(cellRef).Row
1495814953
}
14959-
if min > refs.Value.(cellRef).Row {
14960-
min = refs.Value.(cellRef).Row
14961-
}
1496214954
if max < refs.Value.(cellRef).Row {
1496314955
max = refs.Value.(cellRef).Row
1496414956
}

calc_test.go

+33-15
Original file line numberDiff line numberDiff line change
@@ -2409,7 +2409,7 @@ func TestCalcCellValue(t *testing.T) {
24092409
// ABS
24102410
"=ABS()": {"#VALUE!", "ABS requires 1 numeric argument"},
24112411
"=ABS(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
2412-
"=ABS(~)": {"", newInvalidColumnNameError("~").Error()},
2412+
"=ABS(~)": {"#NAME?", "invalid reference"},
24132413
// ACOS
24142414
"=ACOS()": {"#VALUE!", "ACOS requires 1 numeric argument"},
24152415
"=ACOS(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
@@ -3794,17 +3794,19 @@ func TestCalcCellValue(t *testing.T) {
37943794
"=CHOOSE(2,0)": {"#VALUE!", "index_num should be <= to the number of values"},
37953795
"=CHOOSE(1,NA())": {"#N/A", "#N/A"},
37963796
// COLUMN
3797-
"=COLUMN(1,2)": {"#VALUE!", "COLUMN requires at most 1 argument"},
3798-
"=COLUMN(\"\")": {"#VALUE!", "invalid reference"},
3799-
"=COLUMN(Sheet1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3800-
"=COLUMN(Sheet1!A1!B1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3797+
"=COLUMN(1,2)": {"#VALUE!", "COLUMN requires at most 1 argument"},
3798+
"=COLUMN(\"\")": {"#VALUE!", "invalid reference"},
3799+
"=COLUMN(Sheet1)": {"#NAME?", "invalid reference"},
3800+
"=COLUMN(Sheet1!A1!B1)": {"#NAME?", "invalid reference"},
3801+
"=COLUMN(Sheet1!A1:Sheet2!A2)": {"#NAME?", "invalid reference"},
3802+
"=COLUMN(Sheet1!A1:1A)": {"#NAME?", "invalid reference"},
38013803
// COLUMNS
38023804
"=COLUMNS()": {"#VALUE!", "COLUMNS requires 1 argument"},
38033805
"=COLUMNS(1)": {"#VALUE!", "invalid reference"},
38043806
"=COLUMNS(\"\")": {"#VALUE!", "invalid reference"},
3805-
"=COLUMNS(Sheet1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3806-
"=COLUMNS(Sheet1!A1!B1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3807-
"=COLUMNS(Sheet1!Sheet1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3807+
"=COLUMNS(Sheet1)": {"#NAME?", "invalid reference"},
3808+
"=COLUMNS(Sheet1!A1!B1)": {"#NAME?", "invalid reference"},
3809+
"=COLUMNS(Sheet1!Sheet1)": {"#NAME?", "invalid reference"},
38083810
// FORMULATEXT
38093811
"=FORMULATEXT()": {"#VALUE!", "FORMULATEXT requires 1 argument"},
38103812
"=FORMULATEXT(1)": {"#VALUE!", "#VALUE!"},
@@ -3874,15 +3876,15 @@ func TestCalcCellValue(t *testing.T) {
38743876
// ROW
38753877
"=ROW(1,2)": {"#VALUE!", "ROW requires at most 1 argument"},
38763878
"=ROW(\"\")": {"#VALUE!", "invalid reference"},
3877-
"=ROW(Sheet1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3878-
"=ROW(Sheet1!A1!B1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3879+
"=ROW(Sheet1)": {"#NAME?", "invalid reference"},
3880+
"=ROW(Sheet1!A1!B1)": {"#NAME?", "invalid reference"},
38793881
// ROWS
38803882
"=ROWS()": {"#VALUE!", "ROWS requires 1 argument"},
38813883
"=ROWS(1)": {"#VALUE!", "invalid reference"},
38823884
"=ROWS(\"\")": {"#VALUE!", "invalid reference"},
3883-
"=ROWS(Sheet1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3884-
"=ROWS(Sheet1!A1!B1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3885-
"=ROWS(Sheet1!Sheet1)": {"", newInvalidColumnNameError("Sheet1").Error()},
3885+
"=ROWS(Sheet1)": {"#NAME?", "invalid reference"},
3886+
"=ROWS(Sheet1!A1!B1)": {"#NAME?", "invalid reference"},
3887+
"=ROWS(Sheet1!Sheet1)": {"#NAME?", "invalid reference"},
38863888
// Web Functions
38873889
// ENCODEURL
38883890
"=ENCODEURL()": {"#VALUE!", "ENCODEURL requires 1 argument"},
@@ -4376,6 +4378,7 @@ func TestCalcCellValue(t *testing.T) {
43764378
// SUM
43774379
"=A1/A3": "0.333333333333333",
43784380
"=SUM(A1:A2)": "3",
4381+
"=SUM(Sheet1!A1:Sheet1!A2)": "3",
43794382
"=SUM(Sheet1!A1,A2)": "3",
43804383
"=(-2-SUM(-4+A2))*5": "0",
43814384
"=SUM(Sheet1!A1:Sheet1!A1:A2,A2)": "5",
@@ -5549,8 +5552,7 @@ func TestCalcSHEETS(t *testing.T) {
55495552
assert.NoError(t, err)
55505553
formulaList := map[string]string{
55515554
"=SHEETS(Sheet1!A1:B1)": "1",
5552-
"=SHEETS(Sheet1!A1:Sheet1!A1)": "1",
5553-
"=SHEETS(Sheet1!A1:Sheet2!A1)": "2",
5555+
"=SHEETS(Sheet1!A1:Sheet1!B1)": "1",
55545556
}
55555557
for formula, expected := range formulaList {
55565558
assert.NoError(t, f.SetCellFormula("Sheet1", "A1", formula))
@@ -5905,3 +5907,19 @@ func TestCalcCellResolver(t *testing.T) {
59055907
assert.Equal(t, expected, result, formula)
59065908
}
59075909
}
5910+
5911+
func TestEvalInfixExp(t *testing.T) {
5912+
f := NewFile()
5913+
arg, err := f.evalInfixExp(nil, "Sheet1", "A1", []efp.Token{
5914+
{TSubType: efp.TokenSubTypeRange, TValue: "1A"},
5915+
})
5916+
assert.Equal(t, arg, newEmptyFormulaArg())
5917+
assert.Equal(t, formulaErrorNAME, err.Error())
5918+
}
5919+
5920+
func TestParseToken(t *testing.T) {
5921+
f := NewFile()
5922+
assert.Equal(t, formulaErrorNAME, f.parseToken(nil, "Sheet1",
5923+
efp.Token{TSubType: efp.TokenSubTypeRange, TValue: "1A"}, nil, nil,
5924+
).Error())
5925+
}

0 commit comments

Comments
 (0)