ricardoo27-writeexcel 0.6.12.1

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