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