forked from unidoc/unioffice
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fnlogical.go
228 lines (209 loc) · 5.77 KB
/
fnlogical.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// 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)
}