Todd 0e09e64818 formula: compatibility & bug fixes
- Make UNICODE with no arguments behave like Excel
- Fix OFFSET/INDEX for named ranges
2017-09-20 20:47:03 -04:00

229 lines
5.8 KiB
Go

// Copyright 2017 Baliance. 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 by contacting sales@baliance.com.
package formula
func init() {
RegisterFunction("AND", And)
RegisterFunction("FALSE", False)
RegisterFunction("IF", If)
RegisterFunction("IFERROR", IfError)
RegisterFunction("_xlfn.IFNA", IfNA) // Only in Excel 2013+
// TODO: RegisterFunction("IFS", Ifs) // Only in Excel 2016+
RegisterFunction("NOT", Not)
RegisterFunction("OR", Or)
// TODO: RegisterFunction("SWITCH", Switch) // Only in Excel 2016+
RegisterFunction("TRUE", True) // yup, TRUE()/FALSE() are functions in Excel, news to me...
RegisterFunction("_xlfn.XOR", Xor)
}
// And is an implementation of the Excel AND() function.
func And(args []Result) Result {
if len(args) == 0 {
return MakeErrorResult("AND requires at least one argument")
}
result := true
for _, a := range args {
a = a.AsNumber()
switch a.Type {
case ResultTypeList, ResultTypeArray:
res := And(a.ListValues())
if res.Type == ResultTypeError {
return res
}
if res.ValueNumber == 0 {
result = false
}
case ResultTypeNumber:
if a.ValueNumber == 0 {
result = false
}
case ResultTypeString:
return MakeErrorResult("AND doesn't operate on strings")
case ResultTypeError:
return a
default:
return MakeErrorResult("unsupported argument type in AND")
}
}
return MakeBoolResult(result)
}
// False is an implementation of the Excel FALSE() function. It takes no
// arguments.
func False(args []Result) Result {
if len(args) != 0 {
return MakeErrorResult("FALSE takes no arguments")
}
return MakeBoolResult(false)
}
// If is an implementation of the Excel IF() function. It takes one, two or
// three arguments.
func If(args []Result) Result {
if len(args) == 0 {
return MakeErrorResult("IF requires at least one argument")
}
if len(args) > 3 {
return MakeErrorResult("IF accepts at most three arguments")
}
cond := args[0]
switch cond.Type {
case ResultTypeNumber:
case ResultTypeError:
return cond
default:
return MakeErrorResult("IF initial argument must be numeric")
}
// single argument just returns the condition value
if len(args) == 1 {
return MakeBoolResult(cond.ValueNumber != 0)
}
// true case
if cond.ValueNumber != 0 {
return args[1]
}
// false case
if len(args) == 3 {
return args[2]
}
return MakeBoolResult(false)
}
// IfError is an implementation of the Excel IFERROR() function. It takes two arguments.
func IfError(args []Result) Result {
if len(args) != 2 {
return MakeErrorResult("IFERROR requires two arguments")
}
if args[0].Type != ResultTypeError {
// empty cell returns a zero
if args[0].Type == ResultTypeEmpty {
return MakeNumberResult(0)
}
return args[0]
}
return args[1]
}
// IfNA is an implementation of the Excel IFNA() function. It takes two arguments.
func IfNA(args []Result) Result {
if len(args) != 2 {
return MakeErrorResult("IFNA requires two arguments")
}
if args[0].Type == ResultTypeError && args[0].ValueString == "#N/A" {
return args[1]
}
return args[0]
}
// Not is an implementation of the Excel NOT() function and takes a single
// argument.
func Not(args []Result) Result {
if len(args) != 1 {
return MakeErrorResult("NOT requires one argument")
}
switch args[0].Type {
case ResultTypeError:
return args[0]
case ResultTypeString, ResultTypeList:
return MakeErrorResult("NOT expects a numeric argument")
case ResultTypeNumber:
return MakeBoolResult(!(args[0].ValueNumber != 0))
default:
return MakeErrorResult("unhandled NOT argument type")
}
}
// Or is an implementation of the Excel OR() function and takes a variable
// number of arguments.
func Or(args []Result) Result {
if len(args) == 0 {
return MakeErrorResult("OR requires at least one argument")
}
result := false
for _, a := range args {
switch a.Type {
case ResultTypeList, ResultTypeArray:
res := Or(a.ListValues())
if res.Type == ResultTypeError {
return res
}
if res.ValueNumber != 0 {
result = true
}
case ResultTypeNumber:
if a.ValueNumber != 0 {
result = true
}
case ResultTypeString:
return MakeErrorResult("OR doesn't operate on strings")
case ResultTypeError:
return a
default:
return MakeErrorResult("unsupported argument type in OR")
}
}
return MakeBoolResult(result)
}
// True is an implementation of the Excel TRUE() function. It takes no
// arguments.
func True(args []Result) Result {
if len(args) != 0 {
return MakeErrorResult("TRUE takes no arguments")
}
return MakeBoolResult(true)
}
// Xor is an implementation of the Excel XOR() function and takes a variable
// number of arguments. It's odd to say the least. If any argument is numeric,
// it returns true if the number of non-zero numeric arguments is odd and false
// otherwise. If no argument is numeric, it returns an error.
func Xor(args []Result) Result {
if len(args) < 1 {
return MakeErrorResult("XOR requires at least one argument")
}
cnt := 0
hasNum := false
for _, arg := range args {
switch arg.Type {
case ResultTypeList, ResultTypeArray:
res := Xor(arg.ListValues())
if res.Type == ResultTypeError {
return res
}
if res.ValueNumber != 0 {
cnt++
}
hasNum = true
case ResultTypeNumber:
if arg.ValueNumber != 0 {
cnt++
}
hasNum = true
case ResultTypeString:
// XOR appers to treat strings as zero values.
case ResultTypeError:
return arg
default:
return MakeErrorResult("unsupported argument type in XOR")
}
}
if !hasNum {
return MakeErrorResult("XOR requires numeric input")
}
return MakeBoolResult(cnt%2 != 0)
}