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

* CELL function * CELL moved to fninformation.go * CHOOSE function * CHOOSE function add one test * COLUMN function * COLUMNS function * COUNTIF function * COUNTIF, COUNTIFS, MINIFS, MAXIFS, SUMIF, SUMIFS, some style fixes * SUMIF and SUMIFS moved to the right location * VALUE function * wildcard is added * CELL format fix
393 lines
11 KiB
Go
393 lines
11 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 by contacting sales@baliance.com.
|
|
|
|
package formula
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
func init() {
|
|
// RegisterFunction("ASC") Need to figure out how to test
|
|
// RegisterFunction("BAHTTEXT")
|
|
RegisterFunction("CHAR", Char)
|
|
RegisterFunction("CLEAN", Clean)
|
|
RegisterFunction("CODE", Code)
|
|
RegisterFunction("CONCATENATE", Concatenate)
|
|
// RegisterFunction("CONCAT", ) Need to test with Excel
|
|
// RegisterFunction("DBCS")
|
|
// RegisterFunction("DOLLAR") Need to test with Excel
|
|
RegisterFunction("EXACT", Exact)
|
|
// RegisterFunction("FIND")
|
|
// RegisterFunction("FINDB")
|
|
RegisterFunction("LEFT", Left)
|
|
RegisterFunction("LEFTB", Left) // for now
|
|
RegisterFunction("LEN", Len)
|
|
RegisterFunction("LENB", Len) // for now
|
|
RegisterFunction("LOWER", Lower)
|
|
// RegisterFunction("MID")
|
|
// RegisterFunction("MIDB")
|
|
// RegisterFunction("NUMBERVALUE")
|
|
// RegisterFunction("PHONETIC")
|
|
RegisterFunction("PROPER", Proper)
|
|
// RegisterFunction("REPLACE")
|
|
// RegisterFunction("REPLACEB")
|
|
RegisterFunction("REPT", Rept)
|
|
RegisterFunction("RIGHT", Right)
|
|
RegisterFunction("RIGHTB", Right) // for now
|
|
//RegisterFunction("SEARCH", )
|
|
//RegisterFunction("SEARCHB", )
|
|
//RegisterFunction("SUBSTITUTE", )
|
|
RegisterFunction("T", T)
|
|
//RegisterFunction("TEXT")
|
|
//RegisterFunction("TEXTJOIN")
|
|
RegisterFunction("TRIM", Trim)
|
|
RegisterFunction("_xlfn.UNICHAR", Char) // for now
|
|
RegisterFunction("_xlfn.UNICODE", Unicode)
|
|
RegisterFunction("UPPER", Upper)
|
|
RegisterFunction("VALUE", Value)
|
|
}
|
|
|
|
// Char is an implementation of the Excel CHAR function that takes an integer in
|
|
// the range [0,255] and returns the corresponding ASCII character.
|
|
func Char(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("CHAR requires a single numeric argument")
|
|
}
|
|
c := args[0].AsNumber()
|
|
if c.Type != ResultTypeNumber {
|
|
return MakeErrorResult("CHAR requires a single numeric argument")
|
|
}
|
|
cv := int(c.ValueNumber)
|
|
if cv < 0 || cv > 255 {
|
|
return MakeErrorResult("CHAR requires arguments in the range [0,255]")
|
|
}
|
|
return MakeStringResult(fmt.Sprintf("%c", cv))
|
|
}
|
|
|
|
// Clean is an implementation of the Excel CLEAN function that removes
|
|
// unprintable characters.
|
|
func Clean(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("CLEAN requires a single string argument")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("CHAR requires a single string argument")
|
|
}
|
|
b := bytes.Buffer{}
|
|
for _, c := range s.ValueString {
|
|
if unicode.IsPrint(c) {
|
|
b.WriteRune(c)
|
|
}
|
|
}
|
|
return MakeStringResult(b.String())
|
|
}
|
|
|
|
// Code is an implementation of the Excel CODE function that returns the first
|
|
// character of the string as a number.
|
|
func Code(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("CODE requires a single string argument")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("CODE requires a single string argument")
|
|
}
|
|
// Zero length string returns a zero
|
|
if len(s.ValueString) == 0 {
|
|
return MakeNumberResult(0)
|
|
}
|
|
|
|
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{}
|
|
for _, a := range args {
|
|
a = a.AsString()
|
|
switch a.Type {
|
|
case ResultTypeString:
|
|
buf.WriteString(a.ValueString)
|
|
default:
|
|
return MakeErrorResult("CONCATENATE() requires arguments to be strings")
|
|
}
|
|
}
|
|
return MakeStringResult(buf.String())
|
|
}
|
|
|
|
// Exact is an implementation of the Excel EXACT() which compares two strings.
|
|
func Exact(args []Result) Result {
|
|
if len(args) != 2 {
|
|
return MakeErrorResult("CONCATENATE() requires two string arguments")
|
|
}
|
|
arg1 := args[0].AsString()
|
|
arg2 := args[1].AsString()
|
|
if arg1.Type != ResultTypeString || arg2.Type != ResultTypeString {
|
|
return MakeErrorResult("CONCATENATE() requires two string arguments")
|
|
}
|
|
return MakeBoolResult(arg1.ValueString == arg2.ValueString)
|
|
}
|
|
|
|
// Left implements the Excel LEFT(string,[n]) function which returns the
|
|
// leftmost n characters.
|
|
func Left(args []Result) Result {
|
|
n := 1
|
|
switch len(args) {
|
|
case 1:
|
|
// no length argument returns the single left-most character
|
|
case 2:
|
|
// second argument must be a number
|
|
if args[1].Type != ResultTypeNumber {
|
|
return MakeErrorResult("LEFT expected number argument")
|
|
}
|
|
// Excel truncates floating points
|
|
n = int(args[1].ValueNumber)
|
|
if n < 0 {
|
|
return MakeErrorResult("LEFT expected number argument >= 0")
|
|
}
|
|
if n == 0 { // empty string
|
|
return MakeStringResult("")
|
|
}
|
|
default:
|
|
return MakeErrorResult("LEFT expected one or two arguments")
|
|
}
|
|
|
|
// can't call LEFT on a range
|
|
if args[0].Type == ResultTypeList {
|
|
return MakeErrorResult("LEFT can't be called on a range")
|
|
}
|
|
v := args[0].Value()
|
|
if n > len(v) {
|
|
return MakeStringResult(v)
|
|
}
|
|
return MakeStringResult(v[0:n])
|
|
|
|
}
|
|
|
|
// Len is an implementation of the Excel LEN function that returns length of a string
|
|
func Len(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("LEN requires a single string argument")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("LEN requires a single string argument")
|
|
}
|
|
|
|
return MakeNumberResult(float64(len(s.ValueString)))
|
|
}
|
|
|
|
// Lower is an implementation of the Excel LOWER function that returns a lower
|
|
// case version of a string.
|
|
func Lower(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("LOWER requires a single string argument")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("LOWER requires a single string argument")
|
|
}
|
|
|
|
return MakeStringResult(strings.ToLower(s.ValueString))
|
|
}
|
|
|
|
// Proper is an implementation of the Excel PROPER function that returns a copy
|
|
// of the string with each word capitalized.
|
|
func Proper(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("PROPER requires a single string argument")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("PROPER requires a single string argument")
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
prevWasLetter := false
|
|
for _, c := range s.ValueString {
|
|
if !prevWasLetter && unicode.IsLetter(c) {
|
|
buf.WriteRune(unicode.ToUpper(c))
|
|
} else {
|
|
// seems odd but matches Excel's behavior
|
|
buf.WriteRune(unicode.ToLower(c))
|
|
}
|
|
prevWasLetter = unicode.IsLetter(c)
|
|
}
|
|
|
|
return MakeStringResult(buf.String())
|
|
}
|
|
|
|
// Rept is an implementation of the Excel REPT function that returns n copies of
|
|
// a string.
|
|
func Rept(args []Result) Result {
|
|
if len(args) != 2 {
|
|
return MakeErrorResult("REPT requires two arguments")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("PROPER requires first argument to be a string")
|
|
}
|
|
|
|
n := args[1].AsNumber()
|
|
if n.Type != ResultTypeNumber {
|
|
return MakeErrorResult("PROPER requires second argument to be a number")
|
|
}
|
|
if n.ValueNumber < 0 {
|
|
return MakeErrorResult("PROPER requires second argument to be >= 0")
|
|
}
|
|
if n.ValueNumber == 0 {
|
|
return MakeStringResult("")
|
|
}
|
|
|
|
buf := bytes.Buffer{}
|
|
for i := 0; i < int(n.ValueNumber); i++ {
|
|
buf.WriteString(s.ValueString)
|
|
}
|
|
return MakeStringResult(buf.String())
|
|
}
|
|
|
|
// Right implements the Excel RIGHT(string,[n]) function which returns the
|
|
// rightmost n characters.
|
|
func Right(args []Result) Result {
|
|
n := 1
|
|
switch len(args) {
|
|
case 1:
|
|
// no length argument returns the single right-most character
|
|
case 2:
|
|
// second argument must be a number
|
|
if args[1].Type != ResultTypeNumber {
|
|
return MakeErrorResult("RIGHT expected number argument")
|
|
}
|
|
// Excel truncates floating points
|
|
n = int(args[1].ValueNumber)
|
|
if n < 0 {
|
|
return MakeErrorResult("RIGHT expected number argument >= 0")
|
|
}
|
|
if n == 0 { // empty string
|
|
return MakeStringResult("")
|
|
}
|
|
default:
|
|
return MakeErrorResult("RIGHT accepts one or two arguments")
|
|
}
|
|
|
|
// can't call RIGHT on a range
|
|
if args[0].Type == ResultTypeList {
|
|
return MakeErrorResult("RIGHT can't be called on a range")
|
|
}
|
|
v := args[0].Value()
|
|
m := len(v)
|
|
if n > m {
|
|
return MakeStringResult(v)
|
|
}
|
|
return MakeStringResult(v[m-n : m])
|
|
}
|
|
|
|
// T is an implementation of the Excel T function that returns whether the
|
|
// argument is text.
|
|
func T(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("T requires a single string argument")
|
|
}
|
|
s := args[0]
|
|
if s.Type == ResultTypeError || s.Type == ResultTypeString {
|
|
return s
|
|
}
|
|
return MakeEmptyResult()
|
|
}
|
|
|
|
// Trim is an implementation of the Excel TRIM function that removes leading,
|
|
// trailing and consecutive spaces.
|
|
func Trim(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("TRIM requires a single string argument")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("TRIM requires a single string argument")
|
|
}
|
|
buf := bytes.Buffer{}
|
|
seenLetter := false
|
|
prevWasSpace := false
|
|
trailingSpaces := 0
|
|
for _, c := range s.ValueString {
|
|
isSpace := c == ' '
|
|
if isSpace {
|
|
if !seenLetter {
|
|
continue
|
|
}
|
|
if !prevWasSpace {
|
|
trailingSpaces++
|
|
buf.WriteRune(c)
|
|
}
|
|
} else {
|
|
trailingSpaces = 0
|
|
seenLetter = true
|
|
buf.WriteRune(c)
|
|
}
|
|
prevWasSpace = isSpace
|
|
}
|
|
buf.Truncate(buf.Len() - trailingSpaces)
|
|
return MakeStringResult(buf.String())
|
|
}
|
|
|
|
// Upper is an implementation of the Excel UPPER function that returns a upper
|
|
// case version of a string.
|
|
func Upper(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("UPPER requires a single string argument")
|
|
}
|
|
s := args[0].AsString()
|
|
if s.Type != ResultTypeString {
|
|
return MakeErrorResult("UPPER requires a single string argument")
|
|
}
|
|
|
|
return MakeStringResult(strings.ToUpper(s.ValueString))
|
|
}
|
|
|
|
// Value is an implementation of the Excel VALUE function.
|
|
func Value(args []Result) Result {
|
|
if len(args) != 1 {
|
|
return MakeErrorResult("VALUE requires a single argument")
|
|
}
|
|
|
|
value := args[0]
|
|
if value.Type == ResultTypeNumber {
|
|
return value
|
|
}
|
|
|
|
if value.Type == ResultTypeString {
|
|
result, err := strconv.ParseFloat(value.Value(), 64)
|
|
if err == nil {
|
|
return MakeNumberResult(result)
|
|
}
|
|
}
|
|
|
|
return MakeErrorResult("Incorrect argument for VALUE")
|
|
}
|