formula: compatibility & bug fixes

- Make UNICODE with no arguments behave like Excel
- Fix OFFSET/INDEX for named ranges
This commit is contained in:
Todd 2017-09-20 19:04:21 -05:00
parent e7da598399
commit 0e09e64818
5 changed files with 50 additions and 12 deletions

View File

@ -7,7 +7,10 @@
package formula
import "fmt"
import (
"fmt"
"strings"
)
func init() {
RegisterFunction("INDEX", Index)
@ -51,7 +54,12 @@ func Index(args []Result) Result {
return MakeErrorResult("INDEX has col out of range")
}
return rowVal[col]
rv := rowVal[col]
// empty cell returns a zero
if rv.Type == ResultTypeEmpty {
return MakeNumberResult(0)
}
return rv
}
// Indirect is an implementation of the Excel INDIRECT function that returns the
@ -72,12 +80,20 @@ func Offset(ctx Context, ev Evaluator, args []Result) Result {
return MakeErrorResult("OFFSET requires one or two arguments")
}
ref := args[0].Ref
origin := "A1"
// resolve a named range
for ref.Type == ReferenceTypeNamedRange {
ref = ctx.NamedRange(ref.Value)
}
origin := ""
switch ref.Type {
case ReferenceTypeCell:
origin = ref.Value
case ReferenceTypeRange:
case ReferenceTypeNamedRange:
sp := strings.Split(ref.Value, ":")
if len(sp) == 2 {
origin = sp[0]
}
default:
return MakeErrorResult(fmt.Sprintf("Invalid range in OFFSET(): %s", ref.Type))
}
@ -91,27 +107,26 @@ func Offset(ctx Context, ev Evaluator, args []Result) Result {
if rOff.Type != ResultTypeNumber {
return MakeErrorResult("OFFSET requires numeric row offset")
}
cOff := args[1].AsNumber()
cOff := args[2].AsNumber()
if cOff.Type != ResultTypeNumber {
return MakeErrorResult("OFFSET requires numeric col offset")
}
height := args[1].AsNumber()
height := args[3].AsNumber()
if height.Type != ResultTypeNumber {
return MakeErrorResult("OFFSET requires numeric height")
}
width := args[1].AsNumber()
width := args[4].AsNumber()
if width.Type != ResultTypeNumber {
return MakeErrorResult("OFFSET requires numeric width")
}
colIdx := ColumnToIndex(col)
origRow := rowIdx + uint32(rOff.ValueNumber)
origCol := colIdx + uint32(cOff.ValueNumber)
endRow := origRow + uint32(height.ValueNumber)
endCol := origCol + uint32(width.ValueNumber)
endRow := origRow + uint32(height.ValueNumber) - 1
endCol := origCol + uint32(width.ValueNumber) - 1
beg := fmt.Sprintf("%s%d", IndexToColumn(origCol), origRow)
end := fmt.Sprintf("%s%d", IndexToColumn(endCol), endRow)
return resultFromCellRange(ctx, ev, beg, end)
}

View File

@ -106,6 +106,10 @@ func IfError(args []Result) Result {
}
if args[0].Type != ResultTypeError {
// empty cell returns a zero
if args[0].Type == ResultTypeEmpty {
return MakeNumberResult(0)
}
return args[0]
}

View File

@ -50,7 +50,7 @@ func init() {
//RegisterFunction("TEXTJOIN")
RegisterFunction("TRIM", Trim)
RegisterFunction("_xlfn.UNICHAR", Char) // for now
RegisterFunction("_xlfn.UNICODE", Code) // for now
RegisterFunction("_xlfn.UNICODE", Unicode)
RegisterFunction("UPPER", Upper)
//RegisterFunction("VALUE", )
}
@ -109,6 +109,22 @@ func Code(args []Result) Result {
return MakeNumberResult(float64(s.ValueString[0]))
}
func Unicode(args []Result) Result {
if len(args) != 1 {
return MakeErrorResult("UNICODE requires a single string argument")
}
s := args[0].AsString()
if s.Type != ResultTypeString {
return MakeErrorResult("UNICODE requires a single string argument")
}
// Zero length string returns an error
if len(s.ValueString) == 0 {
return MakeErrorResult("UNICODE requires a non-zero length argument")
}
return MakeNumberResult(float64(s.ValueString[0]))
}
// Concatenate is an implementation of the Excel CONCATENATE() function.
func Concatenate(args []Result) Result {
buf := bytes.Buffer{}

View File

@ -44,6 +44,7 @@ func (r Range) Reference(ctx Context, ev Evaluator) Reference {
// TODO: move these somewhere to remove duplication
func ParseCellReference(s string) (col string, row uint32, err error) {
s = strings.Replace(s, "$", "", -1)
split := -1
lfor:
for i := 0; i < len(s); i++ {
@ -108,7 +109,8 @@ func resultFromCellRange(ctx Context, ev Evaluator, from, to string) Result {
for r := fr; r <= tr; r++ {
args := []Result{}
for c := bc; c <= ec; c++ {
args = append(args, ctx.Cell(fmt.Sprintf("%s%d", IndexToColumn(c), r), ev))
res := ctx.Cell(fmt.Sprintf("%s%d", IndexToColumn(c), r), ev)
args = append(args, res)
}
arr = append(arr, args)
}

View File

@ -16,6 +16,7 @@ import (
// ParseCellReference parses a cell reference of the form 'A10' and splits it
// into column/row segments.
func ParseCellReference(s string) (col string, row uint32, err error) {
s = strings.Replace(s, "$", "", -1)
split := -1
lfor:
for i := 0; i < len(s); i++ {