mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-27 13:48:54 +08:00

* fixed IF and statistical functions * Issue #347 * DATETIME * DAY, DAYS, Cells() fix * MONTH * MINUTE * EDATE * EOMONTH
82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
// Copyright 2017 FoxyUtils ehf. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by the terms of the Affero GNU General
|
|
// Public License version 3.0 as published by the Free Software Foundation and
|
|
// appearing in the file LICENSE included in the packaging of this file. A
|
|
// commercial license can be purchased on https://unidoc.io.
|
|
|
|
package formula
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Evaluator is the interface for a formula evaluator. This is needed so we can
|
|
// pass it to the spreadsheet to let it evaluate formula cells before returning
|
|
// the results.
|
|
type Evaluator interface {
|
|
Eval(ctx Context, formula string) Result
|
|
}
|
|
|
|
func NewEvaluator() Evaluator {
|
|
return &defEval{}
|
|
}
|
|
|
|
type defEval struct {
|
|
isRef bool
|
|
}
|
|
|
|
func (d *defEval) Eval(ctx Context, formula string) Result {
|
|
expr := ParseString(formula)
|
|
if expr != nil {
|
|
d.addInfo(ctx, expr)
|
|
return expr.Eval(ctx, d)
|
|
}
|
|
return MakeErrorResult(fmt.Sprintf("unable to parse formula %s", formula))
|
|
}
|
|
|
|
// addInfo adds information which is needed for some functions but is lost after evaluation. E.g. which zeroes and ones were actually booleans before evaluation and which arguments are actually references.
|
|
func (d *defEval) addInfo(ctx Context, expr Expression) {
|
|
switch expr.(type) {
|
|
case FunctionCall:
|
|
switch expr.(FunctionCall).name {
|
|
case "ISREF":
|
|
for _, arg := range expr.(FunctionCall).args {
|
|
switch arg.(type) {
|
|
case CellRef:
|
|
d.isRef = validateRef(arg.(CellRef))
|
|
return
|
|
case Range:
|
|
switch arg.(Range).from.(type) {
|
|
case CellRef:
|
|
d.isRef = validateRef(arg.(Range).from.(CellRef))
|
|
return
|
|
}
|
|
switch arg.(Range).to.(type) {
|
|
case CellRef:
|
|
d.isRef = validateRef(arg.(Range).to.(CellRef))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var refRegexp *regexp.Regexp = regexp.MustCompile(`^([a-z]+)([0-9]+)$`)
|
|
|
|
func validateRef(cr CellRef) bool {
|
|
if submatch := refRegexp.FindStringSubmatch(strings.ToLower(cr.s)); len(submatch) > 2 {
|
|
col := submatch[1]
|
|
row, err := strconv.Atoi(submatch[2])
|
|
if err != nil { // for the case if the row number is bigger then int capacity
|
|
return false
|
|
}
|
|
return row <= 1048576 && col <= "zz"
|
|
}
|
|
return false
|
|
}
|