writeexcel 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +103 -0
- data/examples/a_simple.rb +42 -0
- data/examples/autofilters.rb +266 -0
- data/examples/copyformat.rb +51 -0
- data/examples/data_validate.rb +278 -0
- data/examples/date_time.rb +86 -0
- data/examples/demo.rb +118 -0
- data/examples/diag_border.rb +35 -0
- data/examples/formats.rb +489 -0
- data/examples/header.rb +136 -0
- data/examples/hidden.rb +28 -0
- data/examples/hyperlink.rb +42 -0
- data/examples/images.rb +52 -0
- data/examples/merge1.rb +39 -0
- data/examples/merge2.rb +44 -0
- data/examples/merge3.rb +65 -0
- data/examples/merge4.rb +82 -0
- data/examples/merge5.rb +79 -0
- data/examples/protection.rb +46 -0
- data/examples/regions.rb +52 -0
- data/examples/repeat.rb +42 -0
- data/examples/republic.png +0 -0
- data/examples/stats.rb +75 -0
- data/examples/stocks.rb +80 -0
- data/examples/tab_colors.rb +30 -0
- data/lib/writeexcel/biffwriter.rb +260 -0
- data/lib/writeexcel/chart.rb +217 -0
- data/lib/writeexcel/excelformulaparser.rb +573 -0
- data/lib/writeexcel/format.rb +1108 -0
- data/lib/writeexcel/formula.rb +986 -0
- data/lib/writeexcel/olewriter.rb +322 -0
- data/lib/writeexcel/properties.rb +250 -0
- data/lib/writeexcel/workbook.rb +2630 -0
- data/lib/writeexcel/worksheet.rb +6377 -0
- data/lib/writeexcel.rb +18 -0
- data/test/perl_output/README +31 -0
- data/test/perl_output/a_simple.xls +0 -0
- data/test/perl_output/biff_add_continue_testdata +0 -0
- data/test/perl_output/data_validate.xls +0 -0
- data/test/perl_output/date_time.xls +0 -0
- data/test/perl_output/demo.xls +0 -0
- data/test/perl_output/diag_border.xls +0 -0
- data/test/perl_output/f_font_biff +0 -0
- data/test/perl_output/f_font_key +1 -0
- data/test/perl_output/f_xf_biff +0 -0
- data/test/perl_output/file_font_biff +0 -0
- data/test/perl_output/file_font_key +1 -0
- data/test/perl_output/file_xf_biff +0 -0
- data/test/perl_output/headers.xls +0 -0
- data/test/perl_output/hidden.xls +0 -0
- data/test/perl_output/hyperlink.xls +0 -0
- data/test/perl_output/images.xls +0 -0
- data/test/perl_output/merge1.xls +0 -0
- data/test/perl_output/merge2.xls +0 -0
- data/test/perl_output/merge3.xls +0 -0
- data/test/perl_output/merge4.xls +0 -0
- data/test/perl_output/merge5.xls +0 -0
- data/test/perl_output/ole_write_header +0 -0
- data/test/perl_output/protection.xls +0 -0
- data/test/perl_output/regions.xls +0 -0
- data/test/perl_output/stats.xls +0 -0
- data/test/perl_output/stocks.xls +0 -0
- data/test/perl_output/tab_colors.xls +0 -0
- data/test/perl_output/unicode_cyrillic.xls +0 -0
- data/test/perl_output/workbook1.xls +0 -0
- data/test/perl_output/workbook2.xls +0 -0
- data/test/perl_output/ws_colinfo +1 -0
- data/test/perl_output/ws_store_colinfo +0 -0
- data/test/perl_output/ws_store_dimensions +0 -0
- data/test/perl_output/ws_store_filtermode +0 -0
- data/test/perl_output/ws_store_filtermode_off +0 -0
- data/test/perl_output/ws_store_filtermode_on +0 -0
- data/test/perl_output/ws_store_selection +0 -0
- data/test/perl_output/ws_store_window2 +1 -0
- data/test/republic.png +0 -0
- data/test/tc_all.rb +31 -0
- data/test/tc_biff.rb +104 -0
- data/test/tc_chart.rb +22 -0
- data/test/tc_example_match.rb +1280 -0
- data/test/tc_format.rb +1267 -0
- data/test/tc_formula.rb +63 -0
- data/test/tc_ole.rb +110 -0
- data/test/tc_workbook.rb +115 -0
- data/test/tc_worksheet.rb +115 -0
- data/test/test_00_IEEE_double.rb +14 -0
- data/test/test_01_add_worksheet.rb +12 -0
- data/test/test_02_merge_formats.rb +58 -0
- data/test/test_04_dimensions.rb +397 -0
- data/test/test_05_rows.rb +182 -0
- data/test/test_06_extsst.rb +80 -0
- data/test/test_11_date_time.rb +484 -0
- data/test/test_12_date_only.rb +506 -0
- data/test/test_13_date_seconds.rb +486 -0
- data/test/test_21_escher.rb +629 -0
- data/test/test_22_mso_drawing_group.rb +739 -0
- data/test/test_23_note.rb +78 -0
- data/test/test_24_txo.rb +80 -0
- data/test/test_26_autofilter.rb +327 -0
- data/test/test_27_autofilter.rb +144 -0
- data/test/test_28_autofilter.rb +174 -0
- data/test/test_29_process_jpg.rb +131 -0
- data/test/test_30_validation_dval.rb +82 -0
- data/test/test_31_validation_dv_strings.rb +131 -0
- data/test/test_32_validation_dv_formula.rb +211 -0
- data/test/test_40_property_types.rb +191 -0
- data/test/test_41_properties.rb +238 -0
- data/test/test_42_set_properties.rb +419 -0
- data/test/ts_all.rb +34 -0
- metadata +170 -0
@@ -0,0 +1,986 @@
|
|
1
|
+
###############################################################################
|
2
|
+
#
|
3
|
+
# Formula - A class for generating Excel formulas.
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# Used in conjunction with Spreadsheet::WriteExcel
|
7
|
+
#
|
8
|
+
# Copyright 2000-2008, John McNamara, jmcnamara@cpan.org
|
9
|
+
#
|
10
|
+
# original written in Perl by John McNamara
|
11
|
+
# converted to Ruby by Hideo Nakamura, cxn03651@msj.biglobe.ne.jp
|
12
|
+
#
|
13
|
+
require 'nkf'
|
14
|
+
require 'strscan'
|
15
|
+
require 'writeexcel/excelformulaparser'
|
16
|
+
|
17
|
+
class Formula < ExcelFormulaParser
|
18
|
+
|
19
|
+
NonAscii = /[^!"#\$%&'\(\)\*\+,\-\.\/\:\;<=>\?@0-9A-Za-z_\[\\\]^` ~\0\n]/
|
20
|
+
|
21
|
+
attr_accessor :byte_order, :workbook, :ext_sheets, :ext_refs, :ext_ref_count
|
22
|
+
|
23
|
+
def initialize(byte_order)
|
24
|
+
@byte_order = byte_order
|
25
|
+
@workbook = ""
|
26
|
+
@ext_sheets = {}
|
27
|
+
@ext_refs = {}
|
28
|
+
@ext_ref_count = 0
|
29
|
+
initialize_hashes
|
30
|
+
end
|
31
|
+
|
32
|
+
###############################################################################
|
33
|
+
#
|
34
|
+
# parse_formula()
|
35
|
+
#
|
36
|
+
# Takes a textual description of a formula and returns a RPN encoded byte
|
37
|
+
# string.
|
38
|
+
#
|
39
|
+
def parse_formula(formula, byte_stream = false)
|
40
|
+
# Build the parse tree for the formula
|
41
|
+
tokens = reverse(parse(formula))
|
42
|
+
|
43
|
+
# Add a volatile token if the formula contains a volatile function.
|
44
|
+
# This must be the first token in the list
|
45
|
+
#
|
46
|
+
tokens.unshift('_vol') if check_volatile(tokens) != 0
|
47
|
+
|
48
|
+
# The return value depends on which Worksheet.pm method is the caller
|
49
|
+
unless byte_stream
|
50
|
+
# Parse formula to see if it throws any errors and then
|
51
|
+
# return raw tokens to Worksheet::store_formula()
|
52
|
+
#
|
53
|
+
tokens
|
54
|
+
else
|
55
|
+
# Return byte stream to Worksheet::write_formula()
|
56
|
+
parse_tokens(tokens)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
###############################################################################
|
61
|
+
#
|
62
|
+
# parse_tokens()
|
63
|
+
#
|
64
|
+
# Convert each token or token pair to its Excel 'ptg' equivalent.
|
65
|
+
#
|
66
|
+
def parse_tokens(tokens)
|
67
|
+
parse_str = ''
|
68
|
+
last_type = ''
|
69
|
+
modifier = ''
|
70
|
+
num_args = 0
|
71
|
+
_class = 0
|
72
|
+
_classary = [1]
|
73
|
+
args = tokens.dup
|
74
|
+
# A note about the class modifiers used below. In general the class,
|
75
|
+
# "reference" or "value", of a function is applied to all of its operands.
|
76
|
+
# However, in certain circumstances the operands can have mixed classes,
|
77
|
+
# e.g. =VLOOKUP with external references. These will eventually be dealt
|
78
|
+
# with by the parser. However, as a workaround the class type of a token
|
79
|
+
# can be changed via the repeat_formula interface. Thus, a _ref2d token can
|
80
|
+
# be changed by the user to _ref2dA or _ref2dR to change its token class.
|
81
|
+
#
|
82
|
+
while (!args.empty?)
|
83
|
+
token = args.shift
|
84
|
+
|
85
|
+
if (token == '_arg')
|
86
|
+
num_args = args.shift
|
87
|
+
elsif (token == '_class')
|
88
|
+
token = args.shift
|
89
|
+
_class = @functions[token][2]
|
90
|
+
# If _class is undef then it means that the function isn't valid.
|
91
|
+
exit "Unknown function #{token}() in formula\n" if _class.nil?
|
92
|
+
_classary.push(_class)
|
93
|
+
elsif (token == '_vol')
|
94
|
+
parse_str = parse_str + convert_volatile()
|
95
|
+
elsif (token == 'ptgBool')
|
96
|
+
token = args.shift
|
97
|
+
parse_str = parse_str + convert_bool(token)
|
98
|
+
elsif (token == '_num')
|
99
|
+
token = args.shift
|
100
|
+
parse_str = parse_str + convert_number(token)
|
101
|
+
elsif (token == '_str')
|
102
|
+
token = args.shift
|
103
|
+
parse_str = parse_str + convert_string(token)
|
104
|
+
elsif (token =~ /^_ref2d/)
|
105
|
+
modifier = token.sub(/_ref2d/, '')
|
106
|
+
_class = _classary[-1]
|
107
|
+
_class = 0 if modifier == 'R'
|
108
|
+
_class = 1 if modifier == 'V'
|
109
|
+
token = args.shift
|
110
|
+
parse_str = parse_str + convert_ref2d(token, _class)
|
111
|
+
elsif (token =~ /^_ref3d/)
|
112
|
+
modifier = token.sub(/_ref3d/,'')
|
113
|
+
_class = _classary[-1]
|
114
|
+
_class = 0 if modifier == 'R'
|
115
|
+
_class = 1 if modifier == 'V'
|
116
|
+
token = args.shift
|
117
|
+
parse_str = parse_str + convert_ref3d(token, _class)
|
118
|
+
elsif (token =~ /^_range2d/)
|
119
|
+
modifier = token.sub(/_range2d/,'')
|
120
|
+
_class = _classary[-1]
|
121
|
+
_class = 0 if modifier == 'R'
|
122
|
+
_class = 1 if modifier == 'V'
|
123
|
+
token = args.shift
|
124
|
+
parse_str = parse_str + convert_range2d(token, _class)
|
125
|
+
elsif (token =~ /^_range3d/)
|
126
|
+
modifier = token.sub(/_range3d/,'')
|
127
|
+
_class = _classary[-1]
|
128
|
+
_class = 0 if modifier == 'R'
|
129
|
+
_class = 1 if modifier == 'V'
|
130
|
+
token = args.shift
|
131
|
+
parse_str = parse_str + convert_range3d(token, _class)
|
132
|
+
elsif (token == '_func')
|
133
|
+
token = args.shift
|
134
|
+
parse_str = parse_str + convert_function(token, num_args.to_i)
|
135
|
+
_classary.pop
|
136
|
+
num_args = 0 # Reset after use
|
137
|
+
elsif @ptg[token]
|
138
|
+
parse_str = parse_str + [@ptg[token]].pack("C")
|
139
|
+
else
|
140
|
+
# Unrecognised token
|
141
|
+
return nil
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
return parse_str
|
147
|
+
end
|
148
|
+
|
149
|
+
def scan(formula)
|
150
|
+
s = StringScanner.new(formula)
|
151
|
+
q = []
|
152
|
+
until s.eos?
|
153
|
+
# order is important.
|
154
|
+
if s.scan(/(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?/)
|
155
|
+
q.push [:NUMBER, s.matched]
|
156
|
+
elsif s.scan(/"([^"]|"")*"/)
|
157
|
+
q.push [:STRING, s.matched]
|
158
|
+
elsif s.scan(/\$?[A-I]?[A-Z]\$?(\d+)?:\$?[A-I]?[A-Z]\$?(\d+)?/)
|
159
|
+
q.push [:RANGE2D , s.matched]
|
160
|
+
elsif s.scan(/[^!(,]+!\$?[A-I]?[A-Z]\$?(\d+)?:\$?[A-I]?[A-Z]\$?(\d+)?/)
|
161
|
+
q.push [:RANGE3D , s.matched]
|
162
|
+
elsif s.scan(/\$?[A-I]?[A-Z]\$?\d+/)
|
163
|
+
q.push [:REF2D, s.matched]
|
164
|
+
elsif s.scan(/[^!(,]+!\$?[A-I]?[A-Z]\$?\d+/)
|
165
|
+
q.push [:REF3D , s.matched]
|
166
|
+
elsif s.scan(/'[^']+'!\$?[A-I]?[A-Z]\$?\d+/)
|
167
|
+
q.push [:REF3D , s.matched]
|
168
|
+
elsif s.scan(/<=/)
|
169
|
+
q.push [:LE , s.matched]
|
170
|
+
elsif s.scan(/>=/)
|
171
|
+
q.push [:GE , s.matched]
|
172
|
+
elsif s.scan(/<>/)
|
173
|
+
q.push [:NE , s.matched]
|
174
|
+
elsif s.scan(/</)
|
175
|
+
q.push [:LT , s.matched]
|
176
|
+
elsif s.scan(/>/)
|
177
|
+
q.push [:GT , s.matched]
|
178
|
+
elsif s.scan(/TRUE/)
|
179
|
+
q.push [:TRUE, s.matched]
|
180
|
+
elsif s.scan(/FALSE/)
|
181
|
+
q.push [:FALSE, s.matched]
|
182
|
+
elsif s.scan(/[A-Z0-9_.]+/)
|
183
|
+
q.push [:FUNC, s.matched]
|
184
|
+
elsif s.scan(/\s+/)
|
185
|
+
;
|
186
|
+
elsif s.scan(/./)
|
187
|
+
q.push [s.matched, s.matched]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
q.push [:EOL, nil]
|
191
|
+
end
|
192
|
+
|
193
|
+
def parse(formula)
|
194
|
+
@q = scan(formula)
|
195
|
+
@q.push [false, nil]
|
196
|
+
@yydebug=true
|
197
|
+
do_parse
|
198
|
+
end
|
199
|
+
|
200
|
+
def next_token
|
201
|
+
@q.shift
|
202
|
+
end
|
203
|
+
|
204
|
+
def reverse(expression)
|
205
|
+
expression.flatten
|
206
|
+
end
|
207
|
+
|
208
|
+
###############################################################################
|
209
|
+
#
|
210
|
+
# get_ext_sheets()
|
211
|
+
#
|
212
|
+
# This semi-public method is used to update the hash of sheet names. It is
|
213
|
+
# updated by the add_worksheet() method of the Workbook class.
|
214
|
+
#
|
215
|
+
# TODO
|
216
|
+
#
|
217
|
+
def get_ext_sheets
|
218
|
+
# TODO
|
219
|
+
refs = @ext_refs
|
220
|
+
return refs
|
221
|
+
|
222
|
+
#my @refs = sort {$refs{$a} <=> $refs{$b}} keys %refs;
|
223
|
+
|
224
|
+
#foreach my $ref (@refs) {
|
225
|
+
# $ref = [split /:/, $ref];
|
226
|
+
#}
|
227
|
+
|
228
|
+
#return @refs;
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
###############################################################################
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
###############################################################################
|
237
|
+
|
238
|
+
|
239
|
+
###############################################################################
|
240
|
+
#
|
241
|
+
# _check_volatile()
|
242
|
+
#
|
243
|
+
# Check if the formula contains a volatile function, i.e. a function that must
|
244
|
+
# be recalculated each time a cell is updated. These formulas require a ptgAttr
|
245
|
+
# with the volatile flag set as the first token in the parsed expression.
|
246
|
+
#
|
247
|
+
# Examples of volatile functions: RAND(), NOW(), TODAY()
|
248
|
+
#
|
249
|
+
def check_volatile(tokens)
|
250
|
+
volatile = 0
|
251
|
+
|
252
|
+
(0..tokens.size-1).each do |i|
|
253
|
+
# If the next token is a function check if it is volatile.
|
254
|
+
if tokens[i] == '_func' and @functions[tokens[i+1]][3] != 0
|
255
|
+
volatile = 1
|
256
|
+
break
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
return volatile
|
261
|
+
end
|
262
|
+
|
263
|
+
###############################################################################
|
264
|
+
#
|
265
|
+
# _convert_volatile()
|
266
|
+
#
|
267
|
+
# Convert _vol to a ptgAttr tag formatted to indicate that the formula contains
|
268
|
+
# a volatile function. See _check_volatile()
|
269
|
+
#
|
270
|
+
def convert_volatile
|
271
|
+
# Set bitFattrSemi flag to indicate volatile function, "w" is set to zero.
|
272
|
+
return [@ptg['ptgAttr'], 0x1, 0x0].pack("CCv")
|
273
|
+
end
|
274
|
+
|
275
|
+
###############################################################################
|
276
|
+
#
|
277
|
+
# _convert_bool()
|
278
|
+
#
|
279
|
+
# Convert a boolean token to ptgBool
|
280
|
+
#
|
281
|
+
def convert_bool(bool)
|
282
|
+
return [@ptg['ptgBool'], bool.to_i].pack("CC")
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
###############################################################################
|
287
|
+
#
|
288
|
+
# _convert_number()
|
289
|
+
#
|
290
|
+
# Convert a number token to ptgInt or ptgNum
|
291
|
+
#
|
292
|
+
def convert_number(num)
|
293
|
+
# Integer in the range 0..2**16-1
|
294
|
+
if ((num =~ /^\d+$/) && (num.to_i <= 65535))
|
295
|
+
return [@ptg['ptgInt'], num.to_i].pack("Cv")
|
296
|
+
else # A float
|
297
|
+
num = [num].pack("d")
|
298
|
+
num.reverse! if @byte_order != 0 && @byte_order != ''
|
299
|
+
return [@ptg['ptgNum']].pack("C") + num
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
###############################################################################
|
304
|
+
#
|
305
|
+
# _convert_string()
|
306
|
+
#
|
307
|
+
# Convert a string to a ptg Str.
|
308
|
+
#
|
309
|
+
def convert_string(str)
|
310
|
+
encoding = 0
|
311
|
+
|
312
|
+
str.sub!(/^"/,'') # Remove leading "
|
313
|
+
str.sub!(/"$/,'') # Remove trailing "
|
314
|
+
str.gsub!(/""/,'"') # Substitute Excel's escaped double quote "" for "
|
315
|
+
|
316
|
+
length = str.length
|
317
|
+
|
318
|
+
# Handle utf8 strings
|
319
|
+
if str =~ NonAscii
|
320
|
+
str = NKF.nkf('-w16L0 -m0 -W', str)
|
321
|
+
encoding = 1
|
322
|
+
end
|
323
|
+
|
324
|
+
exit "String in formula has more than 255 chars\n" if length > 255
|
325
|
+
|
326
|
+
return [@ptg['ptgStr'], length, encoding].pack("CCC") + str
|
327
|
+
end
|
328
|
+
|
329
|
+
###############################################################################
|
330
|
+
#
|
331
|
+
# _convert_ref2d()
|
332
|
+
#
|
333
|
+
# Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV.
|
334
|
+
#
|
335
|
+
def convert_ref2d(cell, _class)
|
336
|
+
# Convert the cell reference
|
337
|
+
row, col = cell_to_packed_rowcol(cell)
|
338
|
+
|
339
|
+
# The ptg value depends on the class of the ptg.
|
340
|
+
if (_class == 0)
|
341
|
+
ptgref = [@ptg['ptgRef']].pack("C")
|
342
|
+
elsif (_class == 1)
|
343
|
+
ptgref = [@ptg['ptgRefV']].pack("C")
|
344
|
+
elsif (_class == 2)
|
345
|
+
ptgref = [@ptg['ptgRefA']].pack("C")
|
346
|
+
else
|
347
|
+
exit "Unknown function class in formula\n"
|
348
|
+
end
|
349
|
+
|
350
|
+
return ptgref + row + col
|
351
|
+
end
|
352
|
+
|
353
|
+
###############################################################################
|
354
|
+
#
|
355
|
+
# _convert_ref3d
|
356
|
+
#
|
357
|
+
# Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a
|
358
|
+
# ptgRef3dV.
|
359
|
+
#
|
360
|
+
def convert_ref3d(token, _class)
|
361
|
+
# Split the ref at the ! symbol
|
362
|
+
ext_ref, cell = token.split('!')
|
363
|
+
|
364
|
+
# Convert the external reference part
|
365
|
+
ext_ref = pack_ext_ref(ext_ref)
|
366
|
+
|
367
|
+
# Convert the cell reference part
|
368
|
+
row, col = cell_to_packed_rowcol(cell)
|
369
|
+
|
370
|
+
# The ptg value depends on the class of the ptg.
|
371
|
+
if (_class == 0)
|
372
|
+
ptgref = [@ptg['ptgRef3d']].pack("C")
|
373
|
+
elsif (_class == 1)
|
374
|
+
ptgref = [@ptg['ptgRef3dV']].pack("C")
|
375
|
+
elsif (_class == 2)
|
376
|
+
ptgref = [@ptg['ptgRef3dA']].pack("C")
|
377
|
+
else
|
378
|
+
exit "Unknown function class in formula\n"
|
379
|
+
end
|
380
|
+
|
381
|
+
return ptgref + ext_ref + row + col
|
382
|
+
end
|
383
|
+
|
384
|
+
###############################################################################
|
385
|
+
#
|
386
|
+
# _convert_range2d()
|
387
|
+
#
|
388
|
+
# Convert an Excel range such as A1:D4 or A:D to a ptgRefV.
|
389
|
+
#
|
390
|
+
def convert_range2d(range, _class)
|
391
|
+
# Split the range into 2 cell refs
|
392
|
+
cell1, cell2 = range.split(':')
|
393
|
+
|
394
|
+
# A range such as A:D is equivalent to A1:D65536, so add rows as required
|
395
|
+
cell1 = cell1 + '1' unless cell1 =~ /\d/
|
396
|
+
cell2 = cell2 + '65536' unless cell2 =~ /\d/
|
397
|
+
|
398
|
+
# Convert the cell references
|
399
|
+
row1, col1 = cell_to_packed_rowcol(cell1)
|
400
|
+
row2, col2 = cell_to_packed_rowcol(cell2)
|
401
|
+
|
402
|
+
# The ptg value depends on the class of the ptg.
|
403
|
+
if (_class == 0)
|
404
|
+
ptgarea = [@ptg['ptgArea']].pack("C")
|
405
|
+
elsif (_class == 1)
|
406
|
+
ptgarea = [@ptg['ptgAreaV']].pack("C")
|
407
|
+
elsif (_class == 2)
|
408
|
+
ptgarea = [@ptg['ptgAreaA']].pack("C")
|
409
|
+
else
|
410
|
+
exit "Unknown function class in formula\n"
|
411
|
+
end
|
412
|
+
|
413
|
+
return ptgarea + row1 + row2 + col1 + col2
|
414
|
+
end
|
415
|
+
|
416
|
+
###############################################################################
|
417
|
+
#
|
418
|
+
# _convert_range3d
|
419
|
+
#
|
420
|
+
# Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to
|
421
|
+
# a ptgArea3dV.
|
422
|
+
#
|
423
|
+
def convert_range3d(token, _class)
|
424
|
+
# Split the ref at the ! symbol
|
425
|
+
ext_ref, range = token.split('!')
|
426
|
+
|
427
|
+
# Convert the external reference part
|
428
|
+
ext_ref = pack_ext_ref(ext_ref)
|
429
|
+
|
430
|
+
# Split the range into 2 cell refs
|
431
|
+
cell1, cell2 = range.split(':')
|
432
|
+
|
433
|
+
# A range such as A:D is equivalent to A1:D65536, so add rows as required
|
434
|
+
cell1 = cell1 + '1' unless cell1 =~ /\d/
|
435
|
+
cell2 = cell2 + '65536' unless cell2 =~ /\d/
|
436
|
+
|
437
|
+
# Convert the cell references
|
438
|
+
row1, col1 = cell_to_packed_rowcol(cell1)
|
439
|
+
row2, col2 = cell_to_packed_rowcol(cell2)
|
440
|
+
|
441
|
+
# The ptg value depends on the class of the ptg.
|
442
|
+
if (_class == 0)
|
443
|
+
ptgarea = [@ptg['ptgArea3d']].pack("C")
|
444
|
+
elsif (_class == 1)
|
445
|
+
ptgarea = [@ptg['ptgArea3dV']].pack("C")
|
446
|
+
elsif (_class == 2)
|
447
|
+
ptgarea = [@ptg['ptgArea3dA']].pack("C")
|
448
|
+
else
|
449
|
+
exit "Unknown function class in formula\n"
|
450
|
+
end
|
451
|
+
|
452
|
+
return ptgarea + ext_ref + row1 + row2 + col1+ col2
|
453
|
+
end
|
454
|
+
|
455
|
+
###############################################################################
|
456
|
+
#
|
457
|
+
# _pack_ext_ref()
|
458
|
+
#
|
459
|
+
# Convert the sheet name part of an external reference, for example "Sheet1" or
|
460
|
+
# "Sheet1:Sheet2", to a packed structure.
|
461
|
+
#
|
462
|
+
def pack_ext_ref(ext_ref)
|
463
|
+
ext_ref.sub!(/^'/,'') # Remove leading ' if any.
|
464
|
+
ext_ref.sub!(/'$/,'') # Remove trailing ' if any.
|
465
|
+
|
466
|
+
# Check if there is a sheet range eg., Sheet1:Sheet2.
|
467
|
+
if (ext_ref =~ /:/)
|
468
|
+
sheet1, sheet2 = ext_ref.split(':')
|
469
|
+
|
470
|
+
sheet1 = get_sheet_index(sheet1)
|
471
|
+
sheet2 = get_sheet_index(sheet2)
|
472
|
+
|
473
|
+
# Reverse max and min sheet numbers if necessary
|
474
|
+
if (sheet1 > sheet2)
|
475
|
+
sheet1, sheet2 = [sheet2, sheet1]
|
476
|
+
end
|
477
|
+
else
|
478
|
+
# Single sheet name only.
|
479
|
+
sheet1, sheet2 = [ext_ref, ext_ref]
|
480
|
+
|
481
|
+
sheet1 = get_sheet_index(sheet1)
|
482
|
+
sheet2 = sheet1
|
483
|
+
end
|
484
|
+
|
485
|
+
key = "#{sheet1}:#{sheet2}"
|
486
|
+
|
487
|
+
unless @ext_refs[key]
|
488
|
+
index = @ext_refs[key]
|
489
|
+
else
|
490
|
+
index = @ext_ref_count
|
491
|
+
@ext_refs[key] = index
|
492
|
+
@ext_ref_count += 1
|
493
|
+
end
|
494
|
+
|
495
|
+
return [index].pack("v")
|
496
|
+
end
|
497
|
+
|
498
|
+
###############################################################################
|
499
|
+
#
|
500
|
+
# _get_sheet_index()
|
501
|
+
#
|
502
|
+
# Look up the index that corresponds to an external sheet name. The hash of
|
503
|
+
# sheet names is updated by the add_worksheet() method of the Workbook class.
|
504
|
+
#
|
505
|
+
def get_sheet_index(sheet_name)
|
506
|
+
# Handle utf8 sheetnames
|
507
|
+
if sheet_name =~ NonAscii
|
508
|
+
sheet_name = NKF.nkf('-w16B0 -m0 -W', sheet_name)
|
509
|
+
end
|
510
|
+
|
511
|
+
if @ext_sheets[sheet_name].nil?
|
512
|
+
exit "Unknown sheet name #{sheet_name} in formula\n"
|
513
|
+
else
|
514
|
+
return @ext_sheets[sheet_name]
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
###############################################################################
|
519
|
+
#
|
520
|
+
# get_ext_ref_count()
|
521
|
+
#
|
522
|
+
# TODO This semi-public method is used to update the hash of sheet names. It is
|
523
|
+
# updated by the add_worksheet() method of the Workbook class.
|
524
|
+
#
|
525
|
+
def get_ext_ref_count
|
526
|
+
return @ext_ref_count
|
527
|
+
end
|
528
|
+
|
529
|
+
###############################################################################
|
530
|
+
#
|
531
|
+
# _convert_function()
|
532
|
+
#
|
533
|
+
# Convert a function to a ptgFunc or ptgFuncVarV depending on the number of
|
534
|
+
# args that it takes.
|
535
|
+
#
|
536
|
+
def convert_function(token, num_args)
|
537
|
+
exit "Unknown function #{token}() in formula\n" if @functions[token][0].nil?
|
538
|
+
|
539
|
+
args = @functions[token][1]
|
540
|
+
|
541
|
+
# Fixed number of args eg. TIME($i,$j,$k).
|
542
|
+
if (args >= 0)
|
543
|
+
# Check that the number of args is valid.
|
544
|
+
if (args != num_args)
|
545
|
+
raise "Incorrect number of arguments for #{token}() in formula\n";
|
546
|
+
else
|
547
|
+
return [@ptg['ptgFuncV'], @functions[token][0]].pack("Cv")
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
# Variable number of args eg. SUM(i,j,k, ..).
|
552
|
+
if (args == -1)
|
553
|
+
return [@ptg['ptgFuncVarV'], num_args, @functions[token][0]].pack("CCv")
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
###############################################################################
|
558
|
+
#
|
559
|
+
# _cell_to_rowcol($cell_ref)
|
560
|
+
#
|
561
|
+
# Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero
|
562
|
+
# indexed row and column number. Also returns two boolean values to indicate
|
563
|
+
# whether the row or column are relative references.
|
564
|
+
# TODO use function in Utility.pm
|
565
|
+
#
|
566
|
+
def cell_to_rowcol(cell)
|
567
|
+
cell =~ /(\$?)([A-I]?[A-Z])(\$?)(\d+)/
|
568
|
+
|
569
|
+
col_rel = $1 == "" ? 1 : 0
|
570
|
+
col = $2
|
571
|
+
row_rel = $3 == "" ? 1 : 0
|
572
|
+
row = $4.to_i
|
573
|
+
|
574
|
+
# Convert base26 column string to a number.
|
575
|
+
# All your Base are belong to us.
|
576
|
+
chars = col.split(//)
|
577
|
+
expn = 0
|
578
|
+
col = 0
|
579
|
+
|
580
|
+
while (!chars.empty?)
|
581
|
+
char = chars.pop # LS char first
|
582
|
+
col = col + (char[0] - "A"[0] + 1) * (26**expn)
|
583
|
+
expn += 1
|
584
|
+
end
|
585
|
+
# Convert 1-index to zero-index
|
586
|
+
row -= 1
|
587
|
+
col -= 1
|
588
|
+
|
589
|
+
return [row, col, row_rel, col_rel]
|
590
|
+
end
|
591
|
+
|
592
|
+
###############################################################################
|
593
|
+
#
|
594
|
+
# _cell_to_packed_rowcol($row, $col, $row_rel, $col_rel)
|
595
|
+
#
|
596
|
+
# pack() row and column into the required 3 byte format.
|
597
|
+
#
|
598
|
+
def cell_to_packed_rowcol(cell)
|
599
|
+
row, col, row_rel, col_rel = cell_to_rowcol(cell)
|
600
|
+
|
601
|
+
exit "Column #{cell} greater than IV in formula\n" if col >= 256
|
602
|
+
exit "Row #{cell} greater than 65536 in formula\n" if row >= 65536
|
603
|
+
|
604
|
+
# Set the high bits to indicate if row or col are relative.
|
605
|
+
col |= col_rel << 14
|
606
|
+
col |= row_rel << 15
|
607
|
+
|
608
|
+
row = [row].pack('v')
|
609
|
+
col = [col].pack('v')
|
610
|
+
|
611
|
+
return [row, col]
|
612
|
+
end
|
613
|
+
|
614
|
+
###############################################################################
|
615
|
+
#
|
616
|
+
# _initialize_hashes()
|
617
|
+
#
|
618
|
+
def initialize_hashes
|
619
|
+
|
620
|
+
# The Excel ptg indices
|
621
|
+
@ptg = {
|
622
|
+
'ptgExp' => 0x01,
|
623
|
+
'ptgTbl' => 0x02,
|
624
|
+
'ptgAdd' => 0x03,
|
625
|
+
'ptgSub' => 0x04,
|
626
|
+
'ptgMul' => 0x05,
|
627
|
+
'ptgDiv' => 0x06,
|
628
|
+
'ptgPower' => 0x07,
|
629
|
+
'ptgConcat' => 0x08,
|
630
|
+
'ptgLT' => 0x09,
|
631
|
+
'ptgLE' => 0x0A,
|
632
|
+
'ptgEQ' => 0x0B,
|
633
|
+
'ptgGE' => 0x0C,
|
634
|
+
'ptgGT' => 0x0D,
|
635
|
+
'ptgNE' => 0x0E,
|
636
|
+
'ptgIsect' => 0x0F,
|
637
|
+
'ptgUnion' => 0x10,
|
638
|
+
'ptgRange' => 0x11,
|
639
|
+
'ptgUplus' => 0x12,
|
640
|
+
'ptgUminus' => 0x13,
|
641
|
+
'ptgPercent' => 0x14,
|
642
|
+
'ptgParen' => 0x15,
|
643
|
+
'ptgMissArg' => 0x16,
|
644
|
+
'ptgStr' => 0x17,
|
645
|
+
'ptgAttr' => 0x19,
|
646
|
+
'ptgSheet' => 0x1A,
|
647
|
+
'ptgEndSheet' => 0x1B,
|
648
|
+
'ptgErr' => 0x1C,
|
649
|
+
'ptgBool' => 0x1D,
|
650
|
+
'ptgInt' => 0x1E,
|
651
|
+
'ptgNum' => 0x1F,
|
652
|
+
'ptgArray' => 0x20,
|
653
|
+
'ptgFunc' => 0x21,
|
654
|
+
'ptgFuncVar' => 0x22,
|
655
|
+
'ptgName' => 0x23,
|
656
|
+
'ptgRef' => 0x24,
|
657
|
+
'ptgArea' => 0x25,
|
658
|
+
'ptgMemArea' => 0x26,
|
659
|
+
'ptgMemErr' => 0x27,
|
660
|
+
'ptgMemNoMem' => 0x28,
|
661
|
+
'ptgMemFunc' => 0x29,
|
662
|
+
'ptgRefErr' => 0x2A,
|
663
|
+
'ptgAreaErr' => 0x2B,
|
664
|
+
'ptgRefN' => 0x2C,
|
665
|
+
'ptgAreaN' => 0x2D,
|
666
|
+
'ptgMemAreaN' => 0x2E,
|
667
|
+
'ptgMemNoMemN' => 0x2F,
|
668
|
+
'ptgNameX' => 0x39,
|
669
|
+
'ptgRef3d' => 0x3A,
|
670
|
+
'ptgArea3d' => 0x3B,
|
671
|
+
'ptgRefErr3d' => 0x3C,
|
672
|
+
'ptgAreaErr3d' => 0x3D,
|
673
|
+
'ptgArrayV' => 0x40,
|
674
|
+
'ptgFuncV' => 0x41,
|
675
|
+
'ptgFuncVarV' => 0x42,
|
676
|
+
'ptgNameV' => 0x43,
|
677
|
+
'ptgRefV' => 0x44,
|
678
|
+
'ptgAreaV' => 0x45,
|
679
|
+
'ptgMemAreaV' => 0x46,
|
680
|
+
'ptgMemErrV' => 0x47,
|
681
|
+
'ptgMemNoMemV' => 0x48,
|
682
|
+
'ptgMemFuncV' => 0x49,
|
683
|
+
'ptgRefErrV' => 0x4A,
|
684
|
+
'ptgAreaErrV' => 0x4B,
|
685
|
+
'ptgRefNV' => 0x4C,
|
686
|
+
'ptgAreaNV' => 0x4D,
|
687
|
+
'ptgMemAreaNV' => 0x4E,
|
688
|
+
'ptgMemNoMemN' => 0x4F,
|
689
|
+
'ptgFuncCEV' => 0x58,
|
690
|
+
'ptgNameXV' => 0x59,
|
691
|
+
'ptgRef3dV' => 0x5A,
|
692
|
+
'ptgArea3dV' => 0x5B,
|
693
|
+
'ptgRefErr3dV' => 0x5C,
|
694
|
+
'ptgAreaErr3d' => 0x5D,
|
695
|
+
'ptgArrayA' => 0x60,
|
696
|
+
'ptgFuncA' => 0x61,
|
697
|
+
'ptgFuncVarA' => 0x62,
|
698
|
+
'ptgNameA' => 0x63,
|
699
|
+
'ptgRefA' => 0x64,
|
700
|
+
'ptgAreaA' => 0x65,
|
701
|
+
'ptgMemAreaA' => 0x66,
|
702
|
+
'ptgMemErrA' => 0x67,
|
703
|
+
'ptgMemNoMemA' => 0x68,
|
704
|
+
'ptgMemFuncA' => 0x69,
|
705
|
+
'ptgRefErrA' => 0x6A,
|
706
|
+
'ptgAreaErrA' => 0x6B,
|
707
|
+
'ptgRefNA' => 0x6C,
|
708
|
+
'ptgAreaNA' => 0x6D,
|
709
|
+
'ptgMemAreaNA' => 0x6E,
|
710
|
+
'ptgMemNoMemN' => 0x6F,
|
711
|
+
'ptgFuncCEA' => 0x78,
|
712
|
+
'ptgNameXA' => 0x79,
|
713
|
+
'ptgRef3dA' => 0x7A,
|
714
|
+
'ptgArea3dA' => 0x7B,
|
715
|
+
'ptgRefErr3dA' => 0x7C,
|
716
|
+
'ptgAreaErr3d' => 0x7D
|
717
|
+
};
|
718
|
+
|
719
|
+
# Thanks to Michael Meeks and Gnumeric for the initial arg values.
|
720
|
+
#
|
721
|
+
# The following hash was generated by "function_locale.pl" in the distro.
|
722
|
+
# Refer to function_locale.pl for non-English function names.
|
723
|
+
#
|
724
|
+
# The array elements are as follow:
|
725
|
+
# ptg: The Excel function ptg code.
|
726
|
+
# args: The number of arguments that the function takes:
|
727
|
+
# >=0 is a fixed number of arguments.
|
728
|
+
# -1 is a variable number of arguments.
|
729
|
+
# class: The reference, value or array class of the function args.
|
730
|
+
# vol: The function is volatile.
|
731
|
+
#
|
732
|
+
@functions = {
|
733
|
+
# ptg args class vol
|
734
|
+
'COUNT' => [ 0, -1, 0, 0 ],
|
735
|
+
'IF' => [ 1, -1, 1, 0 ],
|
736
|
+
'ISNA' => [ 2, 1, 1, 0 ],
|
737
|
+
'ISERROR' => [ 3, 1, 1, 0 ],
|
738
|
+
'SUM' => [ 4, -1, 0, 0 ],
|
739
|
+
'AVERAGE' => [ 5, -1, 0, 0 ],
|
740
|
+
'MIN' => [ 6, -1, 0, 0 ],
|
741
|
+
'MAX' => [ 7, -1, 0, 0 ],
|
742
|
+
'ROW' => [ 8, -1, 0, 0 ],
|
743
|
+
'COLUMN' => [ 9, -1, 0, 0 ],
|
744
|
+
'NA' => [ 10, 0, 0, 0 ],
|
745
|
+
'NPV' => [ 11, -1, 1, 0 ],
|
746
|
+
'STDEV' => [ 12, -1, 0, 0 ],
|
747
|
+
'DOLLAR' => [ 13, -1, 1, 0 ],
|
748
|
+
'FIXED' => [ 14, -1, 1, 0 ],
|
749
|
+
'SIN' => [ 15, 1, 1, 0 ],
|
750
|
+
'COS' => [ 16, 1, 1, 0 ],
|
751
|
+
'TAN' => [ 17, 1, 1, 0 ],
|
752
|
+
'ATAN' => [ 18, 1, 1, 0 ],
|
753
|
+
'PI' => [ 19, 0, 1, 0 ],
|
754
|
+
'SQRT' => [ 20, 1, 1, 0 ],
|
755
|
+
'EXP' => [ 21, 1, 1, 0 ],
|
756
|
+
'LN' => [ 22, 1, 1, 0 ],
|
757
|
+
'LOG10' => [ 23, 1, 1, 0 ],
|
758
|
+
'ABS' => [ 24, 1, 1, 0 ],
|
759
|
+
'INT' => [ 25, 1, 1, 0 ],
|
760
|
+
'SIGN' => [ 26, 1, 1, 0 ],
|
761
|
+
'ROUND' => [ 27, 2, 1, 0 ],
|
762
|
+
'LOOKUP' => [ 28, -1, 0, 0 ],
|
763
|
+
'INDEX' => [ 29, -1, 0, 1 ],
|
764
|
+
'REPT' => [ 30, 2, 1, 0 ],
|
765
|
+
'MID' => [ 31, 3, 1, 0 ],
|
766
|
+
'LEN' => [ 32, 1, 1, 0 ],
|
767
|
+
'VALUE' => [ 33, 1, 1, 0 ],
|
768
|
+
'TRUE' => [ 34, 0, 1, 0 ],
|
769
|
+
'FALSE' => [ 35, 0, 1, 0 ],
|
770
|
+
'AND' => [ 36, -1, 1, 0 ],
|
771
|
+
'OR' => [ 37, -1, 1, 0 ],
|
772
|
+
'NOT' => [ 38, 1, 1, 0 ],
|
773
|
+
'MOD' => [ 39, 2, 1, 0 ],
|
774
|
+
'DCOUNT' => [ 40, 3, 0, 0 ],
|
775
|
+
'DSUM' => [ 41, 3, 0, 0 ],
|
776
|
+
'DAVERAGE' => [ 42, 3, 0, 0 ],
|
777
|
+
'DMIN' => [ 43, 3, 0, 0 ],
|
778
|
+
'DMAX' => [ 44, 3, 0, 0 ],
|
779
|
+
'DSTDEV' => [ 45, 3, 0, 0 ],
|
780
|
+
'VAR' => [ 46, -1, 0, 0 ],
|
781
|
+
'DVAR' => [ 47, 3, 0, 0 ],
|
782
|
+
'TEXT' => [ 48, 2, 1, 0 ],
|
783
|
+
'LINEST' => [ 49, -1, 0, 0 ],
|
784
|
+
'TREND' => [ 50, -1, 0, 0 ],
|
785
|
+
'LOGEST' => [ 51, -1, 0, 0 ],
|
786
|
+
'GROWTH' => [ 52, -1, 0, 0 ],
|
787
|
+
'PV' => [ 56, -1, 1, 0 ],
|
788
|
+
'FV' => [ 57, -1, 1, 0 ],
|
789
|
+
'NPER' => [ 58, -1, 1, 0 ],
|
790
|
+
'PMT' => [ 59, -1, 1, 0 ],
|
791
|
+
'RATE' => [ 60, -1, 1, 0 ],
|
792
|
+
'MIRR' => [ 61, 3, 0, 0 ],
|
793
|
+
'IRR' => [ 62, -1, 0, 0 ],
|
794
|
+
'RAND' => [ 63, 0, 1, 1 ],
|
795
|
+
'MATCH' => [ 64, -1, 0, 0 ],
|
796
|
+
'DATE' => [ 65, 3, 1, 0 ],
|
797
|
+
'TIME' => [ 66, 3, 1, 0 ],
|
798
|
+
'DAY' => [ 67, 1, 1, 0 ],
|
799
|
+
'MONTH' => [ 68, 1, 1, 0 ],
|
800
|
+
'YEAR' => [ 69, 1, 1, 0 ],
|
801
|
+
'WEEKDAY' => [ 70, -1, 1, 0 ],
|
802
|
+
'HOUR' => [ 71, 1, 1, 0 ],
|
803
|
+
'MINUTE' => [ 72, 1, 1, 0 ],
|
804
|
+
'SECOND' => [ 73, 1, 1, 0 ],
|
805
|
+
'NOW' => [ 74, 0, 1, 1 ],
|
806
|
+
'AREAS' => [ 75, 1, 0, 1 ],
|
807
|
+
'ROWS' => [ 76, 1, 0, 1 ],
|
808
|
+
'COLUMNS' => [ 77, 1, 0, 1 ],
|
809
|
+
'OFFSET' => [ 78, -1, 0, 1 ],
|
810
|
+
'SEARCH' => [ 82, -1, 1, 0 ],
|
811
|
+
'TRANSPOSE' => [ 83, 1, 1, 0 ],
|
812
|
+
'TYPE' => [ 86, 1, 1, 0 ],
|
813
|
+
'ATAN2' => [ 97, 2, 1, 0 ],
|
814
|
+
'ASIN' => [ 98, 1, 1, 0 ],
|
815
|
+
'ACOS' => [ 99, 1, 1, 0 ],
|
816
|
+
'CHOOSE' => [ 100, -1, 1, 0 ],
|
817
|
+
'HLOOKUP' => [ 101, -1, 0, 0 ],
|
818
|
+
'VLOOKUP' => [ 102, -1, 0, 0 ],
|
819
|
+
'ISREF' => [ 105, 1, 0, 0 ],
|
820
|
+
'LOG' => [ 109, -1, 1, 0 ],
|
821
|
+
'CHAR' => [ 111, 1, 1, 0 ],
|
822
|
+
'LOWER' => [ 112, 1, 1, 0 ],
|
823
|
+
'UPPER' => [ 113, 1, 1, 0 ],
|
824
|
+
'PROPER' => [ 114, 1, 1, 0 ],
|
825
|
+
'LEFT' => [ 115, -1, 1, 0 ],
|
826
|
+
'RIGHT' => [ 116, -1, 1, 0 ],
|
827
|
+
'EXACT' => [ 117, 2, 1, 0 ],
|
828
|
+
'TRIM' => [ 118, 1, 1, 0 ],
|
829
|
+
'REPLACE' => [ 119, 4, 1, 0 ],
|
830
|
+
'SUBSTITUTE' => [ 120, -1, 1, 0 ],
|
831
|
+
'CODE' => [ 121, 1, 1, 0 ],
|
832
|
+
'FIND' => [ 124, -1, 1, 0 ],
|
833
|
+
'CELL' => [ 125, -1, 0, 1 ],
|
834
|
+
'ISERR' => [ 126, 1, 1, 0 ],
|
835
|
+
'ISTEXT' => [ 127, 1, 1, 0 ],
|
836
|
+
'ISNUMBER' => [ 128, 1, 1, 0 ],
|
837
|
+
'ISBLANK' => [ 129, 1, 1, 0 ],
|
838
|
+
'T' => [ 130, 1, 0, 0 ],
|
839
|
+
'N' => [ 131, 1, 0, 0 ],
|
840
|
+
'DATEVALUE' => [ 140, 1, 1, 0 ],
|
841
|
+
'TIMEVALUE' => [ 141, 1, 1, 0 ],
|
842
|
+
'SLN' => [ 142, 3, 1, 0 ],
|
843
|
+
'SYD' => [ 143, 4, 1, 0 ],
|
844
|
+
'DDB' => [ 144, -1, 1, 0 ],
|
845
|
+
'INDIRECT' => [ 148, -1, 1, 1 ],
|
846
|
+
'CALL' => [ 150, -1, 1, 0 ],
|
847
|
+
'CLEAN' => [ 162, 1, 1, 0 ],
|
848
|
+
'MDETERM' => [ 163, 1, 2, 0 ],
|
849
|
+
'MINVERSE' => [ 164, 1, 2, 0 ],
|
850
|
+
'MMULT' => [ 165, 2, 2, 0 ],
|
851
|
+
'IPMT' => [ 167, -1, 1, 0 ],
|
852
|
+
'PPMT' => [ 168, -1, 1, 0 ],
|
853
|
+
'COUNTA' => [ 169, -1, 0, 0 ],
|
854
|
+
'PRODUCT' => [ 183, -1, 0, 0 ],
|
855
|
+
'FACT' => [ 184, 1, 1, 0 ],
|
856
|
+
'DPRODUCT' => [ 189, 3, 0, 0 ],
|
857
|
+
'ISNONTEXT' => [ 190, 1, 1, 0 ],
|
858
|
+
'STDEVP' => [ 193, -1, 0, 0 ],
|
859
|
+
'VARP' => [ 194, -1, 0, 0 ],
|
860
|
+
'DSTDEVP' => [ 195, 3, 0, 0 ],
|
861
|
+
'DVARP' => [ 196, 3, 0, 0 ],
|
862
|
+
'TRUNC' => [ 197, -1, 1, 0 ],
|
863
|
+
'ISLOGICAL' => [ 198, 1, 1, 0 ],
|
864
|
+
'DCOUNTA' => [ 199, 3, 0, 0 ],
|
865
|
+
'ROUNDUP' => [ 212, 2, 1, 0 ],
|
866
|
+
'ROUNDDOWN' => [ 213, 2, 1, 0 ],
|
867
|
+
'RANK' => [ 216, -1, 0, 0 ],
|
868
|
+
'ADDRESS' => [ 219, -1, 1, 0 ],
|
869
|
+
'DAYS360' => [ 220, -1, 1, 0 ],
|
870
|
+
'TODAY' => [ 221, 0, 1, 1 ],
|
871
|
+
'VDB' => [ 222, -1, 1, 0 ],
|
872
|
+
'MEDIAN' => [ 227, -1, 0, 0 ],
|
873
|
+
'SUMPRODUCT' => [ 228, -1, 2, 0 ],
|
874
|
+
'SINH' => [ 229, 1, 1, 0 ],
|
875
|
+
'COSH' => [ 230, 1, 1, 0 ],
|
876
|
+
'TANH' => [ 231, 1, 1, 0 ],
|
877
|
+
'ASINH' => [ 232, 1, 1, 0 ],
|
878
|
+
'ACOSH' => [ 233, 1, 1, 0 ],
|
879
|
+
'ATANH' => [ 234, 1, 1, 0 ],
|
880
|
+
'DGET' => [ 235, 3, 0, 0 ],
|
881
|
+
'INFO' => [ 244, 1, 1, 1 ],
|
882
|
+
'DB' => [ 247, -1, 1, 0 ],
|
883
|
+
'FREQUENCY' => [ 252, 2, 0, 0 ],
|
884
|
+
'ERROR.TYPE' => [ 261, 1, 1, 0 ],
|
885
|
+
'REGISTER.ID' => [ 267, -1, 1, 0 ],
|
886
|
+
'AVEDEV' => [ 269, -1, 0, 0 ],
|
887
|
+
'BETADIST' => [ 270, -1, 1, 0 ],
|
888
|
+
'GAMMALN' => [ 271, 1, 1, 0 ],
|
889
|
+
'BETAINV' => [ 272, -1, 1, 0 ],
|
890
|
+
'BINOMDIST' => [ 273, 4, 1, 0 ],
|
891
|
+
'CHIDIST' => [ 274, 2, 1, 0 ],
|
892
|
+
'CHIINV' => [ 275, 2, 1, 0 ],
|
893
|
+
'COMBIN' => [ 276, 2, 1, 0 ],
|
894
|
+
'CONFIDENCE' => [ 277, 3, 1, 0 ],
|
895
|
+
'CRITBINOM' => [ 278, 3, 1, 0 ],
|
896
|
+
'EVEN' => [ 279, 1, 1, 0 ],
|
897
|
+
'EXPONDIST' => [ 280, 3, 1, 0 ],
|
898
|
+
'FDIST' => [ 281, 3, 1, 0 ],
|
899
|
+
'FINV' => [ 282, 3, 1, 0 ],
|
900
|
+
'FISHER' => [ 283, 1, 1, 0 ],
|
901
|
+
'FISHERINV' => [ 284, 1, 1, 0 ],
|
902
|
+
'FLOOR' => [ 285, 2, 1, 0 ],
|
903
|
+
'GAMMADIST' => [ 286, 4, 1, 0 ],
|
904
|
+
'GAMMAINV' => [ 287, 3, 1, 0 ],
|
905
|
+
'CEILING' => [ 288, 2, 1, 0 ],
|
906
|
+
'HYPGEOMDIST' => [ 289, 4, 1, 0 ],
|
907
|
+
'LOGNORMDIST' => [ 290, 3, 1, 0 ],
|
908
|
+
'LOGINV' => [ 291, 3, 1, 0 ],
|
909
|
+
'NEGBINOMDIST' => [ 292, 3, 1, 0 ],
|
910
|
+
'NORMDIST' => [ 293, 4, 1, 0 ],
|
911
|
+
'NORMSDIST' => [ 294, 1, 1, 0 ],
|
912
|
+
'NORMINV' => [ 295, 3, 1, 0 ],
|
913
|
+
'NORMSINV' => [ 296, 1, 1, 0 ],
|
914
|
+
'STANDARDIZE' => [ 297, 3, 1, 0 ],
|
915
|
+
'ODD' => [ 298, 1, 1, 0 ],
|
916
|
+
'PERMUT' => [ 299, 2, 1, 0 ],
|
917
|
+
'POISSON' => [ 300, 3, 1, 0 ],
|
918
|
+
'TDIST' => [ 301, 3, 1, 0 ],
|
919
|
+
'WEIBULL' => [ 302, 4, 1, 0 ],
|
920
|
+
'SUMXMY2' => [ 303, 2, 2, 0 ],
|
921
|
+
'SUMX2MY2' => [ 304, 2, 2, 0 ],
|
922
|
+
'SUMX2PY2' => [ 305, 2, 2, 0 ],
|
923
|
+
'CHITEST' => [ 306, 2, 2, 0 ],
|
924
|
+
'CORREL' => [ 307, 2, 2, 0 ],
|
925
|
+
'COVAR' => [ 308, 2, 2, 0 ],
|
926
|
+
'FORECAST' => [ 309, 3, 2, 0 ],
|
927
|
+
'FTEST' => [ 310, 2, 2, 0 ],
|
928
|
+
'INTERCEPT' => [ 311, 2, 2, 0 ],
|
929
|
+
'PEARSON' => [ 312, 2, 2, 0 ],
|
930
|
+
'RSQ' => [ 313, 2, 2, 0 ],
|
931
|
+
'STEYX' => [ 314, 2, 2, 0 ],
|
932
|
+
'SLOPE' => [ 315, 2, 2, 0 ],
|
933
|
+
'TTEST' => [ 316, 4, 2, 0 ],
|
934
|
+
'PROB' => [ 317, -1, 2, 0 ],
|
935
|
+
'DEVSQ' => [ 318, -1, 0, 0 ],
|
936
|
+
'GEOMEAN' => [ 319, -1, 0, 0 ],
|
937
|
+
'HARMEAN' => [ 320, -1, 0, 0 ],
|
938
|
+
'SUMSQ' => [ 321, -1, 0, 0 ],
|
939
|
+
'KURT' => [ 322, -1, 0, 0 ],
|
940
|
+
'SKEW' => [ 323, -1, 0, 0 ],
|
941
|
+
'ZTEST' => [ 324, -1, 0, 0 ],
|
942
|
+
'LARGE' => [ 325, 2, 0, 0 ],
|
943
|
+
'SMALL' => [ 326, 2, 0, 0 ],
|
944
|
+
'QUARTILE' => [ 327, 2, 0, 0 ],
|
945
|
+
'PERCENTILE' => [ 328, 2, 0, 0 ],
|
946
|
+
'PERCENTRANK' => [ 329, -1, 0, 0 ],
|
947
|
+
'MODE' => [ 330, -1, 2, 0 ],
|
948
|
+
'TRIMMEAN' => [ 331, 2, 0, 0 ],
|
949
|
+
'TINV' => [ 332, 2, 1, 0 ],
|
950
|
+
'CONCATENATE' => [ 336, -1, 1, 0 ],
|
951
|
+
'POWER' => [ 337, 2, 1, 0 ],
|
952
|
+
'RADIANS' => [ 342, 1, 1, 0 ],
|
953
|
+
'DEGREES' => [ 343, 1, 1, 0 ],
|
954
|
+
'SUBTOTAL' => [ 344, -1, 0, 0 ],
|
955
|
+
'SUMIF' => [ 345, -1, 0, 0 ],
|
956
|
+
'COUNTIF' => [ 346, 2, 0, 0 ],
|
957
|
+
'COUNTBLANK' => [ 347, 1, 0, 0 ],
|
958
|
+
'ROMAN' => [ 354, -1, 1, 0 ]
|
959
|
+
}
|
960
|
+
|
961
|
+
end
|
962
|
+
|
963
|
+
|
964
|
+
end
|
965
|
+
|
966
|
+
if $0 ==__FILE__
|
967
|
+
|
968
|
+
|
969
|
+
parser = Formula.new
|
970
|
+
puts
|
971
|
+
puts 'type "Q" to quit.'
|
972
|
+
puts
|
973
|
+
while true
|
974
|
+
puts
|
975
|
+
print '? '
|
976
|
+
str = gets.chop!
|
977
|
+
break if /q/i =~ str
|
978
|
+
begin
|
979
|
+
e = parser.parse(str)
|
980
|
+
p parser.reverse(e)
|
981
|
+
rescue ParseError
|
982
|
+
puts $!
|
983
|
+
end
|
984
|
+
end
|
985
|
+
|
986
|
+
end
|