rubyfromexcel 0.0.4

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.
Files changed (188) hide show
  1. data/README +22 -0
  2. data/bin/rubyfromexcel +20 -0
  3. data/examples/create_and_test_examples.rb +37 -0
  4. data/examples/ruby-versions/array-formulas-ruby/sheets/sheet1.rb +59 -0
  5. data/examples/ruby-versions/array-formulas-ruby/sheets/sheet2.rb +9 -0
  6. data/examples/ruby-versions/array-formulas-ruby/specs/sheet1_rspec.rb +156 -0
  7. data/examples/ruby-versions/array-formulas-ruby/specs/sheet2_rspec.rb +8 -0
  8. data/examples/ruby-versions/array-formulas-ruby/spreadsheet.rb +9 -0
  9. data/examples/ruby-versions/complex-test-ruby/sheets/sheet1.rb +305 -0
  10. data/examples/ruby-versions/complex-test-ruby/sheets/sheet2.rb +147 -0
  11. data/examples/ruby-versions/complex-test-ruby/specs/sheet1_rspec.rb +876 -0
  12. data/examples/ruby-versions/complex-test-ruby/specs/sheet2_rspec.rb +412 -0
  13. data/examples/ruby-versions/complex-test-ruby/spreadsheet.rb +9 -0
  14. data/examples/ruby-versions/namedReferenceTest-ruby/sheets/sheet1.rb +9 -0
  15. data/examples/ruby-versions/namedReferenceTest-ruby/sheets/sheet2.rb +8 -0
  16. data/examples/ruby-versions/namedReferenceTest-ruby/specs/sheet1_rspec.rb +16 -0
  17. data/examples/ruby-versions/namedReferenceTest-ruby/specs/sheet2_rspec.rb +16 -0
  18. data/examples/ruby-versions/namedReferenceTest-ruby/spreadsheet.rb +9 -0
  19. data/examples/ruby-versions/pruning-ruby/sheets/sheet1.rb +11 -0
  20. data/examples/ruby-versions/pruning-ruby/sheets/sheet2.rb +14 -0
  21. data/examples/ruby-versions/pruning-ruby/sheets/sheet3.rb +7 -0
  22. data/examples/ruby-versions/pruning-ruby/specs/sheet1_rspec.rb +20 -0
  23. data/examples/ruby-versions/pruning-ruby/specs/sheet2_rspec.rb +20 -0
  24. data/examples/ruby-versions/pruning-ruby/specs/sheet3_rspec.rb +8 -0
  25. data/examples/ruby-versions/pruning-ruby/spreadsheet.rb +9 -0
  26. data/examples/ruby-versions/sharedFormulaTest-ruby/sheets/sheet1.rb +15 -0
  27. data/examples/ruby-versions/sharedFormulaTest-ruby/specs/sheet1_rspec.rb +44 -0
  28. data/examples/ruby-versions/sharedFormulaTest-ruby/spreadsheet.rb +9 -0
  29. data/examples/ruby-versions/table-test-ruby/sheets/sheet1.rb +17 -0
  30. data/examples/ruby-versions/table-test-ruby/sheets/sheet2.rb +5 -0
  31. data/examples/ruby-versions/table-test-ruby/sheets/sheet3.rb +5 -0
  32. data/examples/ruby-versions/table-test-ruby/specs/sheet1_rspec.rb +20 -0
  33. data/examples/ruby-versions/table-test-ruby/specs/sheet2_rspec.rb +8 -0
  34. data/examples/ruby-versions/table-test-ruby/specs/sheet3_rspec.rb +8 -0
  35. data/examples/ruby-versions/table-test-ruby/spreadsheet.rb +9 -0
  36. data/examples/sheets/array-formulas.xlsx +0 -0
  37. data/examples/sheets/complex-test.xlsx +0 -0
  38. data/examples/sheets/namedReferenceTest.xlsx +0 -0
  39. data/examples/sheets/pruning.xlsx +0 -0
  40. data/examples/sheets/sharedFormulaTest.xlsx +0 -0
  41. data/examples/sheets/table-test.xlsx +0 -0
  42. data/examples/sheets/~$array-formulas.xlsx +0 -0
  43. data/examples/unzipped-sheets/array-formulas/[Content_Types].xml +2 -0
  44. data/examples/unzipped-sheets/array-formulas/docProps/app.xml +2 -0
  45. data/examples/unzipped-sheets/array-formulas/docProps/core.xml +2 -0
  46. data/examples/unzipped-sheets/array-formulas/docProps/thumbnail.jpeg +0 -0
  47. data/examples/unzipped-sheets/array-formulas/xl/_rels/workbook.xml.rels +2 -0
  48. data/examples/unzipped-sheets/array-formulas/xl/calcChain.xml +2 -0
  49. data/examples/unzipped-sheets/array-formulas/xl/sharedStrings.xml +2 -0
  50. data/examples/unzipped-sheets/array-formulas/xl/styles.xml +2 -0
  51. data/examples/unzipped-sheets/array-formulas/xl/theme/theme1.xml +2 -0
  52. data/examples/unzipped-sheets/array-formulas/xl/workbook.xml +2 -0
  53. data/examples/unzipped-sheets/array-formulas/xl/worksheets/sheet1.xml +2 -0
  54. data/examples/unzipped-sheets/array-formulas/xl/worksheets/sheet2.xml +2 -0
  55. data/examples/unzipped-sheets/complex-test/[Content_Types].xml +2 -0
  56. data/examples/unzipped-sheets/complex-test/docProps/app.xml +2 -0
  57. data/examples/unzipped-sheets/complex-test/docProps/core.xml +2 -0
  58. data/examples/unzipped-sheets/complex-test/xl/_rels/workbook.xml.rels +2 -0
  59. data/examples/unzipped-sheets/complex-test/xl/calcChain.xml +2 -0
  60. data/examples/unzipped-sheets/complex-test/xl/charts/chart1.xml +2 -0
  61. data/examples/unzipped-sheets/complex-test/xl/charts/chart2.xml +2 -0
  62. data/examples/unzipped-sheets/complex-test/xl/comments1.xml +5 -0
  63. data/examples/unzipped-sheets/complex-test/xl/comments2.xml +5 -0
  64. data/examples/unzipped-sheets/complex-test/xl/drawings/_rels/drawing1.xml.rels +2 -0
  65. data/examples/unzipped-sheets/complex-test/xl/drawings/_rels/drawing2.xml.rels +2 -0
  66. data/examples/unzipped-sheets/complex-test/xl/drawings/drawing1.xml +2 -0
  67. data/examples/unzipped-sheets/complex-test/xl/drawings/drawing2.xml +2 -0
  68. data/examples/unzipped-sheets/complex-test/xl/drawings/vmlDrawing1.vml +46 -0
  69. data/examples/unzipped-sheets/complex-test/xl/drawings/vmlDrawing2.vml +46 -0
  70. data/examples/unzipped-sheets/complex-test/xl/sharedStrings.xml +2 -0
  71. data/examples/unzipped-sheets/complex-test/xl/styles.xml +2 -0
  72. data/examples/unzipped-sheets/complex-test/xl/theme/theme1.xml +2 -0
  73. data/examples/unzipped-sheets/complex-test/xl/workbook.xml +2 -0
  74. data/examples/unzipped-sheets/complex-test/xl/worksheets/_rels/sheet1.xml.rels +2 -0
  75. data/examples/unzipped-sheets/complex-test/xl/worksheets/_rels/sheet2.xml.rels +2 -0
  76. data/examples/unzipped-sheets/complex-test/xl/worksheets/sheet1.xml +2 -0
  77. data/examples/unzipped-sheets/complex-test/xl/worksheets/sheet2.xml +2 -0
  78. data/examples/unzipped-sheets/namedReferenceTest/[Content_Types].xml +2 -0
  79. data/examples/unzipped-sheets/namedReferenceTest/docProps/app.xml +2 -0
  80. data/examples/unzipped-sheets/namedReferenceTest/docProps/core.xml +2 -0
  81. data/examples/unzipped-sheets/namedReferenceTest/docProps/thumbnail.jpeg +0 -0
  82. data/examples/unzipped-sheets/namedReferenceTest/xl/_rels/workbook.xml.rels +2 -0
  83. data/examples/unzipped-sheets/namedReferenceTest/xl/calcChain.xml +2 -0
  84. data/examples/unzipped-sheets/namedReferenceTest/xl/styles.xml +2 -0
  85. data/examples/unzipped-sheets/namedReferenceTest/xl/theme/theme1.xml +2 -0
  86. data/examples/unzipped-sheets/namedReferenceTest/xl/workbook.xml +2 -0
  87. data/examples/unzipped-sheets/namedReferenceTest/xl/worksheets/sheet1.xml +2 -0
  88. data/examples/unzipped-sheets/namedReferenceTest/xl/worksheets/sheet2.xml +2 -0
  89. data/examples/unzipped-sheets/pruning/[Content_Types].xml +2 -0
  90. data/examples/unzipped-sheets/pruning/docProps/app.xml +2 -0
  91. data/examples/unzipped-sheets/pruning/docProps/core.xml +2 -0
  92. data/examples/unzipped-sheets/pruning/docProps/thumbnail.jpeg +0 -0
  93. data/examples/unzipped-sheets/pruning/xl/_rels/workbook.xml.rels +2 -0
  94. data/examples/unzipped-sheets/pruning/xl/calcChain.xml +2 -0
  95. data/examples/unzipped-sheets/pruning/xl/sharedStrings.xml +2 -0
  96. data/examples/unzipped-sheets/pruning/xl/styles.xml +2 -0
  97. data/examples/unzipped-sheets/pruning/xl/theme/theme1.xml +2 -0
  98. data/examples/unzipped-sheets/pruning/xl/workbook.xml +2 -0
  99. data/examples/unzipped-sheets/pruning/xl/worksheets/sheet1.xml +2 -0
  100. data/examples/unzipped-sheets/pruning/xl/worksheets/sheet2.xml +2 -0
  101. data/examples/unzipped-sheets/pruning/xl/worksheets/sheet3.xml +2 -0
  102. data/examples/unzipped-sheets/sharedFormulaTest/[Content_Types].xml +2 -0
  103. data/examples/unzipped-sheets/sharedFormulaTest/docProps/app.xml +2 -0
  104. data/examples/unzipped-sheets/sharedFormulaTest/docProps/core.xml +2 -0
  105. data/examples/unzipped-sheets/sharedFormulaTest/docProps/thumbnail.jpeg +0 -0
  106. data/examples/unzipped-sheets/sharedFormulaTest/xl/_rels/workbook.xml.rels +2 -0
  107. data/examples/unzipped-sheets/sharedFormulaTest/xl/calcChain.xml +2 -0
  108. data/examples/unzipped-sheets/sharedFormulaTest/xl/styles.xml +2 -0
  109. data/examples/unzipped-sheets/sharedFormulaTest/xl/theme/theme1.xml +2 -0
  110. data/examples/unzipped-sheets/sharedFormulaTest/xl/workbook.xml +2 -0
  111. data/examples/unzipped-sheets/sharedFormulaTest/xl/worksheets/sheet1.xml +2 -0
  112. data/examples/unzipped-sheets/table-test/[Content_Types].xml +2 -0
  113. data/examples/unzipped-sheets/table-test/docProps/app.xml +2 -0
  114. data/examples/unzipped-sheets/table-test/docProps/core.xml +2 -0
  115. data/examples/unzipped-sheets/table-test/xl/_rels/workbook.xml.rels +2 -0
  116. data/examples/unzipped-sheets/table-test/xl/calcChain.xml +2 -0
  117. data/examples/unzipped-sheets/table-test/xl/printerSettings/printerSettings1.bin +0 -0
  118. data/examples/unzipped-sheets/table-test/xl/sharedStrings.xml +2 -0
  119. data/examples/unzipped-sheets/table-test/xl/styles.xml +2 -0
  120. data/examples/unzipped-sheets/table-test/xl/tables/table1.xml +2 -0
  121. data/examples/unzipped-sheets/table-test/xl/theme/theme1.xml +2 -0
  122. data/examples/unzipped-sheets/table-test/xl/workbook.xml +2 -0
  123. data/examples/unzipped-sheets/table-test/xl/worksheets/_rels/sheet1.xml.rels +2 -0
  124. data/examples/unzipped-sheets/table-test/xl/worksheets/sheet1.xml +2 -0
  125. data/examples/unzipped-sheets/table-test/xl/worksheets/sheet2.xml +2 -0
  126. data/examples/unzipped-sheets/table-test/xl/worksheets/sheet3.xml +2 -0
  127. data/lib/cells/array/array_formula_builder.rb +58 -0
  128. data/lib/cells/array/array_formula_cell.rb +27 -0
  129. data/lib/cells/array/arraying_formula_cell.rb +67 -0
  130. data/lib/cells/array/single_cell_array_formula_builder.rb +9 -0
  131. data/lib/cells/array/single_cell_array_formula_cell.rb +11 -0
  132. data/lib/cells/cell.rb +98 -0
  133. data/lib/cells/cells.rb +9 -0
  134. data/lib/cells/formula/formula_cell.rb +18 -0
  135. data/lib/cells/formula/simple_formula_cell.rb +4 -0
  136. data/lib/cells/shared/shared_formula_builder.rb +15 -0
  137. data/lib/cells/shared/shared_formula_cell.rb +20 -0
  138. data/lib/cells/shared/sharing_formula_cell.rb +36 -0
  139. data/lib/cells/value/value_cell.rb +24 -0
  140. data/lib/excelfile/excelfile.rb +6 -0
  141. data/lib/excelfile/relationships.rb +24 -0
  142. data/lib/excelfile/shared_strings.rb +21 -0
  143. data/lib/excelfile/sheet_names.rb +6 -0
  144. data/lib/excelfile/table.rb +116 -0
  145. data/lib/excelfile/workbook.rb +108 -0
  146. data/lib/excelfile/worksheet.rb +122 -0
  147. data/lib/formulae/compile/formula_builder.rb +316 -0
  148. data/lib/formulae/formulae.rb +6 -0
  149. data/lib/formulae/parse/formula_peg.rb +213 -0
  150. data/lib/formulae/parse/formula_peg.txt +40 -0
  151. data/lib/formulae/run/excel_functions.rb +375 -0
  152. data/lib/formulae/run/excel_matrix.rb +114 -0
  153. data/lib/formulae/run/excel_range.rb +256 -0
  154. data/lib/formulae/run/reference.rb +79 -0
  155. data/lib/optimiser/dependency_builder.rb +86 -0
  156. data/lib/optimiser/optimiser.rb +3 -0
  157. data/lib/optimiser/shared_formula_dependency_builder.rb +43 -0
  158. data/lib/optimiser/workbook_pruner.rb +80 -0
  159. data/lib/rubyfromexcel.rb +105 -0
  160. data/lib/runtime/runtime_formula_builder.rb +32 -0
  161. data/spec/array_formula_builder_spec.rb +35 -0
  162. data/spec/array_formula_cell_spec.rb +17 -0
  163. data/spec/arraying_formula_cell_spec.rb +38 -0
  164. data/spec/dependency_builder_spec.rb +71 -0
  165. data/spec/excel_functions_spec.rb +381 -0
  166. data/spec/excel_matrix_spec.rb +92 -0
  167. data/spec/excel_range_spec.rb +161 -0
  168. data/spec/formula_builder_spec.rb +230 -0
  169. data/spec/formula_peg_spec.rb +165 -0
  170. data/spec/reference_spec.rb +72 -0
  171. data/spec/relationships_spec.rb +51 -0
  172. data/spec/runtime_formula_builder_spec.rb +55 -0
  173. data/spec/shared_formula_builder_spec.rb +29 -0
  174. data/spec/shared_formula_cell_spec.rb +23 -0
  175. data/spec/shared_formula_dependency_builder_spec.rb +48 -0
  176. data/spec/shared_strings_spec.rb +14 -0
  177. data/spec/sharing_formula_cell_spec.rb +79 -0
  178. data/spec/simple_formula_cell_spec.rb +78 -0
  179. data/spec/single_cell_array_formula_builder_spec.rb +19 -0
  180. data/spec/single_cell_array_formula_cell_spec.rb +25 -0
  181. data/spec/spec_helper.rb +2 -0
  182. data/spec/table_spec.rb +100 -0
  183. data/spec/value_cell_spec.rb +49 -0
  184. data/spec/workbook_pruner_spec.rb +27 -0
  185. data/spec/workbook_spec.rb +283 -0
  186. data/spec/worksheet_failiures_spec.rb +41 -0
  187. data/spec/worksheet_spec.rb +486 -0
  188. metadata +291 -0
@@ -0,0 +1,213 @@
1
+ require 'rubypeg'
2
+
3
+ class Formula < RubyPeg
4
+
5
+ def root
6
+ formula
7
+ end
8
+
9
+ def formula
10
+ node :formula do
11
+ optional { space } && one_or_more { expression }
12
+ end
13
+ end
14
+
15
+ def expression
16
+ string_join || arithmetic || comparison || thing
17
+ end
18
+
19
+ def thing
20
+ function || brackets || any_reference || string || percentage || number || boolean || prefix || named_reference
21
+ end
22
+
23
+ def argument
24
+ expression || null
25
+ end
26
+
27
+ def function
28
+ node :function do
29
+ terminal(/[A-Z]+/) && ignore { terminal("(") } && space && optional { argument } && any_number_of { (space && ignore { terminal(",") } && space && argument) } && space && ignore { terminal(")") }
30
+ end
31
+ end
32
+
33
+ def brackets
34
+ node :brackets do
35
+ ignore { terminal("(") } && space && one_or_more { expression } && space && ignore { terminal(")") }
36
+ end
37
+ end
38
+
39
+ def string_join
40
+ node :string_join do
41
+ thing && one_or_more { (space && ignore { terminal("&") } && space && thing) }
42
+ end
43
+ end
44
+
45
+ def arithmetic
46
+ node :arithmetic do
47
+ thing && one_or_more { (space && operator && space && thing) }
48
+ end
49
+ end
50
+
51
+ def comparison
52
+ node :comparison do
53
+ thing && space && comparator && space && thing
54
+ end
55
+ end
56
+
57
+ def comparator
58
+ node :comparator do
59
+ terminal(">=") || terminal("<=") || terminal("<>") || terminal(">") || terminal("<") || terminal("=")
60
+ end
61
+ end
62
+
63
+ def string
64
+ node :string do
65
+ ignore { terminal("\"") } && terminal(/[^"]*/) && ignore { terminal("\"") }
66
+ end
67
+ end
68
+
69
+ def any_reference
70
+ table_reference || local_table_reference || quoted_sheet_reference || sheet_reference || sheetless_reference
71
+ end
72
+
73
+ def percentage
74
+ node :percentage do
75
+ terminal(/[-+]?[0-9]+\.?[0-9]*/) && ignore { terminal("%") }
76
+ end
77
+ end
78
+
79
+ def number
80
+ node :number do
81
+ terminal(/[-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?/)
82
+ end
83
+ end
84
+
85
+ def operator
86
+ node :operator do
87
+ terminal("+") || terminal("-") || terminal("/") || terminal("*") || terminal("^")
88
+ end
89
+ end
90
+
91
+ def table_reference
92
+ node :table_reference do
93
+ table_name && ignore { terminal("[") } && (complex_structured_reference || simple_structured_reference) && ignore { terminal("]") }
94
+ end
95
+ end
96
+
97
+ def local_table_reference
98
+ node :local_table_reference do
99
+ ignore { terminal("[") } && (complex_structured_reference || overly_structured_reference || simple_structured_reference) && ignore { terminal("]") }
100
+ end
101
+ end
102
+
103
+ def table_name
104
+ terminal(/[.a-zA-Z0-9]+/)
105
+ end
106
+
107
+ def complex_structured_reference
108
+ terminal(/\[[^\u005d]*\],\[[^\u005d]*\]/)
109
+ end
110
+
111
+ def overly_structured_reference
112
+ ignore { terminal("[") } && simple_structured_reference && ignore { terminal("]") }
113
+ end
114
+
115
+ def simple_structured_reference
116
+ terminal(/[^\u005d]*/)
117
+ end
118
+
119
+ def named_reference
120
+ node :named_reference do
121
+ terminal(/[a-zA-Z][\w_.]+/)
122
+ end
123
+ end
124
+
125
+ def quoted_sheet_reference
126
+ node :quoted_sheet_reference do
127
+ single_quoted_string && ignore { terminal("!") } && (sheetless_reference || named_reference)
128
+ end
129
+ end
130
+
131
+ def sheet_reference
132
+ node :sheet_reference do
133
+ terminal(/[a-zA-Z0-9][\w_.]+/) && ignore { terminal("!") } && (sheetless_reference || named_reference)
134
+ end
135
+ end
136
+
137
+ def single_quoted_string
138
+ ignore { terminal("'") } && terminal(/[^']*/) && ignore { terminal("'") }
139
+ end
140
+
141
+ def sheetless_reference
142
+ column_range || row_range || area || cell
143
+ end
144
+
145
+ def column_range
146
+ node :column_range do
147
+ column && ignore { terminal(":") } && column
148
+ end
149
+ end
150
+
151
+ def row_range
152
+ node :row_range do
153
+ row && ignore { terminal(":") } && row
154
+ end
155
+ end
156
+
157
+ def area
158
+ node :area do
159
+ reference && ignore { terminal(":") } && reference
160
+ end
161
+ end
162
+
163
+ def cell
164
+ node :cell do
165
+ reference
166
+ end
167
+ end
168
+
169
+ def row
170
+ terminal(/\$?\d+/)
171
+ end
172
+
173
+ def column
174
+ terminal(/\$?[A-Z]+/)
175
+ end
176
+
177
+ def reference
178
+ terminal(/\$?[A-Z]+\$?[0-9]+/)
179
+ end
180
+
181
+ def boolean
182
+ boolean_true || boolean_false
183
+ end
184
+
185
+ def boolean_true
186
+ node :boolean_true do
187
+ ignore { terminal("TRUE") }
188
+ end
189
+ end
190
+
191
+ def boolean_false
192
+ node :boolean_false do
193
+ ignore { terminal("FALSE") }
194
+ end
195
+ end
196
+
197
+ def prefix
198
+ node :prefix do
199
+ terminal(/[-+]/) && thing
200
+ end
201
+ end
202
+
203
+ def space
204
+ ignore { terminal(/[ \n]*/) }
205
+ end
206
+
207
+ def null
208
+ node :null do
209
+ followed_by { terminal(",") }
210
+ end
211
+ end
212
+
213
+ end
@@ -0,0 +1,40 @@
1
+ formula := space? expression+
2
+ expression = string_join | arithmetic | comparison | thing
3
+ thing = function | brackets | any_reference | string | percentage | number | boolean | prefix | named_reference
4
+ argument = expression | null
5
+ function := /[A-Z]+/ `'(' space argument? (space `',' space argument)* space `')'
6
+ brackets := `'(' space expression+ space `')'
7
+ string_join := thing (space `"&" space thing)+
8
+ arithmetic := thing (space operator space thing)+
9
+ comparison := thing space comparator space thing
10
+ comparator := '>=' | '<=' | '<>' | '>' | '<' | '='
11
+ string := `'"' /[^"]*/ `'"'
12
+ any_reference = table_reference | local_table_reference | quoted_sheet_reference | sheet_reference | sheetless_reference
13
+ percentage := /[-+]?[0-9]+\.?[0-9]*/ `'%'
14
+ number := /[-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?/
15
+ operator := '+' | '-' | '/' | '*' | '^'
16
+ table_reference := table_name `'[' (complex_structured_reference | simple_structured_reference) `']'
17
+ local_table_reference := `'[' (complex_structured_reference | overly_structured_reference | simple_structured_reference) `']'
18
+ table_name = /[.a-zA-Z0-9]+/
19
+ complex_structured_reference = /\[[^\u005d]*\],\[[^\u005d]*\]/
20
+ overly_structured_reference = `'[' simple_structured_reference `']'
21
+ simple_structured_reference = /[^\u005d]*/
22
+ named_reference := /[a-zA-Z][\w_.]+/
23
+ quoted_sheet_reference := single_quoted_string `'!' (sheetless_reference | named_reference)
24
+ sheet_reference := /[a-zA-Z0-9][\w_.]+/ `'!' (sheetless_reference | named_reference)
25
+ single_quoted_string = `"'" /[^']*/ `"'"
26
+ sheetless_reference = column_range | row_range | area | cell
27
+ column_range := column `':' column
28
+ row_range := row `':' row
29
+ area := reference `':' reference
30
+ cell := reference
31
+ row = /\$?\d+/
32
+ column = /\$?[A-Z]+/
33
+ reference = /\$?[A-Z]+\$?[0-9]+/
34
+ boolean = boolean_true | boolean_false
35
+ boolean_true := `'TRUE'
36
+ boolean_false := `'FALSE'
37
+ prefix := /[-+]/ thing
38
+ space = `/[ \n]*/
39
+ null := &','
40
+
@@ -0,0 +1,375 @@
1
+ class Numeric
2
+ def number_like?; true; end
3
+ end
4
+
5
+ class String
6
+ def +@; number_like? ? self.to_f : self; end
7
+ def -@; number_like? ? -self.to_f : self; end
8
+ alias old_plus +
9
+ alias old_multiply *
10
+ def +(other) (other.number_like? && number_like?) ? self.to_f + other : self.old_plus(other) end
11
+ def -(other) (other.number_like? && number_like?) ? self.to_f - other : :value end
12
+ def *(other) (other.number_like? && number_like?) ? self.to_f * other : self.old_multiply(other) end
13
+ def /(other) (other.number_like? && number_like?) ? self.to_f / other : :value end
14
+ def coerce(other); number_like? ? self.to_f.coerce(other) : [other,:value]; end
15
+ def number_like?
16
+ self =~ /^\d+\.?\d*$/
17
+ end
18
+ end
19
+
20
+ class Symbol
21
+ def +@; self; end
22
+ def -@; self; end
23
+ def coerce(other)
24
+ return [other,self] if other.is_a?(Symbol)
25
+ [self,self]
26
+ end
27
+ def +(other) self end
28
+ def -(other) self end
29
+ def *(other) self end
30
+ def /(other) self end
31
+ def **(other) self end
32
+ def to_ruby(include_worksheet = false); self; end
33
+ end
34
+
35
+
36
+ class TrueClass
37
+ def +(other); 1.0 + other; end
38
+ def -(other); 1.0 - other; end
39
+ def *(other); 1.0 * other; end
40
+ def /(other); 1.0 / other; end
41
+ def coerce(other); 1.0.coerce(other); end
42
+ end
43
+
44
+ class FalseClass
45
+ def +(other); 0.0 + other; end
46
+ def -(other); 0.0 - other; end
47
+ def *(other); 0.0 * other; end
48
+ def /(other); 0.0 / other; end
49
+ def coerce(other); 0.0.coerce(other); end
50
+ end
51
+
52
+ module RubyFromExcel
53
+
54
+ module Empty
55
+ # Used to indicate empty cells
56
+ end
57
+
58
+ module ExcelFunctions
59
+
60
+ def result_cache
61
+ @result_cache ||= {}
62
+ end
63
+
64
+ def formula_cache
65
+ @formula_cache ||= {}
66
+ end
67
+
68
+ def recalculate
69
+ formula_cache.clear
70
+ result_cache.clear
71
+ end
72
+
73
+ def set(cell,value)
74
+ instance_variable_name = "@#{cell}"
75
+ unless instance_variable_defined?(instance_variable_name)
76
+ self.class.class_eval do
77
+ if method_defined?(cell)
78
+ alias_method "old_#{cell}", cell
79
+ define_method(cell) do
80
+ instance_variable_get(instance_variable_name) || self.send("old_#{cell}")
81
+ end
82
+ end
83
+ end
84
+ end
85
+ instance_variable_set(instance_variable_name, value)
86
+ end
87
+
88
+ def method_missing(method,*arguments, &block)
89
+ return super unless arguments.empty?
90
+ return super unless block == nil
91
+ return find_or_create_worksheet(method.to_s) if method.to_s =~ /sheet\d+/
92
+ return super unless method.to_s =~ /[a-z]+\d+/
93
+ 0.0.extend(Empty)
94
+ end
95
+
96
+ def find_or_create_worksheet(worksheet_name)
97
+ @worksheets ||= {variable_name => self}
98
+ return @worksheets[worksheet_name] if @worksheets.has_key?(worksheet_name)
99
+ worksheet = self.class.class_eval("#{worksheet_name.capitalize}.new")
100
+ worksheet.instance_variable_set("@worksheets",@worksheets)
101
+ worksheet.instance_variable_set("@formula_cache",formula_cache)
102
+ @worksheets[worksheet_name] = worksheet
103
+ worksheet
104
+ end
105
+
106
+ def sum(*args)
107
+ flatten_and_inject(args) do |counter,arg|
108
+ arg.is_a?(Numeric) ? counter + arg.to_f : counter
109
+ end
110
+ end
111
+
112
+ def sumif(check_range,criteria,sum_range = check_range)
113
+ sumifs(sum_range,check_range,criteria)
114
+ end
115
+
116
+ def sumifs(sum_range,*args)
117
+ return :na if iserr(sum_range)
118
+ return :na if args.any? { |c| iserr(c) }
119
+ if sum_range.is_a?(ExcelRange)
120
+ return :na unless sum_range.single_row_or_column?
121
+ sum_range = sum_range.to_a
122
+ else
123
+ sum_range = [sum_range]
124
+ end
125
+ checks = Hash[*args].to_a.map do |check|
126
+ check_range, check_value = check.first, check.last
127
+ if check_range.is_a?(ExcelRange)
128
+ return :na unless check_range.single_row_or_column?
129
+ check_range = check_range.to_a
130
+ else
131
+ check_range = [check_range]
132
+ end
133
+ check_range = check_range.map { |c| c.to_s.downcase }
134
+ check_value = check_value.to_s.downcase
135
+ [check_range,check_value]
136
+ end
137
+ accumulator = 0
138
+ sum_range.each_with_index do |potential_sum,i|
139
+ next unless checks.all? { |c| c.first[i] == c.last }
140
+ next unless potential_sum.is_a?(Numeric)
141
+ accumulator = accumulator + potential_sum
142
+ end
143
+ accumulator
144
+ end
145
+
146
+ def sumproduct(*ranges)
147
+ ranges.map do |range|
148
+ return :na unless range.respond_to?(:to_a)
149
+ range.to_a
150
+ end.transpose.map do |values|
151
+ values.inject(1) do |cell,total|
152
+ total * cell
153
+ end
154
+ end.inject(0) do |product,total|
155
+ total + product
156
+ end
157
+ end
158
+
159
+ def choose(choice,*choices)
160
+ return choice if iserr(choice)
161
+ choices[choice - 1]
162
+ end
163
+
164
+ def count(*args)
165
+ flatten_and_inject(args) do |counter,arg|
166
+ arg.is_a?(Numeric) && !arg.is_a?(Empty) ? counter + 1 : counter
167
+ end
168
+ end
169
+
170
+ def counta(*args)
171
+ flatten_and_inject(args) do |counter,arg|
172
+ arg.is_a?(Empty) ? counter : counter + 1
173
+ end
174
+ end
175
+
176
+ def max(*args)
177
+ args = args.map { |arg| arg.respond_to?(:to_a) ? arg.to_a : arg }
178
+ args.flatten!
179
+ args.delete_if { |arg| !arg.kind_of?(Numeric) }
180
+ args.delete_if { |arg| arg.kind_of?(Empty) }
181
+ return error if error = args.find { |arg| iserr(arg) }
182
+ args.max
183
+ end
184
+
185
+ def abs(value)
186
+ return value if iserr(value)
187
+ return :value unless value.respond_to?(:abs)
188
+ value.abs
189
+ end
190
+
191
+ def min(*args)
192
+ args = args.map { |arg| arg.respond_to?(:to_a) ? arg.to_a : arg }
193
+ args.flatten!
194
+ args.delete_if { |arg| !arg.kind_of?(Numeric) }
195
+ args.delete_if { |arg| arg.kind_of?(Empty) }
196
+ return error if error = args.find { |arg| iserr(arg) }
197
+ args.min
198
+ end
199
+
200
+ def flatten_and_inject(args,&block)
201
+ args = args.map do |arg|
202
+ return arg if iserr(arg)
203
+ arg.respond_to?(:to_a) ? arg.to_a : arg
204
+ end
205
+ args.flatten!
206
+ args.inject(0,&block)
207
+ end
208
+
209
+ def left(string,characters = 1)
210
+ return string if iserr(string)
211
+ return characters if iserr(characters)
212
+ string.slice(0,characters)
213
+ end
214
+
215
+ def find(string_to_find,string_to_search,start_index = 1)
216
+ return string_to_find if iserr(string_to_find)
217
+ return string_to_search if iserr(string_to_search)
218
+ return start_index if iserr(start_index)
219
+ result = string_to_search.index(string_to_find,start_index - 1 )
220
+ result ? result + 1 : :value
221
+ end
222
+
223
+ def average(*args)
224
+ total = sum(*args)
225
+ number = count(*args)
226
+ return total if iserr(total)
227
+ return number if iserr(number)
228
+ total / number
229
+ end
230
+
231
+ def subtotal(type,*args)
232
+ case type
233
+ when 1.0, 101.0; average(*args)
234
+ when 2.0, 102.0; count(*args)
235
+ when 3.0, 103.0; counta(*args)
236
+ when 9.0, 109.0; sum(*args)
237
+ else
238
+ raise Exception.new("subtotal type #{type} on #{args} not implemented")
239
+ end
240
+ end
241
+
242
+ def match(lookup_value,lookup_array,match_type = 1.0)
243
+ formula_cache[[:match,lookup_value,lookup_array,match_type]] ||= calculate_match(lookup_value,lookup_array,match_type)
244
+ end
245
+
246
+ def calculate_match(lookup_value,lookup_array,match_type = 1.0)
247
+ return lookup_value if iserr(lookup_value)
248
+ return lookup_array if iserr(lookup_array)
249
+ return match_type if iserr(match_type)
250
+ lookup_value = lookup_value.downcase if lookup_value.respond_to?(:downcase)
251
+ case match_type
252
+ when 0, 0.0, false
253
+ lookup_array.each_with_index do |item,index|
254
+ item = item.downcase if item.respond_to?(:downcase)
255
+ return index+1 if lookup_value == item
256
+ end
257
+ return :na
258
+ when 1, 1.0, true
259
+ lookup_array.each_with_index do |item, index|
260
+ next if lookup_value.is_a?(String) && !item.is_a?(String)
261
+ next if lookup_value.is_a?(Numeric) && !item.is_a?(Numeric)
262
+ item = item.downcase if item.respond_to?(:downcase)
263
+ if item > lookup_value
264
+ return :na if index == 0
265
+ return index
266
+ end
267
+ end
268
+ return lookup_array.to_a.size
269
+ when -1, -1.0
270
+ lookup_array.each_with_index do |item, index|
271
+ next if lookup_value.is_a?(String) && !item.is_a?(String)
272
+ next if lookup_value.is_a?(Numeric) && !item.is_a?(Numeric)
273
+ item = item.downcase if item.respond_to?(:downcase)
274
+ if item < lookup_value
275
+ return :na if index == 0
276
+ return index
277
+ end
278
+ end
279
+ return lookup_array.to_a.size - 1
280
+ end
281
+ return :na
282
+ end
283
+
284
+ def index(area,row_number, column_number = nil)
285
+ formula_cache[[:index,area,row_number,column_number]] ||= calculate_index_formula(area,row_number, column_number)
286
+ end
287
+
288
+ def calculate_index_formula(area,row_number, column_number = nil, method = :index)
289
+ return area if iserr(area)
290
+ return row_number if iserr(row_number)
291
+ if area.single_row?
292
+ return area.send(method,row_number,column_number) if column_number
293
+ return area.send(method,1,row_number)
294
+ elsif area.single_column?
295
+ return area.send(method,row_number,column_number) if column_number
296
+ return area.send(method,row_number,1)
297
+ else
298
+ return :ref unless row_number && column_number
299
+ return area.column(column_number-1) if row_number == 0.0
300
+ return area.row(row_number-1) if column_number == 0.0
301
+ return area.send(method,row_number,column_number)
302
+ end
303
+ end
304
+
305
+ def na(*arg)
306
+ :na
307
+ end
308
+
309
+ def ref(*arg)
310
+ :ref
311
+ end
312
+
313
+ def iserr(arg)
314
+ arg.is_a? Symbol
315
+ end
316
+
317
+ def excel_if(condition,true_value,false_value)
318
+ condition ? true_value : false_value
319
+ end
320
+
321
+ def iferror(value,value_if_error)
322
+ iserr(value) ? value_if_error : value
323
+ rescue ZeroDivisionError => e
324
+ puts e
325
+ return :div0
326
+ end
327
+
328
+ def excel_and(*args)
329
+ args.all? {|a| a == true }
330
+ end
331
+
332
+ def excel_or(*args)
333
+ args.any? { |a| a == true }
334
+ end
335
+
336
+ def a(start_cell,end_cell)
337
+ result_cache[[:a,start_cell,end_cell]] ||= Area.new(self,start_cell,end_cell)
338
+ end
339
+
340
+ def r(start_row,end_row)
341
+ result_cache[[:r,start_row,end_row]] ||= Rows.new(self,start_row,end_row)
342
+ end
343
+
344
+ def c(start_column_number,end_column_number)
345
+ result_cache[[:r,start_column_number,end_column_number]] ||= Columns.new(self,start_column_number,end_column_number)
346
+ end
347
+
348
+ def s(full_name_of_worksheet)
349
+ full_name_of_worksheet = $1 if full_name_of_worksheet.to_s =~ /^(\d+)\.0+$/
350
+ self.send(@worksheet_names[full_name_of_worksheet])
351
+ end
352
+
353
+ def t(table_name)
354
+ table = @workbook_tables[table_name]
355
+ return :ref unless table
356
+ return table if table.is_a?(Table)
357
+ @workbook_tables[table_name] = eval(table)
358
+ end
359
+
360
+ def m(*potential_excel_matrices,&block)
361
+ ExcelMatrixCollection.new(*potential_excel_matrices).matrix_map(&block)
362
+ end
363
+
364
+ def variable_name
365
+ @variable_name ||= self.class.to_s.downcase
366
+ end
367
+
368
+ def indirect(reference_text,refering_cell = nil)
369
+ parsed_reference = formula_cache[[:indirect,reference_text]] ||= Formula.parse(reference_text)
370
+ reference = parsed_reference.visit(RuntimeFormulaBuilder.new(self,refering_cell && Reference.new(refering_cell)))
371
+ formula_cache[[:indirect_result,reference]] ||= eval(reference)
372
+ end
373
+
374
+ end
375
+ end