@@ -193,7 +193,8 @@ var (
193193 return fmt.Sprintf("R[%d]C[%d]", row, col), nil
194194 },
195195 }
196- formulaFormats = []*regexp.Regexp{
196+ formulaFnNameReplacer = strings.NewReplacer("_xlfn.", "", ".", "dot")
197+ formulaFormats = []*regexp.Regexp{
197198 regexp.MustCompile(`^(\d+)$`),
198199 regexp.MustCompile(`^=(.*)$`),
199200 regexp.MustCompile(`^<>(.*)$`),
@@ -839,8 +840,8 @@ type formulaFuncs struct {
839840// Z.TEST
840841// ZTEST
841842func (f *File) CalcCellValue(sheet, cell string, opts ...Options) (result string, err error) {
842- cacheKey := fmt.Sprintf("%s!%s", sheet, cell)
843- if cachedResult, found := f.calcCache.Load(cacheKey ); found {
843+ entry := sheet + "!" + cell
844+ if cachedResult, ok := f.calcCache.Load(entry ); ok {
844845 return cachedResult.(string), nil
845846 }
846847 options := f.getOptions(opts...)
@@ -850,7 +851,7 @@ func (f *File) CalcCellValue(sheet, cell string, opts ...Options) (result string
850851 token formulaArg
851852 )
852853 if token, err = f.calcCellValue(&calcContext{
853- entry: fmt.Sprintf("%s!%s", sheet, cell) ,
854+ entry: entry ,
854855 maxCalcIterations: options.MaxCalcIterations,
855856 iterations: make(map[string]uint),
856857 iterationsCache: make(map[string]formulaArg),
@@ -866,25 +867,31 @@ func (f *File) CalcCellValue(sheet, cell string, opts ...Options) (result string
866867 if precision > 15 {
867868 result, err = f.formattedValue(&xlsxC{S: styleIdx, V: strings.ToUpper(strconv.FormatFloat(decimal, 'G', 15, 64))}, rawCellValue, CellTypeNumber)
868869 if err == nil {
869- f.calcCache.Store(cacheKey , result)
870+ f.calcCache.Store(entry , result)
870871 }
871872 return
872873 }
873874 if !strings.HasPrefix(result, "0") {
874875 result, err = f.formattedValue(&xlsxC{S: styleIdx, V: strings.ToUpper(strconv.FormatFloat(decimal, 'f', -1, 64))}, rawCellValue, CellTypeNumber)
875876 }
876877 if err == nil {
877- f.calcCache.Store(cacheKey , result)
878+ f.calcCache.Store(entry , result)
878879 }
879880 return
880881 }
881882 result, err = f.formattedValue(&xlsxC{S: styleIdx, V: token.Value()}, rawCellValue, CellTypeInlineString)
882883 if err == nil {
883- f.calcCache.Store(cacheKey , result)
884+ f.calcCache.Store(entry , result)
884885 }
885886 return
886887}
887888
889+ // clearCalcCache clear all calculation related caches.
890+ func (f *File) clearCalcCache() {
891+ f.calcCache.Clear()
892+ f.formulaArgCache.Clear()
893+ }
894+
888895// calcCellValue calculate cell value by given context, worksheet name and cell
889896// reference.
890897func (f *File) calcCellValue(ctx *calcContext, sheet, cell string) (result formulaArg, err error) {
@@ -1106,8 +1113,8 @@ func (f *File) evalInfixExpFunc(ctx *calcContext, sheet, cell string, token, nex
11061113 }
11071114 prepareEvalInfixExp(opfStack, opftStack, opfdStack, argsStack)
11081115 // call formula function to evaluate
1109- arg := callFuncByName(&formulaFuncs{f: f, sheet: sheet, cell: cell, ctx: ctx}, strings.NewReplacer(
1110- "_xlfn.", "", ".", "dot") .Replace(opfStack.Peek().(efp.Token).TValue),
1116+ arg := callFuncByName(&formulaFuncs{f: f, sheet: sheet, cell: cell, ctx: ctx},
1117+ formulaFnNameReplacer .Replace(opfStack.Peek().(efp.Token).TValue),
11111118 []reflect.Value{reflect.ValueOf(argsStack.Peek().(*list.List))})
11121119 if arg.Type == ArgError && opfStack.Len() == 1 {
11131120 return arg
@@ -1651,7 +1658,11 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
16511658 value string
16521659 err error
16531660 )
1654- ref := fmt.Sprintf("%s!%s", sheet, cell)
1661+ ref := sheet + "!" + cell
1662+ if cached, ok := f.formulaArgCache.Load(ref); ok {
1663+ return cached.(formulaArg), err
1664+ }
1665+
16551666 if formula, _ := f.getCellFormula(sheet, cell, true); len(formula) != 0 {
16561667 ctx.mu.Lock()
16571668 if ctx.entry != ref {
@@ -1660,6 +1671,7 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
16601671 ctx.mu.Unlock()
16611672 arg, _ = f.calcCellValue(ctx, sheet, cell)
16621673 ctx.iterationsCache[ref] = arg
1674+ f.formulaArgCache.Store(ref, arg)
16631675 return arg, nil
16641676 }
16651677 ctx.mu.Unlock()
@@ -1674,29 +1686,29 @@ func (f *File) cellResolver(ctx *calcContext, sheet, cell string) (formulaArg, e
16741686 cellType, _ := f.GetCellType(sheet, cell)
16751687 switch cellType {
16761688 case CellTypeBool:
1677- return arg.ToBool(), err
1689+ arg = arg.ToBool()
16781690 case CellTypeNumber, CellTypeUnset:
16791691 if arg.Value() == "" {
1680- return newEmptyFormulaArg(), err
1692+ arg = newEmptyFormulaArg()
1693+ } else {
1694+ arg = arg.ToNumber()
16811695 }
1682- return arg.ToNumber(), err
16831696 case CellTypeInlineString, CellTypeSharedString:
1684- return arg, err
16851697 case CellTypeFormula:
1686- if value ! = "" {
1687- return arg, err
1698+ if value = = "" {
1699+ arg = newEmptyFormulaArg()
16881700 }
1689- return newEmptyFormulaArg(), err
16901701 case CellTypeDate:
16911702 if value, err = f.GetCellValue(sheet, cell); err == nil {
16921703 if num := newStringFormulaArg(value).ToNumber(); num.Type == ArgNumber {
1693- return num, err
1704+ arg = num
16941705 }
16951706 }
1696- return arg, err
16971707 default:
1698- return newErrorFormulaArg(value, value), err
1708+ arg = newErrorFormulaArg(value, value)
16991709 }
1710+ f.formulaArgCache.Store(ref, arg)
1711+ return arg, err
17001712}
17011713
17021714// rangeResolver extract value as string from given reference and range list.
@@ -1735,13 +1747,39 @@ func (f *File) rangeResolver(ctx *calcContext, cellRefs, cellRanges *list.List)
17351747 return
17361748 }
17371749
1750+ // Detect whole column/row reference, limit to actual data range
1751+ if valueRange[1] == TotalRows {
1752+ actualMaxRow := 0
1753+ for _, rowData := range ws.SheetData.Row {
1754+ if rowData.R > actualMaxRow {
1755+ actualMaxRow = rowData.R
1756+ }
1757+ }
1758+ if actualMaxRow > 0 && actualMaxRow < TotalRows {
1759+ valueRange[1] = actualMaxRow
1760+ }
1761+ }
1762+ if valueRange[3] == MaxColumns {
1763+ actualMaxCol := 0
1764+ for _, rowData := range ws.SheetData.Row {
1765+ for _, cell := range rowData.C {
1766+ col, _, err := CellNameToCoordinates(cell.R)
1767+ if err == nil && col > actualMaxCol {
1768+ actualMaxCol = col
1769+ }
1770+ }
1771+ }
1772+ if actualMaxCol > 0 && actualMaxCol < MaxColumns {
1773+ valueRange[3] = actualMaxCol
1774+ }
1775+ }
1776+
17381777 for row := valueRange[0]; row <= valueRange[1]; row++ {
17391778 colMax := 0
17401779 if row <= len(ws.SheetData.Row) {
17411780 rowData := &ws.SheetData.Row[row-1]
17421781 colMax = min(valueRange[3], len(rowData.C))
17431782 }
1744-
17451783 var matrixRow []formulaArg
17461784 for col := valueRange[2]; col <= valueRange[3]; col++ {
17471785 value := newEmptyFormulaArg()
0 commit comments