rubyfromexcel 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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